Merge remote branch 'common/android-2.6.35' into android-msm-2.6.35
diff --git a/Documentation/android.txt b/Documentation/android.txt
index 72a62af..e1e4188 100644
--- a/Documentation/android.txt
+++ b/Documentation/android.txt
@@ -14,6 +14,12 @@
1.3 Recommended enabled config options
2. Contact
+0. Getting sources:
+-----------------
+
+git clone --reference /path/to/linux-git/for/speedup/ git://android.git.kernel.org/kernel/msm.git
+git checkout -b android-msm-2.6.29 origin/android-msm-2.6.29
+
1. Android
==========
@@ -26,6 +32,7 @@
which can be found at http://android.git.kernel.org in kernel/common.git
and kernel/msm.git
+msm_defconfig should work on qualcomm reference design, HTC Magic and G1/ADP1.
1.1 Required enabled config options
-----------------------------------
@@ -114,6 +121,23 @@
SERIAL_CORE_CONSOLE
+Board code names
+----------------
+
+board-halibut - Qualcomm SURF 7201A
+board-sapphire - HTC Magic
+board-trout - HTC Dream / T-Mobile G1 / Android ADP1
+
+Booting your kernel
+-------------------
+
+hold down camera and red button to boot into rainbow screen. Then
+
+./fastboot boot linux-msm/arch/arm/boot/zImage ramdisk.img
+
+Machine will freeze at rainbow screen for a while, be
+patient. ramdisk.img is required.
+
2. Contact
==========
website: http://android.git.kernel.org
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4824fb4f..ce16900 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -584,7 +584,10 @@
config ARCH_MSM
bool "Qualcomm MSM"
+ select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
select HAVE_CLK
+ select GENERIC_GPIO
select GENERIC_CLOCKEVENTS
help
Support for Qualcomm MSM/QSD based systems. This runs on the
@@ -1302,6 +1305,13 @@
Enable hardware performance counter support for perf events. If
disabled, perf events will use software events only.
+config VMALLOC_RESERVE
+ hex "Reserved vmalloc space"
+ default 0x08000000
+ depends on MMU
+ help
+ Reserved vmalloc space if not specified on the kernel commandline.
+
source "mm/Kconfig"
config LEDS
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 95327b4..1659f471 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -21,7 +21,7 @@
#if defined(CONFIG_DEBUG_ICEDCC)
-#ifdef defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V7)
+#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V7)
.macro loadsp, rb, tmp
.endm
.macro writeb, ch, rb
diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig
new file mode 100644
index 0000000..9da5f98
--- /dev/null
+++ b/arch/arm/configs/mahimahi_defconfig
@@ -0,0 +1,291 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=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_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_QSD8X50=y
+CONFIG_MSM_DEBUG_UART1=y
+CONFIG_HTC_35MM_JACK=y
+# CONFIG_HTC_PWRSINK is not set
+CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000
+CONFIG_MSM_SERIAL_DEBUGGER=y
+CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y
+# CONFIG_MSM_HW3D is not set
+CONFIG_WIFI_CONTROL_FUNC=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x30000000
+CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM=y
+CONFIG_WAKELOCK=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# 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_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=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_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_LED=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=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_ADDRTYPE=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_LOG=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_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=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_L2CAP=y
+CONFIG_BT_SCO=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_BT_HCIUART_LL=y
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_PM is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_UID_STAT=y
+CONFIG_SENSORS_AKM8973=y
+CONFIG_VP_A1026=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_IFB=y
+CONFIG_DUMMY=y
+CONFIG_BCM4329=m
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_CAPELLA_CM3602=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_SERIAL_BCM_BT_LPM=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_W1_MASTER_DS2482=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_BATTERY_DS2784=y
+# CONFIG_HWMON is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DEBUG=y
+CONFIG_REGULATOR_TPS65023=y
+CONFIG_MEDIA_SUPPORT=y
+# CONFIG_IR_CORE is not set
+CONFIG_MSM_CAMERA=y
+CONFIG_S5K3E2FX=y
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+# CONFIG_FB_MSM_MDDI is not set
+CONFIG_GPU_MSM_KGSL=y
+CONFIG_MSM_KGSL_MMU=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_MSM_72K=y
+CONFIG_USB_ANDROID=y
+CONFIG_USB_ANDROID_ADB=y
+CONFIG_USB_ANDROID_DIAG=y
+CONFIG_USB_ANDROID_MASS_STORAGE=y
+CONFIG_USB_ANDROID_RNDIS=y
+CONFIG_USB_ANDROID_RNDIS_WCEIS=y
+CONFIG_USB_ANDROID_ACCESSORY=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_MSM7X00A=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_SLEEP=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_INOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_YAFFS_FS=y
+CONFIG_YAFFS_DISABLE_TAGS_ECC=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_ARM_UNWIND is not set
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig
old mode 100644
new mode 100755
index 2b8f7af..6eb7696
--- a/arch/arm/configs/msm_defconfig
+++ b/arch/arm/configs/msm_defconfig
@@ -1,72 +1,267 @@
CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=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_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_IOSCHED_DEADLINE is not set
-# CONFIG_IOSCHED_CFQ is not set
CONFIG_ARCH_MSM=y
-CONFIG_MACH_HALIBUT=y
+CONFIG_HTC_HEADSET=y
+CONFIG_MSM_SERIAL_DEBUGGER=y
+CONFIG_WIFI_CONTROL_FUNC=y
+CONFIG_WIFI_MEM_PREALLOC=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_PM=y
+CONFIG_WAKELOCK=y
CONFIG_NET=y
+CONFIG_PACKET=y
CONFIG_UNIX=y
+CONFIG_NET_KEY=y
CONFIG_INET=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+CONFIG_INET_ESP=y
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 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_NETFILTER_DEBUG=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=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_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_LED=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=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_ADDRTYPE=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_LOG=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_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=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_L2CAP=y
+CONFIG_BT_SCO=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_BT_HCIUART_LL=y
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_PM is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
CONFIG_MTD=y
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_UID_STAT=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
CONFIG_NETDEVICES=y
+CONFIG_IFB=y
CONFIG_DUMMY=y
+CONFIG_TUN=y
CONFIG_NET_ETHERNET=y
CONFIG_SMC91X=y
CONFIG_PPP=y
CONFIG_PPP_ASYNC=y
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_BSDCOMP=y
-# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_MSM_RMNET_DEBUG=y
+# CONFIG_INPUT_MOUSEDEV is not set
CONFIG_INPUT_EVDEV=y
-# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ELAN_I2C_8232=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
# CONFIG_SERIO is not set
-CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_VT is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM=y
-CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_RX_WAKEUP=y
+CONFIG_SERIAL_MSM_HS=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
+CONFIG_POWER_SUPPLY=y
# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_VIDEO_ALLOW_V4L1 is not set
+CONFIG_MSM_CAMERA=y
+CONFIG_MT9T013=y
+CONFIG_DAB=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
-CONFIG_FB_MODE_HELPERS=y
-CONFIG_FB_TILEBLITTING=y
-CONFIG_FB_MSM=y
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_MSM_72K=y
+CONFIG_USB_ANDROID=y
+CONFIG_USB_ANDROID_ADB=y
+CONFIG_USB_ANDROID_MASS_STORAGE=y
+CONFIG_USB_ANDROID_RNDIS=y
+CONFIG_USB_ANDROID_RNDIS_WCEIS=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_MSM7X00A=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_SLEEP=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
CONFIG_INOTIFY=y
+CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
+CONFIG_YAFFS_FS=y
+CONFIG_YAFFS_DISABLE_TAGS_ECC=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_SCHEDSTATS=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
-CONFIG_DEBUG_LL=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig
new file mode 100644
index 0000000..4538e22
--- /dev/null
+++ b/arch/arm/configs/surf7x30_defconfig
@@ -0,0 +1,210 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=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_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM7X30=y
+CONFIG_MSM_DEBUG_UART1=y
+# CONFIG_HTC_PWRSPLY is not set
+# CONFIG_HTC_PWRSINK is not set
+CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y
+CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y
+CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_HW3D is not set
+CONFIG_WIFI_CONTROL_FUNC=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x30000000
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyMSM,115200n8"
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM=y
+CONFIG_WAKELOCK=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG 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_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=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_BT_HCIUART_LL=y
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_PM is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_KERNEL_DEBUGGER_CORE=y
+CONFIG_UID_STAT=y
+CONFIG_APANIC=y
+CONFIG_APANIC_PLABEL="crashdata"
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMC91X=y
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_MSM=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_CAPELLA_CM3602=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_W1=y
+CONFIG_W1_MASTER_DS2482=y
+CONFIG_POWER_SUPPLY=y
+# CONFIG_HWMON is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DEBUG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_VIDEO_ALLOW_V4L1 is not set
+CONFIG_DAB=y
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_MSM_72K=y
+CONFIG_USB_ANDROID=y
+CONFIG_USB_ANDROID_ACM=y
+CONFIG_USB_ANDROID_ADB=y
+CONFIG_USB_ANDROID_DIAG=y
+CONFIG_USB_ANDROID_MASS_STORAGE=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_SLEEP=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_INOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_YAFFS_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_HIGHMEM=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/swordfish_defconfig b/arch/arm/configs/swordfish_defconfig
new file mode 100644
index 0000000..38fc1f1d
--- /dev/null
+++ b/arch/arm/configs/swordfish_defconfig
@@ -0,0 +1,167 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_QSD8X50=y
+CONFIG_MSM_DEBUG_UART3=y
+# CONFIG_HTC_PWRSINK is not set
+CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y
+CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y
+# CONFIG_MSM_IDLE_STATS is not set
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_HW3D is not set
+# CONFIG_MSM_ADSP is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_PM=y
+CONFIG_WAKELOCK=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=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_BT_HCIUART_LL=y
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_PM is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_KERNEL_DEBUGGER_CORE=y
+CONFIG_UID_STAT=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMC91X=y
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_MSM=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_POWER_SUPPLY=y
+# CONFIG_HWMON is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_GPU_MSM_KGSL=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_MSM7X00A=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_SLEEP=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+# CONFIG_RTC_DRV_MSM7X00A is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_INOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_YAFFS_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_DEBUG_LL=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h
index cc7ef40..5fc2fdb 100644
--- a/arch/arm/include/asm/domain.h
+++ b/arch/arm/include/asm/domain.h
@@ -27,8 +27,13 @@
*
* 36-bit addressing and supersections are only available on
* CPUs based on ARMv6+ or the Intel XSC3 core.
+ *
+ * We cannot use domain 0 for the kernel on QSD8x50 since the kernel domain
+ * is set to manager mode when set_fs(KERNEL_DS) is called. Setting domain 0
+ * to manager mode will disable the workaround for a cpu bug that can cause an
+ * invalid fault status and/or tlb corruption (CONFIG_VERIFY_PERMISSION_FAULT).
*/
-#ifndef CONFIG_IO_36
+#if !defined(CONFIG_IO_36) && !defined(CONFIG_VERIFY_PERMISSION_FAULT)
#define DOMAIN_KERNEL 0
#define DOMAIN_TABLE 0
#define DOMAIN_USER 1
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
new file mode 100644
index 0000000..f8d391a
--- /dev/null
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -0,0 +1,27 @@
+/*
+ * arch/arm/include/asm/mach/mmc.h
+ */
+#ifndef ASMARM_MACH_MMC_H
+#define ASMARM_MACH_MMC_H
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+};
+
+struct mmc_platform_data {
+ unsigned int ocr_mask; /* available voltages */
+ int built_in; /* built-in device flag */
+ u32 (*translate_vdd)(struct device *, unsigned int);
+ unsigned int (*status)(struct device *);
+ struct embedded_sdio_data *embedded_sdio;
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
+};
+
+#endif
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index ab68cf1..bd5a47b 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -21,6 +21,7 @@
#include <asm/memory.h>
#include <mach/vmalloc.h>
+#include <mach/memory.h>
#include <asm/pgtable-hwdef.h>
/*
@@ -314,6 +315,8 @@
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
#define pgprot_writecombine(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)
+#define pgprot_device(prot) \
+ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_NONSHARED)
#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
#define pgprot_dmacoherent(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK|L_PTE_EXEC, L_PTE_MT_BUFFERABLE)
@@ -451,8 +454,17 @@
* remap a physical page `pfn' of size `size' with page protection `prot'
* into virtual address `from'
*/
+
+
+#ifndef HAS_ARCH_IO_REMAP_PFN_RANGE
#define io_remap_pfn_range(vma,from,pfn,size,prot) \
- remap_pfn_range(vma, from, pfn, size, prot)
+ remap_pfn_range(vma,from,pfn,size,prot)
+#else
+extern int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
+#define io_remap_pfn_range(vma,from,pfn,size,prot) \
+ arch_io_remap_pfn_range(vma,from,pfn,size,prot)
+#endif
+
#define pgtable_cache_init() do { } while (0)
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 763e29f..f6771bf 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -75,7 +75,7 @@
.flags = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
- .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
+ .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT), \
.restart_block = { \
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index eb62bf9..49dbcd3 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -172,9 +172,9 @@
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
- mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
+ mov r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
- domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
+ domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 47264a7..041d977 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -15,17 +15,19 @@
bool "MSM7x30"
select ARCH_MSM_SCORPION
select MSM_SMD
- select MSM_VIC
select CPU_V7
select MSM_REMOTE_SPINLOCK_DEKKERS
+ select VERIFY_PERMISSION_FAULT
+ select MSM_DAL
config ARCH_QSD8X50
bool "QSD8X50"
select ARCH_MSM_SCORPION
select MSM_SMD
- select MSM_VIC
select CPU_V7
select MSM_REMOTE_SPINLOCK_LDREX
+ select MSM_DAL
+ select VERIFY_PERMISSION_FAULT
endchoice
config MSM_SOC_REV_A
@@ -36,14 +38,74 @@
config ARCH_MSM_SCORPION
bool
-config MSM_VIC
+config MSM_MDP22
bool
+ depends on ARCH_MSM7X00A
+ default y
+
+config MSM_MDP31
+ bool
+ depends on ARCH_QSD8X50
+ default y
+
+config MSM_MDP40
+ bool
+ depends on ARCH_MSM7X30
+ default y
+
+config MSM_REMOTE_SPINLOCK_DEKKERS
+ bool
+
+config MSM_REMOTE_SPINLOCK_SWP
+ bool
+
+config MSM_REMOTE_SPINLOCK_LDREX
+ bool
+
+config MSM_REMOTE_SPINLOCK
+ bool
+ depends on MSM_REMOTE_SPINLOCK_LDREX || MSM_REMOTE_SPINLOCK_SWP || \
+ MSM_REMOTE_SPINLOCK_DEKKERS
+ default y
+
+config MSM_LEGACY_7X00A_AMSS
+ bool
+
+config MSM_AMSS_VERSION
+ int
+ default 6210 if MSM_AMSS_VERSION_6210
+ default 6220 if MSM_AMSS_VERSION_6220
+ default 6225 if MSM_AMSS_VERSION_6225
+ default 6350 if MSM_AMSS_VERSION_6350
+
+choice
+ prompt "AMSS modem firmware version"
+
+ depends on ARCH_MSM7X00A
+ default MSM_AMSS_VERSION_6225
+
+ config MSM_AMSS_VERSION_6210
+ bool "6.2.10"
+ select MSM_LEGACY_7X00A_AMSS
+
+ config MSM_AMSS_VERSION_6220
+ bool "6.2.20"
+ select MSM_LEGACY_7X00A_AMSS
+
+ config MSM_AMSS_VERSION_6225
+ bool "6.2.20 + New ADSP"
+ select MSM_LEGACY_7X00A_AMSS
+
+ config MSM_AMSS_VERSION_6350
+ bool "6.3.50"
+endchoice
menu "Qualcomm MSM Board Type"
config MACH_HALIBUT
depends on ARCH_MSM
depends on ARCH_MSM7X00A
+ default n
bool "Halibut Board (QCT SURF7201A)"
help
Support for the Qualcomm SURF7201A eval board.
@@ -51,6 +113,7 @@
config MACH_TROUT
depends on ARCH_MSM
depends on ARCH_MSM7X00A
+ default n
bool "HTC Dream (aka trout)"
help
Support for the HTC Dream, T-Mobile G1, Android ADP1 devices.
@@ -61,6 +124,13 @@
help
Support for the Qualcomm MSM7x30 SURF eval board.
+config MACH_SWORDFISH
+ depends on ARCH_QSD8X50
+ default y
+ bool "Swordfish Board (QCT SURF8250)"
+ help
+ Support for the Qualcomm SURF8250 eval board.
+
config MACH_QSD8X50_SURF
depends on ARCH_QSD8X50
bool "QSD8x50 SURF"
@@ -103,7 +173,354 @@
config MSM_SMD_PKG3
bool
+config MACH_SAPPHIRE
+ depends on ARCH_MSM7X00A && !MACH_TROUT && !MACH_HALIBUT
+ default y
+ bool "Sapphire"
+
+config MACH_MAHIMAHI
+ depends on ARCH_QSD8X50
+ default y
+ bool "Mahi-Mahi"
+ help
+ Select this to support the Mahi-Mahi device
+
+config MACH_QSD8X50_FFA
+ depends on ARCH_QSD8X50
+ default y
+ bool "8x50-ffa"
+ help
+ Select this to support the 8x50 ffa device
+
+config MACH_MSM7X30_SURF
+ depends on ARCH_MSM7X30
+ default y
+ bool "QCT SURF7x30 Board"
+ help
+ Select this to support the Qualcomm SURF7X30 development board
+
+config HTC_HEADSET
+ tristate "HTC 2 Wire detection driver"
+ default n
+ help
+ Provides support for detecting HTC 2 wire devices, such as wired
+ headset, on the trout platform. Can be used with the msm serial
+ debugger, but not with serial console.
+
+config HTC_35MM_JACK
+ bool "HTC 3.5mm headset jack"
+ default n
+ help
+ Provides support for 3.5mm headset jack devices, like wired headsets.
+
+config TROUT_BATTCHG
+ depends on (MACH_TROUT || MACH_SAPPHIRE) && POWER_SUPPLY
+ default y
+ bool "Trout battery / charger driver"
+
+config HTC_PWRSPLY
+ depends on MSM_ONCRPCROUTER && POWER_SUPPLY && !TROUT_BATTCHG
+ default y
+ bool "HTC Power supply driver"
+ help
+ Used by HTC devices with a dedicated battery gauge"
+
+config HTC_PWRSINK
+ depends on MSM_SMD
+ default y
+ bool "HTC Power Sink Driver"
+
+config CACHE_FLUSH_RANGE_LIMIT
+ hex "Cache flush range limit"
+ default 0x40000
+ help
+ When flushing a cache range larger then this (hex) limit, flush the
+ entire cache instead. Flushing a large range can be slower than
+ flushing, then refilling, the entire cache.
+
+choice
+ prompt "Default Timer"
+ default MSM7X00A_USE_GP_TIMER
+
+ config MSM7X00A_USE_GP_TIMER
+ bool "GP Timer"
+ help
+ Low resolution timer that allows power collapse from idle.
+
+ config MSM7X00A_USE_DG_TIMER
+ bool "DG Timer"
+ help
+ High resolution timer.
+endchoice
+
+choice
+ prompt "Suspend sleep mode"
+ default MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND
+ help
+ Allows overriding the sleep mode used. Leave at power
+ collapse suspend unless the arm9 image has problems.
+
+ config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND
+ bool "Power collapse suspend"
+ help
+ Lowest sleep state. Returns through reset vector.
+
+ config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE
+ bool "Power collapse"
+ help
+ Sleep state that returns through reset vector.
+
+ config MSM7X00A_SLEEP_MODE_APPS_SLEEP
+ bool "Apps Sleep"
+
+ config MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT
+ bool "Ramp down cpu clock and wait for interrupt"
+
+ config MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT
+ bool "Wait for interrupt"
+endchoice
+
+config MSM7X00A_SLEEP_MODE
+ int
+ default 0 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND
+ default 1 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE
+ default 2 if MSM7X00A_SLEEP_MODE_APPS_SLEEP
+ default 3 if MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT
+ default 4 if MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT
+
+choice
+ prompt "Idle sleep mode"
+ default MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE
+ help
+ Allows overriding the sleep mode used from idle. Leave at power
+ collapse suspend unless the arm9 image has problems.
+
+ config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND
+ bool "Power collapse suspend"
+ help
+ Lowest sleep state. Returns through reset vector.
+
+ config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE
+ bool "Power collapse"
+ help
+ Sleep state that returns through reset vector.
+
+ config MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP
+ bool "Apps Sleep"
+
+ config MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT
+ bool "Ramp down cpu clock and wait for interrupt"
+
+ config MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT
+ bool "Wait for interrupt"
+endchoice
+
+config MSM7X00A_IDLE_SLEEP_MODE
+ int
+ default 0 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND
+ default 1 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE
+ default 2 if MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP
+ default 3 if MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT
+ default 4 if MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT
+
+config MSM7X00A_IDLE_SLEEP_MIN_TIME
+ int "Minimum idle time before sleep"
+ default 20000000
+ help
+ Minimum idle time in nanoseconds before entering low power mode.
+
+config MSM7X00A_IDLE_SPIN_TIME
+ int "Idle spin time before cpu ramp down"
+ default 80000
+ help
+ Spin time in nanoseconds before ramping down cpu clock and entering
+ any low power state.
+
+menuconfig MSM_IDLE_STATS
+ bool "Collect idle statistics"
+ default y
+ help
+ Collect idle statistics and export them in proc/msm_pm_stats.
+
+if MSM_IDLE_STATS
+
+config MSM_IDLE_STATS_FIRST_BUCKET
+ int "First bucket time"
+ default 62500
+ help
+ Upper time limit in nanosconds of first bucket.
+
+config MSM_IDLE_STATS_BUCKET_SHIFT
+ int "Bucket shift"
+ default 2
+
+config MSM_IDLE_STATS_BUCKET_COUNT
+ int "Bucket count"
+ default 10
+
+endif # MSM_IDLE_STATS
+
+config MSM_FIQ_SUPPORT
+ default y
+ bool "Enable installation of an FIQ handler."
+
+config MSM_SERIAL_DEBUGGER
+ select MSM_FIQ_SUPPORT
+ select KERNEL_DEBUGGER_CORE
+ default n
+ bool "FIQ Mode Serial Debugger"
+ help
+ The FIQ serial debugger can accept commands even when the
+ kernel is unresponsive due to being stuck with interrupts
+ disabled. Depends on the kernel debugger core in drivers/misc.
+
+config MSM_SERIAL_DEBUGGER_NO_SLEEP
+ depends on MSM_SERIAL_DEBUGGER
+ default n
+ bool "Keep serial debugger active"
+ help
+ Enables the serial debugger at boot. Passing
+ msm_serial_debugger.no_sleep on the kernel commandline will
+ override this config option.
+
+config MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+ depends on MSM_SERIAL_DEBUGGER
+ default n
+ bool "Don't disable wakeup IRQ when debugger is active"
+ help
+ Don't disable the wakeup irq when enabling the uart clock. This will
+ cause extra interrupts, but it makes the serial debugger usable with
+ radio builds that ignore the uart clock request in power collapse.
+
+config MSM_SERIAL_DEBUGGER_CONSOLE
+ depends on MSM_SERIAL_DEBUGGER
+ default n
+ bool "Console on FIQ Serial Debugger port"
+ help
+ Enables a console so that printk messages are displayed on
+ the debugger serial port as the occur.
+
config MSM_SMD
bool
+config MSM_DAL
+ default n
+ bool "MSM Driver Access Layer (DAL RPC)"
+ help
+ Support for the DAL RPC interface used to communicate with
+ the baseband processor or DSP in newer Qualcomm MSM/QSD
+ chips.
+
+config MSM_ONCRPCROUTER
+ depends on MSM_SMD
+ default y
+ bool "MSM ONCRPC router support"
+ help
+ Support for the MSM ONCRPC router for communication between
+ the ARM9 and ARM11
+
+config MSM_RPCSERVERS
+ depends on MSM_ONCRPCROUTER && ARCH_MSM7X00A
+ default y
+ bool "Kernel side RPC server bundle"
+ help
+ none
+
+config MSM_CPU_FREQ_SCREEN
+ bool
+ default n
+ depends on HAS_EARLYSUSPEND
+ help
+ Simple cpufreq scaling based on screen ON/OFF.
+
+if MSM_CPU_FREQ_SCREEN
+
+config MSM_CPU_FREQ_SCREEN_OFF
+ int "Screen off cpu frequency"
+ default 245760
+
+config MSM_CPU_FREQ_SCREEN_ON
+ int "Screen on cpu frequency"
+ default 384000
+
+endif # MSM_CPU_FREQ_SCREEN
+
+config MSM_HW3D
+ tristate "MSM Hardware 3D Register Driver"
+ depends on EARLYSUSPEND
+ default y
+ help
+ Provides access to registers needed by the userspace OpenGL|ES
+ library.
+
+config MSM_ADSP
+ depends on ARCH_MSM7X00A
+ tristate "MSM ADSP driver"
+ default y
+ help
+ Provides access to registers needed by the userspace aDSP library.
+
+config MSM_ADSP_REPORT_EVENTS
+ bool "Report modem events from the DSP"
+ default y
+ depends on MSM_ADSP
+ help
+ Normally, only messages from the aDSP are reported to userspace.
+ With this option, we report events from the aDSP as well.
+
+config MSM_QDSP6
+ tristate "QDSP6 support"
+ depends on ARCH_QSD8X50
+ default y
+ help
+ Enable support for qdsp6. This provides audio and video functionality.
+
+config MSM_QDSP5V2
+ tristate "QDSP5V2 support"
+ depends on ARCH_MSM7X30
+ default y
+ help
+ Enable support for qdsp5v2, which provides audio processing on 7x30.
+
+config MSM_SSBI
+ tristate "SSBI support"
+ depends on ARCH_MSM7X30
+ default n
+ help
+ Enable support for SSBI bus. This is required for communicatinig with
+ Qualcomm PMICs and Audio codecs.
+
+config WIFI_CONTROL_FUNC
+ bool "Enable WiFi control function abstraction"
+ help
+ Enables Power/Reset/Carddetect function abstraction
+
+config WIFI_MEM_PREALLOC
+ depends on WIFI_CONTROL_FUNC
+ bool "Preallocate memory for WiFi buffers"
+ help
+ Preallocates memory buffers for WiFi driver
+
+config VIRTUAL_KPANIC_PARTITION
+ bool "Create virtual kpanic partition"
+ default n
+ help
+ Creates a virtual mtd partition named 'kpanic', stealing space from
+ the specified mtd partition label.
+ *** DO NOT USE IF YOU ARE USING OTA/RECOVERY ***
+
+config VIRTUAL_KPANIC_PSIZE
+ depends on VIRTUAL_KPANIC_PARTITION
+ int "Default kpanic partition size"
+ default 1048576
+ help
+ Sets the size of the virtual kpanic paritition to create.
+
+config VIRTUAL_KPANIC_SRC
+ depends on VIRTUAL_KPANIC_PARTITION
+ string "Partition to steal from"
+ default "cache"
+ help
+ Sets the partition to steal from to make the virtual one.
+
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 66677f0..e52f964 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,22 +1,74 @@
obj-y += proc_comm.o
-obj-y += io.o idle.o timer.o dma.o
+obj-y += io.o irq.o timer.o dma.o memory.o
obj-y += vreg.o
-obj-y += acpuclock-arm11.o
+obj-y += pmic.o
+obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o
+obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o
+obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o
+obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o
+obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o
obj-y += clock.o clock-pcom.o
-obj-y += gpio.o
-
-ifdef CONFIG_MSM_VIC
-obj-y += irq-vic.o
-else
-obj-y += irq.o
-endif
+obj-y += gpio.o generic_gpio.o
+obj-y += nand_partitions.o
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
+obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
+obj-$(CONFIG_MSM_SMD) += smd_tty.o smd_qmi.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
+obj-$(CONFIG_MSM_DAL) += dal.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o
+obj-$(CONFIG_MSM_ADSP) += qdsp5/
+obj-$(CONFIG_MSM_QDSP5V2) += qdsp5v2/
+obj-$(CONFIG_MSM_QDSP6) += qdsp6/
+obj-$(CONFIG_MSM_HW3D) += hw3d.o
+obj-$(CONFIG_PM) += pm.o
+obj-$(CONFIG_CPU_FREQ) += cpufreq.o
+obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o
+obj-$(CONFIG_MSM_SSBI) += ssbi.o
obj-$(CONFIG_MACH_TROUT) += board-trout.o devices-msm7x00.o
+obj-$(CONFIG_MACH_TROUT) += board-trout-gpio.o
+obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o board-trout-panel.o
+obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o
+obj-$(CONFIG_MACH_TROUT) += board-trout-mmc.o
+obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o
+obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o
+obj-$(CONFIG_MACH_TROUT) += devices_htc.o
+obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o
+obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
+obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
+obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o
+obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o
+obj-$(CONFIG_MACH_SAPPHIRE) += devices_htc.o
+obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o
+obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o
+obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-flashlight.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
+obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o
+obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o
+obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o
+obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o
+obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o
+obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o
+obj-$(CONFIG_MACH_MSM7X30_SURF) += board-surf7x30.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o
+obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o
+obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o
+obj-$(CONFIG_HTC_HEADSET) += htc_headset.o
+obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o
+
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o
+obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 24dfbf8..a423a2e 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -1,3 +1,17 @@
zreladdr-y := 0x10008000
params_phys-y := 0x10000100
initrd_phys-y := 0x10800000
+
+# override for Sapphire
+ zreladdr-$(CONFIG_MACH_SAPPHIRE) := 0x02008000
+params_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02000100
+initrd_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02800000
+
+# for now, override for QSD8x50
+ zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000
+params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100
+initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000
+
+ zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000
+params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100
+initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x01200000
diff --git a/arch/arm/mach-msm/acpuclock-7x30.c b/arch/arm/mach-msm/acpuclock-7x30.c
new file mode 100644
index 0000000..03087e0
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-7x30.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009, Code Aurora Forum. 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/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/sort.h>
+#include <mach/board.h>
+
+#include "acpuclock.h"
+
+unsigned long acpuclk_power_collapse(void)
+{
+ return 0;
+}
+
+unsigned long acpuclk_wait_for_irq(void)
+{
+ return 0;
+}
+
+unsigned long acpuclk_get_wfi_rate(void)
+{
+ return 0;
+}
+
+int acpuclk_set_rate(unsigned long rate, int for_power_collapse)
+{
+ return 0;
+}
+
+unsigned long acpuclk_get_rate(void)
+{
+ return 0;
+}
+
+uint32_t acpuclk_get_switch_time(void)
+{
+ return 0;
+}
+
+static void __init acpuclk_init(void)
+{
+}
+
+void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata)
+{
+ pr_info("acpu_clock_init()\n");
+ acpuclk_init();
+}
diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c
new file mode 100644
index 0000000..691acde
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2009 Google, Inc.
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+
+#include "acpuclock.h"
+#include "proc_comm.h"
+
+#if 0
+#define DEBUG(x...) pr_info(x)
+#else
+#define DEBUG(x...) do {} while (0)
+#endif
+
+#define SHOT_SWITCH 4
+#define HOP_SWITCH 5
+#define SIMPLE_SLEW 6
+#define COMPLEX_SLEW 7
+
+#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
+#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
+
+/* Scorpion PLL registers */
+#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4)
+#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18)
+#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10)
+
+struct clkctl_acpu_speed {
+ unsigned acpu_khz;
+ unsigned clk_cfg;
+ unsigned clk_sel;
+ unsigned sc_l_value;
+ unsigned lpj;
+ int vdd;
+};
+
+/* clock sources */
+#define CLK_TCXO 0 /* 19.2 MHz */
+#define CLK_GLOBAL_PLL 1 /* 768 MHz */
+#define CLK_MODEM_PLL 4 /* 245 MHz (UMTS) or 235.93 MHz (CDMA) */
+
+#define CCTL(src, div) (((src) << 4) | (div - 1))
+
+/* core sources */
+#define SRC_RAW 0 /* clock from SPSS_CLK_CNTL */
+#define SRC_SCPLL 1 /* output of scpll 128-998 MHZ */
+#define SRC_AXI 2 /* 128 MHz */
+#define SRC_PLL1 3 /* 768 MHz */
+
+struct clkctl_acpu_speed acpu_freq_tbl[] = {
+ { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050 },
+ { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050 },
+ { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050 },
+ { 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050 },
+ { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050 },
+ { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050 },
+ { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050 },
+ { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075 },
+ { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100 },
+ { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100 },
+ { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125 },
+ { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150 },
+ { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175 },
+ { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200 },
+ { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200 },
+ { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225 },
+ { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250 },
+ { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1275 },
+ { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275 },
+ { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275 },
+ { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275 },
+ { 0 },
+};
+
+/* select the standby clock that is used when switching scpll
+ * frequencies
+ *
+ * Currently: MPLL
+ */
+struct clkctl_acpu_speed *acpu_stby = &acpu_freq_tbl[2];
+#define IS_ACPU_STANDBY(x) (((x)->clk_cfg == acpu_stby->clk_cfg) && \
+ ((x)->clk_sel == acpu_stby->clk_sel))
+
+struct clkctl_acpu_speed *acpu_mpll = &acpu_freq_tbl[2];
+
+#ifdef CONFIG_CPU_FREQ_TABLE
+static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(acpu_freq_tbl)];
+
+static void __init acpuclk_init_cpufreq_table(void)
+{
+ int i;
+ int vdd;
+ for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) {
+ freq_table[i].index = i;
+ freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+
+ /* Skip speeds using the global pll */
+ if (acpu_freq_tbl[i].acpu_khz == 256000 ||
+ acpu_freq_tbl[i].acpu_khz == 19200)
+ continue;
+
+ vdd = acpu_freq_tbl[i].vdd;
+ /* Allow mpll and the first scpll speeds */
+ if (acpu_freq_tbl[i].acpu_khz == acpu_mpll->acpu_khz ||
+ acpu_freq_tbl[i].acpu_khz == 384000) {
+ freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
+ continue;
+ }
+
+ /* Take the fastest speed available at the specified VDD level */
+ if (vdd != acpu_freq_tbl[i + 1].vdd)
+ freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
+ }
+
+ freq_table[i].index = i;
+ freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+ cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
+}
+#else
+#define acpuclk_init_cpufreq_table() do {} while (0);
+#endif
+
+struct clock_state {
+ struct clkctl_acpu_speed *current_speed;
+ struct mutex lock;
+ uint32_t acpu_switch_time_us;
+ uint32_t max_speed_delta_khz;
+ uint32_t vdd_switch_time_us;
+ unsigned long power_collapse_khz;
+ unsigned long wait_for_irq_khz;
+ struct regulator *regulator;
+};
+
+static struct clock_state drv_state = { 0 };
+
+static DEFINE_SPINLOCK(acpu_lock);
+
+#define PLLMODE_POWERDOWN 0
+#define PLLMODE_BYPASS 1
+#define PLLMODE_STANDBY 2
+#define PLLMODE_FULL_CAL 4
+#define PLLMODE_HALF_CAL 5
+#define PLLMODE_STEP_CAL 6
+#define PLLMODE_NORMAL 7
+#define PLLMODE_MASK 7
+
+static void scpll_power_down(void)
+{
+ uint32_t val;
+
+ /* Wait for any frequency switches to finish. */
+ while (readl(SCPLL_STATUS_ADDR) & 0x1)
+ ;
+
+ /* put the pll in standby mode */
+ val = readl(SCPLL_CTL_ADDR);
+ val = (val & (~PLLMODE_MASK)) | PLLMODE_STANDBY;
+ writel(val, SCPLL_CTL_ADDR);
+ dmb();
+
+ /* wait to stabilize in standby mode */
+ udelay(10);
+
+ val = (val & (~PLLMODE_MASK)) | PLLMODE_POWERDOWN;
+ writel(val, SCPLL_CTL_ADDR);
+ dmb();
+}
+
+static void scpll_set_freq(uint32_t lval)
+{
+ uint32_t val, ctl;
+
+ if (lval > 33)
+ lval = 33;
+ if (lval < 10)
+ lval = 10;
+
+ /* wait for any calibrations or frequency switches to finish */
+ while (readl(SCPLL_STATUS_ADDR) & 0x3)
+ ;
+
+ ctl = readl(SCPLL_CTL_ADDR);
+
+ if ((ctl & PLLMODE_MASK) != PLLMODE_NORMAL) {
+ /* put the pll in standby mode */
+ writel((ctl & (~PLLMODE_MASK)) | PLLMODE_STANDBY, SCPLL_CTL_ADDR);
+ dmb();
+
+ /* wait to stabilize in standby mode */
+ udelay(10);
+
+ /* switch to 384 MHz */
+ val = readl(SCPLL_FSM_CTL_EXT_ADDR);
+ val = (val & (~0x1FF)) | (0x0A << 3) | SHOT_SWITCH;
+ writel(val, SCPLL_FSM_CTL_EXT_ADDR);
+ dmb();
+
+ ctl = readl(SCPLL_CTL_ADDR);
+ writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
+ dmb();
+
+ /* wait for frequency switch to finish */
+ while (readl(SCPLL_STATUS_ADDR) & 0x1)
+ ;
+
+ /* completion bit is not reliable for SHOT switch */
+ udelay(25);
+ }
+
+ /* write the new L val and switch mode */
+ val = readl(SCPLL_FSM_CTL_EXT_ADDR);
+ val = (val & (~0x1FF)) | (lval << 3) | HOP_SWITCH;
+ writel(val, SCPLL_FSM_CTL_EXT_ADDR);
+ dmb();
+
+ ctl = readl(SCPLL_CTL_ADDR);
+ writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
+ dmb();
+
+ /* wait for frequency switch to finish */
+ while (readl(SCPLL_STATUS_ADDR) & 0x1)
+ ;
+}
+
+/* this is still a bit weird... */
+static void select_clock(unsigned src, unsigned config)
+{
+ uint32_t val;
+
+ if (src == SRC_RAW) {
+ uint32_t sel = readl(SPSS_CLK_SEL_ADDR);
+ unsigned shift = (sel & 1) ? 8 : 0;
+
+ /* set other clock source to the new configuration */
+ val = readl(SPSS_CLK_CNTL_ADDR);
+ val = (val & (~(0x7F << shift))) | (config << shift);
+ writel(val, SPSS_CLK_CNTL_ADDR);
+
+ /* switch to other clock source */
+ writel(sel ^ 1, SPSS_CLK_SEL_ADDR);
+
+ dmb(); /* necessary? */
+ }
+
+ /* switch to new source */
+ val = readl(SPSS_CLK_SEL_ADDR) & (~6);
+ writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR);
+}
+
+static int acpuclk_set_vdd_level(int vdd)
+{
+ if (!drv_state.regulator || IS_ERR(drv_state.regulator)) {
+ drv_state.regulator = regulator_get(NULL, "acpu_vcore");
+ if (IS_ERR(drv_state.regulator)) {
+ pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd);
+ /* Assume that the PMIC supports scaling the processor
+ * to its maximum frequency at its default voltage.
+ */
+ return 0;
+ }
+ pr_info("acpuclk_set_vdd_level got regulator\n");
+ }
+ vdd *= 1000; /* mV -> uV */
+ return regulator_set_voltage(drv_state.regulator, vdd, vdd);
+}
+
+int acpuclk_set_rate(unsigned long rate, int for_power_collapse)
+{
+ struct clkctl_acpu_speed *cur, *next;
+ unsigned long flags;
+
+ cur = drv_state.current_speed;
+
+ /* convert to KHz */
+ rate /= 1000;
+
+ DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, for_power_collapse);
+
+ if (rate == 0 || rate == cur->acpu_khz)
+ return 0;
+
+ next = acpu_freq_tbl;
+ for (;;) {
+ if (next->acpu_khz == rate)
+ break;
+ if (next->acpu_khz == 0)
+ return -EINVAL;
+ next++;
+ }
+
+ if (!for_power_collapse) {
+ mutex_lock(&drv_state.lock);
+ /* Increase VDD if needed. */
+ if (next->vdd > cur->vdd) {
+ if (acpuclk_set_vdd_level(next->vdd)) {
+ pr_err("acpuclock: Unable to increase ACPU VDD.\n");
+ mutex_unlock(&drv_state.lock);
+ return -EINVAL;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&acpu_lock, flags);
+
+ DEBUG("sel=%d cfg=%02x lv=%02x -> sel=%d, cfg=%02x lv=%02x\n",
+ cur->clk_sel, cur->clk_cfg, cur->sc_l_value,
+ next->clk_sel, next->clk_cfg, next->sc_l_value);
+
+ if (next->clk_sel == SRC_SCPLL) {
+ if (!IS_ACPU_STANDBY(cur))
+ select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
+ loops_per_jiffy = next->lpj;
+ scpll_set_freq(next->sc_l_value);
+ select_clock(SRC_SCPLL, 0);
+ } else {
+ loops_per_jiffy = next->lpj;
+ if (cur->clk_sel == SRC_SCPLL) {
+ select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
+ select_clock(next->clk_sel, next->clk_cfg);
+ scpll_power_down();
+ } else {
+ select_clock(next->clk_sel, next->clk_cfg);
+ }
+ }
+
+ drv_state.current_speed = next;
+
+ spin_unlock_irqrestore(&acpu_lock, flags);
+ if (!for_power_collapse) {
+ /* Drop VDD level if we can. */
+ if (next->vdd < cur->vdd) {
+ if (acpuclk_set_vdd_level(next->vdd))
+ pr_err("acpuclock: Unable to drop ACPU VDD.\n");
+ }
+ mutex_unlock(&drv_state.lock);
+ }
+
+ return 0;
+}
+
+static unsigned __init acpuclk_find_speed(void)
+{
+ uint32_t sel, val;
+
+ sel = readl(SPSS_CLK_SEL_ADDR);
+ switch ((sel & 6) >> 1) {
+ case 1:
+ val = readl(SCPLL_FSM_CTL_EXT_ADDR);
+ val = (val >> 3) & 0x3f;
+ return val * 38400;
+ case 2:
+ return 128000;
+ default:
+ pr_err("acpu_find_speed: failed\n");
+ BUG();
+ return 0;
+ }
+}
+
+#define PCOM_MODEM_PLL 0
+static int pll_request(unsigned id, unsigned on)
+{
+ on = !!on;
+ return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
+}
+
+static void __init acpuclk_init(void)
+{
+ struct clkctl_acpu_speed *speed;
+ unsigned init_khz;
+
+ init_khz = acpuclk_find_speed();
+
+ /* request the modem pll, and then drop it. We don't want to keep a
+ * ref to it, but we do want to make sure that it is initialized at
+ * this point. The ARM9 will ensure that the MPLL is always on
+ * once it is fully booted, but it may not be up by the time we get
+ * to here. So, our pll_request for it will block until the mpll is
+ * actually up. We want it up because we will want to use it as a
+ * temporary step during frequency scaling. */
+ pll_request(PCOM_MODEM_PLL, 1);
+ pll_request(PCOM_MODEM_PLL, 0);
+
+ if (!(readl(MSM_CLK_CTL_BASE + 0x300) & 1)) {
+ pr_err("%s: MPLL IS NOT ON!!! RUN AWAY!!\n", __func__);
+ BUG();
+ }
+
+ /* Move to 768MHz for boot, which is a safe frequency
+ * for all versions of Scorpion at the moment.
+ */
+ speed = acpu_freq_tbl;
+ for (;;) {
+ if (speed->acpu_khz == 768000)
+ break;
+ if (speed->acpu_khz == 0) {
+ pr_err("acpuclk_init: cannot find 768MHz\n");
+ BUG();
+ }
+ speed++;
+ }
+
+ if (init_khz != speed->acpu_khz) {
+ /* Bootloader needs to have SCPLL operating, but we're
+ * going to step over to the standby clock and make sure
+ * we select the right frequency on SCPLL and then
+ * step back to it, to make sure we're sane here.
+ */
+ select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
+ scpll_power_down();
+ scpll_set_freq(speed->sc_l_value);
+ select_clock(SRC_SCPLL, 0);
+ }
+ drv_state.current_speed = speed;
+
+ for (speed = acpu_freq_tbl; speed->acpu_khz; speed++)
+ speed->lpj = cpufreq_scale(loops_per_jiffy,
+ init_khz, speed->acpu_khz);
+
+ loops_per_jiffy = drv_state.current_speed->lpj;
+}
+
+unsigned long acpuclk_get_rate(void)
+{
+ return drv_state.current_speed->acpu_khz;
+}
+
+uint32_t acpuclk_get_switch_time(void)
+{
+ return drv_state.acpu_switch_time_us;
+}
+
+unsigned long acpuclk_power_collapse(void)
+{
+ int ret = acpuclk_get_rate();
+ if (ret > drv_state.power_collapse_khz)
+ acpuclk_set_rate(drv_state.power_collapse_khz * 1000, 1);
+ return ret * 1000;
+}
+
+unsigned long acpuclk_get_wfi_rate(void)
+{
+ return drv_state.wait_for_irq_khz * 1000;
+}
+
+unsigned long acpuclk_wait_for_irq(void)
+{
+ int ret = acpuclk_get_rate();
+ if (ret > drv_state.wait_for_irq_khz)
+ acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, 1);
+ return ret * 1000;
+}
+
+void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata)
+{
+ spin_lock_init(&acpu_lock);
+ mutex_init(&drv_state.lock);
+
+ drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us;
+ drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz;
+ drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us;
+ drv_state.power_collapse_khz = clkdata->power_collapse_khz;
+ drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz;
+
+ if (clkdata->mpll_khz)
+ acpu_mpll->acpu_khz = clkdata->mpll_khz;
+
+ acpuclk_init();
+ acpuclk_init_cpufreq_table();
+}
diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S
new file mode 100644
index 0000000..2eaf4ea
--- /dev/null
+++ b/arch/arm/mach-msm/arch-init-scorpion.S
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2008, QUALCOMM Incorporated.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/* TODO:
+ * - style cleanup
+ * - do we need to do *all* of this at boot?
+ */
+
+.text
+.code 32
+
+#define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5
+#define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5
+
+.equ TCSR_SPARE2, 0xA8700060
+
+SET_SA:
+ ldr r0, =TCSR_SPARE2
+ ldr r12, [r0]
+
+ /* pack bits 8,2,0 into 2,1,0 */
+ and r0, r12, #0x001
+ and r1, r12, #0x004
+ and r2, r12, #0x100
+ orr r0, r1, lsr #1
+ orr r0, r2, lsr #6
+
+ adr r1, table_l1_acc
+ mov r0, r0, lsl #2
+ ldr r3, [r1, r0]
+
+ /* write 3800XXXX to PVR0F0 */
+ orr r0, r3, #0x38000000
+ mcr p15, 0, r0, c15, c15, 0
+
+ /* write XXXX0000 to PVR2F0 */
+ mov r1, r3, lsl #16
+ mcr p15, 2, r1, c15, c15, 0
+
+ adr r1, table_l2_acc
+ and r0, r12, #0x008
+ and r2, r12, #0x002
+ orr r0, r0, r2, lsl #1
+ ldr r2, [r1, r0]
+
+ /* write to L2VR3F1 */
+ mcr p15, 3, r2, c15, c15, 1
+
+ bx lr
+
+table_l1_acc:
+ .word 0xFC00
+ .word 0xFC00
+ .word 0x7C00
+ .word 0xFC00
+ .word 0x3C00
+ .word 0x0400
+ .word 0x0C00
+ .word 0x1C00
+
+table_l2_acc:
+ .word 0x010102
+ .word 0x010102
+ .word 0x010101
+ .word 0x212102
+
+.globl __cpu_early_init
+__cpu_early_init:
+ //; Zero out r0 for use throughout this code. All other GPRs
+ //; (r1-r3) are set throughout this code to help establish
+ //; a consistent startup state for any code that follows.
+ //; Users should add code at the end of this routine to establish
+ //; their own stack address (r13), add translation page tables, enable
+ //; the caches, etc.
+ MOV r0, #0x0
+
+
+ //; Remove hardcoded cache settings. appsbl_handler.s calls Set_SA
+ //; API to dynamically configure cache for slow/nominal/fast parts
+
+ //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank)
+ //; This must be done early in code (prior to enabling the caches)
+ MOV r1, #0x2
+ MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank D ([15:14] == 2'b00)
+ ORR r1, r1, #0x00004000
+ MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank C ([15:14] == 2'b01)
+ ADD r1, r1, #0x00004000
+ MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10)
+ ADD r1, r1, #0x00004000
+ MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11)
+
+ //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's
+ //; and have all address bits (AM) participate.
+ //; Different settings can be used to improve performance
+ // MOVW r1, #0x01FF
+.word 0xe30011ff // hardcoded MOVW instruction due to lack of compiler support
+ // MOVT r1, #0x01FF
+.word 0xe34011ff // hardcoded MOVT instruction due to lack of compiler support
+ MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR
+
+
+ //; Initialize all I$ Victim Registers to 0 for startup
+ MCR p15, 0, r0, c9, c1, 0 //; WCP15_ICVIC0 r0
+ MCR p15, 0, r0, c9, c1, 1 //; WCP15_ICVIC1 r0
+ MCR p15, 0, r0, c9, c1, 2 //; WCP15_ICVIC2 r0
+ MCR p15, 0, r0, c9, c1, 3 //; WCP15_ICVIC3 r0
+ MCR p15, 0, r0, c9, c1, 4 //; WCP15_ICVIC4 r0
+ MCR p15, 0, r0, c9, c1, 5 //; WCP15_ICVIC5 r0
+ MCR p15, 0, r0, c9, c1, 6 //; WCP15_ICVIC5 r0
+ MCR p15, 0, r0, c9, c1, 7 //; WCP15_ICVIC7 r0
+
+ //; Initialize all I$ Locked Victim Registers (Unlocked Floors) to 0
+ MCR p15, 1, r0, c9, c1, 0 //; WCP15_ICFLOOR0 r0
+ MCR p15, 1, r0, c9, c1, 1 //; WCP15_ICFLOOR1 r0
+ MCR p15, 1, r0, c9, c1, 2 //; WCP15_ICFLOOR2 r0
+ MCR p15, 1, r0, c9, c1, 3 //; WCP15_ICFLOOR3 r0
+ MCR p15, 1, r0, c9, c1, 4 //; WCP15_ICFLOOR4 r0
+ MCR p15, 1, r0, c9, c1, 5 //; WCP15_ICFLOOR5 r0
+ MCR p15, 1, r0, c9, c1, 6 //; WCP15_ICFLOOR6 r0
+ MCR p15, 1, r0, c9, c1, 7 //; WCP15_ICFLOOR7 r0
+
+ //; Initialize all D$ Victim Registers to 0
+ MCR p15, 2, r0, c9, c1, 0 //; WP15_DCVIC0 r0
+ MCR p15, 2, r0, c9, c1, 1 //; WP15_DCVIC1 r0
+ MCR p15, 2, r0, c9, c1, 2 //; WP15_DCVIC2 r0
+ MCR p15, 2, r0, c9, c1, 3 //; WP15_DCVIC3 r0
+ MCR p15, 2, r0, c9, c1, 4 //; WP15_DCVIC4 r0
+ MCR p15, 2, r0, c9, c1, 5 //; WP15_DCVIC5 r0
+ MCR p15, 2, r0, c9, c1, 6 //; WP15_DCVIC6 r0
+ MCR p15, 2, r0, c9, c1, 7 //; WP15_DCVIC7 r0
+
+ //; Initialize all D$ Locked VDCtim Registers (Unlocked Floors) to 0
+ MCR p15, 3, r0, c9, c1, 0 //; WCP15_DCFLOOR0 r0
+ MCR p15, 3, r0, c9, c1, 1 //; WCP15_DCFLOOR1 r0
+ MCR p15, 3, r0, c9, c1, 2 //; WCP15_DCFLOOR2 r0
+ MCR p15, 3, r0, c9, c1, 3 //; WCP15_DCFLOOR3 r0
+ MCR p15, 3, r0, c9, c1, 4 //; WCP15_DCFLOOR4 r0
+ MCR p15, 3, r0, c9, c1, 5 //; WCP15_DCFLOOR5 r0
+ MCR p15, 3, r0, c9, c1, 6 //; WCP15_DCFLOOR6 r0
+ MCR p15, 3, r0, c9, c1, 7 //; WCP15_DCFLOOR7 r0
+
+ //; Initialize ASID to zero
+ MCR p15, 0, r0, c13, c0, 1 //; WCP15_CONTEXTIDR r0
+
+ //; ICIALL to invalidate entire I-Cache
+ MCR p15, 0, r0, c7, c5, 0 //; ICIALLU
+
+ //; DCIALL to invalidate entire D-Cache
+ MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0
+
+
+ //; The VBAR (Vector Base Address Register) should be initialized
+ //; early in your code. We are setting it to zero
+ MCR p15, 0, r0, c12, c0, 0 //; WCP15_VBAR r0
+
+ //; Ensure the MCR's above have completed their operation before continuing
+ DSB
+ ISB
+
+ //;-------------------------------------------------------------------
+ //; There are a number of registers that must be set prior to enabling
+ //; the MMU. The DCAR is one of these registers. We are setting
+ //; it to zero (no access) to easily detect improper setup in subsequent
+ //; code sequences
+ //;-------------------------------------------------------------------
+ //; Setup DACR (Domain Access Control Register) to zero
+ MCR p15, 0, r0, c3, c0, 0 //; WCP15_DACR r0
+
+ //; Setup DCLKCR to allow normal D-Cache line fills
+ MCR p15, 1, r0, c9, c0, 7 //; WCP15_DCLKCR r0
+
+ //; Initialize the ADFSR and EFSR registers.
+ MCR p15, 0, r0, c5, c1, 0 //; ADFSR
+ MCR p15, 7, r0, c15, c0, 1 //; EFSR
+
+ //; Setup the TLBLKCR
+ //; Victim = 6'b000000; Floor = 6'b000000;
+ //; IASIDCFG = 2'b00 (State-Machine); IALLCFG = 2'b01 (Flash); BNA = 1'b0;
+ MOV r1, #0x02
+ MCR p15, 0, r1, c10, c1, 3 //; WCP15_TLBLKCR r1
+
+ //;Make sure TLBLKCR is complete before continuing
+ ISB
+
+ //; Invalidate the UTLB
+ MCR p15, 0, r0, c8, c7, 0 //; UTLBIALL
+
+ //; Make sure UTLB request has been presented to macro before continuing
+ ISB
+
+ //; setup L2CR1 to some default Instruction and data prefetching values
+ //; Users may want specific settings for various performance enhancements
+ //; In Halcyon we do not have broadcasting barriers. So we need to turn
+ // ; on bit 8 of L2CR1; which DBB:( Disable barrier broadcast )
+ MOV r2, #0x100
+ MCR p15, 3, r2, c15, c0, 3 //; WCP15_L2CR1 r0
+
+
+ //; Enable Z bit to enable branch prediction (default is off)
+ MRC p15, 0, r2, c1, c0, 0 //; RCP15_SCTLR r2
+ ORR r2, r2, #0x00000800
+ MCR p15, 0, r2, c1, c0, 0 //; WCP15_SCTLR r2
+
+#ifdef CONFIG_ARCH_QSD8X50
+ /* disable predecode repair cache for thumb2 (DPRC, set bit 4 in PVR0F2) */
+ mrc p15, 0, r2, c15, c15, 2
+ orr r2, r2, #0x10
+ mcr p15, 0, r2, c15, c15, 2
+#endif
+
+ mov r1, lr
+ //; Make sure Link stack is initialized with branch and links to sequential addresses
+ //; This aids in creating a predictable startup environment
+ BL SEQ1
+SEQ1: BL SEQ2
+SEQ2: BL SEQ3
+SEQ3: BL SEQ4
+SEQ4: BL SEQ5
+SEQ5: BL SEQ6
+SEQ6: BL SEQ7
+SEQ7: BL SEQ8
+SEQ8:
+ mov lr, r1
+
+ //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA
+ //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers
+ //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit
+ MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0
+
+
+ //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD]
+ //; Any read to DBGPRSR clear the STICKYPD bit
+ //; ISB guarantees the read completes before attempting to
+ //; execute a CP14 instruction.
+ MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3
+ ISB
+
+ //; Initialize the Watchpoint Control Registers to zero (optional)
+ //;;; MCR p14, 0, r0, c0, c0, 7 ; WCP14_DBGWCR0 r0
+ //;;; MCR p14, 0, r0, c0, c1, 7 ; WCP14_DBGWCR1 r0
+
+
+ //;----------------------------------------------------------------------
+ //; The saved Program Status Registers (SPSRs) should be setup
+ //; prior to any automatic mode switches. The following
+ //; code sets these registers up to a known state. Users will need to
+ //; customize these settings to meet their needs.
+ //;----------------------------------------------------------------------
+ MOV r2, #0x1f
+ MOV r1, #0x17 //;ABT mode
+ msr cpsr_c, r1 //;ABT mode
+ msr spsr_cxfs, r2 //;clear the spsr
+ MOV r1, #0x1b //;UND mode
+ msr cpsr_c, r1 //;UND mode
+ msr spsr_cxfs, r2 //;clear the spsr
+ MOV r1, #0x11 //;FIQ mode
+ msr cpsr_c, r1 //;FIQ mode
+ msr spsr_cxfs, r2 //;clear the spsr
+ MOV r1, #0x12 //;IRQ mode
+ msr cpsr_c, r1 //;IRQ mode
+ msr spsr_cxfs, r2 //;clear the spsr
+ MOV r1, #0x16 //;Monitor mode
+ msr cpsr_c, r1 //;Monitor mode
+ msr spsr_cxfs, r2 //;clear the spsr
+ MOV r1, #0x13 //;SVC mode
+ msr cpsr_c, r1 //;SVC mode
+ msr spsr_cxfs, r2 //;clear the spsr
+
+
+ //;----------------------------------------------------------------------
+ //; Enabling Error reporting is something users may want to do at
+ //; some other point in time. We have chosen some default settings
+ //; that should be reviewed. Most of these registers come up in an
+ //; unpredictable state after reset.
+ //;----------------------------------------------------------------------
+//;Start of error and control setting
+
+ //; setup L2CR0 with various L2/TCM control settings
+ //; enable out of order bus attributes and error reporting
+ //; this register comes up unpredictable after reset
+ // MOVW r1, #0x0F0F
+.word 0xe3001f0f // hardcoded MOVW instruction due to lack of compiler support
+ // MOVT r1, #0xC005
+.word 0xe34c1005 // hardcoded MOVW instruction due to lack of compiler support
+ MCR p15, 3, r1, c15, c0, 1 //; WCP15_L2CR0 r1
+
+ //; setup L2CPUCR
+ //; MOV r2, #0xFF
+ //; Enable I and D cache parity
+ //;L2CPUCR[7:5] = 3~Rh7 ~V enable parity error reporting for modified,
+ //;tag, and data parity errors
+ MOV r2, #0xe0
+ MCR p15, 3, r2, c15, c0, 2 //; WCP15_L2CPUCR r2
+
+ //; setup SPCR
+ //; enable all error reporting (reset value is unpredicatble for most bits)
+ MOV r3, #0x0F
+ MCR p15, 0, r3, c9, c7, 0 //; WCP15_SPCR r3
+
+ //; setup DMACHCRs (reset value unpredictable)
+ //; control setting and enable all error reporting
+ MOV r1, #0x0F
+
+ //; DMACHCR0 = 0000000F
+ MOV r2, #0x00 //; channel 0
+ MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2
+ MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1
+
+ //; DMACHCR1 = 0000000F
+ MOV r2, #0x01 //; channel 1
+ MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2
+ MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1
+
+ //; DMACHCR2 = 0000000F
+ MOV r2, #0x02 //; channel 2
+ MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2
+ MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1
+
+ //; DMACHCR3 = 0000000F
+ MOV r2, #0x03 //; channel 3
+ MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2
+ MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1
+
+ //; Set ACTLR (reset unpredictable)
+ //; Set AVIVT control, error reporting, etc.
+ //; MOV r3, #0x07
+ //; Enable I and D cache parity
+ //;ACTLR[2:0] = 3'h7 - enable parity error reporting from L2/I$/D$)
+ //;ACTLR[5:4] = 2'h3 - enable parity
+ //;ACTLR[19:18] =2'h3 - always generate and check parity(when MMU disabled).
+ //;Value to be written #0xC0037
+ // MOVW r3, #0x0037
+.word 0xe3003037 // hardcoded MOVW instruction due to lack of compiler support
+ // MOVT r3, #0x000C
+.word 0xe340300c // hardcoded MOVW instruction due to lack of compiler support
+ //; read the version_id to determine if d-cache should be disabled
+ LDR r2, = 0xa8e00270 //;Read HW_REVISION_NUMBER, HWIO_HW_REVISION_NUMBER_ADDR
+ LDR r2,[r2]
+ AND r2,r2,#0xf0000000 //;hw_revision mask off bits 28-31
+ //;if HW_revision is 1.0 or older, (revision==0)
+ CMP r2,#0
+ //; Disable d-cache on older QSD8650 (Rev 1.0) silicon
+ orreq r3, r3, #0x4000 //;disable dcache
+ MCR p15, 0, r3, c1, c0, 1 //; WCP15_ACTLR r3
+
+//;End of error and control setting
+
+ //;----------------------------------------------------------------------
+ //; Unlock ETM and read StickyPD to halt the ETM clocks from running.
+ //; This is required for power saving whether the ETM is used or not.
+ //;----------------------------------------------------------------------
+
+ //;Clear ETMOSLSR[LOCK] bit
+ MOV r1, #0x00000000
+ MCR p14, 1, r1, c1, c0, 4 //; WCP14_ETMOSLAR r1
+
+ //;Clear ETMPDSR[STICKYPD] bit
+ MRC p14, 1, r2, c1, c5, 4 //; RCP14_ETMPDSR r2
+
+/*
+#ifdef APPSBL_ETM_ENABLE
+ ;----------------------------------------------------------------------
+ ; Optionally Enable the ETM (Embedded Trace Macro) which is used for debug
+ ;----------------------------------------------------------------------
+
+ ; enable ETM clock if disabled
+ MRC p15, 7, r1, c15, c0, 5 ; RCP15_CPMR r1
+ ORR r1, r1, #0x00000008
+ MCR p15, 7, r1, c15, c0, 5 ; WCP15_CPMR r1
+ ISB
+
+ ; set trigger event to counter1 being zero
+ MOV r3, #0x00000040
+ MCR p14, 1, r3, c0, c2, 0 ; WCP14_ETMTRIGGER r3
+
+ ; clear ETMSR
+ MOV r2, #0x00000000
+ MCR p14, 1, r2, c0, c4, 0 ; WCP14_ETMSR r2
+
+ ; clear trace enable single address comparator usage
+ MCR p14, 1, r2, c0, c7, 0 ; WCP14_ETMTECR2 r2
+
+ ; set trace enable to always
+ MOV r2, #0x0000006F
+ MCR p14, 1, r2, c0, c8, 0 ; WCP14_ETMTEEVR r2
+
+ ; clear trace enable address range comparator usage and exclude nothing
+ MOV r2, #0x01000000
+ MCR p14, 1, r2, c0, c9, 0 ; WCP14_ETMTECR1 r2
+
+ ; set view data to always
+ MOV r2, #0x0000006F
+ MCR p14, 1, r2, c0, c12, 0 ; WCP14_ETMVDEVR r2
+
+ ; clear view data single address comparator usage
+ MOV r2, #0x00000000
+ MCR p14, 1, r2, c0, c13, 0 ; WCP14_ETMVDCR1 r2
+
+ ; clear view data address range comparator usage and exclude nothing
+ MOV r2, #0x00010000
+ MCR p14, 1, r2, c0, c15, 0 ; WCP14_ETMVDCR3 r2
+
+ ; set counter1 to 194
+ MOV r2, #0x000000C2
+ MCR p14, 1, r2, c0, c0, 5 ; WCP14_ETMCNTRLDVR1 r2
+
+ ; set counter1 to never reload
+ MOV r2, #0x0000406F
+ MCR p14, 1, r2, c0, c8, 5 ; WCP14_ETMCNTRLDEVR1 r2
+
+ ; set counter1 to decrement every cycle
+ MOV r2, #0x0000006F
+ MCR p14, 1, r2, c0, c4, 5 ; WCP14_ETMCNTENR1 r2
+
+ ; Set trace synchronization frequency 1024 bytes
+ MOV r2, #0x00000400
+ MCR p14, 1, r2, c0, c8, 7 ; WCP14_ETMSYNCFR r2
+
+ ; Program etm control register
+ ; - Set the CPU to ETM clock ratio to 1:1
+ ; - Set the ETM to perform data address tracing
+ MOV r2, #0x00002008
+ MCR p14, 1, r2, c0, c0, 0 ; WCP14_ETMCR r2
+ ISB
+#endif *//* APPSBL_ETM_ENABLE */
+
+/*
+#ifdef APPSBL_VFP_ENABLE
+ ;----------------------------------------------------------------------
+ ; Perform the following operations if you intend to make use of
+ ; the VFP/Neon unit. Note that the FMXR instruction requires a CPU ID
+ ; indicating the VFP unit is present (i.e.Cortex-A8). .
+ ; Some tools will require full double precision floating point support
+ ; which will become available in Scorpion pass 2
+ ;----------------------------------------------------------------------
+ ; allow full access to CP 10 and 11 space for VFP/NEON use
+ MRC p15, 0, r1, c1, c0, 2 ; Read CP Access Control Register
+ ORR r1, r1, #0x00F00000 ; enable full access for p10,11
+ MCR p15, 0, r1, c1, c0, 2 ; Write CPACR
+
+ ;make sure the CPACR is complete before continuing
+ ISB
+
+ ; Enable VFP itself (certain OSes may want to dynamically set/clear
+ ; the enable bit based on the application being executed
+ MOV r1, #0x40000000
+ FMXR FPEXC, r1
+#endif *//* APPSBL_VFP_ENABLE */
+
+ /* we have no stack, so just tail-call into the SET_SA routine... */
+ b SET_SA
+
+.ltorg
diff --git a/arch/arm/mach-msm/board-halibut-keypad.c b/arch/arm/mach-msm/board-halibut-keypad.c
new file mode 100644
index 0000000..49c1075
--- /dev/null
+++ b/arch/arm/mach-msm/board-halibut-keypad.c
@@ -0,0 +1,177 @@
+/* linux/arch/arm/mach-msm/board-halibut-keypad.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_event.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "board_halibut."
+static int halibut_ffa;
+module_param_named(ffa, halibut_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */
+
+static unsigned int halibut_row_gpios[] = {
+ 31, 32, 33, 34, 35, 41
+#if SCAN_FUNCTION_KEYS
+ , 42
+#endif
+};
+
+static unsigned int halibut_col_gpios[] = { 36, 37, 38, 39, 40 };
+
+/* FFA:
+ 36: KEYSENSE_N(0)
+ 37: KEYSENSE_N(1)
+ 38: KEYSENSE_N(2)
+ 39: KEYSENSE_N(3)
+ 40: KEYSENSE_N(4)
+
+ 31: KYPD_17
+ 32: KYPD_15
+ 33: KYPD_13
+ 34: KYPD_11
+ 35: KYPD_9
+ 41: KYPD_MEMO
+*/
+
+#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(halibut_col_gpios) + (col))
+
+static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_5,
+ [KEYMAP_INDEX(0, 1)] = KEY_9,
+ [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */
+ [KEYMAP_INDEX(0, 3)] = KEY_6,
+ [KEYMAP_INDEX(0, 4)] = KEY_LEFT,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_0,
+ [KEYMAP_INDEX(1, 1)] = KEY_RIGHT,
+ [KEYMAP_INDEX(1, 2)] = KEY_1,
+ [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */
+ [KEYMAP_INDEX(1, 4)] = KEY_SEND,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */
+ [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */
+ [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */
+ [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */
+
+ [KEYMAP_INDEX(3, 0)] = KEY_UP,
+ [KEYMAP_INDEX(3, 1)] = KEY_CLEAR,
+ [KEYMAP_INDEX(3, 2)] = KEY_4,
+ [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */
+ [KEYMAP_INDEX(3, 4)] = KEY_2,
+
+ [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */
+ [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */
+ [KEYMAP_INDEX(4, 2)] = KEY_DOWN,
+ [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */
+ [KEYMAP_INDEX(4, 4)] = KEY_8,
+
+ [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */
+ [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */
+ [KEYMAP_INDEX(5, 3)] = KEY_3,
+ [KEYMAP_INDEX(5, 4)] = KEY_7,
+
+#if SCAN_FUNCTION_KEYS
+ [KEYMAP_INDEX(6, 0)] = KEY_F5,
+ [KEYMAP_INDEX(6, 1)] = KEY_F4,
+ [KEYMAP_INDEX(6, 2)] = KEY_F3,
+ [KEYMAP_INDEX(6, 3)] = KEY_F2,
+ [KEYMAP_INDEX(6, 4)] = KEY_F1
+#endif
+};
+
+static const unsigned short halibut_keymap_ffa[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = {
+ /*[KEYMAP_INDEX(0, 0)] = ,*/
+ /*[KEYMAP_INDEX(0, 1)] = ,*/
+ [KEYMAP_INDEX(0, 2)] = KEY_1,
+ [KEYMAP_INDEX(0, 3)] = KEY_SEND,
+ [KEYMAP_INDEX(0, 4)] = KEY_LEFT,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_3,
+ [KEYMAP_INDEX(1, 1)] = KEY_RIGHT,
+ [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP,
+ /*[KEYMAP_INDEX(1, 3)] = ,*/
+ [KEYMAP_INDEX(1, 4)] = KEY_6,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */
+ [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */
+ [KEYMAP_INDEX(2, 2)] = KEY_0,
+ [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */
+ [KEYMAP_INDEX(2, 4)] = KEY_9,
+
+ [KEYMAP_INDEX(3, 0)] = KEY_UP,
+ [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */
+ [KEYMAP_INDEX(3, 2)] = KEY_4,
+ /*[KEYMAP_INDEX(3, 3)] = ,*/
+ [KEYMAP_INDEX(3, 4)] = KEY_2,
+
+ [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(4, 1)] = KEY_SOUND,
+ [KEYMAP_INDEX(4, 2)] = KEY_DOWN,
+ [KEYMAP_INDEX(4, 3)] = KEY_8,
+ [KEYMAP_INDEX(4, 4)] = KEY_5,
+
+ /*[KEYMAP_INDEX(5, 0)] = ,*/
+ [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */
+ [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */
+ [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */
+ [KEYMAP_INDEX(5, 4)] = KEY_7,
+};
+
+static struct gpio_event_matrix_info halibut_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = halibut_keymap,
+ .output_gpios = halibut_row_gpios,
+ .input_gpios = halibut_col_gpios,
+ .noutputs = ARRAY_SIZE(halibut_row_gpios),
+ .ninputs = ARRAY_SIZE(halibut_col_gpios),
+ .settle_time.tv.nsec = 0,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/
+};
+
+struct gpio_event_info *halibut_keypad_info[] = {
+ &halibut_matrix_info.info
+};
+
+static struct gpio_event_platform_data halibut_keypad_data = {
+ .name = "halibut_keypad",
+ .info = halibut_keypad_info,
+ .info_count = ARRAY_SIZE(halibut_keypad_info)
+};
+
+static struct platform_device halibut_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &halibut_keypad_data,
+ },
+};
+
+static int __init halibut_init_keypad(void)
+{
+ if (!machine_is_halibut())
+ return 0;
+ if (halibut_ffa)
+ halibut_matrix_info.keymap = halibut_keymap_ffa;
+ return platform_device_register(&halibut_keypad_device);
+}
+
+device_initcall(halibut_init_keypad);
diff --git a/arch/arm/mach-msm/board-halibut-panel.c b/arch/arm/mach-msm/board-halibut-panel.c
new file mode 100644
index 0000000..a498c65
--- /dev/null
+++ b/arch/arm/mach-msm/board-halibut-panel.c
@@ -0,0 +1,73 @@
+/* linux/arch/arm/mach-msm/board-halibut-mddi.c
+** Author: Brian Swetland <swetland@google.com>
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/bootmem.h>
+
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+#include <mach/vreg.h>
+
+#include "proc_comm.h"
+#include "devices.h"
+#include "board-halibut.h"
+
+static void halibut_mddi_power_client(struct msm_mddi_client_data *mddi,
+ int on)
+{
+}
+
+static struct resource resources_msm_fb = {
+ .start = MSM_FB_BASE,
+ .end = MSM_FB_BASE + MSM_FB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct msm_fb_data fb_data = {
+ .xres = 800,
+ .yres = 480,
+ .output_format = 0,
+};
+
+static struct msm_mddi_platform_data mddi_pdata = {
+ .clk_rate = 122880000,
+ .power_client = halibut_mddi_power_client,
+ .fb_resource = &resources_msm_fb,
+ .num_clients = 1,
+ .client_platform_data = {
+ {
+ .product_id = (0x4474 << 16 | 0xc065),
+ .name = "mddi_c_dummy",
+ .id = 0,
+ .client_data = &fb_data,
+ .clk_rate = 0,
+ },
+ },
+};
+
+int __init halibut_init_panel(void)
+{
+ int rc;
+
+ if (!machine_is_halibut())
+ return 0;
+
+ rc = platform_device_register(&msm_device_mdp);
+ if (rc)
+ return rc;
+
+ msm_device_mddi0.dev.platform_data = &mddi_pdata;
+ return platform_device_register(&msm_device_mddi0);
+}
+
+device_initcall(halibut_init_panel);
diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c
index 7bd72e8..1940436 100644
--- a/arch/arm/mach-msm/board-halibut.c
+++ b/arch/arm/mach-msm/board-halibut.c
@@ -20,8 +20,11 @@
#include <linux/input.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/bootmem.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/gpio.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -31,11 +34,18 @@
#include <mach/irqs.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
+#include <mach/msm_hsusb.h>
+#include <mach/camera.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/i2c.h>
+#include <linux/android_pmem.h>
+#include <linux/usb/android_composite.h>
#include "devices.h"
+#include "board-halibut.h"
+#include "proc_comm.h"
static struct resource smc91x_resources[] = {
[0] = {
@@ -57,13 +67,355 @@
.resource = smc91x_resources,
};
+static struct i2c_board_info i2c_devices[] = {
+#ifdef CONFIG_MT9D112
+ {
+ I2C_BOARD_INFO("mt9d112", 0x78 >> 1),
+ },
+#endif
+#ifdef CONFIG_S5K3E2FX
+ {
+ I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1),
+ },
+#endif
+#ifdef CONFIG_MT9P012
+ {
+ I2C_BOARD_INFO("mt9p012", 0x6C >> 1),
+ },
+#endif
+#if defined(CONFIG_MT9T013) || defined(CONFIG_SENSORS_MT9T013)
+ {
+ I2C_BOARD_INFO("mt9t013", 0x6C), // 0x78>>1
+ },
+#endif
+};
+
+#ifdef CONFIG_MSM_CAMERA
+static uint32_t camera_off_gpio_table[] = {
+ /* parallel CAMERA interfaces */
+ PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* MCLK */
+};
+
+static uint32_t camera_on_gpio_table[] = {
+ /* parallel CAMERA interfaces */
+ PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */
+};
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for (n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static void config_camera_on_gpios(void)
+{
+ config_gpio_table(camera_on_gpio_table,
+ ARRAY_SIZE(camera_on_gpio_table));
+}
+
+static void config_camera_off_gpios(void)
+{
+ config_gpio_table(camera_off_gpio_table,
+ ARRAY_SIZE(camera_off_gpio_table));
+}
+
+static struct msm_camera_device_platform_data msm_camera_device_data = {
+ .camera_gpio_on = config_camera_on_gpios,
+ .camera_gpio_off = config_camera_off_gpios,
+ .ioext.mdcphy = MSM_MDC_PHYS,
+ .ioext.mdcsz = MSM_MDC_SIZE,
+ .ioext.appphy = MSM_CLK_CTL_PHYS,
+ .ioext.appsz = MSM_CLK_CTL_SIZE,
+};
+
+#ifdef CONFIG_MT9D112
+static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = {
+ .sensor_name = "mt9d112",
+ .sensor_reset = 89,
+ .sensor_pwd = 85,
+ .vcm_pwd = 0,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9d112 = {
+ .name = "msm_camera_mt9d112",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9d112_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_S5K3E2FX
+static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = {
+ .sensor_name = "s5k3e2fx",
+ .sensor_reset = 89,
+ .sensor_pwd = 85,
+ .vcm_pwd = 0,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_s5k3e2fx = {
+ .name = "msm_camera_s5k3e2fx",
+ .dev = {
+ .platform_data = &msm_camera_sensor_s5k3e2fx_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_MT9P012
+static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = {
+ .sensor_name = "mt9p012",
+ .sensor_reset = 89,
+ .sensor_pwd = 85,
+ .vcm_pwd = 88,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9p012 = {
+ .name = "msm_camera_mt9p012",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9p012_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_MT9T013
+static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = {
+ .sensor_name = "mt9t013",
+ .sensor_reset = 89,
+ .sensor_pwd = 85,
+ .vcm_pwd = 0,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9t013 = {
+ .name = "msm_camera_mt9t013",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9t013_data,
+ },
+};
+#endif
+#endif /*CONFIG_MSM_CAMERA*/
+
+#define SND(desc, num) { .name = #desc, .id = num }
+static struct snd_endpoint snd_endpoints_list[] = {
+ SND(HANDSET, 0),
+ SND(HEADSET, 2),
+ SND(SPEAKER, 6),
+ SND(BT, 12),
+ SND(CURRENT, 25),
+};
+#undef SND
+
+static struct msm_snd_endpoints halibut_snd_endpoints = {
+ .endpoints = snd_endpoints_list,
+ .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint)
+};
+
+static struct platform_device halibut_snd = {
+ .name = "msm_snd",
+ .id = -1,
+ .dev = {
+ .platform_data = &halibut_snd_endpoints
+ },
+};
+
+static struct android_pmem_platform_data android_pmem_pdata = {
+ .name = "pmem",
+ .start = MSM_PMEM_MDP_BASE,
+ .size = MSM_PMEM_MDP_SIZE,
+ .no_allocator = 0,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data android_pmem_camera_pdata = {
+ .name = "pmem_camera",
+ .start = MSM_PMEM_CAMERA_BASE,
+ .size = MSM_PMEM_CAMERA_SIZE,
+ .no_allocator = 1,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data android_pmem_adsp_pdata = {
+ .name = "pmem_adsp",
+ .start = MSM_PMEM_ADSP_BASE,
+ .size = MSM_PMEM_ADSP_SIZE,
+ .no_allocator = 0,
+ .cached = 0,
+};
+
+static struct android_pmem_platform_data android_pmem_gpu0_pdata = {
+ .name = "pmem_gpu0",
+ .start = MSM_PMEM_GPU0_BASE,
+ .size = MSM_PMEM_GPU0_SIZE,
+ .no_allocator = 1,
+ .cached = 0,
+};
+
+static struct android_pmem_platform_data android_pmem_gpu1_pdata = {
+ .name = "pmem_gpu1",
+ .start = MSM_PMEM_GPU1_BASE,
+ .size = MSM_PMEM_GPU1_SIZE,
+ .no_allocator = 1,
+ .cached = 0,
+};
+
+static struct platform_device android_pmem_device = {
+ .name = "android_pmem",
+ .id = 0,
+ .dev = { .platform_data = &android_pmem_pdata },
+};
+
+static struct platform_device android_pmem_adsp_device = {
+ .name = "android_pmem",
+ .id = 1,
+ .dev = { .platform_data = &android_pmem_adsp_pdata },
+};
+
+static struct platform_device android_pmem_gpu0_device = {
+ .name = "android_pmem",
+ .id = 2,
+ .dev = { .platform_data = &android_pmem_gpu0_pdata },
+};
+
+static struct platform_device android_pmem_gpu1_device = {
+ .name = "android_pmem",
+ .id = 3,
+ .dev = { .platform_data = &android_pmem_gpu1_pdata },
+};
+
+static struct platform_device android_pmem_camera_device = {
+ .name = "android_pmem",
+ .id = 4,
+ .dev = { .platform_data = &android_pmem_camera_pdata },
+};
+
+static int halibut_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 };
+
+static struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_init_seq = halibut_phy_init_seq,
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "Qualcomm",
+ .product = "Halibut",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+static char *usb_functions[] = { "usb_mass_storage" };
+static char *usb_functions_adb[] = { "usb_mass_storage", "adb" };
+
+static struct android_usb_product usb_products[] = {
+ {
+ .product_id = 0x0c01,
+ .num_functions = ARRAY_SIZE(usb_functions),
+ .functions = usb_functions,
+ },
+ {
+ .product_id = 0x0c02,
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+ },
+};
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x18d1,
+ .product_id = 0x0c01,
+ .version = 0x0100,
+ .serial_number = "42",
+ .product_name = "Halibutdroid",
+ .manufacturer_name = "Qualcomm",
+ .num_products = ARRAY_SIZE(usb_products),
+ .products = usb_products,
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+static struct platform_device fish_battery_device = {
+ .name = "fish_battery",
+};
+
static struct platform_device *devices[] __initdata = {
+#if !defined(CONFIG_MSM_SERIAL_DEBUGGER)
&msm_device_uart3,
+#endif
&msm_device_smd,
&msm_device_nand,
&msm_device_hsusb,
+ &usb_mass_storage_device,
+ &android_usb_device,
&msm_device_i2c,
&smc91x_device,
+ &halibut_snd,
+#ifdef CONFIG_MT9T013
+ &msm_camera_sensor_mt9t013,
+#endif
+#ifdef CONFIG_MT9D112
+ &msm_camera_sensor_mt9d112,
+#endif
+#ifdef CONFIG_S5K3E2FX
+ &msm_camera_sensor_s5k3e2fx,
+#endif
+#ifdef CONFIG_MT9P012
+ &msm_camera_sensor_mt9p012,
+#endif
+ &android_pmem_device,
+ &android_pmem_adsp_device,
+ &android_pmem_gpu0_device,
+ &android_pmem_gpu1_device,
+ &android_pmem_camera_device,
+ &fish_battery_device,
};
extern struct sys_timer msm_timer;
@@ -73,15 +425,38 @@
msm_init_irq();
}
+static struct msm_acpu_clock_platform_data halibut_clock_data = {
+ .acpu_switch_time_us = 50,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 19200000,
+ .wait_for_irq_khz = 128000000,
+};
+
+extern void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq);
+
static void __init halibut_init(void)
{
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3,
+ &msm_device_uart3.dev, 1);
+#endif
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ msm_acpu_clock_init(&halibut_clock_data);
+#ifdef CONFIG_MSM_CAMERA
+ config_camera_off_gpios(); /* might not be necessary */
+#endif
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
platform_add_devices(devices, ARRAY_SIZE(devices));
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+ msm_hsusb_set_vbus_state(1);
}
static void __init halibut_fixup(struct machine_desc *desc, struct tag *tags,
char **cmdline, struct meminfo *mi)
{
- mi->nr_banks=1;
+ mi->nr_banks = 1;
mi->bank[0].start = PHYS_OFFSET;
mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
mi->bank[0].size = (101*1024*1024);
@@ -95,7 +470,7 @@
MACHINE_START(HALIBUT, "Halibut Board (QCT SURF7200A)")
#ifdef CONFIG_MSM_DEBUG_UART
- .phys_io = MSM_DEBUG_UART_PHYS,
+ .phys_io = MSM_DEBUG_UART_PHYS,
.io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
#endif
.boot_params = 0x10000100,
diff --git a/arch/arm/mach-msm/board-halibut.h b/arch/arm/mach-msm/board-halibut.h
new file mode 100644
index 0000000..edcdacb
--- /dev/null
+++ b/arch/arm/mach-msm/board-halibut.h
@@ -0,0 +1,20 @@
+/* linux/arch/arm/mach-msm/board-trout.h
+ * ** Author: Brian Swetland <swetland@google.com>
+ * */
+#ifndef __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H
+#define __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H
+
+#define MSM_PMEM_GPU0_BASE (0x10000000 + 64*SZ_1M)
+#define MSM_PMEM_GPU0_SIZE 0x800000
+#define MSM_PMEM_MDP_BASE (MSM_PMEM_GPU0_BASE + MSM_PMEM_GPU0_SIZE)
+#define MSM_PMEM_MDP_SIZE 0x800000
+#define MSM_PMEM_ADSP_BASE (MSM_PMEM_MDP_BASE + MSM_PMEM_MDP_SIZE)
+#define MSM_PMEM_ADSP_SIZE 0x800000
+#define MSM_PMEM_GPU1_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE)
+#define MSM_PMEM_GPU1_SIZE 0x800000
+#define MSM_FB_BASE (MSM_PMEM_GPU1_BASE + MSM_PMEM_GPU1_SIZE)
+#define MSM_FB_SIZE 0x200000
+#define MSM_PMEM_CAMERA_BASE (MSM_FB_BASE + MSM_FB_SIZE)
+#define MSM_PMEM_CAMERA_SIZE 0xA00000
+
+#endif
diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c
new file mode 100644
index 0000000..074d84e
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-audio.c
@@ -0,0 +1,283 @@
+/* arch/arm/mach-msm/board-mahimahi-audio.c
+ *
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (C) 2009 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/gpio.h>
+#include <linux/delay.h>
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/htc_acoustic_qsd.h>
+
+#include "board-mahimahi.h"
+#include "proc_comm.h"
+#include "pmic.h"
+#include "board-mahimahi-tpa2018d1.h"
+
+#if 0
+#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static struct mutex mic_lock;
+static struct mutex bt_sco_lock;
+
+static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = {
+ [Q6_HW_HANDSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_HEADSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_SPEAKER] = {
+ .min_gain = -1500,
+ .max_gain = 0,
+ },
+ [Q6_HW_TTY] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_SCO] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_A2DP] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+};
+
+void mahimahi_headset_enable(int en)
+{
+ D("%s %d\n", __func__, en);
+ /* enable audio amp */
+ if (en) mdelay(15);
+ gpio_set_value(MAHIMAHI_AUD_JACKHP_EN, !!en);
+}
+
+void mahimahi_speaker_enable(int en)
+{
+ struct spkr_config_mode scm;
+ memset(&scm, 0, sizeof(scm));
+
+ D("%s %d\n", __func__, en);
+ if (en) {
+ scm.is_right_chan_en = 0;
+ scm.is_left_chan_en = 1;
+ scm.is_stereo_en = 0;
+ scm.is_hpf_en = 1;
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+ pmic_set_spkr_configuration(&scm);
+ pmic_spkr_en(LEFT_SPKR, 1);
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ /* unmute */
+ pmic_spkr_en_mute(LEFT_SPKR, 1);
+ } else {
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+
+ pmic_spkr_en(LEFT_SPKR, 0);
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ pmic_set_spkr_configuration(&scm);
+ }
+
+ if (is_cdma_version(system_rev))
+ tpa2018d1_set_speaker_amp(en);
+}
+
+void mahimahi_receiver_enable(int en)
+{
+ if (is_cdma_version(system_rev) &&
+ ((system_rev == 0xC1) || (system_rev == 0xC2))) {
+ struct spkr_config_mode scm;
+ memset(&scm, 0, sizeof(scm));
+
+ D("%s %d\n", __func__, en);
+ if (en) {
+ scm.is_right_chan_en = 1;
+ scm.is_left_chan_en = 0;
+ scm.is_stereo_en = 0;
+ scm.is_hpf_en = 1;
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+ pmic_set_spkr_configuration(&scm);
+ pmic_spkr_en(RIGHT_SPKR, 1);
+
+ /* unmute */
+ pmic_spkr_en_mute(RIGHT_SPKR, 1);
+ } else {
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ pmic_set_spkr_configuration(&scm);
+ }
+ }
+}
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for (n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static uint32_t bt_sco_enable[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 1, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 2, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 2, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+};
+
+static uint32_t bt_sco_disable[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 0, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 0, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 0, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+};
+
+void mahimahi_bt_sco_enable(int en)
+{
+ static int bt_sco_refcount;
+ D("%s %d\n", __func__, en);
+
+ mutex_lock(&bt_sco_lock);
+ if (en) {
+ if (++bt_sco_refcount == 1)
+ config_gpio_table(bt_sco_enable,
+ ARRAY_SIZE(bt_sco_enable));
+ } else {
+ if (--bt_sco_refcount == 0) {
+ config_gpio_table(bt_sco_disable,
+ ARRAY_SIZE(bt_sco_disable));
+ gpio_set_value(MAHIMAHI_BT_PCM_OUT, 0);
+ }
+ }
+ mutex_unlock(&bt_sco_lock);
+}
+
+void mahimahi_mic_enable(int en)
+{
+ static int old_state = 0, new_state = 0;
+
+ D("%s %d\n", __func__, en);
+
+ mutex_lock(&mic_lock);
+ if (!!en)
+ new_state++;
+ else
+ new_state--;
+
+ if (new_state == 1 && old_state == 0) {
+ gpio_set_value(MAHIMAHI_AUD_2V5_EN, 1);
+ mdelay(60);
+ } else if (new_state == 0 && old_state == 1)
+ gpio_set_value(MAHIMAHI_AUD_2V5_EN, 0);
+ else
+ D("%s: do nothing %d %d\n", __func__, old_state, new_state);
+
+ old_state = new_state;
+ mutex_unlock(&mic_lock);
+}
+
+void mahimahi_analog_init(void)
+{
+ D("%s\n", __func__);
+ /* stereo pmic init */
+ pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_spkr_en_right_chan(OFF_CMD);
+ pmic_spkr_en_left_chan(OFF_CMD);
+ pmic_spkr_add_right_left_chan(OFF_CMD);
+ pmic_spkr_en_stereo(OFF_CMD);
+ pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD);
+ pmic_spkr_bypass_mux(OFF_CMD);
+ pmic_spkr_en_hpf(ON_CMD);
+ pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD);
+ pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ);
+ pmic_mic_set_volt(MIC_VOLT_1_80V);
+
+ gpio_request(MAHIMAHI_AUD_JACKHP_EN, "aud_jackhp_en");
+ gpio_request(MAHIMAHI_BT_PCM_OUT, "bt_pcm_out");
+
+ gpio_direction_output(MAHIMAHI_AUD_JACKHP_EN, 0);
+
+ mutex_lock(&bt_sco_lock);
+ config_gpio_table(bt_sco_disable,
+ ARRAY_SIZE(bt_sco_disable));
+ gpio_direction_output(MAHIMAHI_BT_PCM_OUT, 0);
+ mutex_unlock(&bt_sco_lock);
+}
+
+int mahimahi_get_rx_vol(uint8_t hw, int level)
+{
+ int vol;
+
+ if (level > 100)
+ level = 100;
+ else if (level < 0)
+ level = 0;
+
+ if (is_cdma_version(system_rev) && hw == Q6_HW_HANDSET) {
+ int handset_volume[6] = { -1600, -1300, -1000, -600, -300, 0 };
+ vol = handset_volume[5 * level / 100];
+ } else {
+ struct q6_hw_info *info;
+ info = &q6_audio_hw[hw];
+ vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100;
+ }
+
+ D("%s %d\n", __func__, vol);
+ return vol;
+}
+
+static struct qsd_acoustic_ops acoustic = {
+ .enable_mic_bias = mahimahi_mic_enable,
+};
+
+static struct q6audio_analog_ops ops = {
+ .init = mahimahi_analog_init,
+ .speaker_enable = mahimahi_speaker_enable,
+ .headset_enable = mahimahi_headset_enable,
+ .receiver_enable = mahimahi_receiver_enable,
+ .bt_sco_enable = mahimahi_bt_sco_enable,
+ .int_mic_enable = mahimahi_mic_enable,
+ .ext_mic_enable = mahimahi_mic_enable,
+ .get_rx_vol = mahimahi_get_rx_vol,
+};
+
+void __init mahimahi_audio_init(void)
+{
+ mutex_init(&mic_lock);
+ mutex_init(&bt_sco_lock);
+ q6audio_register_analog_ops(&ops);
+ acoustic_register_ops(&acoustic);
+ if (is_cdma_version(system_rev) &&
+ ((system_rev == 0xC1) || (system_rev == 0xC2)))
+ q6audio_set_acdb_file("default_PMIC.acdb");
+}
diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.c b/arch/arm/mach-msm/board-mahimahi-flashlight.c
new file mode 100644
index 0000000..829b1f1
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-flashlight.c
@@ -0,0 +1,279 @@
+/*
+ * arch/arm/mach-msm/flashlight.c - flashlight driver
+ *
+ * Copyright (C) 2009 zion huang <zion_huang@htc.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; version 2 of the License.
+ */
+
+#define DEBUG
+
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/wakelock.h>
+#include <linux/hrtimer.h>
+#include <mach/msm_iomap.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+#include "board-mahimahi-flashlight.h"
+
+struct flashlight_struct {
+ struct led_classdev fl_lcdev;
+ struct early_suspend early_suspend_flashlight;
+ spinlock_t spin_lock;
+ struct hrtimer timer;
+ int brightness;
+ int gpio_torch;
+ int gpio_flash;
+ int flash_duration_ms;
+};
+
+static struct flashlight_struct the_fl;
+
+static inline void toggle(void)
+{
+ gpio_direction_output(the_fl.gpio_torch, 0);
+ udelay(2);
+ gpio_direction_output(the_fl.gpio_torch, 1);
+ udelay(2);
+}
+
+static void flashlight_hw_command(uint8_t addr, uint8_t data)
+{
+ int i;
+
+ for (i = 0; i < addr + 17; i++)
+ toggle();
+ udelay(500);
+
+ for (i = 0; i < data; i++)
+ toggle();
+ udelay(500);
+}
+
+static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ spin_lock_irqsave(&the_fl.spin_lock, flags);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ the_fl.brightness = LED_OFF;
+ spin_unlock_irqrestore(&the_fl.spin_lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+int flashlight_control(int mode)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ pr_debug("%s: mode %d -> %d\n", __func__,
+ the_fl.brightness, mode);
+
+ spin_lock_irqsave(&the_fl.spin_lock, flags);
+
+ the_fl.brightness = mode;
+
+ switch (mode) {
+ case FLASHLIGHT_TORCH:
+ pr_info("%s: half\n", __func__);
+ /* If we are transitioning from flash to torch, make sure to
+ * cancel the flash timeout timer, otherwise when it expires,
+ * the torch will go off as well.
+ */
+ hrtimer_cancel(&the_fl.timer);
+ flashlight_hw_command(2, 4);
+ break;
+
+ case FLASHLIGHT_FLASH:
+ pr_info("%s: full\n", __func__);
+ hrtimer_cancel(&the_fl.timer);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ udelay(40);
+ gpio_direction_output(the_fl.gpio_flash, 1);
+ hrtimer_start(&the_fl.timer,
+ ktime_set(the_fl.flash_duration_ms / 1000,
+ (the_fl.flash_duration_ms % 1000) *
+ NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ /* Flash overrides torch mode, and after the flash period, the
+ * flash LED will turn off.
+ */
+ mode = LED_OFF;
+ break;
+
+ case FLASHLIGHT_OFF:
+ pr_info("%s: off\n", __func__);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ gpio_direction_output(the_fl.gpio_torch, 0);
+ break;
+
+ default:
+ pr_err("%s: unknown flash_light flags: %d\n", __func__, mode);
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ spin_unlock_irqrestore(&the_fl.spin_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(flashlight_control);
+
+static void fl_lcdev_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int level;
+ switch (brightness) {
+ case LED_HALF:
+ level = FLASHLIGHT_TORCH;
+ break;
+ case LED_FULL:
+ level = FLASHLIGHT_FLASH;
+ break;
+ case LED_OFF:
+ default:
+ level = FLASHLIGHT_OFF;
+ };
+
+ flashlight_control(level);
+}
+
+static void flashlight_early_suspend(struct early_suspend *handler)
+{
+ flashlight_control(FLASHLIGHT_OFF);
+}
+
+static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata)
+{
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ if (fl_pdata->gpio_init) {
+ ret = fl_pdata->gpio_init();
+ if (ret < 0) {
+ pr_err("%s: gpio init failed: %d\n", __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ if (fl_pdata->torch) {
+ ret = gpio_request(fl_pdata->torch, "flashlight_torch");
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed\n", __func__);
+ return ret;
+ }
+ }
+
+ if (fl_pdata->flash) {
+ ret = gpio_request(fl_pdata->flash, "flashlight_flash");
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed\n", __func__);
+ gpio_free(fl_pdata->torch);
+ return ret;
+ }
+ }
+
+ the_fl.gpio_torch = fl_pdata->torch;
+ the_fl.gpio_flash = fl_pdata->flash;
+ the_fl.flash_duration_ms = fl_pdata->flash_duration_ms;
+ return 0;
+}
+
+static int flashlight_probe(struct platform_device *pdev)
+{
+ struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
+ int err = 0;
+
+ pr_debug("%s\n", __func__);
+
+ err = flashlight_setup_gpio(fl_pdata);
+ if (err < 0) {
+ pr_err("%s: setup GPIO failed\n", __func__);
+ goto fail_free_mem;
+ }
+
+ spin_lock_init(&the_fl.spin_lock);
+ the_fl.fl_lcdev.name = pdev->name;
+ the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set;
+ the_fl.fl_lcdev.brightness = LED_OFF;
+ err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev);
+ if (err < 0) {
+ pr_err("failed on led_classdev_register\n");
+ goto fail_free_gpio;
+ }
+
+ hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ the_fl.timer.function = flashlight_timeout;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend;
+ the_fl.early_suspend_flashlight.resume = NULL;
+ register_early_suspend(&the_fl.early_suspend_flashlight);
+#endif
+
+ return 0;
+
+fail_free_gpio:
+ if (fl_pdata->torch)
+ gpio_free(fl_pdata->torch);
+ if (fl_pdata->flash)
+ gpio_free(fl_pdata->flash);
+fail_free_mem:
+ return err;
+}
+
+static int flashlight_remove(struct platform_device *pdev)
+{
+ struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
+
+ pr_debug("%s\n", __func__);
+
+ hrtimer_cancel(&the_fl.timer);
+ unregister_early_suspend(&the_fl.early_suspend_flashlight);
+ flashlight_control(FLASHLIGHT_OFF);
+ led_classdev_unregister(&the_fl.fl_lcdev);
+ if (fl_pdata->torch)
+ gpio_free(fl_pdata->torch);
+ if (fl_pdata->flash)
+ gpio_free(fl_pdata->flash);
+ return 0;
+}
+
+static struct platform_driver flashlight_driver = {
+ .probe = flashlight_probe,
+ .remove = flashlight_remove,
+ .driver = {
+ .name = FLASHLIGHT_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init flashlight_init(void)
+{
+ pr_debug("%s\n", __func__);
+ return platform_driver_register(&flashlight_driver);
+}
+
+static void __exit flashlight_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&flashlight_driver);
+}
+
+module_init(flashlight_init);
+module_exit(flashlight_exit);
+
+MODULE_AUTHOR("Zion Huang <zion_huang@htc.com>");
+MODULE_DESCRIPTION("flash light driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.h b/arch/arm/mach-msm/board-mahimahi-flashlight.h
new file mode 100644
index 0000000..93b4095
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-flashlight.h
@@ -0,0 +1,20 @@
+#ifndef __ASM_ARM_ARCH_FLASHLIGHT_H
+#define __ASM_ARM_ARCH_FLASHLIGHT_H
+
+#define FLASHLIGHT_NAME "flashlight"
+
+#define FLASHLIGHT_OFF 0
+#define FLASHLIGHT_TORCH 1
+#define FLASHLIGHT_FLASH 2
+#define FLASHLIGHT_NUM 3
+
+struct flashlight_platform_data {
+ int (*gpio_init) (void);
+ int torch;
+ int flash;
+ int flash_duration_ms;
+};
+
+int flashlight_control(int level);
+
+#endif /*__ASM_ARM_ARCH_FLASHLIGHT_H*/
diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c
new file mode 100644
index 0000000..ab38847
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-keypad.c
@@ -0,0 +1,265 @@
+/* arch/arm/mach-msm/board-mahimahi-keypad.c
+ *
+ * Copyright (C) 2009 Google, Inc
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * Author: Dima Zavin <dima@android.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.
+ */
+
+#include <linux/gpio_event.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/keyreset.h>
+#include <linux/platform_device.h>
+#include <mach/vreg.h>
+
+#include <asm/mach-types.h>
+
+#include "board-mahimahi.h"
+
+struct jog_axis_info {
+ struct gpio_event_axis_info info;
+ uint16_t in_state;
+ uint16_t out_state;
+};
+
+static struct vreg *jog_vreg;
+static bool jog_just_on;
+static unsigned long jog_on_jiffies;
+
+static unsigned int mahimahi_col_gpios[] = { 33, 32, 31 };
+static unsigned int mahimahi_row_gpios[] = { 42, 41, 40 };
+
+#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(mahimahi_row_gpios) + (row))
+#define KEYMAP_SIZE (ARRAY_SIZE(mahimahi_col_gpios) * \
+ ARRAY_SIZE(mahimahi_row_gpios))
+
+/* keypad */
+static const unsigned short mahimahi_keymap[KEYMAP_SIZE] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE),
+};
+
+static const unsigned short mahimahi_cdma_keymap[KEYMAP_SIZE] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE),
+
+ /* Key (2, 2) is not a physical key on mahimahi. The purpose of
+ * registering the unused matrix key as a dummy <end> key is to make
+ * userland able to send/receive the key event for some requested tests
+ * in lab. of some CDMA carriers (e.g. Verizon).
+ */
+ [KEYMAP_INDEX(2, 2)] = KEY_END,
+};
+
+static struct gpio_event_matrix_info mahimahi_keypad_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = mahimahi_keymap,
+ .output_gpios = mahimahi_col_gpios,
+ .input_gpios = mahimahi_row_gpios,
+ .noutputs = ARRAY_SIZE(mahimahi_col_gpios),
+ .ninputs = ARRAY_SIZE(mahimahi_row_gpios),
+ .settle_time.tv.nsec = 40 * NSEC_PER_USEC,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ |
+ GPIOKPF_REMOVE_PHANTOM_KEYS |
+ GPIOKPF_PRINT_UNMAPPED_KEYS),
+};
+
+static struct gpio_event_direct_entry mahimahi_keypad_key_map[] = {
+ {
+ .gpio = MAHIMAHI_GPIO_POWER_KEY,
+ .code = KEY_POWER,
+ },
+};
+
+static struct gpio_event_input_info mahimahi_keypad_key_info = {
+ .info.func = gpio_event_input_func,
+ .info.no_suspend = true,
+ .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC,
+ .flags = 0,
+ .type = EV_KEY,
+ .keymap = mahimahi_keypad_key_map,
+ .keymap_size = ARRAY_SIZE(mahimahi_keypad_key_map)
+};
+
+/* jogball */
+static uint16_t jogball_axis_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ struct jog_axis_info *ai =
+ container_of(info, struct jog_axis_info, info);
+ uint16_t out = ai->out_state;
+
+ if (jog_just_on) {
+ if (jiffies == jog_on_jiffies || jiffies == jog_on_jiffies + 1)
+ goto ignore;
+ jog_just_on = 0;
+ }
+ if((ai->in_state ^ in) & 1)
+ out--;
+ if((ai->in_state ^ in) & 2)
+ out++;
+ ai->out_state = out;
+ignore:
+ ai->in_state = in;
+ return out;
+}
+
+static int jogball_power(const struct gpio_event_platform_data *pdata, bool on)
+{
+ if (on) {
+ vreg_enable(jog_vreg);
+ jog_just_on = 1;
+ jog_on_jiffies = jiffies;
+ } else {
+ vreg_disable(jog_vreg);
+ }
+
+ return 0;
+}
+
+static int jogball_power_cdma(const struct gpio_event_platform_data *pdata, bool on)
+{
+ if (on) {
+ gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 1);
+ jog_just_on = 1;
+ jog_on_jiffies = jiffies;
+ } else {
+ gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 0);
+ }
+
+ return 0;
+}
+
+static uint32_t jogball_x_gpios[] = {
+ MAHIMAHI_GPIO_BALL_LEFT, MAHIMAHI_GPIO_BALL_RIGHT,
+};
+static uint32_t jogball_y_gpios[] = {
+ MAHIMAHI_GPIO_BALL_UP, MAHIMAHI_GPIO_BALL_DOWN,
+};
+
+static struct jog_axis_info jogball_x_axis = {
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(jogball_x_gpios),
+ .dev = 1,
+ .type = EV_REL,
+ .code = REL_X,
+ .decoded_size = 1U << ARRAY_SIZE(jogball_x_gpios),
+ .map = jogball_axis_map,
+ .gpio = jogball_x_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,
+ }
+};
+
+static struct jog_axis_info jogball_y_axis = {
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(jogball_y_gpios),
+ .dev = 1,
+ .type = EV_REL,
+ .code = REL_Y,
+ .decoded_size = 1U << ARRAY_SIZE(jogball_y_gpios),
+ .map = jogball_axis_map,
+ .gpio = jogball_y_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,
+ }
+};
+
+static struct gpio_event_info *mahimahi_input_info[] = {
+ &mahimahi_keypad_matrix_info.info,
+ &mahimahi_keypad_key_info.info,
+ &jogball_x_axis.info.info,
+ &jogball_y_axis.info.info,
+};
+
+static struct gpio_event_platform_data mahimahi_input_data = {
+ .names = {
+ "mahimahi-keypad",
+ "mahimahi-nav",
+ NULL,
+ },
+ .info = mahimahi_input_info,
+ .info_count = ARRAY_SIZE(mahimahi_input_info),
+ .power = jogball_power,
+};
+
+static struct platform_device mahimahi_input_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 0,
+ .dev = {
+ .platform_data = &mahimahi_input_data,
+ },
+};
+
+static int mahimahi_reset_keys_up[] = {
+ KEY_VOLUMEUP,
+ 0,
+};
+
+static struct keyreset_platform_data mahimahi_reset_keys_pdata = {
+ .keys_up = mahimahi_reset_keys_up,
+ .keys_down = {
+ KEY_POWER,
+ KEY_VOLUMEDOWN,
+ BTN_MOUSE,
+ 0
+ },
+};
+
+struct platform_device mahimahi_reset_keys_device = {
+ .name = KEYRESET_NAME,
+ .dev = {
+ .platform_data = &mahimahi_reset_keys_pdata,
+ },
+};
+
+
+static int __init mahimahi_init_keypad_jogball(void)
+{
+ int ret;
+
+ if (!machine_is_mahimahi())
+ return 0;
+
+ ret = platform_device_register(&mahimahi_reset_keys_device);
+ if (ret != 0)
+ return ret;
+
+ if (is_cdma_version(system_rev)) {
+ mahimahi_keypad_matrix_info.keymap = mahimahi_cdma_keymap;
+ /* In the CDMA version, jogball power is supplied by a gpio. */
+ ret = gpio_request(MAHIMAHI_CDMA_JOG_2V6_EN, "jog_en");
+ if (ret < 0) {
+ pr_err("%s: gpio_request(%d) failed: %d\n", __func__,
+ MAHIMAHI_CDMA_JOG_2V6_EN, ret);
+ return ret;
+ }
+ mahimahi_input_data.power = jogball_power_cdma;
+ } else {
+ /* in UMTS version, jogball power is supplied by pmic */
+ jog_vreg = vreg_get(&mahimahi_input_device.dev, "gp2");
+ if (jog_vreg == NULL)
+ return -ENOENT;
+ }
+
+ ret = platform_device_register(&mahimahi_input_device);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+device_initcall(mahimahi_init_keypad_jogball);
diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c
new file mode 100644
index 0000000..da30672
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-microp.c
@@ -0,0 +1,2261 @@
+/* board-mahimahi-microp.c
+ * Copyright (C) 2009 Google.
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * The Microp on mahimahi is an i2c device that supports
+ * the following functions
+ * - LEDs (Green, Amber, Jogball backlight)
+ * - Lightsensor
+ * - Headset remotekeys
+ * - G-sensor
+ * - Interrupts
+ *
+ * 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <asm/uaccess.h>
+#include <linux/wakelock.h>
+#include <asm/mach-types.h>
+#include <mach/htc_pwrsink.h>
+#include <linux/earlysuspend.h>
+#include <linux/bma150.h>
+#include <linux/lightsensor.h>
+#include <asm/mach/mmc.h>
+#include <mach/htc_35mm_jack.h>
+#include <asm/setup.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+
+#include "board-mahimahi.h"
+
+
+#define MICROP_I2C_NAME "mahimahi-microp"
+
+#define MICROP_LSENSOR_ADC_CHAN 6
+#define MICROP_REMOTE_KEY_ADC_CHAN 7
+
+#define MICROP_I2C_WCMD_MISC 0x20
+#define MICROP_I2C_WCMD_SPI_EN 0x21
+#define MICROP_I2C_WCMD_AUTO_BL_CTL 0x23
+#define MICROP_I2C_RCMD_SPI_BL_STATUS 0x24
+#define MICROP_I2C_WCMD_BUTTONS_LED_CTRL 0x25
+#define MICROP_I2C_RCMD_VERSION 0x30
+#define MICROP_I2C_WCMD_ADC_TABLE 0x42
+#define MICROP_I2C_WCMD_LED_MODE 0x53
+#define MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME 0x54
+#define MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME 0x55
+#define MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME 0x57
+#define MICROP_I2C_WCMD_JOGBALL_LED_MODE 0x5A
+#define MICROP_I2C_RCMD_JOGBALL_LED_REMAIN_TIME 0x5B
+#define MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET 0x5C
+#define MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET 0x5D
+#define MICROP_I2C_WCMD_READ_ADC_VALUE_REQ 0x60
+#define MICROP_I2C_RCMD_ADC_VALUE 0x62
+#define MICROP_I2C_WCMD_REMOTEKEY_TABLE 0x63
+#define MICROP_I2C_WCMD_LCM_REGISTER 0x70
+#define MICROP_I2C_WCMD_GSENSOR_REG 0x73
+#define MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ 0x74
+#define MICROP_I2C_RCMD_GSENSOR_REG_DATA 0x75
+#define MICROP_I2C_WCMD_GSENSOR_DATA_REQ 0x76
+#define MICROP_I2C_RCMD_GSENSOR_X_DATA 0x77
+#define MICROP_I2C_RCMD_GSENSOR_Y_DATA 0x78
+#define MICROP_I2C_RCMD_GSENSOR_Z_DATA 0x79
+#define MICROP_I2C_RCMD_GSENSOR_DATA 0x7A
+#define MICROP_I2C_WCMD_OJ_REG 0x7B
+#define MICROP_I2C_WCMD_OJ_REG_DATA_REQ 0x7C
+#define MICROP_I2C_RCMD_OJ_REG_DATA 0x7D
+#define MICROP_I2C_WCMD_OJ_POS_DATA_REQ 0x7E
+#define MICROP_I2C_RCMD_OJ_POS_DATA 0x7F
+#define MICROP_I2C_WCMD_GPI_INT_CTL_EN 0x80
+#define MICROP_I2C_WCMD_GPI_INT_CTL_DIS 0x81
+#define MICROP_I2C_RCMD_GPI_INT_STATUS 0x82
+#define MICROP_I2C_RCMD_GPI_STATUS 0x83
+#define MICROP_I2C_WCMD_GPI_INT_STATUS_CLR 0x84
+#define MICROP_I2C_RCMD_GPI_INT_SETTING 0x85
+#define MICROP_I2C_RCMD_REMOTE_KEYCODE 0x87
+#define MICROP_I2C_WCMD_REMOTE_KEY_DEBN_TIME 0x88
+#define MICROP_I2C_WCMD_REMOTE_PLUG_DEBN_TIME 0x89
+#define MICROP_I2C_WCMD_SIMCARD_DEBN_TIME 0x8A
+#define MICROP_I2C_WCMD_GPO_LED_STATUS_EN 0x90
+#define MICROP_I2C_WCMD_GPO_LED_STATUS_DIS 0x91
+
+#define IRQ_GSENSOR (1<<10)
+#define IRQ_LSENSOR (1<<9)
+#define IRQ_REMOTEKEY (1<<7)
+#define IRQ_HEADSETIN (1<<2)
+#define IRQ_SDCARD (1<<0)
+
+#define READ_GPI_STATE_HPIN (1<<2)
+#define READ_GPI_STATE_SDCARD (1<<0)
+
+#define ALS_CALIBRATE_MODE 147
+
+/* Check pattern, to check if ALS has been calibrated */
+#define ALS_CALIBRATED 0x6DA5
+
+/* delay for deferred light sensor read */
+#define LS_READ_DELAY (HZ/2)
+
+/*#define DEBUG_BMA150 */
+#ifdef DEBUG_BMA150
+/* Debug logging of accelleration data */
+#define GSENSOR_LOG_MAX 2048 /* needs to be power of 2 */
+#define GSENSOR_LOG_MASK (GSENSOR_LOG_MAX - 1)
+
+struct gsensor_log {
+ ktime_t timestamp;
+ short x;
+ short y;
+ short z;
+};
+
+static DEFINE_MUTEX(gsensor_log_lock);
+static struct gsensor_log gsensor_log[GSENSOR_LOG_MAX];
+static unsigned gsensor_log_head;
+static unsigned gsensor_log_tail;
+
+void gsensor_log_status(ktime_t time, short x, short y, short z)
+{
+ unsigned n;
+ mutex_lock(&gsensor_log_lock);
+ n = gsensor_log_head;
+ gsensor_log[n].timestamp = time;
+ gsensor_log[n].x = x;
+ gsensor_log[n].y = y;
+ gsensor_log[n].z = z;
+ n = (n + 1) & GSENSOR_LOG_MASK;
+ if (n == gsensor_log_tail)
+ gsensor_log_tail = (gsensor_log_tail + 1) & GSENSOR_LOG_MASK;
+ gsensor_log_head = n;
+ mutex_unlock(&gsensor_log_lock);
+}
+
+static int gsensor_log_print(struct seq_file *sf, void *private)
+{
+ unsigned n;
+
+ mutex_lock(&gsensor_log_lock);
+ seq_printf(sf, "timestamp X Y Z\n");
+ for (n = gsensor_log_tail;
+ n != gsensor_log_head;
+ n = (n + 1) & GSENSOR_LOG_MASK) {
+ seq_printf(sf, "%10d.%010d %6d %6d %6d\n",
+ gsensor_log[n].timestamp.tv.sec,
+ gsensor_log[n].timestamp.tv.nsec,
+ gsensor_log[n].x, gsensor_log[n].y,
+ gsensor_log[n].z);
+ }
+ mutex_unlock(&gsensor_log_lock);
+ return 0;
+}
+
+static int gsensor_log_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, gsensor_log_print, NULL);
+}
+
+static struct file_operations gsensor_log_fops = {
+ .open = gsensor_log_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* def DEBUG_BMA150 */
+
+static int microp_headset_has_mic(void);
+static int microp_enable_headset_plug_event(void);
+static int microp_enable_key_event(void);
+static int microp_disable_key_event(void);
+
+static struct h35mm_platform_data mahimahi_h35mm_data = {
+ .plug_event_enable = microp_enable_headset_plug_event,
+ .headset_has_mic = microp_headset_has_mic,
+ .key_event_enable = microp_enable_key_event,
+ .key_event_disable = microp_disable_key_event,
+};
+
+static struct platform_device mahimahi_h35mm = {
+ .name = "htc_headset",
+ .id = -1,
+ .dev = {
+ .platform_data = &mahimahi_h35mm_data,
+ },
+};
+
+enum led_type {
+ GREEN_LED,
+ AMBER_LED,
+ RED_LED,
+ BLUE_LED,
+ JOGBALL_LED,
+ BUTTONS_LED,
+ NUM_LEDS,
+};
+
+static uint16_t lsensor_adc_table[10] = {
+ 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x26E, 0x3FF
+};
+
+static uint16_t remote_key_adc_table[6] = {
+ 0, 33, 43, 110, 129, 220
+};
+
+static uint32_t golden_adc = 0xC0;
+static uint32_t als_kadc;
+
+static struct wake_lock microp_i2c_wakelock;
+
+static struct i2c_client *private_microp_client;
+
+struct microp_int_pin {
+ uint16_t int_gsensor;
+ uint16_t int_lsensor;
+ uint16_t int_reset;
+ uint16_t int_simcard;
+ uint16_t int_hpin;
+ uint16_t int_remotekey;
+};
+
+struct microp_led_data {
+ int type;
+ struct led_classdev ldev;
+ struct mutex led_data_mutex;
+ struct work_struct brightness_work;
+ spinlock_t brightness_lock;
+ enum led_brightness brightness;
+ uint8_t mode;
+ uint8_t blink;
+};
+
+struct microp_i2c_work {
+ struct work_struct work;
+ struct i2c_client *client;
+ int (*intr_debounce)(uint8_t *pin_status);
+ void (*intr_function)(uint8_t *pin_status);
+};
+
+struct microp_i2c_client_data {
+ struct microp_led_data leds[NUM_LEDS];
+ uint16_t version;
+ struct microp_i2c_work work;
+ struct delayed_work hpin_debounce_work;
+ struct delayed_work ls_read_work;
+ struct early_suspend early_suspend;
+ uint8_t enable_early_suspend;
+ uint8_t enable_reset_button;
+ int microp_is_suspend;
+ int auto_backlight_enabled;
+ uint8_t light_sensor_enabled;
+ uint8_t force_light_sensor_read;
+ uint8_t button_led_value;
+ int headset_is_in;
+ int is_hpin_pin_stable;
+ struct input_dev *ls_input_dev;
+ uint32_t als_kadc;
+ uint32_t als_gadc;
+ uint8_t als_calibrating;
+};
+
+static char *hex2string(uint8_t *data, int len)
+{
+ static char buf[101];
+ int i;
+
+ i = (sizeof(buf) - 1) / 4;
+ if (len > i)
+ len = i;
+
+ for (i = 0; i < len; i++)
+ sprintf(buf + i * 4, "[%02X]", data[i]);
+
+ return buf;
+}
+
+#define I2C_READ_RETRY_TIMES 10
+#define I2C_WRITE_RETRY_TIMES 10
+
+static int i2c_read_block(struct i2c_client *client, uint8_t addr,
+ uint8_t *data, int length)
+{
+ int retry;
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ }
+ };
+
+ mdelay(1);
+ for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret == 2) {
+ dev_dbg(&client->dev, "R [%02X] = %s\n", addr,
+ hex2string(data, length));
+ return 0;
+ }
+ msleep(10);
+ }
+
+ dev_err(&client->dev, "i2c_read_block retry over %d\n",
+ I2C_READ_RETRY_TIMES);
+ return -EIO;
+}
+
+#define MICROP_I2C_WRITE_BLOCK_SIZE 21
+static int i2c_write_block(struct i2c_client *client, uint8_t addr,
+ uint8_t *data, int length)
+{
+ int retry;
+ uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE];
+ int ret;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = buf,
+ }
+ };
+
+ dev_dbg(&client->dev, "W [%02X] = %s\n", addr,
+ hex2string(data, length));
+
+ if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) {
+ dev_err(&client->dev, "i2c_write_block length too long\n");
+ return -E2BIG;
+ }
+
+ buf[0] = addr;
+ memcpy((void *)&buf[1], (void *)data, length);
+
+ mdelay(1);
+ for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) {
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret == 1)
+ return 0;
+ msleep(10);
+ }
+ dev_err(&client->dev, "i2c_write_block retry over %d\n",
+ I2C_WRITE_RETRY_TIMES);
+ return -EIO;
+}
+
+static int microp_read_adc(uint8_t channel, uint16_t *value)
+{
+ struct i2c_client *client;
+ int ret;
+ uint8_t cmd[2], data[2];
+
+ client = private_microp_client;
+ cmd[0] = 0;
+ cmd[1] = channel;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ,
+ cmd, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: request adc fail\n", __func__);
+ return -EIO;
+ }
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: read adc fail\n", __func__);
+ return -EIO;
+ }
+ *value = data[0] << 8 | data[1];
+ return 0;
+}
+
+static int microp_read_gpi_status(struct i2c_client *client, uint16_t *status)
+{
+ uint8_t data[2];
+ int ret;
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_STATUS, data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: read failed\n", __func__);
+ return -EIO;
+ }
+ *status = (data[0] << 8) | data[1];
+ return 0;
+}
+
+static int microp_interrupt_enable(struct i2c_client *client,
+ uint16_t interrupt_mask)
+{
+ uint8_t data[2];
+ int ret = -1;
+
+ data[0] = interrupt_mask >> 8;
+ data[1] = interrupt_mask & 0xFF;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_EN, data, 2);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: enable 0x%x interrupt failed\n",
+ __func__, interrupt_mask);
+ return ret;
+}
+
+static int microp_interrupt_disable(struct i2c_client *client,
+ uint16_t interrupt_mask)
+{
+ uint8_t data[2];
+ int ret = -1;
+
+ data[0] = interrupt_mask >> 8;
+ data[1] = interrupt_mask & 0xFF;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_DIS, data, 2);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: disable 0x%x interrupt failed\n",
+ __func__, interrupt_mask);
+ return ret;
+}
+
+
+/*
+ * SD slot card-detect support
+ */
+static unsigned int sdslot_cd = 0;
+static void (*sdslot_status_cb)(int card_present, void *dev_id);
+static void *sdslot_mmc_dev;
+
+int mahimahi_microp_sdslot_status_register(
+ void (*cb)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (sdslot_status_cb)
+ return -EBUSY;
+ sdslot_status_cb = cb;
+ sdslot_mmc_dev = dev_id;
+ return 0;
+}
+
+unsigned int mahimahi_microp_sdslot_status(struct device *dev)
+{
+ return sdslot_cd;
+}
+
+static void mahimahi_microp_sdslot_update_status(int status)
+{
+ sdslot_cd = !(status & READ_GPI_STATE_SDCARD);
+ if (sdslot_status_cb)
+ sdslot_status_cb(sdslot_cd, sdslot_mmc_dev);
+}
+
+/*
+ *Headset Support
+*/
+static void hpin_debounce_do_work(struct work_struct *work)
+{
+ uint16_t gpi_status = 0;
+ struct microp_i2c_client_data *cdata;
+ int insert = 0;
+ struct i2c_client *client;
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ microp_read_gpi_status(client, &gpi_status);
+ insert = (gpi_status & READ_GPI_STATE_HPIN) ? 0 : 1;
+ if (insert != cdata->headset_is_in) {
+ cdata->headset_is_in = insert;
+ pr_debug("headset %s\n", insert ? "inserted" : "removed");
+ htc_35mm_jack_plug_event(cdata->headset_is_in,
+ &cdata->is_hpin_pin_stable);
+ }
+}
+
+static int microp_enable_headset_plug_event(void)
+{
+ int ret;
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ uint16_t stat;
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ /* enable microp interrupt to detect changes */
+ ret = microp_interrupt_enable(client, IRQ_HEADSETIN);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed to enable irqs\n",
+ __func__);
+ return 0;
+ }
+ /* see if headset state has changed */
+ microp_read_gpi_status(client, &stat);
+ stat = !(stat & READ_GPI_STATE_HPIN);
+ if(cdata->headset_is_in != stat) {
+ cdata->headset_is_in = stat;
+ pr_debug("Headset state changed\n");
+ htc_35mm_jack_plug_event(stat, &cdata->is_hpin_pin_stable);
+ }
+
+ return 1;
+}
+
+static int microp_headset_detect_mic(void)
+{
+ uint16_t data;
+
+ microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &data);
+ if (data >= 200)
+ return 1;
+ else
+ return 0;
+}
+
+static int microp_headset_has_mic(void)
+{
+ int mic1 = -1;
+ int mic2 = -1;
+ int count = 0;
+
+ mic2 = microp_headset_detect_mic();
+
+ /* debounce the detection wait until 2 consecutive read are equal */
+ while ((mic1 != mic2) && (count < 10)) {
+ mic1 = mic2;
+ msleep(600);
+ mic2 = microp_headset_detect_mic();
+ count++;
+ }
+
+ pr_info("%s: microphone (%d) %s\n", __func__, count,
+ mic1 ? "present" : "not present");
+
+ return mic1;
+}
+
+static int microp_enable_key_event(void)
+{
+ int ret;
+ struct i2c_client *client;
+
+ client = private_microp_client;
+
+ if (!is_cdma_version(system_rev))
+ gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 1);
+
+ /* turn on key interrupt */
+ /* enable microp interrupt to detect changes */
+ ret = microp_interrupt_enable(client, IRQ_REMOTEKEY);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed to enable irqs\n",
+ __func__);
+ return ret;
+ }
+ return 0;
+}
+
+static int microp_disable_key_event(void)
+{
+ int ret;
+ struct i2c_client *client;
+
+ client = private_microp_client;
+
+ /* shutdown key interrupt */
+ if (!is_cdma_version(system_rev))
+ gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0);
+
+ /* disable microp interrupt to detect changes */
+ ret = microp_interrupt_disable(client, IRQ_REMOTEKEY);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed to disable irqs\n",
+ __func__);
+ return ret;
+ }
+ return 0;
+}
+
+static int get_remote_keycode(int *keycode)
+{
+ struct i2c_client *client = private_microp_client;
+ int ret;
+ uint8_t data[2];
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_REMOTE_KEYCODE, data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: read remote keycode fail\n",
+ __func__);
+ return -EIO;
+ }
+ pr_debug("%s: key = 0x%x\n", __func__, data[1]);
+ if (!data[1]) {
+ *keycode = 0;
+ return 1; /* no keycode */
+ } else {
+ *keycode = data[1];
+ }
+ return 0;
+}
+
+static ssize_t microp_i2c_remotekey_adc_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ uint16_t value;
+ int i, button = 0;
+ int ret;
+
+ client = to_i2c_client(dev);
+
+ microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &value);
+
+ for (i = 0; i < 3; i++) {
+ if ((value >= remote_key_adc_table[2 * i]) &&
+ (value <= remote_key_adc_table[2 * i + 1])) {
+ button = i + 1;
+ }
+
+ }
+
+ ret = sprintf(buf, "Remote Key[0x%03X] => button %d\n",
+ value, button);
+
+ return ret;
+}
+
+static DEVICE_ATTR(key_adc, 0644, microp_i2c_remotekey_adc_show, NULL);
+
+/*
+ * LED support
+*/
+static int microp_i2c_write_led_mode(struct i2c_client *client,
+ struct led_classdev *led_cdev,
+ uint8_t mode, uint16_t off_timer)
+{
+ struct microp_i2c_client_data *cdata;
+ struct microp_led_data *ldata;
+ uint8_t data[7];
+ int ret;
+
+ cdata = i2c_get_clientdata(client);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+
+
+ if (ldata->type == GREEN_LED) {
+ data[0] = 0x01;
+ data[1] = mode;
+ data[2] = off_timer >> 8;
+ data[3] = off_timer & 0xFF;
+ data[4] = 0x00;
+ data[5] = 0x00;
+ data[6] = 0x00;
+ } else if (ldata->type == AMBER_LED) {
+ data[0] = 0x02;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = mode;
+ data[5] = off_timer >> 8;
+ data[6] = off_timer & 0xFF;
+ } else if (ldata->type == RED_LED) {
+ data[0] = 0x02;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = mode? 5: 0;
+ data[5] = off_timer >> 8;
+ data[6] = off_timer & 0xFF;
+ } else if (ldata->type == BLUE_LED) {
+ data[0] = 0x04;
+ data[1] = mode;
+ data[2] = off_timer >> 8;
+ data[3] = off_timer & 0xFF;
+ data[4] = 0x00;
+ data[5] = 0x00;
+ data[6] = 0x00;
+ }
+
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_LED_MODE, data, 7);
+ if (ret == 0) {
+ mutex_lock(&ldata->led_data_mutex);
+ if (mode > 1)
+ ldata->blink = mode;
+ else
+ ldata->mode = mode;
+ mutex_unlock(&ldata->led_data_mutex);
+ }
+ return ret;
+}
+
+static ssize_t microp_i2c_led_blink_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ int ret;
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+
+ mutex_lock(&ldata->led_data_mutex);
+ ret = sprintf(buf, "%d\n", ldata->blink ? ldata->blink - 1 : 0);
+ mutex_unlock(&ldata->led_data_mutex);
+
+ return ret;
+}
+
+static ssize_t microp_i2c_led_blink_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ struct i2c_client *client;
+ int val, ret;
+ uint8_t mode;
+
+ val = -1;
+ sscanf(buf, "%u", &val);
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+ client = to_i2c_client(dev->parent);
+
+ mutex_lock(&ldata->led_data_mutex);
+ switch (val) {
+ case 0: /* stop flashing */
+ mode = ldata->mode;
+ ldata->blink = 0;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ mode = val + 1;
+ break;
+
+ default:
+ mutex_unlock(&ldata->led_data_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(&ldata->led_data_mutex);
+
+ ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff);
+ if (ret)
+ dev_err(&client->dev, "%s set blink failed\n", led_cdev->name);
+
+ return count;
+}
+
+static DEVICE_ATTR(blink, 0644, microp_i2c_led_blink_show,
+ microp_i2c_led_blink_store);
+
+static ssize_t microp_i2c_led_off_timer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct microp_i2c_client_data *cdata;
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ struct i2c_client *client;
+ uint8_t data[2];
+ int ret, offtime;
+
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+ client = to_i2c_client(dev->parent);
+ cdata = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "Getting %s remaining time\n", led_cdev->name);
+
+ if (ldata->type == GREEN_LED) {
+ ret = i2c_read_block(client,
+ MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME, data, 2);
+ } else if (ldata->type == AMBER_LED) {
+ ret = i2c_read_block(client,
+ MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME,
+ data, 2);
+ } else if (ldata->type == RED_LED) {
+ ret = i2c_read_block(client,
+ MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME,
+ data, 2);
+ } else if (ldata->type == BLUE_LED) {
+ ret = i2c_read_block(client,
+ MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME, data, 2);
+ } else {
+ dev_err(&client->dev, "Unknown led %s\n", ldata->ldev.name);
+ return -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(&client->dev,
+ "%s get off_timer failed\n", led_cdev->name);
+ }
+ offtime = (int)((data[1] | data[0] << 8) * 2);
+
+ ret = sprintf(buf, "Time remains %d:%d\n", offtime / 60, offtime % 60);
+ return ret;
+}
+
+static ssize_t microp_i2c_led_off_timer_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ struct i2c_client *client;
+ int min, sec, ret;
+ uint16_t off_timer;
+
+ min = -1;
+ sec = -1;
+ sscanf(buf, "%d %d", &min, &sec);
+
+ if (min < 0 || min > 255)
+ return -EINVAL;
+ if (sec < 0 || sec > 255)
+ return -EINVAL;
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+ client = to_i2c_client(dev->parent);
+
+ dev_dbg(&client->dev, "Setting %s off_timer to %d min %d sec\n",
+ led_cdev->name, min, sec);
+
+ if (!min && !sec)
+ off_timer = 0xFFFF;
+ else
+ off_timer = (min * 60 + sec) / 2;
+
+ ret = microp_i2c_write_led_mode(client, led_cdev,
+ ldata->mode, off_timer);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s set off_timer %d min %d sec failed\n",
+ led_cdev->name, min, sec);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(off_timer, 0644, microp_i2c_led_off_timer_show,
+ microp_i2c_led_off_timer_store);
+
+static ssize_t microp_i2c_jogball_color_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ struct i2c_client *client;
+ int rpwm, gpwm, bpwm, ret;
+ uint8_t data[4];
+
+ rpwm = -1;
+ gpwm = -1;
+ bpwm = -1;
+ sscanf(buf, "%d %d %d", &rpwm, &gpwm, &bpwm);
+
+ if (rpwm < 0 || rpwm > 255)
+ return -EINVAL;
+ if (gpwm < 0 || gpwm > 255)
+ return -EINVAL;
+ if (bpwm < 0 || bpwm > 255)
+ return -EINVAL;
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+ client = to_i2c_client(dev->parent);
+
+ dev_dbg(&client->dev, "Setting %s color to R=%d, G=%d, B=%d\n",
+ led_cdev->name, rpwm, gpwm, bpwm);
+
+ data[0] = rpwm;
+ data[1] = gpwm;
+ data[2] = bpwm;
+ data[3] = 0x00;
+
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET,
+ data, 4);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s set color R=%d G=%d B=%d failed\n",
+ led_cdev->name, rpwm, gpwm, bpwm);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(color, 0644, NULL, microp_i2c_jogball_color_store);
+
+static ssize_t microp_i2c_jogball_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *led_cdev;
+ struct microp_led_data *ldata;
+ struct i2c_client *client;
+ int period = -1;
+ int ret;
+ uint8_t data[4];
+
+ sscanf(buf, "%d", &period);
+
+ if (period < 2 || period > 12)
+ return -EINVAL;
+
+ led_cdev = (struct led_classdev *)dev_get_drvdata(dev);
+ ldata = container_of(led_cdev, struct microp_led_data, ldev);
+ client = to_i2c_client(dev->parent);
+
+ dev_info(&client->dev, "Setting Jogball flash period to %d\n", period);
+
+ data[0] = 0x00;
+ data[1] = period;
+
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET,
+ data, 2);
+ if (ret) {
+ dev_err(&client->dev, "%s set period=%d failed\n",
+ led_cdev->name, period);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(period, 0644, NULL, microp_i2c_jogball_period_store);
+
+static void microp_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ unsigned long flags;
+ struct i2c_client *client = to_i2c_client(led_cdev->dev->parent);
+ struct microp_led_data *ldata =
+ container_of(led_cdev, struct microp_led_data, ldev);
+
+ dev_dbg(&client->dev, "Setting %s brightness current %d new %d\n",
+ led_cdev->name, led_cdev->brightness, brightness);
+
+ if (brightness > 255)
+ brightness = 255;
+ led_cdev->brightness = brightness;
+
+ spin_lock_irqsave(&ldata->brightness_lock, flags);
+ ldata->brightness = brightness;
+ spin_unlock_irqrestore(&ldata->brightness_lock, flags);
+
+ schedule_work(&ldata->brightness_work);
+}
+
+static void microp_led_brightness_set_work(struct work_struct *work)
+{
+ unsigned long flags;
+ struct microp_led_data *ldata =
+ container_of(work, struct microp_led_data, brightness_work);
+ struct led_classdev *led_cdev = &ldata->ldev;
+
+ struct i2c_client *client = to_i2c_client(led_cdev->dev->parent);
+
+ enum led_brightness brightness;
+ int ret;
+ uint8_t mode;
+
+ spin_lock_irqsave(&ldata->brightness_lock, flags);
+ brightness = ldata->brightness;
+ spin_unlock_irqrestore(&ldata->brightness_lock, flags);
+
+ if (brightness)
+ mode = 1;
+ else
+ mode = 0;
+
+ ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff);
+ if (ret) {
+ dev_err(&client->dev,
+ "led_brightness_set failed to set mode\n");
+ }
+}
+
+struct device_attribute *green_amber_attrs[] = {
+ &dev_attr_blink,
+ &dev_attr_off_timer,
+};
+
+struct device_attribute *jogball_attrs[] = {
+ &dev_attr_color,
+ &dev_attr_period,
+};
+
+static void microp_led_buttons_brightness_set_work(struct work_struct *work)
+{
+
+ unsigned long flags;
+ struct microp_led_data *ldata =
+ container_of(work, struct microp_led_data, brightness_work);
+ struct led_classdev *led_cdev = &ldata->ldev;
+
+ struct i2c_client *client = to_i2c_client(led_cdev->dev->parent);
+ struct microp_i2c_client_data *cdata = i2c_get_clientdata(client);
+
+
+ uint8_t data[4] = {0, 0, 0};
+ int ret = 0;
+ enum led_brightness brightness;
+ uint8_t value;
+
+
+ spin_lock_irqsave(&ldata->brightness_lock, flags);
+ brightness = ldata->brightness;
+ spin_unlock_irqrestore(&ldata->brightness_lock, flags);
+
+ value = brightness >= 255 ? 0x20 : 0;
+
+ /* avoid a flicker that can occur when writing the same value */
+ if (cdata->button_led_value == value)
+ return;
+ cdata->button_led_value = value;
+
+ /* in 40ms */
+ data[0] = 0x05;
+ /* duty cycle 0-255 */
+ data[1] = value;
+ /* bit2 == change brightness */
+ data[3] = 0x04;
+
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_BUTTONS_LED_CTRL,
+ data, 4);
+ if (ret < 0)
+ dev_err(&client->dev, "%s failed on set buttons\n", __func__);
+}
+
+static void microp_led_jogball_brightness_set_work(struct work_struct *work)
+{
+ unsigned long flags;
+ struct microp_led_data *ldata =
+ container_of(work, struct microp_led_data, brightness_work);
+ struct led_classdev *led_cdev = &ldata->ldev;
+
+ struct i2c_client *client = to_i2c_client(led_cdev->dev->parent);
+ uint8_t data[3] = {0, 0, 0};
+ int ret = 0;
+ enum led_brightness brightness;
+
+ spin_lock_irqsave(&ldata->brightness_lock, flags);
+ brightness = ldata->brightness;
+ spin_unlock_irqrestore(&ldata->brightness_lock, flags);
+
+ switch (brightness) {
+ case 0:
+ data[0] = 0;
+ break;
+ case 3:
+ data[0] = 1;
+ data[1] = data[2] = 0xFF;
+ break;
+ case 7:
+ data[0] = 2;
+ data[1] = 0;
+ data[2] = 60;
+ break;
+ default:
+ dev_warn(&client->dev, "%s: unknown value: %d\n",
+ __func__, brightness);
+ break;
+ }
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_MODE,
+ data, 3);
+ if (ret < 0)
+ dev_err(&client->dev, "%s failed on set jogball mode:0x%2.2X\n",
+ __func__, data[0]);
+}
+
+/*
+ * Light Sensor Support
+ */
+static int microp_i2c_auto_backlight_mode(struct i2c_client *client,
+ uint8_t enabled)
+{
+ uint8_t data[2];
+ int ret = 0;
+
+ data[0] = 0;
+ if (enabled)
+ data[1] = 1;
+ else
+ data[1] = 0;
+
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2);
+ if (ret != 0)
+ pr_err("%s: set auto light sensor fail\n", __func__);
+
+ return ret;
+}
+
+static int lightsensor_enable(void)
+{
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ int ret;
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ if (cdata->microp_is_suspend) {
+ pr_err("%s: abort, uP is going to suspend after #\n",
+ __func__);
+ return -EIO;
+ }
+
+ disable_irq(client->irq);
+ ret = microp_i2c_auto_backlight_mode(client, 1);
+ if (ret < 0) {
+ pr_err("%s: set auto light sensor fail\n", __func__);
+ enable_irq(client->irq);
+ return ret;
+ }
+
+ cdata->auto_backlight_enabled = 1;
+ /* TEMPORARY HACK: schedule a deferred light sensor read
+ * to work around sensor manager race condition
+ */
+ schedule_delayed_work(&cdata->ls_read_work, LS_READ_DELAY);
+ schedule_work(&cdata->work.work);
+
+ return 0;
+}
+
+static int lightsensor_disable(void)
+{
+ /* update trigger data when done */
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ int ret;
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ if (cdata->microp_is_suspend) {
+ pr_err("%s: abort, uP is going to suspend after #\n",
+ __func__);
+ return -EIO;
+ }
+
+ cancel_delayed_work(&cdata->ls_read_work);
+
+ ret = microp_i2c_auto_backlight_mode(client, 0);
+ if (ret < 0)
+ pr_err("%s: disable auto light sensor fail\n",
+ __func__);
+ else
+ cdata->auto_backlight_enabled = 0;
+ return 0;
+}
+
+static int microp_lightsensor_read(uint16_t *adc_value,
+ uint8_t *adc_level)
+{
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ uint8_t i;
+ int ret;
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ ret = microp_read_adc(MICROP_LSENSOR_ADC_CHAN, adc_value);
+ if (ret != 0)
+ return -1;
+
+ if (*adc_value > 0x3FF) {
+ pr_warning("%s: get wrong value: 0x%X\n",
+ __func__, *adc_value);
+ return -1;
+ } else {
+ if (!cdata->als_calibrating) {
+ *adc_value = *adc_value
+ * cdata->als_gadc / cdata->als_kadc;
+ if (*adc_value > 0x3FF)
+ *adc_value = 0x3FF;
+ }
+
+ *adc_level = ARRAY_SIZE(lsensor_adc_table) - 1;
+ for (i = 0; i < ARRAY_SIZE(lsensor_adc_table); i++) {
+ if (*adc_value <= lsensor_adc_table[i]) {
+ *adc_level = i;
+ break;
+ }
+ }
+ pr_debug("%s: ADC value: 0x%X, level: %d #\n",
+ __func__, *adc_value, *adc_level);
+ }
+
+ return 0;
+}
+
+static ssize_t microp_i2c_lightsensor_adc_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ uint8_t adc_level = 0;
+ uint16_t adc_value = 0;
+ int ret;
+
+ ret = microp_lightsensor_read(&adc_value, &adc_level);
+
+ ret = sprintf(buf, "ADC[0x%03X] => level %d\n", adc_value, adc_level);
+
+ return ret;
+}
+
+static DEVICE_ATTR(ls_adc, 0644, microp_i2c_lightsensor_adc_show, NULL);
+
+static ssize_t microp_i2c_ls_auto_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ uint8_t data[2] = {0, 0};
+ int ret;
+
+ client = to_i2c_client(dev);
+
+ i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2);
+ ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n",
+ data[0], data[1]);
+
+ return ret;
+}
+
+static ssize_t microp_i2c_ls_auto_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ uint8_t enable = 0;
+ int ls_auto;
+
+ ls_auto = -1;
+ sscanf(buf, "%d", &ls_auto);
+
+ if (ls_auto != 0 && ls_auto != 1 && ls_auto != ALS_CALIBRATE_MODE)
+ return -EINVAL;
+
+ client = to_i2c_client(dev);
+ cdata = i2c_get_clientdata(client);
+
+ if (ls_auto) {
+ enable = 1;
+ cdata->als_calibrating = (ls_auto == ALS_CALIBRATE_MODE) ? 1 : 0;
+ cdata->auto_backlight_enabled = 1;
+ } else {
+ enable = 0;
+ cdata->als_calibrating = 0;
+ cdata->auto_backlight_enabled = 0;
+ }
+
+ microp_i2c_auto_backlight_mode(client, enable);
+
+ return count;
+}
+
+static DEVICE_ATTR(ls_auto, 0644, microp_i2c_ls_auto_show,
+ microp_i2c_ls_auto_store);
+
+DEFINE_MUTEX(api_lock);
+static int lightsensor_opened;
+
+static int lightsensor_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ pr_debug("%s\n", __func__);
+ mutex_lock(&api_lock);
+ if (lightsensor_opened) {
+ pr_err("%s: already opened\n", __func__);
+ rc = -EBUSY;
+ }
+ lightsensor_opened = 1;
+ mutex_unlock(&api_lock);
+ return rc;
+}
+
+static int lightsensor_release(struct inode *inode, struct file *file)
+{
+ pr_debug("%s\n", __func__);
+ mutex_lock(&api_lock);
+ lightsensor_opened = 0;
+ mutex_unlock(&api_lock);
+ return 0;
+}
+
+static long lightsensor_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc, val;
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+
+ mutex_lock(&api_lock);
+
+ client = private_microp_client;
+ cdata = i2c_get_clientdata(client);
+
+ pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case LIGHTSENSOR_IOCTL_ENABLE:
+ if (get_user(val, (unsigned long __user *)arg)) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = val ? lightsensor_enable() : lightsensor_disable();
+ break;
+ case LIGHTSENSOR_IOCTL_GET_ENABLED:
+ val = cdata->auto_backlight_enabled;
+ pr_debug("%s enabled %d\n", __func__, val);
+ rc = put_user(val, (unsigned long __user *)arg);
+ break;
+ default:
+ pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&api_lock);
+ return rc;
+}
+
+static struct file_operations lightsensor_fops = {
+ .owner = THIS_MODULE,
+ .open = lightsensor_open,
+ .release = lightsensor_release,
+ .unlocked_ioctl = lightsensor_ioctl
+};
+
+struct miscdevice lightsensor_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "lightsensor",
+ .fops = &lightsensor_fops
+};
+
+/*
+ * G-sensor
+ */
+static int microp_spi_enable(uint8_t on)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = private_microp_client;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &on, 1);
+ if (ret < 0) {
+ dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__);
+ return ret;
+ }
+ msleep(10);
+ return ret;
+}
+
+static int gsensor_read_reg(uint8_t reg, uint8_t *data)
+{
+ struct i2c_client *client;
+ int ret;
+ uint8_t tmp[2];
+
+ client = private_microp_client;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ,
+ ®, 1);
+ if (ret < 0) {
+ dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__);
+ return ret;
+ }
+ msleep(10);
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_REG_DATA, tmp, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,"%s: i2c_read_block fail\n", __func__);
+ return ret;
+ }
+ *data = tmp[1];
+ return ret;
+}
+
+static int gsensor_write_reg(uint8_t reg, uint8_t data)
+{
+ struct i2c_client *client;
+ int ret;
+ uint8_t tmp[2];
+
+ client = private_microp_client;
+
+ tmp[0] = reg;
+ tmp[1] = data;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG, tmp, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int gsensor_read_acceleration(short *buf)
+{
+ struct i2c_client *client;
+ int ret;
+ uint8_t tmp[6];
+ struct microp_i2c_client_data *cdata;
+
+ client = private_microp_client;
+
+ cdata = i2c_get_clientdata(client);
+
+ tmp[0] = 1;
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_DATA_REQ,
+ tmp, 1);
+ if (ret < 0) {
+ dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__);
+ return ret;
+ }
+
+ msleep(10);
+
+ if (cdata->version <= 0x615) {
+ /*
+ * Note the data is a 10bit signed value from the chip.
+ */
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_X_DATA,
+ tmp, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: i2c_read_block fail\n",
+ __func__);
+ return ret;
+ }
+ buf[0] = (short)(tmp[0] << 8 | tmp[1]);
+ buf[0] >>= 6;
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Y_DATA,
+ tmp, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: i2c_read_block fail\n",
+ __func__);
+ return ret;
+ }
+ buf[1] = (short)(tmp[0] << 8 | tmp[1]);
+ buf[1] >>= 6;
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Z_DATA,
+ tmp, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: i2c_read_block fail\n",
+ __func__);
+ return ret;
+ }
+ buf[2] = (short)(tmp[0] << 8 | tmp[1]);
+ buf[2] >>= 6;
+ } else {
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_DATA,
+ tmp, 6);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: i2c_read_block fail\n",
+ __func__);
+ return ret;
+ }
+ buf[0] = (short)(tmp[0] << 8 | tmp[1]);
+ buf[0] >>= 6;
+ buf[1] = (short)(tmp[2] << 8 | tmp[3]);
+ buf[1] >>= 6;
+ buf[2] = (short)(tmp[4] << 8 | tmp[5]);
+ buf[2] >>= 6;
+ }
+
+#ifdef DEBUG_BMA150
+ /* Log this to debugfs */
+ gsensor_log_status(ktime_get(), buf[0], buf[1], buf[2]);
+#endif
+ return 1;
+}
+
+static int gsensor_init_hw(void)
+{
+ uint8_t reg;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ microp_spi_enable(1);
+
+ ret = gsensor_read_reg(RANGE_BWIDTH_REG, ®);
+ if (ret < 0 )
+ return -EIO;
+ reg &= 0xe0;
+ ret = gsensor_write_reg(RANGE_BWIDTH_REG, reg);
+ if (ret < 0 )
+ return -EIO;
+
+ ret = gsensor_read_reg(SMB150_CONF2_REG, ®);
+ if (ret < 0 )
+ return -EIO;
+ reg |= (1 << 3);
+ ret = gsensor_write_reg(SMB150_CONF2_REG, reg);
+
+ return ret;
+}
+
+static int bma150_set_mode(char mode)
+{
+ uint8_t reg;
+ int ret;
+
+ pr_debug("%s mode = %d\n", __func__, mode);
+ if (mode == BMA_MODE_NORMAL)
+ microp_spi_enable(1);
+
+
+ ret = gsensor_read_reg(SMB150_CTRL_REG, ®);
+ if (ret < 0 )
+ return -EIO;
+ reg = (reg & 0xfe) | mode;
+ ret = gsensor_write_reg(SMB150_CTRL_REG, reg);
+
+ if (mode == BMA_MODE_SLEEP)
+ microp_spi_enable(0);
+
+ return ret;
+}
+static int gsensor_read(uint8_t *data)
+{
+ int ret;
+ uint8_t reg = data[0];
+
+ ret = gsensor_read_reg(reg, &data[1]);
+ pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]);
+ return ret;
+}
+
+static int gsensor_write(uint8_t *data)
+{
+ int ret;
+ uint8_t reg = data[0];
+
+ pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]);
+ ret = gsensor_write_reg(reg, data[1]);
+ return ret;
+}
+
+static int bma150_open(struct inode *inode, struct file *file)
+{
+ pr_debug("%s\n", __func__);
+ return nonseekable_open(inode, file);
+}
+
+static int bma150_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int bma150_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ char rwbuf[8];
+ int ret = -1;
+ short buf[8], temp;
+
+ switch (cmd) {
+ case BMA_IOCTL_READ:
+ case BMA_IOCTL_WRITE:
+ case BMA_IOCTL_SET_MODE:
+ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ if (copy_from_user(&buf, argp, sizeof(buf)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case BMA_IOCTL_INIT:
+ ret = gsensor_init_hw();
+ if (ret < 0)
+ return ret;
+ break;
+
+ case BMA_IOCTL_READ:
+ if (rwbuf[0] < 1)
+ return -EINVAL;
+ ret = gsensor_read(rwbuf);
+ if (ret < 0)
+ return ret;
+ break;
+ case BMA_IOCTL_WRITE:
+ if (rwbuf[0] < 2)
+ return -EINVAL;
+ ret = gsensor_write(rwbuf);
+ if (ret < 0)
+ return ret;
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ ret = gsensor_read_acceleration(&buf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case BMA_IOCTL_SET_MODE:
+ bma150_set_mode(rwbuf[0]);
+ break;
+ case BMA_IOCTL_GET_INT:
+ temp = 0;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case BMA_IOCTL_READ:
+ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ if (copy_to_user(argp, &buf, sizeof(buf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_GET_INT:
+ if (copy_to_user(argp, &temp, sizeof(temp)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct file_operations bma_fops = {
+ .owner = THIS_MODULE,
+ .open = bma150_open,
+ .release = bma150_release,
+ .ioctl = bma150_ioctl,
+};
+
+static struct miscdevice spi_bma_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = BMA150_G_SENSOR_NAME,
+ .fops = &bma_fops,
+};
+
+/*
+ * Interrupt
+ */
+static irqreturn_t microp_i2c_intr_irq_handler(int irq, void *dev_id)
+{
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+
+ client = to_i2c_client(dev_id);
+ cdata = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "intr_irq_handler\n");
+
+ disable_irq_nosync(client->irq);
+ schedule_work(&cdata->work.work);
+ return IRQ_HANDLED;
+}
+
+static void microp_i2c_intr_work_func(struct work_struct *work)
+{
+ struct microp_i2c_work *up_work;
+ struct i2c_client *client;
+ struct microp_i2c_client_data *cdata;
+ uint8_t data[3], adc_level;
+ uint16_t intr_status = 0, adc_value, gpi_status = 0;
+ int keycode = 0, ret = 0;
+
+ up_work = container_of(work, struct microp_i2c_work, work);
+ client = up_work->client;
+ cdata = i2c_get_clientdata(client);
+
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS, data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: read interrupt status fail\n",
+ __func__);
+ }
+
+ intr_status = data[0]<<8 | data[1];
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR,
+ data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: clear interrupt status fail\n",
+ __func__);
+ }
+ pr_debug("intr_status=0x%02x\n", intr_status);
+
+ if ((intr_status & IRQ_LSENSOR) || cdata->force_light_sensor_read) {
+ ret = microp_lightsensor_read(&adc_value, &adc_level);
+ if (cdata->force_light_sensor_read) {
+ /* report an invalid value first to ensure we trigger an event
+ * when adc_level is zero.
+ */
+ input_report_abs(cdata->ls_input_dev, ABS_MISC, -1);
+ input_sync(cdata->ls_input_dev);
+ cdata->force_light_sensor_read = 0;
+ }
+ input_report_abs(cdata->ls_input_dev, ABS_MISC, (int)adc_level);
+ input_sync(cdata->ls_input_dev);
+ }
+
+ if (intr_status & IRQ_SDCARD) {
+ microp_read_gpi_status(client, &gpi_status);
+ mahimahi_microp_sdslot_update_status(gpi_status);
+ }
+
+ if (intr_status & IRQ_HEADSETIN) {
+ cdata->is_hpin_pin_stable = 0;
+ wake_lock_timeout(µp_i2c_wakelock, 3*HZ);
+ if (!cdata->headset_is_in)
+ schedule_delayed_work(&cdata->hpin_debounce_work,
+ msecs_to_jiffies(500));
+ else
+ schedule_delayed_work(&cdata->hpin_debounce_work,
+ msecs_to_jiffies(300));
+ }
+ if (intr_status & IRQ_REMOTEKEY) {
+ if ((get_remote_keycode(&keycode) == 0) &&
+ (cdata->is_hpin_pin_stable)) {
+ htc_35mm_key_event(keycode, &cdata->is_hpin_pin_stable);
+ }
+ }
+
+ enable_irq(client->irq);
+}
+
+static void ls_read_do_work(struct work_struct *work)
+{
+ struct i2c_client *client = private_microp_client;
+ struct microp_i2c_client_data *cdata = i2c_get_clientdata(client);
+
+ /* force a light sensor reading */
+ disable_irq(client->irq);
+ cdata->force_light_sensor_read = 1;
+ schedule_work(&cdata->work.work);
+}
+
+static int microp_function_initialize(struct i2c_client *client)
+{
+ struct microp_i2c_client_data *cdata;
+ uint8_t data[20];
+ uint16_t stat, interrupts = 0;
+ int i;
+ int ret;
+ struct led_classdev *led_cdev;
+
+ cdata = i2c_get_clientdata(client);
+
+ /* Light Sensor */
+ if (als_kadc >> 16 == ALS_CALIBRATED)
+ cdata->als_kadc = als_kadc & 0xFFFF;
+ else {
+ cdata->als_kadc = 0;
+ pr_info("%s: no ALS calibrated\n", __func__);
+ }
+
+ if (cdata->als_kadc && golden_adc) {
+ cdata->als_kadc =
+ (cdata->als_kadc > 0 && cdata->als_kadc < 0x400)
+ ? cdata->als_kadc : golden_adc;
+ cdata->als_gadc =
+ (golden_adc > 0)
+ ? golden_adc : cdata->als_kadc;
+ } else {
+ cdata->als_kadc = 1;
+ cdata->als_gadc = 1;
+ }
+ pr_info("%s: als_kadc=0x%x, als_gadc=0x%x\n",
+ __func__, cdata->als_kadc, cdata->als_gadc);
+
+ for (i = 0; i < 10; i++) {
+ data[i] = (uint8_t)(lsensor_adc_table[i]
+ * cdata->als_kadc / cdata->als_gadc >> 8);
+ data[i + 10] = (uint8_t)(lsensor_adc_table[i]
+ * cdata->als_kadc / cdata->als_gadc);
+ }
+ ret = i2c_write_block(client, MICROP_I2C_WCMD_ADC_TABLE, data, 20);
+ if (ret)
+ goto exit;
+
+ ret = gpio_request(MAHIMAHI_GPIO_LS_EN_N, "microp_i2c");
+ if (ret < 0) {
+ dev_err(&client->dev, "failed on request gpio ls_on\n");
+ goto exit;
+ }
+ ret = gpio_direction_output(MAHIMAHI_GPIO_LS_EN_N, 0);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed on gpio_direction_output"
+ "ls_on\n");
+ goto err_gpio_ls;
+ }
+ cdata->light_sensor_enabled = 1;
+
+ /* Headset */
+ for (i = 0; i < 6; i++) {
+ data[i] = (uint8_t)(remote_key_adc_table[i] >> 8);
+ data[i + 6] = (uint8_t)(remote_key_adc_table[i]);
+ }
+ ret = i2c_write_block(client,
+ MICROP_I2C_WCMD_REMOTEKEY_TABLE, data, 12);
+ if (ret)
+ goto exit;
+
+ INIT_DELAYED_WORK(
+ &cdata->hpin_debounce_work, hpin_debounce_do_work);
+ INIT_DELAYED_WORK(
+ &cdata->ls_read_work, ls_read_do_work);
+
+ /* SD Card */
+ interrupts |= IRQ_SDCARD;
+
+ /* set LED initial state */
+ for (i = 0; i < BLUE_LED; i++) {
+ led_cdev = &cdata->leds[i].ldev;
+ microp_i2c_write_led_mode(client, led_cdev, 0, 0xffff);
+ }
+
+ /* enable the interrupts */
+ ret = microp_interrupt_enable(client, interrupts);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed to enable gpi irqs\n",
+ __func__);
+ goto err_irq_en;
+ }
+
+ microp_read_gpi_status(client, &stat);
+ mahimahi_microp_sdslot_update_status(stat);
+
+ return 0;
+
+err_irq_en:
+err_gpio_ls:
+ gpio_free(MAHIMAHI_GPIO_LS_EN_N);
+exit:
+ return ret;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void microp_early_suspend(struct early_suspend *h)
+{
+ struct microp_i2c_client_data *cdata;
+ struct i2c_client *client = private_microp_client;
+ int ret;
+
+ if (!client) {
+ pr_err("%s: dataset: client is empty\n", __func__);
+ return;
+ }
+ cdata = i2c_get_clientdata(client);
+
+ cdata->microp_is_suspend = 1;
+
+ disable_irq(client->irq);
+ ret = cancel_work_sync(&cdata->work.work);
+ if (ret != 0) {
+ enable_irq(client->irq);
+ }
+
+ if (cdata->auto_backlight_enabled)
+ microp_i2c_auto_backlight_mode(client, 0);
+ if (cdata->light_sensor_enabled == 1) {
+ gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 1);
+ cdata->light_sensor_enabled = 0;
+ }
+}
+
+void microp_early_resume(struct early_suspend *h)
+{
+ struct i2c_client *client = private_microp_client;
+ struct microp_i2c_client_data *cdata;
+
+ if (!client) {
+ pr_err("%s: dataset: client is empty\n", __func__);
+ return;
+ }
+ cdata = i2c_get_clientdata(client);
+
+ gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 0);
+ cdata->light_sensor_enabled = 1;
+
+ if (cdata->auto_backlight_enabled)
+ microp_i2c_auto_backlight_mode(client, 1);
+
+ cdata->microp_is_suspend = 0;
+ enable_irq(client->irq);
+}
+#endif
+
+static int microp_i2c_suspend(struct i2c_client *client,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int microp_i2c_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static struct {
+ const char *name;
+ void (*led_set_work)(struct work_struct *);
+ struct device_attribute **attrs;
+ int attr_cnt;
+} microp_leds[] = {
+ [GREEN_LED] = {
+ .name = "green",
+ .led_set_work = microp_led_brightness_set_work,
+ .attrs = green_amber_attrs,
+ .attr_cnt = ARRAY_SIZE(green_amber_attrs)
+ },
+ [AMBER_LED] = {
+ .name = "amber",
+ .led_set_work = microp_led_brightness_set_work,
+ .attrs = green_amber_attrs,
+ .attr_cnt = ARRAY_SIZE(green_amber_attrs)
+ },
+ [RED_LED] = {
+ .name = "red",
+ .led_set_work = microp_led_brightness_set_work,
+ .attrs = green_amber_attrs,
+ .attr_cnt = ARRAY_SIZE(green_amber_attrs)
+ },
+ [BLUE_LED] = {
+ .name = "blue",
+ .led_set_work = microp_led_brightness_set_work,
+ .attrs = green_amber_attrs,
+ .attr_cnt = ARRAY_SIZE(green_amber_attrs)
+ },
+ [JOGBALL_LED] = {
+ .name = "jogball-backlight",
+ .led_set_work = microp_led_jogball_brightness_set_work,
+ .attrs = jogball_attrs,
+ .attr_cnt = ARRAY_SIZE(jogball_attrs)
+ },
+ [BUTTONS_LED] = {
+ .name = "button-backlight",
+ .led_set_work = microp_led_buttons_brightness_set_work
+ },
+};
+
+static int microp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct microp_i2c_client_data *cdata;
+ uint8_t data[6];
+ int ret;
+ int i;
+ int j;
+
+ private_microp_client = client;
+ ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2);
+ if (ret || !(data[0] && data[1])) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "failed on get microp version\n");
+ goto err_exit;
+ }
+ dev_info(&client->dev, "microp version [%02X][%02X]\n",
+ data[0], data[1]);
+
+ ret = gpio_request(MAHIMAHI_GPIO_UP_RESET_N, "microp_i2c_wm");
+ if (ret < 0) {
+ dev_err(&client->dev, "failed on request gpio reset\n");
+ goto err_exit;
+ }
+ ret = gpio_direction_output(MAHIMAHI_GPIO_UP_RESET_N, 1);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed on gpio_direction_output reset\n");
+ goto err_gpio_reset;
+ }
+
+ cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL);
+ if (!cdata) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "failed on allocat cdata\n");
+ goto err_cdata;
+ }
+
+ i2c_set_clientdata(client, cdata);
+ cdata->version = data[0] << 8 | data[1];
+ cdata->microp_is_suspend = 0;
+ cdata->auto_backlight_enabled = 0;
+ cdata->light_sensor_enabled = 0;
+
+ wake_lock_init(µp_i2c_wakelock, WAKE_LOCK_SUSPEND,
+ "microp_i2c_present");
+
+ /* Light Sensor */
+ ret = device_create_file(&client->dev, &dev_attr_ls_adc);
+ ret = device_create_file(&client->dev, &dev_attr_ls_auto);
+ cdata->ls_input_dev = input_allocate_device();
+ if (!cdata->ls_input_dev) {
+ pr_err("%s: could not allocate input device\n", __func__);
+ ret = -ENOMEM;
+ goto err_request_input_dev;
+ }
+ cdata->ls_input_dev->name = "lightsensor-level";
+ set_bit(EV_ABS, cdata->ls_input_dev->evbit);
+ input_set_abs_params(cdata->ls_input_dev, ABS_MISC, 0, 9, 0, 0);
+
+ ret = input_register_device(cdata->ls_input_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: can not register input device\n",
+ __func__);
+ goto err_register_input_dev;
+ }
+
+ ret = misc_register(&lightsensor_misc);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: can not register misc device\n",
+ __func__);
+ goto err_register_misc_register;
+ }
+
+ /* LEDs */
+ ret = 0;
+ for (i = 0; i < ARRAY_SIZE(microp_leds) && !ret; ++i) {
+ struct microp_led_data *ldata = &cdata->leds[i];
+
+ ldata->type = i;
+ ldata->ldev.name = microp_leds[i].name;
+ ldata->ldev.brightness_set = microp_brightness_set;
+ mutex_init(&ldata->led_data_mutex);
+ INIT_WORK(&ldata->brightness_work, microp_leds[i].led_set_work);
+ spin_lock_init(&ldata->brightness_lock);
+ ret = led_classdev_register(&client->dev, &ldata->ldev);
+ if (ret) {
+ ldata->ldev.name = NULL;
+ break;
+ }
+
+ for (j = 0; j < microp_leds[i].attr_cnt && !ret; ++j)
+ ret = device_create_file(ldata->ldev.dev,
+ microp_leds[i].attrs[j]);
+ }
+ if (ret) {
+ dev_err(&client->dev, "failed to add leds\n");
+ goto err_add_leds;
+ }
+
+ /* Headset */
+ cdata->headset_is_in = 0;
+ cdata->is_hpin_pin_stable = 1;
+ platform_device_register(&mahimahi_h35mm);
+
+ ret = device_create_file(&client->dev, &dev_attr_key_adc);
+
+ /* G-sensor */
+ ret = misc_register(&spi_bma_device);
+ if (ret < 0) {
+ pr_err("%s: init bma150 misc_register fail\n",
+ __func__);
+ goto err_register_bma150;
+ }
+#ifdef DEBUG_BMA150
+ debugfs_create_file("gsensor_log", 0444, NULL, NULL, &gsensor_log_fops);
+#endif
+ /* Setup IRQ handler */
+ INIT_WORK(&cdata->work.work, microp_i2c_intr_work_func);
+ cdata->work.client = client;
+
+ ret = request_irq(client->irq,
+ microp_i2c_intr_irq_handler,
+ IRQF_TRIGGER_LOW,
+ "microp_interrupt",
+ &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "request_irq failed\n");
+ goto err_intr;
+ }
+ ret = set_irq_wake(client->irq, 1);
+ if (ret) {
+ dev_err(&client->dev, "set_irq_wake failed\n");
+ goto err_intr;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (cdata->enable_early_suspend) {
+ cdata->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ cdata->early_suspend.suspend = microp_early_suspend;
+ cdata->early_suspend.resume = microp_early_resume;
+ register_early_suspend(&cdata->early_suspend);
+ }
+#endif
+
+ ret = microp_function_initialize(client);
+ if (ret) {
+ dev_err(&client->dev, "failed on microp function initialize\n");
+ goto err_fun_init;
+ }
+
+ return 0;
+
+err_fun_init:
+err_intr:
+ misc_deregister(&spi_bma_device);
+
+err_register_bma150:
+ platform_device_unregister(&mahimahi_h35mm);
+ device_remove_file(&client->dev, &dev_attr_key_adc);
+
+err_add_leds:
+ for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) {
+ if (!cdata->leds[i].ldev.name)
+ continue;
+ led_classdev_unregister(&cdata->leds[i].ldev);
+ for (j = 0; j < microp_leds[i].attr_cnt; ++j)
+ device_remove_file(cdata->leds[i].ldev.dev,
+ microp_leds[i].attrs[j]);
+ }
+
+ misc_deregister(&lightsensor_misc);
+
+err_register_misc_register:
+ input_unregister_device(cdata->ls_input_dev);
+
+err_register_input_dev:
+ input_free_device(cdata->ls_input_dev);
+
+err_request_input_dev:
+ wake_lock_destroy(µp_i2c_wakelock);
+ device_remove_file(&client->dev, &dev_attr_ls_adc);
+ device_remove_file(&client->dev, &dev_attr_ls_auto);
+ kfree(cdata);
+ i2c_set_clientdata(client, NULL);
+
+err_cdata:
+err_gpio_reset:
+ gpio_free(MAHIMAHI_GPIO_UP_RESET_N);
+err_exit:
+ return ret;
+}
+
+static int __devexit microp_i2c_remove(struct i2c_client *client)
+{
+ struct microp_i2c_client_data *cdata;
+ int i;
+ int j;
+
+ cdata = i2c_get_clientdata(client);
+
+ for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) {
+ struct microp_led_data *ldata = &cdata->leds[i];
+ cancel_work_sync(&ldata->brightness_work);
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (cdata->enable_early_suspend) {
+ unregister_early_suspend(&cdata->early_suspend);
+ }
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) {
+ if (!cdata->leds[i].ldev.name)
+ continue;
+ led_classdev_unregister(&cdata->leds[i].ldev);
+ for (j = 0; j < microp_leds[i].attr_cnt; ++j)
+ device_remove_file(cdata->leds[i].ldev.dev,
+ microp_leds[i].attrs[j]);
+ }
+
+ free_irq(client->irq, &client->dev);
+
+ gpio_free(MAHIMAHI_GPIO_UP_RESET_N);
+
+ misc_deregister(&lightsensor_misc);
+ input_unregister_device(cdata->ls_input_dev);
+ input_free_device(cdata->ls_input_dev);
+ device_remove_file(&client->dev, &dev_attr_ls_adc);
+ device_remove_file(&client->dev, &dev_attr_key_adc);
+ device_remove_file(&client->dev, &dev_attr_ls_auto);
+
+ platform_device_unregister(&mahimahi_h35mm);
+
+ /* G-sensor */
+ misc_deregister(&spi_bma_device);
+
+ kfree(cdata);
+
+ return 0;
+}
+
+#define ATAG_ALS 0x5441001b
+static int __init parse_tag_als_kadc(const struct tag *tags)
+{
+ int found = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_ALS) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ als_kadc = t->u.revision.rev;
+ pr_debug("%s: als_kadc = 0x%x\n", __func__, als_kadc);
+ return 0;
+}
+__tagtable(ATAG_ALS, parse_tag_als_kadc);
+
+static const struct i2c_device_id microp_i2c_id[] = {
+ { MICROP_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver microp_i2c_driver = {
+ .driver = {
+ .name = MICROP_I2C_NAME,
+ },
+ .id_table = microp_i2c_id,
+ .probe = microp_i2c_probe,
+ .suspend = microp_i2c_suspend,
+ .resume = microp_i2c_resume,
+ .remove = __devexit_p(microp_i2c_remove),
+};
+
+
+static int __init microp_i2c_init(void)
+{
+ return i2c_add_driver(µp_i2c_driver);
+}
+
+static void __exit microp_i2c_exit(void)
+{
+ i2c_del_driver(µp_i2c_driver);
+}
+
+module_init(microp_i2c_init);
+module_exit(microp_i2c_exit);
+
+MODULE_AUTHOR("Eric Olsen <eolsen@android.com>");
+MODULE_DESCRIPTION("MicroP I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c
new file mode 100644
index 0000000..78ed97f
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-mmc.c
@@ -0,0 +1,454 @@
+/* linux/arch/arm/mach-msm/board-mahimahi-mmc.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC 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/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/platform_device.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/mmc.h>
+
+#include <mach/vreg.h>
+
+#include "board-mahimahi.h"
+#include "devices.h"
+#include "proc_comm.h"
+
+#undef MAHIMAHI_DEBUG_MMC
+
+static bool opt_disable_sdcard;
+static int __init mahimahi_disablesdcard_setup(char *str)
+{
+ opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0);
+ return 1;
+}
+
+__setup("board_mahimahi.disable_sdcard=", mahimahi_disablesdcard_setup);
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for(n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static uint32_t sdcard_on_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+};
+
+static uint32_t sdcard_off_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+};
+
+static struct vreg *sdslot_vreg;
+static uint32_t sdslot_vdd = 0xffffffff;
+static uint32_t sdslot_vreg_enabled;
+
+static struct {
+ int mask;
+ int level;
+} mmc_vdd_table[] = {
+ { MMC_VDD_165_195, 1800 },
+ { MMC_VDD_20_21, 2050 },
+ { MMC_VDD_21_22, 2150 },
+ { MMC_VDD_22_23, 2250 },
+ { MMC_VDD_23_24, 2350 },
+ { MMC_VDD_24_25, 2450 },
+ { MMC_VDD_25_26, 2550 },
+ { MMC_VDD_26_27, 2650 },
+ { MMC_VDD_27_28, 2750 },
+ { MMC_VDD_28_29, 2850 },
+ { MMC_VDD_29_30, 2950 },
+};
+
+static uint32_t mahimahi_sdslot_switchvdd(struct device *dev, unsigned int vdd)
+{
+ int i;
+ int ret;
+
+ if (vdd == sdslot_vdd)
+ return 0;
+
+ sdslot_vdd = vdd;
+
+ if (vdd == 0) {
+ config_gpio_table(sdcard_off_gpio_table,
+ ARRAY_SIZE(sdcard_off_gpio_table));
+ vreg_disable(sdslot_vreg);
+ sdslot_vreg_enabled = 0;
+ return 0;
+ }
+
+ if (!sdslot_vreg_enabled) {
+ ret = vreg_enable(sdslot_vreg);
+ if (ret)
+ pr_err("%s: Error enabling vreg (%d)\n", __func__, ret);
+ config_gpio_table(sdcard_on_gpio_table,
+ ARRAY_SIZE(sdcard_on_gpio_table));
+ sdslot_vreg_enabled = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
+ if (mmc_vdd_table[i].mask != (1 << vdd))
+ continue;
+ ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level);
+ if (ret)
+ pr_err("%s: Error setting level (%d)\n", __func__, ret);
+ return 0;
+ }
+
+ pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd);
+ return 0;
+}
+
+static uint32_t mahimahi_cdma_sdslot_switchvdd(struct device *dev, unsigned int vdd)
+{
+ if (!vdd == !sdslot_vdd)
+ return 0;
+
+ /* In CDMA version, the vdd of sdslot is not configurable, and it is
+ * fixed in 2.85V by hardware design.
+ */
+
+ sdslot_vdd = vdd ? MMC_VDD_28_29 : 0;
+
+ if (vdd) {
+ gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 1);
+ config_gpio_table(sdcard_on_gpio_table,
+ ARRAY_SIZE(sdcard_on_gpio_table));
+ } else {
+ config_gpio_table(sdcard_off_gpio_table,
+ ARRAY_SIZE(sdcard_off_gpio_table));
+ gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 0);
+ }
+
+ sdslot_vreg_enabled = !!vdd;
+
+ return 0;
+}
+
+static unsigned int mahimahi_sdslot_status_rev0(struct device *dev)
+{
+ return !gpio_get_value(MAHIMAHI_GPIO_SDMC_CD_REV0_N);
+}
+
+#define MAHIMAHI_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | \
+ MMC_VDD_21_22 | MMC_VDD_22_23 | \
+ MMC_VDD_23_24 | MMC_VDD_24_25 | \
+ MMC_VDD_25_26 | MMC_VDD_26_27 | \
+ MMC_VDD_27_28 | MMC_VDD_28_29 | \
+ MMC_VDD_29_30)
+
+int mahimahi_microp_sdslot_status_register(void (*cb)(int, void *), void *);
+unsigned int mahimahi_microp_sdslot_status(struct device *);
+
+static struct mmc_platform_data mahimahi_sdslot_data = {
+ .ocr_mask = MAHIMAHI_MMC_VDD,
+ .status = mahimahi_microp_sdslot_status,
+ .register_status_notify = mahimahi_microp_sdslot_status_register,
+ .translate_vdd = mahimahi_sdslot_switchvdd,
+};
+
+static uint32_t wifi_on_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+static uint32_t wifi_off_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+/* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
+ * we use predefined value (sdio_vsn=2) here to initial sdio driver well
+ */
+static struct embedded_sdio_data mahimahi_wifi_emb_data = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+};
+
+static int mahimahi_wifi_cd = 0; /* WIFI virtual 'card detect' status */
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+
+static int mahimahi_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static unsigned int mahimahi_wifi_status(struct device *dev)
+{
+ return mahimahi_wifi_cd;
+}
+
+static struct mmc_platform_data mahimahi_wifi_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .built_in = 1,
+ .status = mahimahi_wifi_status,
+ .register_status_notify = mahimahi_wifi_status_register,
+ .embedded_sdio = &mahimahi_wifi_emb_data,
+};
+
+int mahimahi_wifi_set_carddetect(int val)
+{
+ pr_info("%s: %d\n", __func__, val);
+ mahimahi_wifi_cd = val;
+ if (wifi_status_cb) {
+ wifi_status_cb(val, wifi_status_cb_devid);
+ } else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int mahimahi_wifi_power_state;
+
+int mahimahi_wifi_power(int on)
+{
+ printk("%s: %d\n", __func__, on);
+
+ if (on) {
+ config_gpio_table(wifi_on_gpio_table,
+ ARRAY_SIZE(wifi_on_gpio_table));
+ mdelay(50);
+ } else {
+ config_gpio_table(wifi_off_gpio_table,
+ ARRAY_SIZE(wifi_off_gpio_table));
+ }
+
+ mdelay(100);
+ gpio_set_value(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */
+ mdelay(200);
+
+ mahimahi_wifi_power_state = on;
+ return 0;
+}
+
+static int mahimahi_wifi_reset_state;
+
+int mahimahi_wifi_reset(int on)
+{
+ printk("%s: do nothing\n", __func__);
+ mahimahi_wifi_reset_state = on;
+ return 0;
+}
+
+int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags);
+
+int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart)
+{
+ uint32_t id;
+
+ printk("%s()+\n", __func__);
+
+ /* initial WIFI_SHUTDOWN# */
+ id = PCOM_GPIO_CFG(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+
+ msm_add_sdcc(1, &mahimahi_wifi_data, 0, 0);
+
+ if (debug_uart) {
+ pr_info("%s: sdcard disabled due to debug uart\n", __func__);
+ goto done;
+ }
+ if (opt_disable_sdcard) {
+ pr_info("%s: sdcard disabled on cmdline\n", __func__);
+ goto done;
+ }
+
+ sdslot_vreg_enabled = 0;
+
+ if (is_cdma_version(sys_rev)) {
+ /* In the CDMA version, sdslot is supplied by a gpio. */
+ int rc = gpio_request(MAHIMAHI_CDMA_SD_2V85_EN, "sdslot_en");
+ if (rc < 0) {
+ pr_err("%s: gpio_request(%d) failed: %d\n", __func__,
+ MAHIMAHI_CDMA_SD_2V85_EN, rc);
+ return rc;
+ }
+ mahimahi_sdslot_data.translate_vdd = mahimahi_cdma_sdslot_switchvdd;
+ } else {
+ /* in UMTS version, sdslot is supplied by pmic */
+ sdslot_vreg = vreg_get(0, "gp6");
+ if (IS_ERR(sdslot_vreg))
+ return PTR_ERR(sdslot_vreg);
+ }
+
+ if (system_rev > 0)
+ msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0);
+ else {
+ mahimahi_sdslot_data.status = mahimahi_sdslot_status_rev0;
+ mahimahi_sdslot_data.register_status_notify = NULL;
+ set_irq_wake(MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), 1);
+ msm_add_sdcc(2, &mahimahi_sdslot_data,
+ MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N),
+ IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE);
+ }
+
+done:
+ printk("%s()-\n", __func__);
+ return 0;
+}
+
+#if defined(MAHIMAHI_DEBUG_MMC) && defined(CONFIG_DEBUG_FS)
+
+static int mahimahimmc_dbg_wifi_reset_set(void *data, u64 val)
+{
+ mahimahi_wifi_reset((int) val);
+ return 0;
+}
+
+static int mahimahimmc_dbg_wifi_reset_get(void *data, u64 *val)
+{
+ *val = mahimahi_wifi_reset_state;
+ return 0;
+}
+
+static int mahimahimmc_dbg_wifi_cd_set(void *data, u64 val)
+{
+ mahimahi_wifi_set_carddetect((int) val);
+ return 0;
+}
+
+static int mahimahimmc_dbg_wifi_cd_get(void *data, u64 *val)
+{
+ *val = mahimahi_wifi_cd;
+ return 0;
+}
+
+static int mahimahimmc_dbg_wifi_pwr_set(void *data, u64 val)
+{
+ mahimahi_wifi_power((int) val);
+ return 0;
+}
+
+static int mahimahimmc_dbg_wifi_pwr_get(void *data, u64 *val)
+{
+ *val = mahimahi_wifi_power_state;
+ return 0;
+}
+
+static int mahimahimmc_dbg_sd_pwr_set(void *data, u64 val)
+{
+ mahimahi_sdslot_switchvdd(NULL, (unsigned int) val);
+ return 0;
+}
+
+static int mahimahimmc_dbg_sd_pwr_get(void *data, u64 *val)
+{
+ *val = sdslot_vdd;
+ return 0;
+}
+
+static int mahimahimmc_dbg_sd_cd_set(void *data, u64 val)
+{
+ return -ENOSYS;
+}
+
+static int mahimahimmc_dbg_sd_cd_get(void *data, u64 *val)
+{
+ *val = mahimahi_sdslot_data.status(NULL);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_reset_fops,
+ mahimahimmc_dbg_wifi_reset_get,
+ mahimahimmc_dbg_wifi_reset_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_cd_fops,
+ mahimahimmc_dbg_wifi_cd_get,
+ mahimahimmc_dbg_wifi_cd_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_pwr_fops,
+ mahimahimmc_dbg_wifi_pwr_get,
+ mahimahimmc_dbg_wifi_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_pwr_fops,
+ mahimahimmc_dbg_sd_pwr_get,
+ mahimahimmc_dbg_sd_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_cd_fops,
+ mahimahimmc_dbg_sd_cd_get,
+ mahimahimmc_dbg_sd_cd_set, "%llu\n");
+
+static int __init mahimahimmc_dbg_init(void)
+{
+ struct dentry *dent;
+
+ if (!machine_is_mahimahi())
+ return 0;
+
+ dent = debugfs_create_dir("mahimahi_mmc_dbg", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("wifi_reset", 0644, dent, NULL,
+ &mahimahimmc_dbg_wifi_reset_fops);
+ debugfs_create_file("wifi_cd", 0644, dent, NULL,
+ &mahimahimmc_dbg_wifi_cd_fops);
+ debugfs_create_file("wifi_pwr", 0644, dent, NULL,
+ &mahimahimmc_dbg_wifi_pwr_fops);
+ debugfs_create_file("sd_pwr", 0644, dent, NULL,
+ &mahimahimmc_dbg_sd_pwr_fops);
+ debugfs_create_file("sd_cd", 0644, dent, NULL,
+ &mahimahimmc_dbg_sd_cd_fops);
+ return 0;
+}
+
+device_initcall(mahimahimmc_dbg_init);
+#endif
diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c
new file mode 100644
index 0000000..64b66b7
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-panel.c
@@ -0,0 +1,998 @@
+/* linux/arch/arm/mach-msm/board-mahimahi-panel.c
+ *
+ * Copyright (c) 2009 Google Inc.
+ * Author: Dima Zavin <dima@android.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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+#include <mach/msm_iomap.h>
+#include <mach/vreg.h>
+#include "proc_comm.h"
+
+#include "board-mahimahi.h"
+#include "devices.h"
+
+
+#define SPI_CONFIG (0x00000000)
+#define SPI_IO_CONTROL (0x00000004)
+#define SPI_OPERATIONAL (0x00000030)
+#define SPI_ERROR_FLAGS_EN (0x00000038)
+#define SPI_ERROR_FLAGS (0x00000038)
+#define SPI_OUTPUT_FIFO (0x00000100)
+
+static void __iomem *spi_base;
+static struct clk *spi_clk ;
+static struct vreg *vreg_lcm_rftx_2v6;
+static struct vreg *vreg_lcm_aux_2v6;
+
+static int qspi_send(uint32_t id, uint8_t data)
+{
+ uint32_t err;
+
+ /* bit-5: OUTPUT_FIFO_NOT_EMPTY */
+ while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) {
+ if ((err = readl(spi_base + SPI_ERROR_FLAGS))) {
+ pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__,
+ err);
+ return -EIO;
+ }
+ }
+ writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO);
+ udelay(100);
+
+ return 0;
+}
+
+static int qspi_send_9bit(uint32_t id, uint8_t data)
+{
+ uint32_t err;
+
+ while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) {
+ err = readl(spi_base + SPI_ERROR_FLAGS);
+ if (err) {
+ pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__,
+ err);
+ return -EIO;
+ }
+ }
+ writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO);
+ udelay(100);
+
+ return 0;
+}
+
+static int lcm_writeb(uint8_t reg, uint8_t val)
+{
+ qspi_send(0x0, reg);
+ qspi_send(0x1, val);
+ return 0;
+}
+
+static int lcm_writew(uint8_t reg, uint16_t val)
+{
+ qspi_send(0x0, reg);
+ qspi_send(0x1, val >> 8);
+ qspi_send(0x1, val & 0xff);
+ return 0;
+}
+
+static struct resource resources_msm_fb[] = {
+ {
+ .start = MSM_FB_BASE,
+ .end = MSM_FB_BASE + MSM_FB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct lcm_tbl {
+ uint8_t reg;
+ uint8_t val;
+};
+
+static struct lcm_tbl samsung_oled_rgb565_init_table[] = {
+ { 0x31, 0x08 },
+ { 0x32, 0x14 },
+ { 0x30, 0x2 },
+ { 0x27, 0x1 },
+ { 0x12, 0x8 },
+ { 0x13, 0x8 },
+ { 0x15, 0x0 },
+ { 0x16, 0x02 },
+ { 0x39, 0x24 },
+ { 0x17, 0x22 },
+ { 0x18, 0x33 },
+ { 0x19, 0x3 },
+ { 0x1A, 0x1 },
+ { 0x22, 0xA4 },
+ { 0x23, 0x0 },
+ { 0x26, 0xA0 },
+};
+
+static struct lcm_tbl samsung_oled_rgb666_init_table[] = {
+ { 0x31, 0x08 },
+ { 0x32, 0x14 },
+ { 0x30, 0x2 },
+ { 0x27, 0x1 },
+ { 0x12, 0x8 },
+ { 0x13, 0x8 },
+ { 0x15, 0x0 },
+ { 0x16, 0x01 },
+ { 0x39, 0x24 },
+ { 0x17, 0x22 },
+ { 0x18, 0x33 },
+ { 0x19, 0x3 },
+ { 0x1A, 0x1 },
+ { 0x22, 0xA4 },
+ { 0x23, 0x0 },
+ { 0x26, 0xA0 },
+};
+
+static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table;
+static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table);
+
+#define OLED_GAMMA_TABLE_SIZE (7 * 3)
+static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = {
+ /* level 10 */
+ {
+ /* Gamma-R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x3f },
+ { 0x43, 0x35 },
+ { 0x44, 0x30 },
+ { 0x45, 0x2c },
+ { 0x46, 0x13 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x0 },
+ { 0x53, 0x0 },
+ { 0x54, 0x27 },
+ { 0x55, 0x2b },
+ { 0x56, 0x12 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x3f },
+ { 0x63, 0x34 },
+ { 0x64, 0x2f },
+ { 0x65, 0x2b },
+ { 0x66, 0x1b },
+ },
+
+ /* level 40 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x3e },
+ { 0x43, 0x2e },
+ { 0x44, 0x2d },
+ { 0x45, 0x28 },
+ { 0x46, 0x21 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x0 },
+ { 0x53, 0x21 },
+ { 0x54, 0x2a },
+ { 0x55, 0x28 },
+ { 0x56, 0x20 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x3e },
+ { 0x63, 0x2d },
+ { 0x64, 0x2b },
+ { 0x65, 0x26 },
+ { 0x66, 0x2d },
+ },
+
+ /* level 70 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x35 },
+ { 0x43, 0x2c },
+ { 0x44, 0x2b },
+ { 0x45, 0x26 },
+ { 0x46, 0x29 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x0 },
+ { 0x53, 0x25 },
+ { 0x54, 0x29 },
+ { 0x55, 0x26 },
+ { 0x56, 0x28 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x34 },
+ { 0x63, 0x2b },
+ { 0x64, 0x2a },
+ { 0x65, 0x23 },
+ { 0x66, 0x37 },
+ },
+
+ /* level 100 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x30 },
+ { 0x43, 0x2a },
+ { 0x44, 0x2b },
+ { 0x45, 0x24 },
+ { 0x46, 0x2f },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x0 },
+ { 0x53, 0x25 },
+ { 0x54, 0x29 },
+ { 0x55, 0x24 },
+ { 0x56, 0x2e },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x2f },
+ { 0x63, 0x29 },
+ { 0x64, 0x29 },
+ { 0x65, 0x21 },
+ { 0x66, 0x3f },
+ },
+
+ /* level 130 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x2e },
+ { 0x43, 0x29 },
+ { 0x44, 0x2a },
+ { 0x45, 0x23 },
+ { 0x46, 0x34 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0xa },
+ { 0x53, 0x25 },
+ { 0x54, 0x28 },
+ { 0x55, 0x23 },
+ { 0x56, 0x33 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x2d },
+ { 0x63, 0x28 },
+ { 0x64, 0x27 },
+ { 0x65, 0x20 },
+ { 0x66, 0x46 },
+ },
+
+ /* level 160 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x2b },
+ { 0x43, 0x29 },
+ { 0x44, 0x28 },
+ { 0x45, 0x23 },
+ { 0x46, 0x38 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0xb },
+ { 0x53, 0x25 },
+ { 0x54, 0x27 },
+ { 0x55, 0x23 },
+ { 0x56, 0x37 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x29 },
+ { 0x63, 0x28 },
+ { 0x64, 0x25 },
+ { 0x65, 0x20 },
+ { 0x66, 0x4b },
+ },
+
+ /* level 190 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x29 },
+ { 0x43, 0x29 },
+ { 0x44, 0x27 },
+ { 0x45, 0x22 },
+ { 0x46, 0x3c },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x10 },
+ { 0x53, 0x26 },
+ { 0x54, 0x26 },
+ { 0x55, 0x22 },
+ { 0x56, 0x3b },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x28 },
+ { 0x63, 0x28 },
+ { 0x64, 0x24 },
+ { 0x65, 0x1f },
+ { 0x66, 0x50 },
+ },
+
+ /* level 220 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x28 },
+ { 0x43, 0x28 },
+ { 0x44, 0x28 },
+ { 0x45, 0x20 },
+ { 0x46, 0x40 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x11 },
+ { 0x53, 0x25 },
+ { 0x54, 0x27 },
+ { 0x55, 0x20 },
+ { 0x56, 0x3f },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x27 },
+ { 0x63, 0x26 },
+ { 0x64, 0x26 },
+ { 0x65, 0x1c },
+ { 0x66, 0x56 },
+ },
+
+ /* level 250 */
+ {
+ /* Gamma -R */
+ { 0x40, 0x0 },
+ { 0x41, 0x3f },
+ { 0x42, 0x2a },
+ { 0x43, 0x27 },
+ { 0x44, 0x27 },
+ { 0x45, 0x1f },
+ { 0x46, 0x44 },
+ /* Gamma -G */
+ { 0x50, 0x0 },
+ { 0x51, 0x0 },
+ { 0x52, 0x17 },
+ { 0x53, 0x24 },
+ { 0x54, 0x26 },
+ { 0x55, 0x1f },
+ { 0x56, 0x43 },
+ /* Gamma -B */
+ { 0x60, 0x0 },
+ { 0x61, 0x3f },
+ { 0x62, 0x2a },
+ { 0x63, 0x25 },
+ { 0x64, 0x24 },
+ { 0x65, 0x1b },
+ { 0x66, 0x5c },
+ },
+};
+#define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table)
+
+#define SAMSUNG_OLED_MIN_VAL 10
+#define SAMSUNG_OLED_MAX_VAL 250
+#define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \
+ (SAMSUNG_OLED_MAX_VAL - \
+ SAMSUNG_OLED_MIN_VAL) / 2)
+
+#define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \
+ SAMSUNG_OLED_MIN_VAL) / \
+ (SAMSUNG_OLED_NUM_LEVELS - 1))
+
+
+#define SONY_TFT_DEF_USER_VAL 102
+#define SONY_TFT_MIN_USER_VAL 30
+#define SONY_TFT_MAX_USER_VAL 255
+#define SONY_TFT_DEF_PANEL_VAL 155
+#define SONY_TFT_MIN_PANEL_VAL 26
+#define SONY_TFT_MAX_PANEL_VAL 255
+
+
+static DEFINE_MUTEX(panel_lock);
+static struct work_struct brightness_delayed_work;
+static DEFINE_SPINLOCK(brightness_lock);
+static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL;
+static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL;
+static uint8_t table_sel_vals[] = { 0x43, 0x34 };
+static int table_sel_idx = 0;
+static uint8_t tft_panel_on;
+
+static void gamma_table_bank_select(void)
+{
+ lcm_writeb(0x39, table_sel_vals[table_sel_idx]);
+ table_sel_idx ^= 1;
+}
+
+static void samsung_oled_set_gamma_val(int val)
+{
+ int i;
+ int level;
+ int frac;
+
+ val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL);
+ val = (val / 2) * 2;
+
+ level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP;
+ frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP;
+
+ clk_enable(spi_clk);
+
+ for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) {
+ unsigned int v1;
+ unsigned int v2 = 0;
+ u8 v;
+ if (frac == 0) {
+ v = samsung_oled_gamma_table[level][i].val;
+ } else {
+
+ v1 = samsung_oled_gamma_table[level][i].val;
+ v2 = samsung_oled_gamma_table[level+1][i].val;
+ v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) +
+ v2 * frac) / SAMSUNG_OLED_LEVEL_STEP;
+ }
+ lcm_writeb(samsung_oled_gamma_table[level][i].reg, v);
+ }
+
+ gamma_table_bank_select();
+ clk_disable(spi_clk);
+ last_val = val;
+}
+
+static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops)
+{
+ pr_info("%s: +()\n", __func__);
+ mutex_lock(&panel_lock);
+
+ clk_enable(spi_clk);
+ /* Set the gamma write target to 4, leave the current gamma set at 2 */
+ lcm_writeb(0x39, 0x24);
+ clk_disable(spi_clk);
+
+ mutex_unlock(&panel_lock);
+ pr_info("%s: -()\n", __func__);
+ return 0;
+}
+
+static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops)
+{
+ int i;
+
+ pr_info("%s: +()\n", __func__);
+
+ mutex_lock(&panel_lock);
+
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+ udelay(50);
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+ udelay(20);
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+ msleep(20);
+
+ clk_enable(spi_clk);
+
+ for (i = 0; i < init_table_sz; i++)
+ lcm_writeb(init_tablep[i].reg, init_tablep[i].val);
+
+ lcm_writew(0xef, 0xd0e8);
+ lcm_writeb(0x1d, 0xa0);
+ table_sel_idx = 0;
+ gamma_table_bank_select();
+ samsung_oled_set_gamma_val(last_val);
+ msleep(250);
+ lcm_writeb(0x14, 0x03);
+ clk_disable(spi_clk);
+
+ mutex_unlock(&panel_lock);
+
+ pr_info("%s: -()\n", __func__);
+ return 0;
+}
+
+static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops)
+{
+ pr_info("%s: +()\n", __func__);
+ mutex_lock(&panel_lock);
+
+ clk_enable(spi_clk);
+ lcm_writeb(0x14, 0x0);
+ mdelay(1);
+ lcm_writeb(0x1d, 0xa1);
+ clk_disable(spi_clk);
+ msleep(200);
+
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+
+ mutex_unlock(&panel_lock);
+ pr_info("%s: -()\n", __func__);
+ return 0;
+}
+
+struct lcm_cmd {
+ int reg;
+ uint32_t val;
+ unsigned delay;
+};
+
+#define LCM_GPIO_CFG(gpio, func, str) \
+ PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str)
+
+static uint32_t sony_tft_display_on_gpio_table[] = {
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 1, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 1, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 1, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 1, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 1, GPIO_4MA),
+};
+
+static uint32_t sony_tft_display_off_gpio_table[] = {
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 0, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 0, GPIO_8MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 0, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 0, GPIO_4MA),
+ LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 0, GPIO_4MA),
+};
+
+#undef LCM_GPIO_CFG
+
+#define SONY_TFT_DEF_PANEL_DELTA \
+ (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL)
+#define SONY_TFT_DEF_USER_DELTA \
+ (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL)
+
+static void sony_tft_set_pwm_val(int val)
+{
+ pr_info("%s: %d\n", __func__, val);
+
+ last_val = val;
+
+ if (!tft_panel_on)
+ return;
+
+ if (val <= SONY_TFT_DEF_USER_VAL) {
+ if (val <= SONY_TFT_MIN_USER_VAL)
+ val = SONY_TFT_MIN_PANEL_VAL;
+ else
+ val = SONY_TFT_DEF_PANEL_DELTA *
+ (val - SONY_TFT_MIN_USER_VAL) /
+ SONY_TFT_DEF_USER_DELTA +
+ SONY_TFT_MIN_PANEL_VAL;
+ } else
+ val = (SONY_TFT_MAX_PANEL_VAL - SONY_TFT_DEF_PANEL_VAL) *
+ (val - SONY_TFT_DEF_USER_VAL) /
+ (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) +
+ SONY_TFT_DEF_PANEL_VAL;
+
+ clk_enable(spi_clk);
+ qspi_send_9bit(0x0, 0x51);
+ qspi_send_9bit(0x1, val);
+ qspi_send_9bit(0x0, 0x53);
+ qspi_send_9bit(0x1, 0x24);
+ clk_disable(spi_clk);
+}
+
+#undef SONY_TFT_DEF_PANEL_DELTA
+#undef SONY_TFT_DEF_USER_DELTA
+
+static void sony_tft_panel_config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for (n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+
+static int sony_tft_panel_power(int on)
+{
+ unsigned id, on_off;
+
+ if (on) {
+ on_off = 0;
+
+ vreg_enable(vreg_lcm_aux_2v6);
+ vreg_enable(vreg_lcm_rftx_2v6);
+
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+
+ id = PM_VREG_PDOWN_RFTX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ mdelay(10);
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+ mdelay(10);
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+ udelay(500);
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+ mdelay(10);
+ sony_tft_panel_config_gpio_table(
+ sony_tft_display_on_gpio_table,
+ ARRAY_SIZE(sony_tft_display_on_gpio_table));
+ } else {
+ on_off = 1;
+
+ gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+
+ mdelay(120);
+
+ vreg_disable(vreg_lcm_rftx_2v6);
+ vreg_disable(vreg_lcm_aux_2v6);
+
+ id = PM_VREG_PDOWN_RFTX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ sony_tft_panel_config_gpio_table(
+ sony_tft_display_off_gpio_table,
+ ARRAY_SIZE(sony_tft_display_off_gpio_table));
+ }
+ return 0;
+}
+
+static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops)
+{
+ return 0;
+}
+
+static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops)
+{
+ pr_info("%s: +()\n", __func__);
+
+ mutex_lock(&panel_lock);
+
+ if (tft_panel_on) {
+ pr_info("%s: -() already unblanked\n", __func__);
+ goto done;
+ }
+
+ sony_tft_panel_power(1);
+ msleep(45);
+
+ clk_enable(spi_clk);
+ qspi_send_9bit(0x0, 0x11);
+ msleep(5);
+ qspi_send_9bit(0x0, 0x3a);
+ qspi_send_9bit(0x1, 0x05);
+ msleep(100);
+ qspi_send_9bit(0x0, 0x29);
+ /* unlock register page for pwm setting */
+ qspi_send_9bit(0x0, 0xf0);
+ qspi_send_9bit(0x1, 0x5a);
+ qspi_send_9bit(0x1, 0x5a);
+ qspi_send_9bit(0x0, 0xf1);
+ qspi_send_9bit(0x1, 0x5a);
+ qspi_send_9bit(0x1, 0x5a);
+ qspi_send_9bit(0x0, 0xd0);
+ qspi_send_9bit(0x1, 0x5a);
+ qspi_send_9bit(0x1, 0x5a);
+
+ qspi_send_9bit(0x0, 0xc2);
+ qspi_send_9bit(0x1, 0x53);
+ qspi_send_9bit(0x1, 0x12);
+ clk_disable(spi_clk);
+ msleep(100);
+ tft_panel_on = 1;
+ sony_tft_set_pwm_val(last_val);
+
+ pr_info("%s: -()\n", __func__);
+done:
+ mutex_unlock(&panel_lock);
+ return 0;
+}
+
+static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops)
+{
+ pr_info("%s: +()\n", __func__);
+
+ mutex_lock(&panel_lock);
+
+ clk_enable(spi_clk);
+ qspi_send_9bit(0x0, 0x28);
+ qspi_send_9bit(0x0, 0x10);
+ clk_disable(spi_clk);
+
+ msleep(40);
+ sony_tft_panel_power(0);
+ tft_panel_on = 0;
+
+ mutex_unlock(&panel_lock);
+
+ pr_info("%s: -()\n", __func__);
+ return 0;
+}
+
+static struct msm_lcdc_panel_ops mahimahi_lcdc_amoled_panel_ops = {
+ .init = samsung_oled_panel_init,
+ .blank = samsung_oled_panel_blank,
+ .unblank = samsung_oled_panel_unblank,
+};
+
+static struct msm_lcdc_panel_ops mahimahi_lcdc_tft_panel_ops = {
+ .init = sony_tft_panel_init,
+ .blank = sony_tft_panel_blank,
+ .unblank = sony_tft_panel_unblank,
+};
+
+
+static struct msm_lcdc_timing mahimahi_lcdc_amoled_timing = {
+ .clk_rate = 24576000,
+ .hsync_pulse_width = 4,
+ .hsync_back_porch = 8,
+ .hsync_front_porch = 8,
+ .hsync_skew = 0,
+ .vsync_pulse_width = 2,
+ .vsync_back_porch = 8,
+ .vsync_front_porch = 8,
+ .vsync_act_low = 1,
+ .hsync_act_low = 1,
+ .den_act_low = 1,
+};
+
+static struct msm_lcdc_timing mahimahi_lcdc_tft_timing = {
+ .clk_rate = 24576000,
+ .hsync_pulse_width = 2,
+ .hsync_back_porch = 20,
+ .hsync_front_porch = 20,
+ .hsync_skew = 0,
+ .vsync_pulse_width = 2,
+ .vsync_back_porch = 6,
+ .vsync_front_porch = 4,
+ .vsync_act_low = 1,
+ .hsync_act_low = 1,
+ .den_act_low = 0,
+};
+
+static struct msm_fb_data mahimahi_lcdc_fb_data = {
+ .xres = 480,
+ .yres = 800,
+ .width = 48,
+ .height = 80,
+ .output_format = MSM_MDP_OUT_IF_FMT_RGB565,
+};
+
+static struct msm_lcdc_platform_data mahimahi_lcdc_amoled_platform_data = {
+ .panel_ops = &mahimahi_lcdc_amoled_panel_ops,
+ .timing = &mahimahi_lcdc_amoled_timing,
+ .fb_id = 0,
+ .fb_data = &mahimahi_lcdc_fb_data,
+ .fb_resource = &resources_msm_fb[0],
+};
+
+static struct msm_lcdc_platform_data mahimahi_lcdc_tft_platform_data = {
+ .panel_ops = &mahimahi_lcdc_tft_panel_ops,
+ .timing = &mahimahi_lcdc_tft_timing,
+ .fb_id = 0,
+ .fb_data = &mahimahi_lcdc_fb_data,
+ .fb_resource = &resources_msm_fb[0],
+};
+
+static struct platform_device mahimahi_lcdc_amoled_device = {
+ .name = "msm_mdp_lcdc",
+ .id = -1,
+ .dev = {
+ .platform_data = &mahimahi_lcdc_amoled_platform_data,
+ },
+};
+
+static struct platform_device mahimahi_lcdc_tft_device = {
+ .name = "msm_mdp_lcdc",
+ .id = -1,
+ .dev = {
+ .platform_data = &mahimahi_lcdc_tft_platform_data,
+ },
+};
+
+static int mahimahi_init_spi_hack(void)
+{
+ int ret;
+
+ spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE);
+ if (!spi_base)
+ return -1;
+
+ spi_clk = clk_get(&msm_device_spi.dev, "spi_clk");
+ if (IS_ERR(spi_clk)) {
+ pr_err("%s: unable to get spi_clk\n", __func__);
+ ret = PTR_ERR(spi_clk);
+ goto err_clk_get;
+ }
+
+ clk_enable(spi_clk);
+
+ printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG));
+ printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL));
+ printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL));
+ printk("spi: SPI_ERROR_FLAGS_EN=%x\n",
+ readl(spi_base + SPI_ERROR_FLAGS_EN));
+ printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS));
+ printk("-%s()\n", __FUNCTION__);
+ clk_disable(spi_clk);
+
+ return 0;
+
+err_clk_get:
+ iounmap(spi_base);
+ return ret;
+}
+
+static void mahimahi_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness val)
+{
+ unsigned long flags;
+ led_cdev->brightness = val;
+
+ spin_lock_irqsave(&brightness_lock, flags);
+ new_val = val;
+ spin_unlock_irqrestore(&brightness_lock, flags);
+
+ schedule_work(&brightness_delayed_work);
+}
+
+static void mahimahi_brightness_amoled_set_work(struct work_struct *work_ptr)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&brightness_lock, flags);
+ val = new_val;
+ spin_unlock_irqrestore(&brightness_lock, flags);
+
+ mutex_lock(&panel_lock);
+ samsung_oled_set_gamma_val(val);
+ mutex_unlock(&panel_lock);
+}
+
+static void mahimahi_brightness_tft_set_work(struct work_struct *work_ptr)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&brightness_lock, flags);
+ val = new_val;
+ spin_unlock_irqrestore(&brightness_lock, flags);
+
+ mutex_lock(&panel_lock);
+ sony_tft_set_pwm_val(val);
+ mutex_unlock(&panel_lock);
+}
+
+static struct led_classdev mahimahi_brightness_led = {
+ .name = "lcd-backlight",
+ .brightness = LED_FULL,
+ .brightness_set = mahimahi_brightness_set,
+};
+
+int __init mahimahi_init_panel(void)
+{
+ int ret;
+
+ if (!machine_is_mahimahi())
+ return 0;
+
+ if (system_rev > 0xC0) {
+ /* CDMA version (except for EVT1) supports RGB666 */
+ init_tablep = samsung_oled_rgb666_init_table;
+ init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table);
+ mahimahi_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666;
+ }
+
+ ret = platform_device_register(&msm_device_mdp);
+ if (ret != 0)
+ return ret;
+
+ ret = mahimahi_init_spi_hack();
+ if (ret != 0)
+ return ret;
+
+ if (gpio_get_value(MAHIMAHI_GPIO_LCD_ID0)) {
+ pr_info("%s: tft panel\n", __func__);
+ vreg_lcm_rftx_2v6 = vreg_get(0, "rftx");
+ if (IS_ERR(vreg_lcm_rftx_2v6))
+ return PTR_ERR(vreg_lcm_rftx_2v6);
+ vreg_set_level(vreg_lcm_rftx_2v6, 2600);
+
+ vreg_lcm_aux_2v6 = vreg_get(0, "gp4");
+ if (IS_ERR(vreg_lcm_aux_2v6))
+ return PTR_ERR(vreg_lcm_aux_2v6);
+
+ if (gpio_get_value(MAHIMAHI_GPIO_LCD_RST_N))
+ tft_panel_on = 1;
+ ret = platform_device_register(&mahimahi_lcdc_tft_device);
+ INIT_WORK(&brightness_delayed_work, mahimahi_brightness_tft_set_work);
+ } else {
+ pr_info("%s: amoled panel\n", __func__);
+ ret = platform_device_register(&mahimahi_lcdc_amoled_device);
+ INIT_WORK(&brightness_delayed_work, mahimahi_brightness_amoled_set_work);
+ }
+
+ if (ret != 0)
+ return ret;
+
+ ret = led_classdev_register(NULL, &mahimahi_brightness_led);
+ if (ret != 0) {
+ pr_err("%s: Cannot register brightness led\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+device_initcall(mahimahi_init_panel);
diff --git a/arch/arm/mach-msm/board-mahimahi-rfkill.c b/arch/arm/mach-msm/board-mahimahi-rfkill.c
new file mode 100644
index 0000000..05c9bb0
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-rfkill.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC 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/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+#include <asm/gpio.h>
+#include <asm/mach-types.h>
+
+#include "board-mahimahi.h"
+
+static struct rfkill *bt_rfk;
+static const char bt_name[] = "bcm4329";
+
+static int bluetooth_set_power(void *data, bool blocked)
+{
+ if (!blocked) {
+ gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 1);
+ gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 1);
+ } else {
+ gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0);
+ gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 0);
+ }
+ return 0;
+}
+
+static struct rfkill_ops mahimahi_rfkill_ops = {
+ .set_block = bluetooth_set_power,
+};
+
+static int mahimahi_rfkill_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ bool default_state = true; /* off */
+
+ rc = gpio_request(MAHIMAHI_GPIO_BT_RESET_N, "bt_reset");
+ if (rc)
+ goto err_gpio_reset;
+ rc = gpio_request(MAHIMAHI_GPIO_BT_SHUTDOWN_N, "bt_shutdown");
+ if (rc)
+ goto err_gpio_shutdown;
+
+ bluetooth_set_power(NULL, default_state);
+
+ bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH,
+ &mahimahi_rfkill_ops, NULL);
+ if (!bt_rfk) {
+ rc = -ENOMEM;
+ goto err_rfkill_alloc;
+ }
+
+ rfkill_set_states(bt_rfk, default_state, false);
+
+ /* userspace cannot take exclusive control */
+
+ rc = rfkill_register(bt_rfk);
+ if (rc)
+ goto err_rfkill_reg;
+
+ return 0;
+
+err_rfkill_reg:
+ rfkill_destroy(bt_rfk);
+err_rfkill_alloc:
+ gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N);
+err_gpio_shutdown:
+ gpio_free(MAHIMAHI_GPIO_BT_RESET_N);
+err_gpio_reset:
+ return rc;
+}
+
+static int mahimahi_rfkill_remove(struct platform_device *dev)
+{
+ rfkill_unregister(bt_rfk);
+ rfkill_destroy(bt_rfk);
+ gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N);
+ gpio_free(MAHIMAHI_GPIO_BT_RESET_N);
+
+ return 0;
+}
+
+static struct platform_driver mahimahi_rfkill_driver = {
+ .probe = mahimahi_rfkill_probe,
+ .remove = mahimahi_rfkill_remove,
+ .driver = {
+ .name = "mahimahi_rfkill",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mahimahi_rfkill_init(void)
+{
+ if (!machine_is_mahimahi())
+ return 0;
+
+ return platform_driver_register(&mahimahi_rfkill_driver);
+}
+
+static void __exit mahimahi_rfkill_exit(void)
+{
+ platform_driver_unregister(&mahimahi_rfkill_driver);
+}
+
+module_init(mahimahi_rfkill_init);
+module_exit(mahimahi_rfkill_exit);
+MODULE_DESCRIPTION("mahimahi rfkill");
+MODULE_AUTHOR("Nick Pelly <npelly@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.c b/arch/arm/mach-msm/board-mahimahi-smb329.c
new file mode 100755
index 0000000..b80db78
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-smb329.c
@@ -0,0 +1,177 @@
+/* drivers/i2c/chips/smb329.c
+ *
+ * SMB329B Switch Charger (SUMMIT Microelectronics)
+ *
+ * Copyright (C) 2009 HTC Corporation
+ * Author: Justin Lin <Justin_Lin@htc.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/atomic.h>
+
+#include "board-mahimahi-smb329.h"
+
+static struct smb329_data {
+ struct i2c_client *client;
+ uint8_t version;
+ struct work_struct work;
+ struct mutex state_lock;
+ int chg_state;
+} smb329;
+
+static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ /* write the first byte of buffer as the register address */
+ value[0] = reg;
+ msg.addr = smb329.client->addr;
+ msg.len = num_bytes + 1;
+ msg.flags = 0;
+ msg.buf = value;
+
+ ret = i2c_transfer(smb329.client->adapter, &msg, 1);
+
+ return (ret >= 0) ? 0 : ret;
+}
+
+static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes)
+{
+ int ret;
+ struct i2c_msg msg[2];
+
+ /* setup the address to read */
+ msg[0].addr = smb329.client->addr;
+ msg[0].len = 1;
+ msg[0].flags = 0;
+ msg[0].buf = ®
+
+ /* setup the read buffer */
+ msg[1].addr = smb329.client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = num_bytes;
+ msg[1].buf = value;
+
+ ret = i2c_transfer(smb329.client->adapter, msg, 2);
+
+ return (ret >= 0) ? 0 : ret;
+}
+
+static int smb329_i2c_write_byte(uint8_t value, uint8_t reg)
+{
+ int ret;
+ uint8_t buf[2] = { 0 };
+
+ buf[1] = value;
+ ret = smb329_i2c_write(buf, reg, 1);
+ if (ret)
+ pr_err("smb329: write byte error (%d)\n", ret);
+
+ return ret;
+}
+
+static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg)
+{
+ int ret = smb329_i2c_read(value, reg, 1);
+ if (ret)
+ pr_err("smb329: read byte error (%d)\n", ret);
+
+ return ret;
+}
+
+int smb329_set_charger_ctrl(uint32_t ctl)
+{
+ mutex_lock(&smb329.state_lock);
+ smb329.chg_state = ctl;
+ schedule_work(&smb329.work);
+ mutex_unlock(&smb329.state_lock);
+ return 0;
+}
+
+static void smb329_work_func(struct work_struct *work)
+{
+ mutex_lock(&smb329.state_lock);
+
+ switch (smb329.chg_state) {
+ case SMB329_ENABLE_FAST_CHG:
+ pr_info("smb329: charger on (fast)\n");
+ smb329_i2c_write_byte(0x84, 0x31);
+ smb329_i2c_write_byte(0x08, 0x05);
+ if ((smb329.version & 0x18) == 0x0)
+ smb329_i2c_write_byte(0xA9, 0x00);
+ break;
+
+ case SMB329_DISABLE_CHG:
+ case SMB329_ENABLE_SLOW_CHG:
+ pr_info("smb329: charger off/slow\n");
+ smb329_i2c_write_byte(0x88, 0x31);
+ smb329_i2c_write_byte(0x08, 0x05);
+ break;
+ default:
+ pr_err("smb329: unknown charger state %d\n",
+ smb329.chg_state);
+ }
+
+ mutex_unlock(&smb329.state_lock);
+}
+
+static int smb329_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ dev_dbg(&client->dev, "[SMB329]:I2C fail\n");
+ return -EIO;
+ }
+
+ smb329.client = client;
+ mutex_init(&smb329.state_lock);
+ INIT_WORK(&smb329.work, smb329_work_func);
+
+ smb329_i2c_read_byte(&smb329.version, 0x3B);
+ pr_info("smb329 version: 0x%02x\n", smb329.version);
+
+ return 0;
+}
+
+static const struct i2c_device_id smb329_id[] = {
+ { "smb329", 0 },
+ { },
+};
+
+static struct i2c_driver smb329_driver = {
+ .driver.name = "smb329",
+ .id_table = smb329_id,
+ .probe = smb329_probe,
+};
+
+static int __init smb329_init(void)
+{
+ int ret = i2c_add_driver(&smb329_driver);
+ if (ret)
+ pr_err("smb329_init: failed\n");
+
+ return ret;
+}
+
+module_init(smb329_init);
+
+MODULE_AUTHOR("Justin Lin <Justin_Lin@htc.com>");
+MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.h b/arch/arm/mach-msm/board-mahimahi-smb329.h
new file mode 100644
index 0000000..13b326f
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-smb329.h
@@ -0,0 +1,32 @@
+/* include/linux/smb329.h - smb329 switch charger driver
+ *
+ * Copyright (C) 2009 HTC 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 _LINUX_SMB329_H
+#define _LINUX_SMB329_H
+
+#ifdef __KERNEL__
+
+enum {
+ SMB329_DISABLE_CHG,
+ SMB329_ENABLE_SLOW_CHG,
+ SMB329_ENABLE_FAST_CHG,
+};
+
+extern int smb329_set_charger_ctrl(uint32_t ctl);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SMB329_H */
+
diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c
new file mode 100644
index 0000000..78919b9b
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c
@@ -0,0 +1,368 @@
+/* drivers/i2c/chips/tpa2018d1.c
+ *
+ * TI TPA2018D1 Speaker Amplifier
+ *
+ * Copyright (C) 2009 HTC 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.
+ *
+ */
+
+/* TODO: content validation in TPA2018_SET_CONFIG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/tpa2018d1.h>
+
+#include "board-mahimahi-tpa2018d1.h"
+
+static struct i2c_client *this_client;
+static struct tpa2018d1_platform_data *pdata;
+static int is_on;
+static char spk_amp_cfg[8];
+static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */
+ 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21
+};
+static const char spk_amp_off[] = {0x01, 0xa2};
+
+static DEFINE_MUTEX(spk_amp_lock);
+static int tpa2018d1_opened;
+static char *config_data;
+static int tpa2018d1_num_modes;
+
+#define DEBUG 0
+
+static int tpa2018_i2c_write(const char *txData, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msg, 1) < 0) {
+ pr_err("%s: I2C transfer error\n", __func__);
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int tpa2018_i2c_read(char *rxData, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msgs, 1) < 0) {
+ pr_err("%s: I2C transfer error\n", __func__);
+ return -EIO;
+ }
+
+#if DEBUG
+ do {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("%s: rx[%d] = %2x\n",
+ __func__, i, rxData[i]);
+ } while(0);
+#endif
+
+ return 0;
+}
+
+static int tpa2018d1_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ mutex_lock(&spk_amp_lock);
+
+ if (tpa2018d1_opened) {
+ pr_err("%s: busy\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+
+ tpa2018d1_opened = 1;
+done:
+ mutex_unlock(&spk_amp_lock);
+ return rc;
+}
+
+static int tpa2018d1_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&spk_amp_lock);
+ tpa2018d1_opened = 0;
+ mutex_unlock(&spk_amp_lock);
+
+ return 0;
+}
+
+static int tpa2018d1_read_config(void __user *argp)
+{
+ int rc = 0;
+ unsigned char reg_idx = 0x01;
+ unsigned char tmp[7];
+
+ if (!is_on) {
+ gpio_set_value(pdata->gpio_tpa2018_spk_en, 1);
+ msleep(5); /* According to TPA2018D1 Spec */
+ }
+
+ rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx));
+ if (rc < 0)
+ goto err;
+
+ rc = tpa2018_i2c_read(tmp, sizeof(tmp));
+ if (rc < 0)
+ goto err;
+
+ if (copy_to_user(argp, &tmp, sizeof(tmp)))
+ rc = -EFAULT;
+
+err:
+ if (!is_on)
+ gpio_set_value(pdata->gpio_tpa2018_spk_en, 0);
+ return rc;
+}
+
+static int tpa2018d1_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int rc = 0;
+ int mode = -1;
+ int offset = 0;
+ struct tpa2018d1_config_data cfg;
+
+ mutex_lock(&spk_amp_lock);
+
+ switch (cmd) {
+ case TPA2018_SET_CONFIG:
+ if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg)))
+ rc = -EFAULT;
+ break;
+
+ case TPA2018_READ_CONFIG:
+ rc = tpa2018d1_read_config(argp);
+ break;
+
+ case TPA2018_SET_MODE:
+ if (copy_from_user(&mode, argp, sizeof(mode))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (mode >= tpa2018d1_num_modes || mode < 0) {
+ pr_err("%s: unsupported tpa2018d1 mode %d\n",
+ __func__, mode);
+ rc = -EINVAL;
+ break;
+ }
+ if (!config_data) {
+ pr_err("%s: no config data!\n", __func__);
+ rc = -EIO;
+ break;
+ }
+ memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN,
+ TPA2018D1_CMD_LEN);
+ break;
+
+ case TPA2018_SET_PARAM:
+ if (copy_from_user(&cfg, argp, sizeof(cfg))) {
+ pr_err("%s: copy from user failed.\n", __func__);
+ rc = -EFAULT;
+ break;
+ }
+ tpa2018d1_num_modes = cfg.mode_num;
+ if (tpa2018d1_num_modes > TPA2018_NUM_MODES) {
+ pr_err("%s: invalid number of modes %d\n", __func__,
+ tpa2018d1_num_modes);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) {
+ pr_err("%s: invalid data length %d, expecting %d\n",
+ __func__, cfg.data_len,
+ tpa2018d1_num_modes * TPA2018D1_CMD_LEN);
+ rc = -EINVAL;
+ break;
+ }
+ /* Free the old data */
+ if (config_data)
+ kfree(config_data);
+ config_data = kmalloc(cfg.data_len, GFP_KERNEL);
+ if (!config_data) {
+ pr_err("%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) {
+ pr_err("%s: copy data from user failed.\n", __func__);
+ kfree(config_data);
+ config_data = NULL;
+ rc = -EFAULT;
+ break;
+ }
+ /* replace default setting with playback setting */
+ if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) {
+ offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN;
+ memcpy(spk_amp_cfg, config_data + offset,
+ TPA2018D1_CMD_LEN);
+ }
+ break;
+
+ default:
+ pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&spk_amp_lock);
+ return rc;
+}
+
+static struct file_operations tpa2018d1_fops = {
+ .owner = THIS_MODULE,
+ .open = tpa2018d1_open,
+ .release = tpa2018d1_release,
+ .ioctl = tpa2018d1_ioctl,
+};
+
+static struct miscdevice tpa2018d1_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tpa2018d1",
+ .fops = &tpa2018d1_fops,
+};
+
+void tpa2018d1_set_speaker_amp(int on)
+{
+ if (!pdata) {
+ pr_err("%s: no platform data!\n", __func__);
+ return;
+ }
+ mutex_lock(&spk_amp_lock);
+ if (on && !is_on) {
+ gpio_set_value(pdata->gpio_tpa2018_spk_en, 1);
+ msleep(5); /* According to TPA2018D1 Spec */
+
+ if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) {
+ is_on = 1;
+ pr_info("%s: ON\n", __func__);
+ }
+ } else if (!on && is_on) {
+ if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) {
+ is_on = 0;
+ msleep(2);
+ gpio_set_value(pdata->gpio_tpa2018_spk_en, 0);
+ pr_info("%s: OFF\n", __func__);
+ }
+ }
+ mutex_unlock(&spk_amp_lock);
+}
+
+static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+
+ pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ ret = -EINVAL;
+ pr_err("%s: platform data is NULL\n", __func__);
+ goto err_no_pdata;
+ }
+
+ this_client = client;
+
+ ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018");
+ if (ret < 0) {
+ pr_err("%s: gpio request aud_spk_en pin failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1);
+ if (ret < 0) {
+ pr_err("%s: request aud_spk_en gpio direction failed\n",
+ __func__);
+ goto err_free_gpio;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c check functionality error\n", __func__);
+ ret = -ENODEV;
+ goto err_free_gpio;
+ }
+
+ gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */
+
+ ret = misc_register(&tpa2018d1_device);
+ if (ret) {
+ pr_err("%s: tpa2018d1_device register failed\n", __func__);
+ goto err_free_gpio;
+ }
+ memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on));
+ return 0;
+
+err_free_gpio:
+ gpio_free(pdata->gpio_tpa2018_spk_en);
+err_no_pdata:
+ return ret;
+}
+
+static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int tpa2018d1_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id tpa2018d1_id[] = {
+ { TPA2018D1_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver tpa2018d1_driver = {
+ .probe = tpa2018d1_probe,
+ .suspend = tpa2018d1_suspend,
+ .resume = tpa2018d1_resume,
+ .id_table = tpa2018d1_id,
+ .driver = {
+ .name = TPA2018D1_I2C_NAME,
+ },
+};
+
+static int __init tpa2018d1_init(void)
+{
+ pr_info("%s\n", __func__);
+ return i2c_add_driver(&tpa2018d1_driver);
+}
+
+module_init(tpa2018d1_init);
+
+MODULE_DESCRIPTION("tpa2018d1 speaker amp driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h
new file mode 100644
index 0000000..dc11012
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h
@@ -0,0 +1,35 @@
+/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver
+ *
+ * Copyright (C) 2009 HTC 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 __ASM_ARM_ARCH_TPA2018D1_H
+#define __ASM_ARM_ARCH_TPA2018D1_H
+
+#define TPA2018D1_I2C_NAME "tpa2018d1"
+#define TPA2018D1_CMD_LEN 8
+
+struct tpa2018d1_platform_data {
+ uint32_t gpio_tpa2018_spk_en;
+};
+
+struct tpa2018d1_config_data {
+ unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */
+ unsigned int mode_num;
+ unsigned int data_len;
+};
+
+extern void tpa2018d1_set_speaker_amp(int on);
+
+#endif /* __ASM_ARM_ARCH_TPA2018D1_H */
diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c
new file mode 100644
index 0000000..8cd2476
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-wifi.c
@@ -0,0 +1,146 @@
+/* linux/arch/arm/mach-msm/board-mahimahi-wifi.c
+*/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <asm/mach-types.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/skbuff.h>
+#include <linux/wlan_plat.h>
+
+#include "board-mahimahi.h"
+
+int mahimahi_wifi_power(int on);
+int mahimahi_wifi_reset(int on);
+int mahimahi_wifi_set_carddetect(int on);
+
+#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4
+#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160
+#define PREALLOC_WLAN_SECTION_HEADER 24
+
+#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
+#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
+
+#define WLAN_SKB_BUF_NUM 16
+
+static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
+
+typedef struct wifi_mem_prealloc_struct {
+ void *mem_ptr;
+ unsigned long size;
+} wifi_mem_prealloc_t;
+
+static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = {
+ { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
+};
+
+static void *mahimahi_wifi_mem_prealloc(int section, unsigned long size)
+{
+ if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS)
+ return wlan_static_skb;
+ if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS))
+ return NULL;
+ if (wifi_mem_array[section].size < size)
+ return NULL;
+ return wifi_mem_array[section].mem_ptr;
+}
+
+int __init mahimahi_init_wifi_mem(void)
+{
+ int i;
+
+ for(i=0;( i < WLAN_SKB_BUF_NUM );i++) {
+ if (i < (WLAN_SKB_BUF_NUM/2))
+ wlan_static_skb[i] = dev_alloc_skb(4096);
+ else
+ wlan_static_skb[i] = dev_alloc_skb(8192);
+ }
+ for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) {
+ wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size,
+ GFP_KERNEL);
+ if (wifi_mem_array[i].mem_ptr == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static struct resource mahimahi_wifi_resources[] = {
+ [0] = {
+ .name = "bcm4329_wlan_irq",
+ .start = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ),
+ .end = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct wifi_platform_data mahimahi_wifi_control = {
+ .set_power = mahimahi_wifi_power,
+ .set_reset = mahimahi_wifi_reset,
+ .set_carddetect = mahimahi_wifi_set_carddetect,
+ .mem_prealloc = mahimahi_wifi_mem_prealloc,
+};
+
+static struct platform_device mahimahi_wifi_device = {
+ .name = "bcm4329_wlan",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mahimahi_wifi_resources),
+ .resource = mahimahi_wifi_resources,
+ .dev = {
+ .platform_data = &mahimahi_wifi_control,
+ },
+};
+
+extern unsigned char *get_wifi_nvs_ram(void);
+extern int wifi_calibration_size_set(void);
+
+static unsigned mahimahi_wifi_update_nvs(char *str, int add_flag)
+{
+#define NVS_LEN_OFFSET 0x0C
+#define NVS_DATA_OFFSET 0x40
+ unsigned char *ptr;
+ unsigned len;
+
+ if (!str)
+ return -EINVAL;
+ ptr = get_wifi_nvs_ram();
+ /* Size in format LE assumed */
+ memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len));
+ /* if the last byte in NVRAM is 0, trim it */
+ if (ptr[NVS_DATA_OFFSET + len - 1] == 0)
+ len -= 1;
+ if (add_flag) {
+ strcpy(ptr + NVS_DATA_OFFSET + len, str);
+ len += strlen(str);
+ } else {
+ if (strnstr(ptr + NVS_DATA_OFFSET, str, len))
+ len -= strlen(str);
+ }
+ memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len));
+ wifi_calibration_size_set();
+ return 0;
+}
+
+static int __init mahimahi_wifi_init(void)
+{
+ int ret;
+
+ if (!machine_is_mahimahi())
+ return 0;
+
+ printk("%s: start\n", __func__);
+ mahimahi_wifi_update_nvs("sd_oobonly=1\r\n", 0);
+ mahimahi_wifi_update_nvs("btc_params70=0x32\r\n", 1);
+ mahimahi_init_wifi_mem();
+ ret = platform_device_register(&mahimahi_wifi_device);
+ return ret;
+}
+
+late_initcall(mahimahi_wifi_init);
diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c
index bcbefdf..23285d5 100644
--- a/arch/arm/mach-msm/board-mahimahi.c
+++ b/arch/arm/mach-msm/board-mahimahi.c
@@ -15,13 +15,27 @@
*
*/
+#include <linux/cy8c_tmg_ts.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/usb/android_composite.h>
+#include <linux/usb/f_accessory.h>
+
+#include <linux/android_pmem.h>
+#include <linux/synaptics_i2c_rmi.h>
+#include <linux/a1026.h>
+#include <linux/capella_cm3602.h>
+#include <linux/akm8973.h>
+#include <linux/regulator/machine.h>
+#include <linux/ds2784_battery.h>
+#include <../../../drivers/staging/android/timed_gpio.h>
+#include <../../../drivers/w1/w1.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -30,27 +44,1120 @@
#include <mach/board.h>
#include <mach/hardware.h>
+#include <mach/msm_hsusb.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_serial_debugger.h>
#include <mach/system.h>
+#include <mach/msm_serial_hs.h>
+#include <mach/bcm_bt_lpm.h>
+#include <mach/msm_smd.h>
#include "board-mahimahi.h"
#include "devices.h"
#include "proc_comm.h"
+#include "board-mahimahi-flashlight.h"
+#include "board-mahimahi-tpa2018d1.h"
+#include "board-mahimahi-smb329.h"
static uint debug_uart;
module_param_named(debug_uart, debug_uart, uint, 0);
+extern void notify_usb_connected(int);
+extern void msm_init_pmic_vibrator(void);
+extern void __init mahimahi_audio_init(void);
+
+extern int microp_headset_has_mic(void);
+
+static void config_gpio_table(uint32_t *table, int len);
+
+static int mahimahi_phy_init_seq[] = {
+ 0x0C, 0x31,
+ 0x31, 0x32,
+ 0x1D, 0x0D,
+ 0x1D, 0x10,
+ -1 };
+
+static void mahimahi_usb_phy_reset(void)
+{
+ u32 id;
+ int ret;
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+
+ msleep(1);
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+}
+
+static void mahimahi_usb_hw_reset(bool enable)
+{
+ u32 id;
+ int ret;
+ u32 func;
+
+ id = PCOM_CLKRGM_APPS_RESET_USBH;
+ if (enable)
+ func = PCOM_CLK_REGIME_SEC_RESET_ASSERT;
+ else
+ func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT;
+ ret = msm_proc_comm(func, &id, NULL);
+ if (ret)
+ pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable,
+ ret);
+}
+
+
+static struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_init_seq = mahimahi_phy_init_seq,
+ .phy_reset = mahimahi_usb_phy_reset,
+ .hw_reset = mahimahi_usb_hw_reset,
+ .usb_connected = notify_usb_connected,
+};
+
+static char *usb_functions_ums[] = {
+ "usb_mass_storage",
+};
+
+static char *usb_functions_ums_adb[] = {
+ "usb_mass_storage",
+ "adb",
+};
+
+static char *usb_functions_rndis[] = {
+ "rndis",
+};
+
+static char *usb_functions_rndis_adb[] = {
+ "rndis",
+ "adb",
+};
+
+#ifdef CONFIG_USB_ANDROID_ACCESSORY
+static char *usb_functions_accessory[] = { "accessory" };
+static char *usb_functions_accessory_adb[] = { "accessory", "adb" };
+#endif
+
+#ifdef CONFIG_USB_ANDROID_DIAG
+static char *usb_functions_adb_diag[] = {
+ "usb_mass_storage",
+ "adb",
+ "diag",
+};
+#endif
+
+static char *usb_functions_all[] = {
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ "rndis",
+#endif
+#ifdef CONFIG_USB_ANDROID_ACCESSORY
+ "accessory",
+#endif
+ "usb_mass_storage",
+ "adb",
+#ifdef CONFIG_USB_ANDROID_ACM
+ "acm",
+#endif
+#ifdef CONFIG_USB_ANDROID_DIAG
+ "diag",
+#endif
+};
+
+static struct android_usb_product usb_products[] = {
+ {
+ .product_id = 0x4e11,
+ .num_functions = ARRAY_SIZE(usb_functions_ums),
+ .functions = usb_functions_ums,
+ },
+ {
+ .product_id = 0x4e12,
+ .num_functions = ARRAY_SIZE(usb_functions_ums_adb),
+ .functions = usb_functions_ums_adb,
+ },
+ {
+ .product_id = 0x4e13,
+ .num_functions = ARRAY_SIZE(usb_functions_rndis),
+ .functions = usb_functions_rndis,
+ },
+ {
+ .product_id = 0x4e14,
+ .num_functions = ARRAY_SIZE(usb_functions_rndis_adb),
+ .functions = usb_functions_rndis_adb,
+ },
+#ifdef CONFIG_USB_ANDROID_ACCESSORY
+ {
+ .vendor_id = USB_ACCESSORY_VENDOR_ID,
+ .product_id = USB_ACCESSORY_PRODUCT_ID,
+ .num_functions = ARRAY_SIZE(usb_functions_accessory),
+ .functions = usb_functions_accessory,
+ },
+ {
+ .vendor_id = USB_ACCESSORY_VENDOR_ID,
+ .product_id = USB_ACCESSORY_ADB_PRODUCT_ID,
+ .num_functions = ARRAY_SIZE(usb_functions_accessory_adb),
+ .functions = usb_functions_accessory_adb,
+ },
+#endif
+#ifdef CONFIG_USB_ANDROID_DIAG
+ {
+ .product_id = 0x4e17,
+ .num_functions = ARRAY_SIZE(usb_functions_adb_diag),
+ .functions = usb_functions_adb_diag,
+ },
+#endif
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "Google, Inc.",
+ .product = "Nexus One",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+static struct usb_ether_platform_data rndis_pdata = {
+ /* ethaddr is filled by board_serialno_setup */
+ .vendorID = 0x18d1,
+ .vendorDescr = "Google, Inc.",
+};
+
+static struct platform_device rndis_device = {
+ .name = "rndis",
+ .id = -1,
+ .dev = {
+ .platform_data = &rndis_pdata,
+ },
+};
+#endif
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x18d1,
+ .product_id = 0x4e11,
+ .version = 0x0100,
+ .product_name = "Nexus One",
+ .manufacturer_name = "Google, Inc.",
+ .num_products = ARRAY_SIZE(usb_products),
+ .products = usb_products,
+ .num_functions = ARRAY_SIZE(usb_functions_all),
+ .functions = usb_functions_all,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+static struct platform_device mahimahi_rfkill = {
+ .name = "mahimahi_rfkill",
+ .id = -1,
+};
+
+static struct resource msm_kgsl_resources[] = {
+ {
+ .name = "kgsl_reg_memory",
+ .start = MSM_GPU_REG_PHYS,
+ .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "kgsl_phys_memory",
+ .start = MSM_GPU_MEM_BASE,
+ .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_GRAPHICS,
+ .end = INT_GRAPHICS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+#define PWR_RAIL_GRP_CLK 8
+static int mahimahi_kgsl_power_rail_mode(int follow_clk)
+{
+ int mode = follow_clk ? 0 : 1;
+ int rail_id = PWR_RAIL_GRP_CLK;
+
+ return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode);
+}
+
+static int mahimahi_kgsl_power(bool on)
+{
+ int cmd;
+ int rail_id = PWR_RAIL_GRP_CLK;
+
+ cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE;
+ return msm_proc_comm(cmd, &rail_id, NULL);
+}
+
+static struct platform_device msm_kgsl_device = {
+ .name = "kgsl",
+ .id = -1,
+ .resource = msm_kgsl_resources,
+ .num_resources = ARRAY_SIZE(msm_kgsl_resources),
+};
+
+static struct android_pmem_platform_data mdp_pmem_pdata = {
+ .name = "pmem",
+ .start = MSM_PMEM_MDP_BASE,
+ .size = MSM_PMEM_MDP_SIZE,
+ .no_allocator = 0,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data android_pmem_adsp_pdata = {
+ .name = "pmem_adsp",
+ .start = MSM_PMEM_ADSP_BASE,
+ .size = MSM_PMEM_ADSP_SIZE,
+ .no_allocator = 0,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data android_pmem_camera_pdata = {
+ .name = "pmem_camera",
+ .start = MSM_PMEM_CAMERA_BASE,
+ .size = MSM_PMEM_CAMERA_SIZE,
+ .no_allocator = 1,
+ .cached = 1,
+};
+
+static struct platform_device android_pmem_mdp_device = {
+ .name = "android_pmem",
+ .id = 0,
+ .dev = {
+ .platform_data = &mdp_pmem_pdata
+ },
+};
+
+static struct platform_device android_pmem_adsp_device = {
+ .name = "android_pmem",
+ .id = 1,
+ .dev = {
+ .platform_data = &android_pmem_adsp_pdata,
+ },
+};
+
+static struct platform_device android_pmem_camera_device = {
+ .name = "android_pmem",
+ .id = 2,
+ .dev = {
+ .platform_data = &android_pmem_camera_pdata,
+ },
+};
+
+static struct resource ram_console_resources[] = {
+ {
+ .start = MSM_RAM_CONSOLE_BASE,
+ .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ram_console_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ram_console_resources),
+ .resource = ram_console_resources,
+};
+
+static int mahimahi_ts_power(int on)
+{
+ pr_info("%s: power %d\n", __func__, on);
+
+ if (on) {
+ /* level shifter should be off */
+ gpio_set_value(MAHIMAHI_GPIO_TP_EN, 1);
+ msleep(120);
+ /* enable touch panel level shift */
+ gpio_set_value(MAHIMAHI_GPIO_TP_LS_EN, 1);
+ msleep(3);
+ } else {
+ gpio_set_value(MAHIMAHI_GPIO_TP_LS_EN, 0);
+ gpio_set_value(MAHIMAHI_GPIO_TP_EN, 0);
+ udelay(50);
+ }
+
+ return 0;
+}
+
+struct cy8c_i2c_platform_data mahimahi_cy8c_ts_data = {
+ .version = 0x0001,
+ .abs_x_min = 0,
+ .abs_x_max = 479,
+ .abs_y_min = 0,
+ .abs_y_max = 799,
+ .abs_pressure_min = 0,
+ .abs_pressure_max = 255,
+ .abs_width_min = 0,
+ .abs_width_max = 10,
+ .power = mahimahi_ts_power,
+};
+
+static struct synaptics_i2c_rmi_platform_data mahimahi_synaptics_ts_data[] = {
+ {
+ .version = 0x105,
+ .power = mahimahi_ts_power,
+ .flags = SYNAPTICS_FLIP_Y,
+ .inactive_left = -15 * 0x10000 / 480,
+ .inactive_right = -15 * 0x10000 / 480,
+ .inactive_top = -15 * 0x10000 / 800,
+ .inactive_bottom = -50 * 0x10000 / 800,
+ .sensitivity_adjust = 9,
+ },
+ {
+ .flags = SYNAPTICS_FLIP_Y,
+ .inactive_left = -15 * 0x10000 / 480,
+ .inactive_right = -15 * 0x10000 / 480,
+ .inactive_top = -15 * 0x10000 / 800,
+ .inactive_bottom = -40 * 0x10000 / 800,
+ .sensitivity_adjust = 12,
+ },
+};
+
+static struct a1026_platform_data a1026_data = {
+ .gpio_a1026_micsel = MAHIMAHI_AUD_MICPATH_SEL,
+ .gpio_a1026_wakeup = MAHIMAHI_AUD_A1026_WAKEUP,
+ .gpio_a1026_reset = MAHIMAHI_AUD_A1026_RESET,
+ .gpio_a1026_clk = MAHIMAHI_AUD_A1026_CLK,
+ /*.gpio_a1026_int = MAHIMAHI_AUD_A1026_INT,*/
+};
+
+static struct akm8973_platform_data compass_platform_data = {
+ .layouts = MAHIMAHI_LAYOUTS,
+ .project_name = MAHIMAHI_PROJECT_NAME,
+ .reset = MAHIMAHI_GPIO_COMPASS_RST_N,
+ .intr = MAHIMAHI_GPIO_COMPASS_INT_N,
+};
+
+static struct regulator_consumer_supply tps65023_dcdc1_supplies[] = {
+ {
+ .supply = "acpu_vcore",
+ },
+};
+
+static struct regulator_init_data tps65023_data[5] = {
+ {
+ .constraints = {
+ .name = "dcdc1", /* VREG_MSMC2_1V29 */
+ .min_uV = 1000000,
+ .max_uV = 1300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ },
+ .consumer_supplies = tps65023_dcdc1_supplies,
+ .num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1_supplies),
+ },
+ /* dummy values for unused regulators to not crash driver: */
+ {
+ .constraints = {
+ .name = "dcdc2", /* VREG_MSMC1_1V26 */
+ .min_uV = 1260000,
+ .max_uV = 1260000,
+ },
+ },
+ {
+ .constraints = {
+ .name = "dcdc3", /* unused */
+ .min_uV = 800000,
+ .max_uV = 3300000,
+ },
+ },
+ {
+ .constraints = {
+ .name = "ldo1", /* unused */
+ .min_uV = 1000000,
+ .max_uV = 3150000,
+ },
+ },
+ {
+ .constraints = {
+ .name = "ldo2", /* V_USBPHY_3V3 */
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ },
+ },
+};
+
+
+static void ds2482_set_slp_n(unsigned n)
+{
+ gpio_direction_output(MAHIMAHI_GPIO_DS2482_SLP_N, n);
+}
+
+static struct tpa2018d1_platform_data tpa2018_data = {
+ .gpio_tpa2018_spk_en = MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN,
+};
+
+static struct i2c_board_info base_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("ds2482", 0x30 >> 1),
+ .platform_data = ds2482_set_slp_n,
+ },
+ {
+ I2C_BOARD_INFO("cy8c-tmg-ts", 0x34),
+ .platform_data = &mahimahi_cy8c_ts_data,
+ .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_TP_INT_N),
+ },
+ {
+ I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x40),
+ .platform_data = mahimahi_synaptics_ts_data,
+ .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_TP_INT_N)
+ },
+ {
+ I2C_BOARD_INFO("mahimahi-microp", 0x66),
+ .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_UP_INT_N)
+ },
+ {
+ I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1),
+ },
+ {
+ I2C_BOARD_INFO("tps65023", 0x48),
+ .platform_data = tps65023_data,
+ },
+};
+
+static struct i2c_board_info rev0_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO(AKM8973_I2C_NAME, 0x1C),
+ .platform_data = &compass_platform_data,
+ .irq = MSM_GPIO_TO_INT(MAHIMAHI_REV0_GPIO_COMPASS_INT_N),
+ },
+};
+
+static struct i2c_board_info rev1_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("audience_a1026", 0x3E),
+ .platform_data = &a1026_data,
+ /*.irq = MSM_GPIO_TO_INT(MAHIMAHI_AUD_A1026_INT)*/
+ },
+ {
+ I2C_BOARD_INFO(AKM8973_I2C_NAME, 0x1C),
+ .platform_data = &compass_platform_data,
+ .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_COMPASS_INT_N),
+ },
+};
+
+static struct i2c_board_info rev_CX_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("tpa2018d1", 0x58),
+ .platform_data = &tpa2018_data,
+ },
+ {
+ I2C_BOARD_INFO("smb329", 0x6E >> 1),
+ },
+};
+
+static void config_gpio_table(uint32_t *table, int len);
+
+static uint32_t camera_off_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* HSYNC */
+ PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* VSYNC */
+ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */
+};
+
+static uint32_t camera_on_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_16MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* HSYNC */
+ PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* VSYNC */
+ PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* MCLK */
+};
+
+void config_camera_on_gpios(void)
+{
+ config_gpio_table(camera_on_gpio_table,
+ ARRAY_SIZE(camera_on_gpio_table));
+}
+
+void config_camera_off_gpios(void)
+{
+ config_gpio_table(camera_off_gpio_table,
+ ARRAY_SIZE(camera_off_gpio_table));
+}
+
+static struct resource msm_camera_resources[] = {
+ {
+ .start = MSM_VFE_PHYS,
+ .end = MSM_VFE_PHYS + MSM_VFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_VFE,
+ INT_VFE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct msm_camera_device_platform_data msm_camera_device_data = {
+ .camera_gpio_on = config_camera_on_gpios,
+ .camera_gpio_off = config_camera_off_gpios,
+ .ioext.mdcphy = MSM_MDC_PHYS,
+ .ioext.mdcsz = MSM_MDC_SIZE,
+ .ioext.appphy = MSM_CLK_CTL_PHYS,
+ .ioext.appsz = MSM_CLK_CTL_SIZE,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = {
+ .sensor_name = "s5k3e2fx",
+ .sensor_reset = 144, /* CAM1_RST */
+ .sensor_pwd = 143, /* CAM1_PWDN, enabled in a9 */
+ /*.vcm_pwd = 31, */ /* CAM1_VCM_EN, enabled in a9 */
+ .pdata = &msm_camera_device_data,
+ .resource = msm_camera_resources,
+ .num_resources = ARRAY_SIZE(msm_camera_resources),
+ .camera_flash = flashlight_control,
+ .num_flash_levels = FLASHLIGHT_NUM,
+};
+
+static struct platform_device msm_camera_sensor_s5k3e2fx = {
+ .name = "msm_camera_s5k3e2fx",
+ .dev = {
+ .platform_data = &msm_camera_sensor_s5k3e2fx_data,
+ },
+};
+
+static int capella_cm3602_power(int on)
+{
+ /* TODO eolsen Add Voltage reg control */
+ if (on) {
+ gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 0);
+ } else {
+ gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 1);
+ }
+
+ return 0;
+}
+
+
+static struct capella_cm3602_platform_data capella_cm3602_pdata = {
+ .power = capella_cm3602_power,
+ .p_out = MAHIMAHI_GPIO_PROXIMITY_INT_N
+};
+
+static struct platform_device capella_cm3602 = {
+ .name = CAPELLA_CM3602,
+ .id = -1,
+ .dev = {
+ .platform_data = &capella_cm3602_pdata
+ }
+};
+
+static uint32_t flashlight_gpio_table[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+};
+
+static uint32_t flashlight_gpio_table_rev_CX[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+};
+
+
+static int config_mahimahi_flashlight_gpios(void)
+{
+ if (is_cdma_version(system_rev)) {
+ config_gpio_table(flashlight_gpio_table_rev_CX,
+ ARRAY_SIZE(flashlight_gpio_table_rev_CX));
+ } else {
+ config_gpio_table(flashlight_gpio_table,
+ ARRAY_SIZE(flashlight_gpio_table));
+ }
+ return 0;
+}
+
+static struct flashlight_platform_data mahimahi_flashlight_data = {
+ .gpio_init = config_mahimahi_flashlight_gpios,
+ .torch = MAHIMAHI_GPIO_FLASHLIGHT_TORCH,
+ .flash = MAHIMAHI_GPIO_FLASHLIGHT_FLASH,
+ .flash_duration_ms = 600
+};
+
+static struct platform_device mahimahi_flashlight_device = {
+ .name = "flashlight",
+ .dev = {
+ .platform_data = &mahimahi_flashlight_data,
+ },
+};
+static struct timed_gpio timed_gpios[] = {
+ {
+ .name = "vibrator",
+ .gpio = MAHIMAHI_GPIO_VIBRATOR_ON,
+ .max_timeout = 15000,
+ },
+};
+
+static struct timed_gpio_platform_data timed_gpio_data = {
+ .num_gpios = ARRAY_SIZE(timed_gpios),
+ .gpios = timed_gpios,
+};
+
+static struct platform_device mahimahi_timed_gpios = {
+ .name = "timed-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &timed_gpio_data,
+ },
+};
+
+static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = {
+ .rx_wakeup_irq = -1,
+ .inject_rx_on_wakeup = 0,
+ .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked,
+};
+
+static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = {
+ .gpio_wake = MAHIMAHI_GPIO_BT_WAKE,
+ .gpio_host_wake = MAHIMAHI_GPIO_BT_HOST_WAKE,
+ .request_clock_off_locked = msm_hs_request_clock_off_locked,
+ .request_clock_on_locked = msm_hs_request_clock_on_locked,
+};
+
+struct platform_device bcm_bt_lpm_device = {
+ .name = "bcm_bt_lpm",
+ .id = 0,
+ .dev = {
+ .platform_data = &bcm_bt_lpm_pdata,
+ },
+};
+
+static int ds2784_charge(int on, int fast)
+{
+ if (is_cdma_version(system_rev)) {
+ if (!on)
+ smb329_set_charger_ctrl(SMB329_DISABLE_CHG);
+ else
+ smb329_set_charger_ctrl(fast ? SMB329_ENABLE_FAST_CHG : SMB329_ENABLE_SLOW_CHG);
+ }
+ else
+ gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, !!fast);
+ gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, !on);
+ return 0;
+}
+
+static int w1_ds2784_add_slave(struct w1_slave *sl)
+{
+ struct dd {
+ struct platform_device pdev;
+ struct ds2784_platform_data pdata;
+ } *p;
+
+ int rc;
+
+ p = kzalloc(sizeof(struct dd), GFP_KERNEL);
+ if (!p) {
+ pr_err("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, "charger_en");
+ if (rc < 0) {
+ pr_err("%s: gpio_request(%d) failed: %d\n", __func__,
+ MAHIMAHI_GPIO_BATTERY_CHARGER_EN, rc);
+ kfree(p);
+ return rc;
+ }
+
+ if (!is_cdma_version(system_rev)) {
+ rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, "charger_current");
+ if (rc < 0) {
+ pr_err("%s: gpio_request(%d) failed: %d\n", __func__,
+ MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, rc);
+ gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN);
+ kfree(p);
+ return rc;
+ }
+ }
+
+ p->pdev.name = "ds2784-battery";
+ p->pdev.id = -1;
+ p->pdev.dev.platform_data = &p->pdata;
+ p->pdata.charge = ds2784_charge;
+ p->pdata.w1_slave = sl;
+
+ platform_device_register(&p->pdev);
+
+ return 0;
+}
+
+static struct w1_family_ops w1_ds2784_fops = {
+ .add_slave = w1_ds2784_add_slave,
+};
+
+static struct w1_family w1_ds2784_family = {
+ .fid = W1_FAMILY_DS2784,
+ .fops = &w1_ds2784_fops,
+};
+
+static int __init ds2784_battery_init(void)
+{
+ return w1_register_family(&w1_ds2784_family);
+}
+
static struct platform_device *devices[] __initdata = {
#if !defined(CONFIG_MSM_SERIAL_DEBUGGER)
&msm_device_uart1,
#endif
+ &bcm_bt_lpm_device,
&msm_device_uart_dm1,
+ &ram_console_device,
+ &mahimahi_rfkill,
+ &msm_device_smd,
&msm_device_nand,
+ &msm_device_hsusb,
+ &usb_mass_storage_device,
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ &rndis_device,
+#endif
+ &android_usb_device,
+ &android_pmem_mdp_device,
+ &android_pmem_adsp_device,
+ &android_pmem_camera_device,
+ &msm_kgsl_device,
+ &msm_device_i2c,
+ &capella_cm3602,
+ &msm_camera_sensor_s5k3e2fx,
+ &mahimahi_flashlight_device,
+};
+
+
+static uint32_t bt_gpio_table[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_CTS, 2, GPIO_INPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RX, 2, GPIO_INPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_RESET_N, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_WAKE, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+};
+
+static uint32_t bt_gpio_table_rev_CX[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_CTS, 2, GPIO_INPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RX, 2, GPIO_INPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT,
+ GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_RESET_N, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_CDMA_GPIO_BT_WAKE, 0, GPIO_OUTPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT,
+ GPIO_PULL_DOWN, GPIO_4MA),
+};
+
+static uint32_t misc_gpio_table[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_LCD_RST_N, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_LED_3V3_EN, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_DOCK, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_4MA),
+};
+
+static uint32_t key_int_shutdown_gpio_table[] = {
+ PCOM_GPIO_CFG(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA),
+};
+
+static void mahimahi_headset_init(void)
+{
+ if (is_cdma_version(system_rev))
+ return;
+ config_gpio_table(key_int_shutdown_gpio_table,
+ ARRAY_SIZE(key_int_shutdown_gpio_table));
+ gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0);
+}
+
+#define ATAG_BDADDR 0x43294329 /* mahimahi bluetooth address tag */
+#define ATAG_BDADDR_SIZE 4
+#define BDADDR_STR_SIZE 18
+
+static char bdaddr[BDADDR_STR_SIZE];
+
+module_param_string(bdaddr, bdaddr, sizeof(bdaddr), 0400);
+MODULE_PARM_DESC(bdaddr, "bluetooth address");
+
+static int __init parse_tag_bdaddr(const struct tag *tag)
+{
+ unsigned char *b = (unsigned char *)&tag->u;
+
+ if (tag->hdr.size != ATAG_BDADDR_SIZE)
+ return -EINVAL;
+
+ snprintf(bdaddr, BDADDR_STR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X",
+ b[0], b[1], b[2], b[3], b[4], b[5]);
+
+ return 0;
+}
+
+__tagtable(ATAG_BDADDR, parse_tag_bdaddr);
+
+static int __init board_serialno_setup(char *serialno)
+{
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ int i;
+ char *src = serialno;
+
+ /* create a fake MAC address from our serial number.
+ * first byte is 0x02 to signify locally administered.
+ */
+ rndis_pdata.ethaddr[0] = 0x02;
+ for (i = 0; *src; i++) {
+ /* XOR the USB serial across the remaining bytes */
+ rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++;
+ }
+#endif
+
+ android_usb_pdata.serial_number = serialno;
+ return 1;
+}
+__setup("androidboot.serialno=", board_serialno_setup);
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for(n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static struct msm_acpu_clock_platform_data mahimahi_clock_data = {
+ .acpu_switch_time_us = 20,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 245000,
+ .wait_for_irq_khz = 245000,
+ .mpll_khz = 245000
+};
+
+static struct msm_acpu_clock_platform_data mahimahi_cdma_clock_data = {
+ .acpu_switch_time_us = 20,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 235930,
+ .wait_for_irq_khz = 235930,
+ .mpll_khz = 235930
+};
+
+static ssize_t mahimahi_virtual_keys_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ if (system_rev > 2 && system_rev != 0xC0) {
+ /* center: x: back: 55, menu: 172, home: 298, search 412, y: 835 */
+ return sprintf(buf,
+ __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":55:835:90:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":172:835:125:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":298:835:115:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":412:835:95:55"
+ "\n");
+ } else {
+ /* center: x: home: 55, menu: 185, back: 305, search 425, y: 835 */
+ return sprintf(buf,
+ __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":55:835:70:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":185:835:100:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":305:835:70:55"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":425:835:70:55"
+ "\n");
+ }
+}
+
+static struct kobj_attribute mahimahi_virtual_keys_attr = {
+ .attr = {
+ .name = "virtualkeys.synaptics-rmi-touchscreen",
+ .mode = S_IRUGO,
+ },
+ .show = &mahimahi_virtual_keys_show,
+};
+
+static struct attribute *mahimahi_properties_attrs[] = {
+ &mahimahi_virtual_keys_attr.attr,
+ NULL
+};
+
+static struct attribute_group mahimahi_properties_attr_group = {
+ .attrs = mahimahi_properties_attrs,
+};
+
+static void mahimahi_reset(void)
+{
+ gpio_set_value(MAHIMAHI_GPIO_PS_HOLD, 0);
+}
+
+int mahimahi_init_mmc(int sysrev, unsigned debug_uart);
+
+static const struct smd_tty_channel_desc smd_cdma_default_channels[] = {
+ { .id = 0, .name = "SMD_DS" },
+ { .id = 19, .name = "SMD_DATA3" },
+ { .id = 27, .name = "SMD_GPSNMEA" }
};
static void __init mahimahi_init(void)
{
+ int ret;
+ struct kobject *properties_kobj;
+
+ printk("mahimahi_init() revision=%d\n", system_rev);
+
+ if (is_cdma_version(system_rev))
+ smd_set_channel_list(smd_cdma_default_channels,
+ ARRAY_SIZE(smd_cdma_default_channels));
+
+ msm_hw_reset_hook = mahimahi_reset;
+
+ if (is_cdma_version(system_rev))
+ msm_acpu_clock_init(&mahimahi_cdma_clock_data);
+ else
+ msm_acpu_clock_init(&mahimahi_clock_data);
+
+ msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1,
+ &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(139));
+
+ config_gpio_table(misc_gpio_table, ARRAY_SIZE(misc_gpio_table));
+
+ if (is_cdma_version(system_rev)) {
+ bcm_bt_lpm_pdata.gpio_wake = MAHIMAHI_CDMA_GPIO_BT_WAKE;
+ mahimahi_flashlight_data.torch = MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH;
+ config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX));
+ } else {
+ config_gpio_table(bt_gpio_table, ARRAY_SIZE(bt_gpio_table));
+ }
+
+ gpio_request(MAHIMAHI_GPIO_TP_LS_EN, "tp_ls_en");
+ gpio_direction_output(MAHIMAHI_GPIO_TP_LS_EN, 0);
+ gpio_request(MAHIMAHI_GPIO_TP_EN, "tp_en");
+ gpio_direction_output(MAHIMAHI_GPIO_TP_EN, 0);
+ gpio_request(MAHIMAHI_GPIO_PROXIMITY_EN, "proximity_en");
+ gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 1);
+ gpio_request(MAHIMAHI_GPIO_COMPASS_RST_N, "compass_rst");
+ gpio_direction_output(MAHIMAHI_GPIO_COMPASS_RST_N, 1);
+ gpio_request(MAHIMAHI_GPIO_COMPASS_INT_N, "compass_int");
+ gpio_direction_input(MAHIMAHI_GPIO_COMPASS_INT_N);
+
+ gpio_request(MAHIMAHI_GPIO_DS2482_SLP_N, "ds2482_slp_n");
+
+ /* set the gpu power rail to manual mode so clk en/dis will not
+ * turn off gpu power, and hang it on resume */
+ mahimahi_kgsl_power_rail_mode(0);
+ mahimahi_kgsl_power(true);
+
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata;
+
platform_add_devices(devices, ARRAY_SIZE(devices));
+
+ i2c_register_board_info(0, base_i2c_devices,
+ ARRAY_SIZE(base_i2c_devices));
+
+ if (system_rev == 0) {
+ /* Only board after XB with Audience A1026 */
+ i2c_register_board_info(0, rev0_i2c_devices,
+ ARRAY_SIZE(rev0_i2c_devices));
+ }
+
+ if (system_rev > 0) {
+ /* Only board after XB with Audience A1026 */
+ i2c_register_board_info(0, rev1_i2c_devices,
+ ARRAY_SIZE(rev1_i2c_devices));
+ }
+
+ if (is_cdma_version(system_rev)) {
+ /* Only CDMA version with TI TPA2018D1 Speaker Amp. */
+ i2c_register_board_info(0, rev_CX_i2c_devices,
+ ARRAY_SIZE(rev_CX_i2c_devices));
+ if ((system_rev & 0x0F) == 0x00) {
+ a1026_data.gpio_a1026_clk = MAHIMAHI_CDMA_XA_AUD_A1026_CLK;
+ } else if ((system_rev & 0x0F) >= 0x01) {
+ a1026_data.gpio_a1026_wakeup = MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP;
+ a1026_data.gpio_a1026_reset = MAHIMAHI_CDMA_XB_AUD_A1026_RESET;
+ a1026_data.gpio_a1026_clk = MAHIMAHI_CDMA_XB_AUD_A1026_CLK;
+ }
+ }
+
+ ret = mahimahi_init_mmc(system_rev, debug_uart);
+ if (ret != 0)
+ pr_crit("%s: Unable to initialize MMC\n", __func__);
+
+ properties_kobj = kobject_create_and_add("board_properties", NULL);
+ if (properties_kobj)
+ ret = sysfs_create_group(properties_kobj,
+ &mahimahi_properties_attr_group);
+ if (!properties_kobj || ret)
+ pr_err("failed to create board_properties\n");
+
+ mahimahi_audio_init();
+ mahimahi_headset_init();
+
+ if (system_rev > 0)
+ platform_device_register(&mahimahi_timed_gpios);
+ else
+ msm_init_pmic_vibrator();
+
+ ds2784_battery_init();
}
static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags,
@@ -67,8 +1174,8 @@
static void __init mahimahi_map_io(void)
{
- msm_map_common_io();
- msm_clock_init();
+ msm_map_qsd8x50_io();
+ msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50);
}
extern struct sys_timer msm_timer;
diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h
new file mode 100644
index 0000000..9696a47
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi.h
@@ -0,0 +1,175 @@
+/* arch/arm/mach-msm/board-mahimahi.h
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ * Author: Haley Teng <Haley_Teng@htc.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 __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H
+#define __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H
+
+#include <mach/board.h>
+
+#define MSM_SMI_BASE 0x02B00000
+#define MSM_SMI_SIZE 0x01500000
+
+#define MSM_RAM_CONSOLE_BASE 0x03A00000
+#define MSM_RAM_CONSOLE_SIZE 0x00040000
+
+#define MSM_FB_BASE 0x03B00000
+#define MSM_FB_SIZE 0x00465000
+
+#define MSM_EBI1_BANK0_BASE 0x20000000
+#define MSM_EBI1_BANK0_SIZE 0x0E000000
+
+#define MSM_GPU_MEM_BASE 0x2DB00000
+#define MSM_GPU_MEM_SIZE 0x00500000
+
+#define MSM_EBI1_BANK1_BASE 0x30000000
+#define MSM_EBI1_BANK1_SIZE 0x10000000
+
+#define MSM_PMEM_MDP_BASE 0x30000000
+#define MSM_PMEM_MDP_SIZE 0x02000000
+
+#define MSM_PMEM_ADSP_BASE 0x32000000
+#define MSM_PMEM_ADSP_SIZE 0x02900000
+
+#define MSM_PMEM_CAMERA_BASE 0x34900000
+#define MSM_PMEM_CAMERA_SIZE 0x00800000
+
+#define MSM_HIGHMEM_BASE 0x35100000
+#define MSM_HIGHMEM_SIZE 0x0AF00000
+
+#define MAHIMAHI_GPIO_PS_HOLD 25
+
+#define MAHIMAHI_GPIO_UP_INT_N 35
+#define MAHIMAHI_GPIO_UP_RESET_N 82
+#define MAHIMAHI_GPIO_LS_EN_N 119
+
+#define MAHIMAHI_GPIO_TP_INT_N 92
+#define MAHIMAHI_GPIO_TP_LS_EN 93
+#define MAHIMAHI_GPIO_TP_EN 160
+
+#define MAHIMAHI_GPIO_POWER_KEY 94
+#define MAHIMAHI_GPIO_SDMC_CD_REV0_N 153
+
+#define MAHIMAHI_GPIO_WIFI_SHUTDOWN_N 127
+#define MAHIMAHI_GPIO_WIFI_IRQ 152
+
+#define MAHIMAHI_GPIO_BALL_UP 38
+#define MAHIMAHI_GPIO_BALL_DOWN 37
+#define MAHIMAHI_GPIO_BALL_LEFT 145
+#define MAHIMAHI_GPIO_BALL_RIGHT 21
+
+#define MAHIMAHI_GPIO_BT_UART1_RTS 43
+#define MAHIMAHI_GPIO_BT_UART1_CTS 44
+#define MAHIMAHI_GPIO_BT_UART1_RX 45
+#define MAHIMAHI_GPIO_BT_UART1_TX 46
+#define MAHIMAHI_GPIO_BT_RESET_N 146
+#define MAHIMAHI_GPIO_BT_SHUTDOWN_N 128
+
+#define MAHIMAHI_GPIO_BT_WAKE 57
+#define MAHIMAHI_GPIO_BT_HOST_WAKE 86
+
+#define MAHIMAHI_GPIO_PROXIMITY_INT_N 90
+#define MAHIMAHI_GPIO_PROXIMITY_EN 120
+
+#define MAHIMAHI_GPIO_DS2482_SLP_N 87
+#define MAHIMAHI_GPIO_VIBRATOR_ON 89
+/* Compass */
+#define MAHIMAHI_REV0_GPIO_COMPASS_INT_N 36
+
+#define MAHIMAHI_GPIO_COMPASS_INT_N 153
+#define MAHIMAHI_GPIO_COMPASS_RST_N 107
+#define MAHIMAHI_PROJECT_NAME "mahimahi"
+#define MAHIMAHI_LAYOUTS { \
+ { {-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \
+ { { 0, -1, 0}, { 1, 0, 0}, {0, 0, -1} }, \
+ { { 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} }, \
+ { {-1, 0, 0}, { 0, 0, -1}, {0, 1, 0} } \
+}
+
+/* Audio */
+#define MAHIMAHI_AUD_JACKHP_EN 157
+#define MAHIMAHI_AUD_2V5_EN 158
+#define MAHIMAHI_AUD_MICPATH_SEL 111
+#define MAHIMAHI_AUD_A1026_INT 112
+#define MAHIMAHI_AUD_A1026_WAKEUP 113
+#define MAHIMAHI_AUD_A1026_RESET 129
+#define MAHIMAHI_AUD_A1026_CLK -1
+#define MAHIMAHI_CDMA_XA_AUD_A1026_CLK 105
+/* NOTE: MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP on CDMA is the same GPIO as
+ * MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT on UMTS. Also,
+ * MAHIMAHI_CDMA_XB_AUD_A1026_RESET is the same as
+ * GPIO MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN on UMTS.
+ */
+#define MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP 16
+#define MAHIMAHI_CDMA_XB_AUD_A1026_RESET 19
+#define MAHIMAHI_CDMA_XB_AUD_A1026_CLK -1
+
+/* Bluetooth PCM */
+#define MAHIMAHI_BT_PCM_OUT 68
+#define MAHIMAHI_BT_PCM_IN 69
+#define MAHIMAHI_BT_PCM_SYNC 70
+#define MAHIMAHI_BT_PCM_CLK 71
+/* flash light */
+#define MAHIMAHI_GPIO_FLASHLIGHT_TORCH 58
+#define MAHIMAHI_GPIO_FLASHLIGHT_FLASH 84
+
+#define MAHIMAHI_GPIO_LED_3V3_EN 85
+#define MAHIMAHI_GPIO_LCD_RST_N 29
+#define MAHIMAHI_GPIO_LCD_ID0 147
+
+/* 3.5mm remote control key interrupt shutdown signal */
+#define MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN 19
+
+#define MAHIMAHI_GPIO_DOCK 106
+
+/* speaker amplifier enable pin for mahimahi CDMA version */
+#define MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN 104
+
+#define MAHIMAHI_GPIO_BATTERY_DETECTION 39
+#define MAHIMAHI_GPIO_BATTERY_CHARGER_EN 22
+#define MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT 16
+
+#define MAHIMAHI_CDMA_GPIO_BT_WAKE 28
+#define MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH 26
+
+#define MAHIMAHI_CDMA_SD_2V85_EN 100
+#define MAHIMAHI_CDMA_JOG_2V6_EN 150
+/* display relative */
+#define MAHIMAHI_LCD_SPI_CLK (17)
+#define MAHIMAHI_LCD_SPI_DO (18)
+#define MAHIMAHI_LCD_SPI_CSz (20)
+#define MAHIMAHI_LCD_RSTz (29)
+#define MAHIMAHI_LCD_R1 (114)
+#define MAHIMAHI_LCD_R2 (115)
+#define MAHIMAHI_LCD_R3 (116)
+#define MAHIMAHI_LCD_R4 (117)
+#define MAHIMAHI_LCD_R5 (118)
+#define MAHIMAHI_LCD_G0 (121)
+#define MAHIMAHI_LCD_G1 (122)
+#define MAHIMAHI_LCD_G2 (123)
+#define MAHIMAHI_LCD_G3 (124)
+#define MAHIMAHI_LCD_G4 (125)
+#define MAHIMAHI_LCD_G5 (126)
+#define MAHIMAHI_LCD_B1 (130)
+#define MAHIMAHI_LCD_B2 (131)
+#define MAHIMAHI_LCD_B3 (132)
+#define MAHIMAHI_LCD_B4 (133)
+#define MAHIMAHI_LCD_B5 (134)
+#define MAHIMAHI_LCD_PCLK (135)
+#define MAHIMAHI_LCD_VSYNC (136)
+#define MAHIMAHI_LCD_HSYNC (137)
+#define MAHIMAHI_LCD_DE (138)
+#define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0)
+
+#endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */
diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c
index e3cc807..2bbaee7 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -36,8 +36,8 @@
extern struct sys_timer msm_timer;
static struct msm_gpio uart3_config_data[] = {
- { GPIO_CFG(86, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_Rx"},
- { GPIO_CFG(87, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_Tx"},
+ { GPIO_CFG(86, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_Rx"},
+ { GPIO_CFG(87, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_Tx"},
};
static struct platform_device *devices[] __initdata = {
diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c
new file mode 100644
index 0000000..375440c
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-gpio.c
@@ -0,0 +1,326 @@
+/* arch/arm/mach-msm/board-sapphire-gpio.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/sysdev.h>
+
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+
+#include "gpio_chip.h"
+#include "board-sapphire.h"
+
+#ifdef DEBUG_SAPPHIRE_GPIO
+#define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg)
+#else
+#define DBG(fmt, arg...) do {} while (0)
+#endif
+
+#define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E)
+#define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08)
+#define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C)
+
+/*CPLD misc reg offset*/
+static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/
+ 0x00, /*misc2 reg*/
+ 0x02, /*misc3 reg*/
+ 0x04, /*misc4 reg*/
+ 0x06}; /*misc5 reg*/
+/*CPLD INT Bank*/
+/*BANK0: int1 status, int2 level, int3 mask*/
+static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} };
+
+static uint8_t sapphire_cpld_initdata[4] = {
+ [0] = 0x80, /* for serial debug UART3, low current misc2*/
+ [1] = 0x34, /* jog & tp enable, I2C pull misc3*/
+ [3] = 0x04, /* mmdi 32k en misc5*/
+};
+
+/*save current working int mask, so the value can be restored after resume.
+Sapphire has only bank0.*/
+static uint8_t sapphire_int_mask[] = {
+ [0] = 0xfb, /* enable all interrupts, bit 2 is not used */
+};
+
+/*Sleep have to prepare the wake up source in advance.
+default to disable all wakeup sources when suspend.*/
+static uint8_t sapphire_sleep_int_mask[] = {
+ [0] = 0x00, /* bit2 is not used */
+};
+
+static int sapphire_suspended;
+
+static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n)
+{
+ if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/
+ return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n));
+ else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/
+ return !!(readb(CPLD_INT_LEVEL_REG_G(n)) &
+ CPLD_GPIO_BIT_POS_MASK(n));
+ return 0;
+}
+
+/*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6
+Reading from write-only registers is undefined, so the writing value
+should be kept in shadow for later usage.*/
+int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
+{
+ unsigned long flags;
+ uint8_t reg_val;
+ if (n > SAPPHIRE_GPIO_END)
+ return -1;
+
+ local_irq_save(flags);
+ reg_val = readb(CPLD_GPIO_REG(n));
+ if (on)
+ reg_val |= CPLD_GPIO_BIT_POS_MASK(n);
+ else
+ reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n);
+ writeb(reg_val, CPLD_GPIO_REG(n));
+
+ DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL));
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio,
+ unsigned long flags)
+{
+ if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH))
+ sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH);
+
+ DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL));
+
+ return 0;
+}
+
+static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio,
+ unsigned int *irqp, unsigned long *irqnumflagsp)
+{
+ DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL));
+ DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n",
+ SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT);
+ if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) ||
+ (gpio > SAPPHIRE_GPIO_LAST_INT))
+ return -ENOENT;
+ *irqp = SAPPHIRE_GPIO_TO_INT(gpio);
+ DBG("*irqp=%d\r\n", *irqp);
+ if (irqnumflagsp)
+ *irqnumflagsp = 0;
+ return 0;
+}
+
+/*write 1 to clear INT status bit.*/
+static void sapphire_gpio_irq_ack(unsigned int irq)
+{
+ /*write 1 to clear*/
+ writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq));
+}
+
+/*unmask/enable the INT
+static void sapphire_gpio_irq_unmask(unsigned int irq)*/
+static void sapphire_gpio_irq_enable(unsigned int irq)
+{
+ unsigned long flags;
+ uint8_t reg_val;
+
+ local_irq_save(flags); /*disabling all interrupts*/
+
+ reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq);
+ DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq),
+ SAPPHIRE_INT_BIT_MASK(irq));
+ DBG("sapphire_suspended=%d\r\n", sapphire_suspended);
+ /*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n",
+ irq, bank, reg_val);*/
+ if (!sapphire_suspended)
+ writeb(reg_val, CPLD_INT_MASK_REG(irq));
+
+ reg_val = readb(CPLD_INT_MASK_REG(irq));
+ DBG("reg_val= 0x%x\r\n", reg_val);
+ DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL));
+
+ local_irq_restore(flags); /*restore the interrupts*/
+}
+
+/*mask/disable INT
+static void sapphire_gpio_irq_mask(unsigned int irq)*/
+static void sapphire_gpio_irq_disable(unsigned int irq)
+{
+ unsigned long flags;
+ uint8_t reg_val;
+
+ local_irq_save(flags);
+ reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq);
+ /*CPLD INT MASK is r/w now.*/
+
+ /*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n",
+ irq, bank, reg_val);*/
+ DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq),
+ SAPPHIRE_INT_BIT_MASK(irq));
+ DBG("sapphire_suspended=%d\r\n", sapphire_suspended);
+ if (!sapphire_suspended)
+ writeb(reg_val, CPLD_INT_MASK_REG(irq));
+
+ reg_val = readb(CPLD_INT_MASK_REG(irq));
+ DBG("reg_val= 0x%x\r\n", reg_val);
+ DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL));
+
+ local_irq_restore(flags);
+}
+
+/*preparing enable/disable wake source before sleep*/
+int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on)
+{
+ unsigned long flags;
+ uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq);
+
+ local_irq_save(flags);
+
+ if (on) /*wake on -> mask the bit*/
+ sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask;
+ else /*no wake -> unmask the bit*/
+ sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask;
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*Sapphire has only one INT Bank.*/
+static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int j;
+ unsigned v;
+ int int_base = SAPPHIRE_INT_START;
+
+ v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/
+
+ for (j = 0; j < 8 ; j++) { /*8 bit per bank*/
+ if (v & (1U << j)) { /*got the INT Bit*/
+ DBG("generic_handle_irq j=0x%x\r\n", j);
+ generic_handle_irq(int_base + j);
+ }
+ }
+
+ desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/
+ DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL));
+}
+
+/*Save current working sources before sleep, so we can restore it after
+ * resume.*/
+static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+ sapphire_suspended = 1;
+ /*save current masking*/
+ sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE +
+ SAPPHIRE_GPIO_INT_B0_MASK_REG);
+
+ /*set waking source before sleep.*/
+ writeb(sapphire_sleep_int_mask[0],
+ SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG);
+
+ return 0;
+}
+
+/*All the registers will be kept till a power loss...*/
+int sapphire_sysdev_resume(struct sys_device *dev)
+{
+ /*restore the working mask saved before sleep*/
+ writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE +
+ SAPPHIRE_GPIO_INT_B0_MASK_REG);
+ sapphire_suspended = 0;
+ return 0;
+}
+
+/**
+ * linux/irq.h :: struct irq_chip
+ * @enable: enable the interrupt (defaults to chip->unmask if NULL)
+ * @disable: disable the interrupt (defaults to chip->mask if NULL)
+ * @ack: start of a new interrupt
+ * @mask: mask an interrupt source
+ * @mask_ack: ack and mask an interrupt source
+ * @unmask: unmask an interrupt source
+ */
+static struct irq_chip sapphire_gpio_irq_chip = {
+ .name = "sapphiregpio",
+ .ack = sapphire_gpio_irq_ack,
+ .mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/
+ .unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/
+ .set_wake = sapphire_gpio_irq_set_wake,
+ /*.set_type = sapphire_gpio_irq_set_type,*/
+};
+
+/*Thomas:For CPLD*/
+static struct gpio_chip sapphire_gpio_chip = {
+ .start = SAPPHIRE_GPIO_START,
+ .end = SAPPHIRE_GPIO_END,
+ .configure = sapphire_gpio_configure,
+ .get_irq_num = sapphire_gpio_get_irq_num,
+ .read = sapphire_gpio_read,
+ .write = sapphire_gpio_write,
+/* .read_detect_status = sapphire_gpio_read_detect_status,
+ .clear_detect_status = sapphire_gpio_clear_detect_status */
+};
+
+struct sysdev_class sapphire_sysdev_class = {
+ .name = "sapphiregpio_irq",
+ .suspend = sapphire_sysdev_suspend,
+ .resume = sapphire_sysdev_resume,
+};
+
+static struct sys_device sapphire_irq_device = {
+ .cls = &sapphire_sysdev_class,
+};
+
+int sapphire_init_gpio(void)
+{
+ int i;
+ if (!machine_is_sapphire())
+ return 0;
+
+ DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END);
+ DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS);
+ for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) {
+ set_irq_chip(i, &sapphire_gpio_irq_chip);
+ set_irq_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ register_gpio_chip(&sapphire_gpio_chip);
+
+ /*setup CPLD INT connecting to SOC's gpio 17 */
+ set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH);
+ set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler);
+ set_irq_wake(MSM_GPIO_TO_INT(17), 1);
+
+ if (sysdev_class_register(&sapphire_sysdev_class) == 0)
+ sysdev_register(&sapphire_irq_device);
+
+ return 0;
+}
+
+int sapphire_init_cpld(unsigned int sys_rev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++)
+ writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2);
+ return 0;
+}
+
+postcore_initcall(sapphire_init_gpio);
diff --git a/arch/arm/mach-msm/board-sapphire-h2w.c b/arch/arm/mach-msm/board-sapphire-h2w.c
new file mode 100644
index 0000000..aa83e21
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-h2w.c
@@ -0,0 +1,545 @@
+/*
+ * H2W device detection driver.
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Authors:
+ * Laurence Chen <Laurence_Chen@htc.com>
+ * Nick Pelly <npelly@google.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.
+ *
+ */
+
+/* For detecting HTC 2 Wire devices, such as wired headset.
+
+ Logically, the H2W driver is always present, and H2W state (hi->state)
+ indicates what is currently plugged into the H2W interface.
+
+ When the headset is plugged in, CABLE_IN1 is pulled low. When the headset
+ button is pressed, CABLE_IN2 is pulled low. These two lines are shared with
+ the TX and RX (respectively) of UART3 - used for serial debugging.
+
+ This headset driver keeps the CPLD configured as UART3 for as long as
+ possible, so that we can do serial FIQ debugging even when the kernel is
+ locked and this driver no longer runs. So it only configures the CPLD to
+ GPIO while the headset is plugged in, and for 10ms during detection work.
+
+ Unfortunately we can't leave the CPLD as UART3 while a headset is plugged
+ in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA
+ drain on sapphire.
+
+ The headset detection work involves setting CPLD to GPIO, and then pulling
+ CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still
+ pull this line low, whereas other attachments such as a serial console
+ would get pulled up by this stronger pullup.
+
+ Headset insertion/removal causes UEvent's to be sent, and
+ /sys/class/switch/h2w/state to be updated.
+
+ Button presses are interpreted as input event (KEY_MEDIA). Button presses
+ are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm
+ jack adapters do not work until a headset is plugged into the adapter. This
+ is to avoid serial RX traffic causing spurious button press events.
+
+ We tend to check the status of CABLE_IN1 a few more times than strictly
+ necessary during headset detection, to avoid spurious headset insertion
+ events caused by serial debugger TX traffic.
+*/
+
+
+#include <linux/module.h>
+#include <linux/sysdev.h>
+#include <linux/fs.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/mutex.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <asm/atomic.h>
+#include <mach/board.h>
+#include <mach/vreg.h>
+#include <asm/mach-types.h>
+#include "board-sapphire.h"
+
+#ifdef CONFIG_DEBUG_SAPPHIRE_H2W
+#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg)
+#else
+#define H2W_DBG(fmt, arg...) do {} while (0)
+#endif
+
+static struct workqueue_struct *g_detection_work_queue;
+static void detection_work(struct work_struct *work);
+static DECLARE_WORK(g_detection_work, detection_work);
+enum {
+ NO_DEVICE = 0,
+ HTC_HEADSET = 1,
+};
+
+enum {
+ UART3 = 0,
+ GPIO = 1,
+};
+
+struct h2w_info {
+ struct switch_dev sdev;
+ struct input_dev *input;
+
+ atomic_t btn_state;
+ int ignore_btn;
+
+ unsigned int irq;
+ unsigned int irq_btn;
+
+ struct hrtimer timer;
+ ktime_t debounce_time;
+
+ struct hrtimer btn_timer;
+ ktime_t btn_debounce_time;
+};
+static struct h2w_info *hi;
+
+static ssize_t sapphire_h2w_print_name(struct switch_dev *sdev, char *buf)
+{
+ switch (switch_get_state(&hi->sdev)) {
+ case NO_DEVICE:
+ return sprintf(buf, "No Device\n");
+ case HTC_HEADSET:
+ return sprintf(buf, "Headset\n");
+ }
+ return -EINVAL;
+}
+
+static void configure_cpld(int route)
+{
+ H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO");
+ switch (route) {
+ case UART3:
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1);
+ break;
+ case GPIO:
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0);
+ break;
+ }
+}
+
+static void button_pressed(void)
+{
+ H2W_DBG("");
+ atomic_set(&hi->btn_state, 1);
+ input_report_key(hi->input, KEY_MEDIA, 1);
+ input_sync(hi->input);
+}
+
+static void button_released(void)
+{
+ H2W_DBG("");
+ atomic_set(&hi->btn_state, 0);
+ input_report_key(hi->input, KEY_MEDIA, 0);
+ input_sync(hi->input);
+}
+
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER
+extern void msm_serial_debug_enable(int);
+#endif
+
+static void insert_headset(void)
+{
+ unsigned long irq_flags;
+
+ H2W_DBG("");
+
+ switch_set_state(&hi->sdev, HTC_HEADSET);
+ configure_cpld(GPIO);
+
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER
+ msm_serial_debug_enable(false);
+#endif
+
+
+ /* On some non-standard headset adapters (usually those without a
+ * button) the btn line is pulled down at the same time as the detect
+ * line. We can check here by sampling the button line, if it is
+ * low then it is probably a bad adapter so ignore the button.
+ * If the button is released then we stop ignoring the button, so that
+ * the user can recover from the situation where a headset is plugged
+ * in with button held down.
+ */
+ hi->ignore_btn = !gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2);
+
+ /* Enable button irq */
+ local_irq_save(irq_flags);
+ enable_irq(hi->irq_btn);
+ local_irq_restore(irq_flags);
+
+ hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */
+}
+
+static void remove_headset(void)
+{
+ unsigned long irq_flags;
+
+ H2W_DBG("");
+
+ switch_set_state(&hi->sdev, NO_DEVICE);
+ configure_cpld(UART3);
+
+ /* Disable button */
+ local_irq_save(irq_flags);
+ disable_irq(hi->irq_btn);
+ local_irq_restore(irq_flags);
+
+ if (atomic_read(&hi->btn_state))
+ button_released();
+
+ hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */
+}
+
+static void detection_work(struct work_struct *work)
+{
+ unsigned long irq_flags;
+ int clk, cable_in1;
+
+ H2W_DBG("");
+
+ if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1) != 0) {
+ /* Headset not plugged in */
+ if (switch_get_state(&hi->sdev) == HTC_HEADSET)
+ remove_headset();
+ return;
+ }
+
+ /* Something plugged in, lets make sure its a headset */
+
+ /* Switch CPLD to GPIO to do detection */
+ configure_cpld(GPIO);
+ /* Disable headset interrupt while detecting.*/
+ local_irq_save(irq_flags);
+ disable_irq(hi->irq);
+ local_irq_restore(irq_flags);
+
+ /* Set GPIO_CABLE_IN1 as output high */
+ gpio_direction_output(SAPPHIRE_GPIO_CABLE_IN1, 1);
+ /* Delay 10ms for pin stable. */
+ msleep(10);
+ /* Save H2W_CLK */
+ clk = gpio_get_value(SAPPHIRE_GPIO_H2W_CLK_GPI);
+ /* Set GPIO_CABLE_IN1 as input */
+ gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1);
+
+ /* Restore IRQs */
+ local_irq_save(irq_flags);
+ enable_irq(hi->irq);
+ local_irq_restore(irq_flags);
+
+ cable_in1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1);
+
+ if (cable_in1 == 0 && clk == 0) {
+ if (switch_get_state(&hi->sdev) == NO_DEVICE)
+ insert_headset();
+ } else {
+ configure_cpld(UART3);
+ H2W_DBG("CABLE_IN1 was low, but not a headset "
+ "(recent cable_in1 = %d, clk = %d)", cable_in1, clk);
+ }
+}
+
+static enum hrtimer_restart button_event_timer_func(struct hrtimer *data)
+{
+ H2W_DBG("");
+
+ if (switch_get_state(&hi->sdev) == HTC_HEADSET) {
+ if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2)) {
+ if (hi->ignore_btn)
+ hi->ignore_btn = 0;
+ else if (atomic_read(&hi->btn_state))
+ button_released();
+ } else {
+ if (!hi->ignore_btn && !atomic_read(&hi->btn_state))
+ button_pressed();
+ }
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data)
+{
+ H2W_DBG("");
+
+ queue_work(g_detection_work_queue, &g_detection_work);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t detect_irq_handler(int irq, void *dev_id)
+{
+ int value1, value2;
+ int retry_limit = 10;
+
+ H2W_DBG("");
+ do {
+ value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1);
+ set_irq_type(hi->irq, value1 ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1);
+ } while (value1 != value2 && retry_limit-- > 0);
+
+ H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit));
+
+ if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) {
+ if (switch_get_state(&hi->sdev) == HTC_HEADSET)
+ hi->ignore_btn = 1;
+ /* Do the rest of the work in timer context */
+ hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t button_irq_handler(int irq, void *dev_id)
+{
+ int value1, value2;
+ int retry_limit = 10;
+
+ H2W_DBG("");
+ do {
+ value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2);
+ set_irq_type(hi->irq_btn, value1 ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2);
+ } while (value1 != value2 && retry_limit-- > 0);
+
+ H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit));
+
+ hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL);
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static void h2w_debug_set(void *data, u64 val)
+{
+ switch_set_state(&hi->sdev, (int)val);
+}
+
+static u64 h2w_debug_get(void *data)
+{
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n");
+static int __init h2w_debug_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("h2w", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops);
+
+ return 0;
+}
+
+device_initcall(h2w_debug_init);
+#endif
+
+static int sapphire_h2w_probe(struct platform_device *pdev)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ printk(KERN_INFO "H2W: Registering H2W (headset) driver\n");
+ hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL);
+ if (!hi)
+ return -ENOMEM;
+
+ atomic_set(&hi->btn_state, 0);
+ hi->ignore_btn = 0;
+
+ hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */
+ hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */
+ hi->sdev.name = "h2w";
+ hi->sdev.print_name = sapphire_h2w_print_name;
+
+ ret = switch_dev_register(&hi->sdev);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ g_detection_work_queue = create_workqueue("detection");
+ if (g_detection_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto err_create_work_queue;
+ }
+
+ ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN1, "h2w_detect");
+ if (ret < 0)
+ goto err_request_detect_gpio;
+
+ ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN2, "h2w_button");
+ if (ret < 0)
+ goto err_request_button_gpio;
+
+ ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1);
+ if (ret < 0)
+ goto err_set_detect_gpio;
+
+ ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN2);
+ if (ret < 0)
+ goto err_set_button_gpio;
+
+ hi->irq = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN1);
+ if (hi->irq < 0) {
+ ret = hi->irq;
+ goto err_get_h2w_detect_irq_num_failed;
+ }
+
+ hi->irq_btn = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN2);
+ if (hi->irq_btn < 0) {
+ ret = hi->irq_btn;
+ goto err_get_button_irq_num_failed;
+ }
+
+ /* Set CPLD MUX to H2W <-> CPLD GPIO */
+ configure_cpld(UART3);
+ /* Set the CPLD connected H2W GPIO's to input */
+ gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0);
+
+ hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hi->timer.function = detect_event_timer_func;
+ hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hi->btn_timer.function = button_event_timer_func;
+
+ ret = request_irq(hi->irq, detect_irq_handler,
+ IRQF_TRIGGER_LOW, "h2w_detect", NULL);
+ if (ret < 0)
+ goto err_request_detect_irq;
+
+ /* Disable button until plugged in */
+ set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN);
+ ret = request_irq(hi->irq_btn, button_irq_handler,
+ IRQF_TRIGGER_LOW, "h2w_button", NULL);
+ if (ret < 0)
+ goto err_request_h2w_headset_button_irq;
+
+ ret = set_irq_wake(hi->irq, 1);
+ if (ret < 0)
+ goto err_request_input_dev;
+ ret = set_irq_wake(hi->irq_btn, 1);
+ if (ret < 0)
+ goto err_request_input_dev;
+
+ hi->input = input_allocate_device();
+ if (!hi->input) {
+ ret = -ENOMEM;
+ goto err_request_input_dev;
+ }
+
+ hi->input->name = "h2w headset";
+ hi->input->evbit[0] = BIT_MASK(EV_KEY);
+ hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA);
+
+ ret = input_register_device(hi->input);
+ if (ret < 0)
+ goto err_register_input_dev;
+
+ return 0;
+
+err_register_input_dev:
+ input_free_device(hi->input);
+err_request_input_dev:
+ free_irq(hi->irq_btn, 0);
+err_request_h2w_headset_button_irq:
+ free_irq(hi->irq, 0);
+err_request_detect_irq:
+err_get_button_irq_num_failed:
+err_get_h2w_detect_irq_num_failed:
+err_set_button_gpio:
+err_set_detect_gpio:
+ gpio_free(SAPPHIRE_GPIO_CABLE_IN2);
+err_request_button_gpio:
+ gpio_free(SAPPHIRE_GPIO_CABLE_IN1);
+err_request_detect_gpio:
+ destroy_workqueue(g_detection_work_queue);
+err_create_work_queue:
+ switch_dev_unregister(&hi->sdev);
+err_switch_dev_register:
+ printk(KERN_ERR "H2W: Failed to register driver\n");
+
+ return ret;
+}
+
+static int sapphire_h2w_remove(struct platform_device *pdev)
+{
+ H2W_DBG("");
+ if (switch_get_state(&hi->sdev))
+ remove_headset();
+ input_unregister_device(hi->input);
+ gpio_free(SAPPHIRE_GPIO_CABLE_IN2);
+ gpio_free(SAPPHIRE_GPIO_CABLE_IN1);
+ free_irq(hi->irq_btn, 0);
+ free_irq(hi->irq, 0);
+ destroy_workqueue(g_detection_work_queue);
+ switch_dev_unregister(&hi->sdev);
+
+ return 0;
+}
+
+static struct platform_device sapphire_h2w_device = {
+ .name = "sapphire-h2w",
+};
+
+static struct platform_driver sapphire_h2w_driver = {
+ .probe = sapphire_h2w_probe,
+ .remove = sapphire_h2w_remove,
+ .driver = {
+ .name = "sapphire-h2w",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sapphire_h2w_init(void)
+{
+ if (!machine_is_sapphire())
+ return 0;
+ int ret;
+ H2W_DBG("");
+ ret = platform_driver_register(&sapphire_h2w_driver);
+ if (ret)
+ return ret;
+ return platform_device_register(&sapphire_h2w_device);
+}
+
+static void __exit sapphire_h2w_exit(void)
+{
+ platform_device_unregister(&sapphire_h2w_device);
+ platform_driver_unregister(&sapphire_h2w_driver);
+}
+
+module_init(sapphire_h2w_init);
+module_exit(sapphire_h2w_exit);
+
+MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>");
+MODULE_DESCRIPTION("HTC 2 Wire detection driver for sapphire");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c
new file mode 100755
index 0000000..5c8fc37
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-keypad.c
@@ -0,0 +1,132 @@
+/* arch/arm/mach-msm/board-sapphire-keypad.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+*/
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio_event.h>
+#include <asm/mach-types.h>
+#include "gpio_chip.h"
+#include "board-sapphire.h"
+static char *keycaps = "--qwerty";
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "board_sapphire."
+module_param_named(keycaps, keycaps, charp, 0);
+
+
+static unsigned int sapphire_col_gpios[] = { 35, 34 };
+
+/* KP_MKIN2 (GPIO40) is not used? */
+static unsigned int sapphire_row_gpios[] = { 42, 41 };
+
+#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(sapphire_row_gpios) + (row))
+
+/*scan matrix key*/
+/* HOME(up) MENU (up) Back Search */
+static const unsigned short sapphire_keymap2[ARRAY_SIZE(sapphire_col_gpios) * ARRAY_SIZE(sapphire_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_COMPOSE,
+ [KEYMAP_INDEX(0, 1)] = KEY_BACK,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_MENU,
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+};
+
+/* HOME(up) + MENU (down)*/
+static const unsigned short sapphire_keymap1[ARRAY_SIZE(sapphire_col_gpios) *
+ ARRAY_SIZE(sapphire_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_BACK,
+ [KEYMAP_INDEX(0, 1)] = KEY_MENU,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_HOME,
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+};
+
+/* MENU(up) + HOME (down)*/
+static const unsigned short sapphire_keymap0[ARRAY_SIZE(sapphire_col_gpios) *
+ ARRAY_SIZE(sapphire_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_BACK,
+ [KEYMAP_INDEX(0, 1)] = KEY_HOME,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_MENU,
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+};
+
+static struct gpio_event_matrix_info sapphire_keypad_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = sapphire_keymap2,
+ .output_gpios = sapphire_col_gpios,
+ .input_gpios = sapphire_row_gpios,
+ .noutputs = ARRAY_SIZE(sapphire_col_gpios),
+ .ninputs = ARRAY_SIZE(sapphire_row_gpios),
+ .settle_time.tv.nsec = 40 * NSEC_PER_USEC,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .debounce_delay.tv.nsec = 50 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ |
+ GPIOKPF_REMOVE_PHANTOM_KEYS |
+ GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/
+};
+
+static struct gpio_event_direct_entry sapphire_keypad_nav_map[] = {
+ { SAPPHIRE_POWER_KEY, KEY_END },
+ { SAPPHIRE_VOLUME_UP, KEY_VOLUMEUP },
+ { SAPPHIRE_VOLUME_DOWN, KEY_VOLUMEDOWN },
+};
+
+static struct gpio_event_input_info sapphire_keypad_nav_info = {
+ .info.func = gpio_event_input_func,
+ .flags = 0,
+ .type = EV_KEY,
+ .keymap = sapphire_keypad_nav_map,
+ .debounce_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .keymap_size = ARRAY_SIZE(sapphire_keypad_nav_map)
+};
+
+static struct gpio_event_info *sapphire_keypad_info[] = {
+ &sapphire_keypad_matrix_info.info,
+ &sapphire_keypad_nav_info.info,
+};
+
+static struct gpio_event_platform_data sapphire_keypad_data = {
+ .name = "sapphire-keypad",
+ .info = sapphire_keypad_info,
+ .info_count = ARRAY_SIZE(sapphire_keypad_info)
+};
+
+static struct platform_device sapphire_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 0,
+ .dev = {
+ .platform_data = &sapphire_keypad_data,
+ },
+};
+
+static int __init sapphire_init_keypad(void)
+{
+ if (!machine_is_sapphire())
+ return 0;
+
+ switch (sapphire_get_hwid()) {
+ case 0:
+ sapphire_keypad_matrix_info.keymap = sapphire_keymap0;
+ break;
+ default:
+ if(system_rev != 0x80)
+ sapphire_keypad_matrix_info.keymap = sapphire_keymap1;
+ break;
+ }
+ return platform_device_register(&sapphire_keypad_device);
+}
+
+device_initcall(sapphire_init_keypad);
+
diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c
new file mode 100755
index 0000000..a8a7963
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-mmc.c
@@ -0,0 +1,486 @@
+/* linux/arch/arm/mach-msm/board-sapphire-mmc.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/vreg.h>
+#include <mach/htc_pwrsink.h>
+
+#include <asm/mach/mmc.h>
+
+#include "devices.h"
+#include "gpio_chip.h"
+#include "board-sapphire.h"
+#include "proc_comm.h"
+
+#define DEBUG_SDSLOT_VDD 0
+
+extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags);
+
+/* ---- COMMON ---- */
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for (n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+/* ---- SDCARD ---- */
+
+static uint32_t sdcard_on_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+};
+
+static uint32_t sdcard_off_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+};
+
+static uint opt_disable_sdcard;
+
+static int __init sapphire_disablesdcard_setup(char *str)
+{
+ int cal = simple_strtol(str, NULL, 0);
+
+ opt_disable_sdcard = cal;
+ return 1;
+}
+
+__setup("board_sapphire.disable_sdcard=", sapphire_disablesdcard_setup);
+
+static struct vreg *vreg_sdslot; /* SD slot power */
+
+struct mmc_vdd_xlat {
+ int mask;
+ int level;
+};
+
+static struct mmc_vdd_xlat mmc_vdd_table[] = {
+ { MMC_VDD_165_195, 1800 },
+ { MMC_VDD_20_21, 2050 },
+ { MMC_VDD_21_22, 2150 },
+ { MMC_VDD_22_23, 2250 },
+ { MMC_VDD_23_24, 2350 },
+ { MMC_VDD_24_25, 2450 },
+ { MMC_VDD_25_26, 2550 },
+ { MMC_VDD_26_27, 2650 },
+ { MMC_VDD_27_28, 2750 },
+ { MMC_VDD_28_29, 2850 },
+ { MMC_VDD_29_30, 2950 },
+};
+
+static unsigned int sdslot_vdd = 0xffffffff;
+static unsigned int sdslot_vreg_enabled;
+
+static uint32_t sapphire_sdslot_switchvdd(struct device *dev, unsigned int vdd)
+{
+ int i, rc;
+
+ BUG_ON(!vreg_sdslot);
+
+ if (vdd == sdslot_vdd)
+ return 0;
+
+ sdslot_vdd = vdd;
+
+ if (vdd == 0) {
+#if DEBUG_SDSLOT_VDD
+ printk(KERN_DEBUG "%s: Disabling SD slot power\n", __func__);
+#endif
+ config_gpio_table(sdcard_off_gpio_table,
+ ARRAY_SIZE(sdcard_off_gpio_table));
+ vreg_disable(vreg_sdslot);
+ sdslot_vreg_enabled = 0;
+ return 0;
+ }
+
+ if (!sdslot_vreg_enabled) {
+ rc = vreg_enable(vreg_sdslot);
+ if (rc) {
+ printk(KERN_ERR "%s: Error enabling vreg (%d)\n",
+ __func__, rc);
+ }
+ config_gpio_table(sdcard_on_gpio_table,
+ ARRAY_SIZE(sdcard_on_gpio_table));
+ sdslot_vreg_enabled = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
+ if (mmc_vdd_table[i].mask == (1 << vdd)) {
+#if DEBUG_SDSLOT_VDD
+ printk(KERN_DEBUG "%s: Setting level to %u\n",
+ __func__, mmc_vdd_table[i].level);
+#endif
+ rc = vreg_set_level(vreg_sdslot,
+ mmc_vdd_table[i].level);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error setting vreg level (%d)\n",
+ __func__, rc);
+ }
+ return 0;
+ }
+ }
+
+ printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd);
+ return 0;
+}
+
+static unsigned int sapphire_sdslot_status(struct device *dev)
+{
+ unsigned int status;
+
+ status = (unsigned int) gpio_get_value(SAPPHIRE_GPIO_SDMC_CD_N);
+ return !status;
+}
+
+#define SAPPHIRE_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \
+ | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \
+ | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \
+ | MMC_VDD_28_29 | MMC_VDD_29_30)
+
+static struct mmc_platform_data sapphire_sdslot_data = {
+ .ocr_mask = SAPPHIRE_MMC_VDD,
+ .status = sapphire_sdslot_status,
+ .translate_vdd = sapphire_sdslot_switchvdd,
+};
+
+/* ---- WIFI ---- */
+
+static uint32_t wifi_on_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+static uint32_t wifi_off_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */
+static int sapphire_wifi_cd = 0; /* WIFI virtual 'card detect' status */
+
+static struct sdio_embedded_func wifi_func = {
+ .f_class = SDIO_CLASS_WLAN,
+ .f_maxblksize = 512,
+};
+
+static struct embedded_sdio_data sapphire_wifi_emb_data = {
+ .cis = {
+ .vendor = 0x104c,
+ .device = 0x9066,
+ .blksize = 512,
+ .max_dtr = 20000000,
+ },
+ .cccr = {
+ .multi_block = 0,
+ .low_speed = 0,
+ .wide_bus = 1,
+ .high_power = 0,
+ .high_speed = 0,
+ },
+ .funcs = &wifi_func,
+ .num_funcs = 1,
+};
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+
+static int sapphire_wifi_status_register(void (*callback)(int card_present,
+ void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static unsigned int sapphire_wifi_status(struct device *dev)
+{
+ return sapphire_wifi_cd;
+}
+
+int sapphire_wifi_set_carddetect(int val)
+{
+ printk(KERN_DEBUG "%s: %d\n", __func__, val);
+ sapphire_wifi_cd = val;
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ printk(KERN_WARNING "%s: Nobody to notify\n", __func__);
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sapphire_wifi_set_carddetect);
+#endif
+
+int sapphire_wifi_power_state=0;
+int sapphire_bt_power_state=0;
+
+int sapphire_wifi_power(int on)
+{
+ int rc;
+
+ printk(KERN_DEBUG "%s: %d\n", __func__, on);
+
+ if (on) {
+ config_gpio_table(wifi_on_gpio_table,
+ ARRAY_SIZE(wifi_on_gpio_table));
+ rc = vreg_enable(vreg_wifi_osc);
+ if (rc)
+ return rc;
+ htc_pwrsink_set(PWRSINK_WIFI, 70);
+ } else {
+ config_gpio_table(wifi_off_gpio_table,
+ ARRAY_SIZE(wifi_off_gpio_table));
+ htc_pwrsink_set(PWRSINK_WIFI, 0);
+ }
+ gpio_set_value(SAPPHIRE_GPIO_MAC_32K_EN, on);
+ mdelay(100);
+ gpio_set_value(SAPPHIRE_GPIO_WIFI_EN, on);
+ mdelay(100);
+ if (!on) {
+ if(!sapphire_bt_power_state)
+ {
+ vreg_disable(vreg_wifi_osc);
+ printk("WiFi disable vreg_wifi_osc.\n");
+ }
+ else
+ printk("WiFi shouldn't disable vreg_wifi_osc. BT is using it!!\n");
+ }
+ sapphire_wifi_power_state = on;
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sapphire_wifi_power);
+#endif
+
+/* Eenable VREG_MMC pin to turn on fastclock oscillator : colin */
+int sapphire_bt_fastclock_power(int on)
+{
+ int rc;
+
+ printk(KERN_DEBUG "sapphire_bt_fastclock_power on = %d\n", on);
+ if (vreg_wifi_osc) {
+ if (on) {
+ rc = vreg_enable(vreg_wifi_osc);
+ printk(KERN_DEBUG "BT vreg_enable vreg_mmc, rc=%d\n",
+ rc);
+ if (rc) {
+ printk("Error turn sapphire_bt_fastclock_power rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ if (!sapphire_wifi_power_state) {
+ vreg_disable(vreg_wifi_osc);
+ printk(KERN_DEBUG "BT disable vreg_wifi_osc.\n");
+ } else
+ printk(KERN_DEBUG "BT shouldn't disable vreg_wifi_osc. WiFi is using it!!\n");
+ }
+ }
+ sapphire_bt_power_state = on;
+ return 0;
+}
+EXPORT_SYMBOL(sapphire_bt_fastclock_power);
+
+static int sapphire_wifi_reset_state;
+void sapphire_wifi_reset(int on)
+{
+ printk(KERN_DEBUG "%s: %d\n", __func__, on);
+ gpio_set_value(SAPPHIRE_GPIO_WIFI_PA_RESETX, !on);
+ sapphire_wifi_reset_state = on;
+ mdelay(50);
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sapphire_wifi_reset);
+#endif
+
+static struct mmc_platform_data sapphire_wifi_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .status = sapphire_wifi_status,
+ .register_status_notify = sapphire_wifi_status_register,
+ .embedded_sdio = &sapphire_wifi_emb_data,
+};
+
+int __init sapphire_init_mmc(unsigned int sys_rev)
+{
+ wifi_status_cb = NULL;
+
+ sdslot_vreg_enabled = 0;
+
+ vreg_sdslot = vreg_get(0, "gp6");
+ if (IS_ERR(vreg_sdslot))
+ return PTR_ERR(vreg_sdslot);
+ vreg_wifi_osc = vreg_get(0, "mmc");
+ if (IS_ERR(vreg_wifi_osc))
+ return PTR_ERR(vreg_wifi_osc);
+
+ set_irq_wake(SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 1);
+
+ msm_add_sdcc(1, &sapphire_wifi_data, 0, 0);
+
+ if (!opt_disable_sdcard)
+ msm_add_sdcc(2, &sapphire_sdslot_data,
+ SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 0);
+ else
+ printk(KERN_INFO "sapphire: SD-Card interface disabled\n");
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static int sapphiremmc_dbg_wifi_reset_set(void *data, u64 val)
+{
+ sapphire_wifi_reset((int) val);
+ return 0;
+}
+
+static int sapphiremmc_dbg_wifi_reset_get(void *data, u64 *val)
+{
+ *val = sapphire_wifi_reset_state;
+ return 0;
+}
+
+static int sapphiremmc_dbg_wifi_cd_set(void *data, u64 val)
+{
+ sapphire_wifi_set_carddetect((int) val);
+ return 0;
+}
+
+static int sapphiremmc_dbg_wifi_cd_get(void *data, u64 *val)
+{
+ *val = sapphire_wifi_cd;
+ return 0;
+}
+
+static int sapphiremmc_dbg_wifi_pwr_set(void *data, u64 val)
+{
+ sapphire_wifi_power((int) val);
+ return 0;
+}
+
+static int sapphiremmc_dbg_wifi_pwr_get(void *data, u64 *val)
+{
+
+ *val = sapphire_wifi_power_state;
+ return 0;
+}
+
+static int sapphiremmc_dbg_sd_pwr_set(void *data, u64 val)
+{
+ sapphire_sdslot_switchvdd(NULL, (unsigned int) val);
+ return 0;
+}
+
+static int sapphiremmc_dbg_sd_pwr_get(void *data, u64 *val)
+{
+ *val = sdslot_vdd;
+ return 0;
+}
+
+static int sapphiremmc_dbg_sd_cd_set(void *data, u64 val)
+{
+ return -ENOSYS;
+}
+
+static int sapphiremmc_dbg_sd_cd_get(void *data, u64 *val)
+{
+ *val = sapphire_sdslot_status(NULL);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_reset_fops,
+ sapphiremmc_dbg_wifi_reset_get,
+ sapphiremmc_dbg_wifi_reset_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_cd_fops,
+ sapphiremmc_dbg_wifi_cd_get,
+ sapphiremmc_dbg_wifi_cd_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_pwr_fops,
+ sapphiremmc_dbg_wifi_pwr_get,
+ sapphiremmc_dbg_wifi_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_pwr_fops,
+ sapphiremmc_dbg_sd_pwr_get,
+ sapphiremmc_dbg_sd_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_cd_fops,
+ sapphiremmc_dbg_sd_cd_get,
+ sapphiremmc_dbg_sd_cd_set, "%llu\n");
+
+static int __init sapphiremmc_dbg_init(void)
+{
+ struct dentry *dent;
+
+ if (!machine_is_sapphire())
+ return 0;
+
+ dent = debugfs_create_dir("sapphiremmc_dbg", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("wifi_reset", 0644, dent, NULL,
+ &sapphiremmc_dbg_wifi_reset_fops);
+ debugfs_create_file("wifi_cd", 0644, dent, NULL,
+ &sapphiremmc_dbg_wifi_cd_fops);
+ debugfs_create_file("wifi_pwr", 0644, dent, NULL,
+ &sapphiremmc_dbg_wifi_pwr_fops);
+
+ debugfs_create_file("sd_pwr", 0644, dent, NULL,
+ &sapphiremmc_dbg_sd_pwr_fops);
+ debugfs_create_file("sd_cd", 0644, dent, NULL,
+ &sapphiremmc_dbg_sd_cd_fops);
+
+ return 0;
+}
+
+device_initcall(sapphiremmc_dbg_init);
+
+#endif
diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c
new file mode 100644
index 0000000..f71ca01
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-panel.c
@@ -0,0 +1,1272 @@
+/* linux/arch/arm/mach-msm/board-sapphire-panel.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+#include <mach/vreg.h>
+#include <mach/htc_pwrsink.h>
+
+#include "gpio_chip.h"
+#include "board-sapphire.h"
+#include "proc_comm.h"
+#include "devices.h"
+
+#define DEBUG_SAPPHIRE_PANEL 0
+#define userid 0xD10
+
+#define VSYNC_GPIO 97
+
+enum sapphire_panel_type {
+ SAPPHIRE_PANEL_SHARP = 0,
+ SAPPHIRE_PANEL_TOPPOLY,
+ NUM_OF_SAPPHIRE_PANELS,
+};
+static int g_panel_id = -1 ;
+static int g_panel_inited = 0 ;
+
+#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132
+#define GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS 102
+#define SDBB SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS
+#define GDBB GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS
+
+static int sapphire_backlight_off;
+static int sapphire_backlight_brightness =
+ SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS;
+
+static uint8_t sapphire_backlight_last_level = 33;
+static DEFINE_MUTEX(sapphire_backlight_lock);
+
+/* Divide dimming level into 12 sections, and restrict maximum level to 27 */
+#define DIMMING_STEPS 12
+static unsigned dimming_levels[NUM_OF_SAPPHIRE_PANELS][DIMMING_STEPS] = {
+ {0, 1, 2, 3, 6, 9, 11, 13, 16, 19, 22, 25}, /* Sharp */
+ {0, 1, 2, 4, 7, 10, 13, 15, 18, 21, 24, 27}, /* Toppolly */
+};
+static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87,
+ 100};
+
+static void sapphire_set_backlight_level(uint8_t level)
+{
+ unsigned dimming_factor = 255/DIMMING_STEPS + 1;
+ int index, new_level ;
+ unsigned percent;
+ unsigned long flags;
+ int i = 0;
+
+ /* Non-linear transform for the difference between two
+ * kind of default backlight settings.
+ */
+ new_level = level<=GDBB ?
+ level*SDBB/GDBB : (SDBB + (level-GDBB)*(255-SDBB) / (255-GDBB)) ;
+ index = new_level/dimming_factor ;
+
+#if DEBUG_SAPPHIRE_PANEL
+ printk(KERN_INFO "level=%d, new level=%d, dimming_levels[%d]=%d\n",
+ level, new_level, index, dimming_levels[g_panel_id][index]);
+#endif
+ percent = pwrsink_percents[index];
+ level = dimming_levels[g_panel_id][index];
+
+ if (sapphire_backlight_last_level == level)
+ return;
+
+ if (level == 0) {
+ gpio_set_value(27, 0);
+ msleep(2);
+ } else {
+ local_irq_save(flags);
+ if (sapphire_backlight_last_level == 0) {
+ gpio_set_value(27, 1);
+ udelay(40);
+ sapphire_backlight_last_level = 33;
+ }
+ i = (sapphire_backlight_last_level - level + 33) % 33;
+ while (i-- > 0) {
+ gpio_set_value(27, 0);
+ udelay(1);
+ gpio_set_value(27, 1);
+ udelay(1);
+ }
+ local_irq_restore(flags);
+ }
+ sapphire_backlight_last_level = level;
+ htc_pwrsink_set(PWRSINK_BACKLIGHT, percent);
+}
+
+#define MDDI_CLIENT_CORE_BASE 0x108000
+#define LCD_CONTROL_BLOCK_BASE 0x110000
+#define SPI_BLOCK_BASE 0x120000
+#define I2C_BLOCK_BASE 0x130000
+#define PWM_BLOCK_BASE 0x140000
+#define GPIO_BLOCK_BASE 0x150000
+#define SYSTEM_BLOCK1_BASE 0x160000
+#define SYSTEM_BLOCK2_BASE 0x170000
+
+
+#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24)
+#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C)
+#define PWM0OFF (PWM_BLOCK_BASE|0x1C)
+
+#define V_VDDE2E_VDD2_GPIO 0
+#define V_VDDE2E_VDD2_GPIO_5M 89
+#define MDDI_RST_N 82
+
+#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00)
+#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04)
+#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08)
+#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C)
+#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10)
+#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14)
+#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18)
+#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C)
+#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20)
+#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24)
+#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28)
+#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C)
+#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30)
+#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34)
+#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38)
+#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C)
+#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40)
+#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44)
+#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48)
+#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C)
+#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50)
+#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54)
+
+#define SRST (LCD_CONTROL_BLOCK_BASE|0x00)
+#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04)
+#define START (LCD_CONTROL_BLOCK_BASE|0x08)
+#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C)
+#define CMN (LCD_CONTROL_BLOCK_BASE|0x10)
+#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14)
+#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18)
+#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C)
+#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20)
+#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24)
+#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28)
+#define PXL (LCD_CONTROL_BLOCK_BASE|0x30)
+#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34)
+#define HSW (LCD_CONTROL_BLOCK_BASE|0x38)
+#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C)
+#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40)
+#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44)
+#define VSW (LCD_CONTROL_BLOCK_BASE|0x48)
+#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C)
+#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50)
+#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54)
+#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58)
+#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C)
+#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60)
+#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64)
+#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68)
+#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C)
+#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70)
+#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74)
+#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78)
+#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C)
+#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80)
+#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84)
+#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88)
+#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C)
+
+#define SSICTL (SPI_BLOCK_BASE|0x00)
+#define SSITIME (SPI_BLOCK_BASE|0x04)
+#define SSITX (SPI_BLOCK_BASE|0x08)
+#define SSIRX (SPI_BLOCK_BASE|0x0C)
+#define SSIINTC (SPI_BLOCK_BASE|0x10)
+#define SSIINTS (SPI_BLOCK_BASE|0x14)
+#define SSIDBG1 (SPI_BLOCK_BASE|0x18)
+#define SSIDBG2 (SPI_BLOCK_BASE|0x1C)
+#define SSIID (SPI_BLOCK_BASE|0x20)
+
+#define WKREQ (SYSTEM_BLOCK1_BASE|0x00)
+#define CLKENB (SYSTEM_BLOCK1_BASE|0x04)
+#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08)
+#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C)
+#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00)
+
+#define GPIODATA (GPIO_BLOCK_BASE|0x00)
+#define GPIODIR (GPIO_BLOCK_BASE|0x04)
+#define GPIOIS (GPIO_BLOCK_BASE|0x08)
+#define GPIOIBE (GPIO_BLOCK_BASE|0x0C)
+#define GPIOIEV (GPIO_BLOCK_BASE|0x10)
+#define GPIOIE (GPIO_BLOCK_BASE|0x14)
+#define GPIORIS (GPIO_BLOCK_BASE|0x18)
+#define GPIOMIS (GPIO_BLOCK_BASE|0x1C)
+#define GPIOIC (GPIO_BLOCK_BASE|0x20)
+#define GPIOOMS (GPIO_BLOCK_BASE|0x24)
+#define GPIOPC (GPIO_BLOCK_BASE|0x28)
+#define GPIOID (GPIO_BLOCK_BASE|0x30)
+
+#define SPI_WRITE(reg, val) \
+ { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \
+ { 0, 5 },
+
+#define SPI_WRITE1(reg) \
+ { SSITX, (reg) & 0xff }, \
+ { 0, 5 },
+
+struct mddi_table {
+ uint32_t reg;
+ uint32_t value;
+};
+static struct mddi_table mddi_toshiba_init_table[] = {
+ { DPSET0, 0x09e90046 },
+ { DPSET1, 0x00000118 },
+ { DPSUS, 0x00000000 },
+ { DPRUN, 0x00000001 },
+ { 1, 14 }, /* msleep 14 */
+ { SYSCKENA, 0x00000001 },
+ /*{ CLKENB, 0x000000EF } */
+ { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */
+ /*{ CLKENB, 0x000025CB }, Clock enable register */
+
+ { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */
+ { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */
+ { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */
+ { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */
+ { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */
+
+ { GPIOIBE, 0x000003FF },
+ { GPIOIS, 0x00000000 },
+ { GPIOIC, 0x000003FF },
+ { GPIOIE, 0x00000000 },
+
+ { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */
+ { 1, 1 }, /* msleep 1 */
+ { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */
+ { DRAMPWR, 0x00000001 }, /* eDRAM power */
+};
+
+static struct mddi_table mddi_toshiba_panel_init_table[] = {
+ { SRST, 0x00000003 }, /* FIFO/LCDC not reset */
+ { PORT_ENB, 0x00000001 }, /* Enable sync. Port */
+ { START, 0x00000000 }, /* To stop operation */
+ /*{ START, 0x00000001 }, To start operation */
+ { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */
+ { CMN, 0x00000000 },
+ { GAMMA, 0x00000000 }, /* No Gamma correction */
+ { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */
+ { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */
+ { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */
+ { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */
+ { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */
+ { PXL, 0x00000001 }, /* 1. RGB666 */
+ /* 2. Data is valid from 1st frame of beginning. */
+ { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */
+ { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */
+ { HSW, 0x00000004 }, /* HSW= 10 PCLK */
+ { VSW, 0x00000001 }, /* VSW=2 HCYCLE */
+ { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */
+ { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */
+ { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */
+ { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { CLKENB, 0x000025CB }, /* Clock enable register */
+
+ { SSICTL, 0x00000170 }, /* SSI control register */
+ { SSITIME, 0x00000250 }, /* SSI timing control register */
+ { SSICTL, 0x00000172 }, /* SSI control register */
+};
+
+
+static struct mddi_table mddi_sharp_init_table[] = {
+ { VCYCLE, 0x000001eb },
+ { HCYCLE, 0x000000ae },
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { 1, 1 }, /* msleep 1 */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { 1, 10 }, /* msleep 10 */
+ SPI_WRITE(0x5f, 0x01)
+ SPI_WRITE1(0x11)
+ { 1, 200 }, /* msleep 200 */
+ SPI_WRITE1(0x29)
+ SPI_WRITE1(0xde)
+ { START, 0x00000001 }, /* To start operation */
+};
+
+static struct mddi_table mddi_sharp_deinit_table[] = {
+ { 1, 200 }, /* msleep 200 */
+ SPI_WRITE(0x10, 0x1)
+ { 1, 100 }, /* msleep 100 */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { 1, 10 }, /* msleep 10 */
+};
+
+static struct mddi_table mddi_tpo_init_table[] = {
+ { VCYCLE, 0x000001e5 },
+ { HCYCLE, 0x000000ac },
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { 0, 20 }, /* udelay 20 */
+ { GPIODATA, 0x00000004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { 0, 20 }, /* udelay 20 */
+
+ SPI_WRITE(0x08, 0x01)
+ { 0, 500 }, /* udelay 500 */
+ SPI_WRITE(0x08, 0x00)
+ SPI_WRITE(0x02, 0x00)
+ SPI_WRITE(0x03, 0x04)
+ SPI_WRITE(0x04, 0x0e)
+ SPI_WRITE(0x09, 0x02)
+ SPI_WRITE(0x0b, 0x08)
+ SPI_WRITE(0x0c, 0x53)
+ SPI_WRITE(0x0d, 0x01)
+ SPI_WRITE(0x0e, 0xe0)
+ SPI_WRITE(0x0f, 0x01)
+ SPI_WRITE(0x10, 0x58)
+ SPI_WRITE(0x20, 0x1e)
+ SPI_WRITE(0x21, 0x0a)
+ SPI_WRITE(0x22, 0x0a)
+ SPI_WRITE(0x23, 0x1e)
+ SPI_WRITE(0x25, 0x32)
+ SPI_WRITE(0x26, 0x00)
+ SPI_WRITE(0x27, 0xac)
+ SPI_WRITE(0x29, 0x06)
+ SPI_WRITE(0x2a, 0xa4)
+ SPI_WRITE(0x2b, 0x45)
+ SPI_WRITE(0x2c, 0x45)
+ SPI_WRITE(0x2d, 0x15)
+ SPI_WRITE(0x2e, 0x5a)
+ SPI_WRITE(0x2f, 0xff)
+ SPI_WRITE(0x30, 0x6b)
+ SPI_WRITE(0x31, 0x0d)
+ SPI_WRITE(0x32, 0x48)
+ SPI_WRITE(0x33, 0x82)
+ SPI_WRITE(0x34, 0xbd)
+ SPI_WRITE(0x35, 0xe7)
+ SPI_WRITE(0x36, 0x18)
+ SPI_WRITE(0x37, 0x94)
+ SPI_WRITE(0x38, 0x01)
+ SPI_WRITE(0x39, 0x5d)
+ SPI_WRITE(0x3a, 0xae)
+ SPI_WRITE(0x3b, 0xff)
+ SPI_WRITE(0x07, 0x09)
+ { 0, 10 }, /* udelay 10 */
+ { START, 0x00000001 }, /* To start operation */
+};
+
+static struct mddi_table mddi_tpo_deinit_table[] = {
+ SPI_WRITE(0x07, 0x19)
+ { START, 0x00000000 }, /* To stop operation */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { 0, 5 }, /* usleep 5 */
+};
+
+
+#define GPIOSEL_VWAKEINT (1U << 0)
+#define INTMASK_VWAKEOUT (1U << 0)
+
+static void sapphire_process_mddi_table(
+ struct msm_mddi_client_data *client_data,
+ const struct mddi_table *table,
+ size_t count)
+{
+ int i;
+ for (i = 0; i < count; i++) {
+ uint32_t reg = table[i].reg;
+ uint32_t value = table[i].value;
+
+ if (reg == 0)
+ udelay(value);
+ else if (reg == 1)
+ msleep(value);
+ else
+ client_data->remote_write(client_data, value, reg);
+ }
+}
+
+static struct vreg *vreg_lcm_2v85;
+
+static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data,
+ int on)
+{
+ unsigned id, on_off;
+#if DEBUG_SAPPHIRE_PANEL
+ printk(KERN_INFO "sapphire_mddi_client_power:%d\r\n", on);
+#endif
+ if (on) {
+ on_off = 0;
+ id = PM_VREG_PDOWN_MDDI_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+
+ gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 1);
+ mdelay(5); /* delay time >5ms and <10ms */
+
+ if (is_12pin_camera())
+ gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 1);
+ else
+ gpio_set_value(V_VDDE2E_VDD2_GPIO, 1);
+
+ gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 1);
+ msleep(3);
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ vreg_enable(vreg_lcm_2v85);
+ msleep(3);
+ } else {
+ gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0);
+ gpio_set_value(MDDI_RST_N, 0);
+ msleep(10);
+ vreg_disable(vreg_lcm_2v85);
+ on_off = 1;
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ msleep(5);
+ if (is_12pin_camera())
+ gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 0);
+ else
+ gpio_set_value(V_VDDE2E_VDD2_GPIO, 0);
+
+ msleep(200);
+ gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 0);
+ id = PM_VREG_PDOWN_MDDI_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ }
+}
+
+static int sapphire_mddi_toshiba_client_init(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id;
+
+ /* Set the MDDI_RST_N accroding to MDDI client repectively(
+ * been set in sapphire_mddi_power_client() originally)
+ */
+ gpio_set_value(MDDI_RST_N, 1);
+ msleep(10);
+
+ client_data->auto_hibernate(client_data, 0);
+ sapphire_process_mddi_table(client_data, mddi_toshiba_init_table,
+ ARRAY_SIZE(mddi_toshiba_init_table));
+ client_data->auto_hibernate(client_data, 1);
+ g_panel_id = panel_id =
+ (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ if (panel_id > 1) {
+#if DEBUG_SAPPHIRE_PANEL
+ printk(KERN_ERR "unknown panel id at mddi_enable\n");
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+static int sapphire_mddi_toshiba_client_uninit(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ gpio_set_value(MDDI_RST_N, 0);
+ msleep(10);
+
+ return 0;
+}
+
+static int sapphire_mddi_panel_unblank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id, ret = 0;
+
+ sapphire_set_backlight_level(0);
+ client_data->auto_hibernate(client_data, 0);
+ sapphire_process_mddi_table(client_data, mddi_toshiba_panel_init_table,
+ ARRAY_SIZE(mddi_toshiba_panel_init_table));
+ panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ switch (panel_id) {
+ case 0:
+#if DEBUG_SAPPHIRE_PANEL
+ printk(KERN_DEBUG "init sharp panel\n");
+#endif
+ sapphire_process_mddi_table(client_data,
+ mddi_sharp_init_table,
+ ARRAY_SIZE(mddi_sharp_init_table));
+ break;
+ case 1:
+#if DEBUG_SAPPHIRE_PANEL
+ printk(KERN_DEBUG "init tpo panel\n");
+#endif
+ sapphire_process_mddi_table(client_data,
+ mddi_tpo_init_table,
+ ARRAY_SIZE(mddi_tpo_init_table));
+ break;
+ default:
+
+ printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id);
+ ret = -1;
+ };
+ mutex_lock(&sapphire_backlight_lock);
+ sapphire_set_backlight_level(sapphire_backlight_brightness);
+ sapphire_backlight_off = 0;
+ mutex_unlock(&sapphire_backlight_lock);
+ client_data->auto_hibernate(client_data, 1);
+ /* reenable vsync */
+ client_data->remote_write(client_data, GPIOSEL_VWAKEINT,
+ GPIOSEL);
+ client_data->remote_write(client_data, INTMASK_VWAKEOUT,
+ INTMASK);
+ return ret;
+
+}
+
+static int sapphire_mddi_panel_blank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id, ret = 0;
+
+ panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ client_data->auto_hibernate(client_data, 0);
+ switch (panel_id) {
+ case 0:
+ printk(KERN_DEBUG "deinit sharp panel\n");
+ sapphire_process_mddi_table(client_data,
+ mddi_sharp_deinit_table,
+ ARRAY_SIZE(mddi_sharp_deinit_table));
+ break;
+ case 1:
+ printk(KERN_DEBUG "deinit tpo panel\n");
+ sapphire_process_mddi_table(client_data,
+ mddi_tpo_deinit_table,
+ ARRAY_SIZE(mddi_tpo_deinit_table));
+ break;
+ default:
+ printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id);
+ ret = -1;
+ };
+ client_data->auto_hibernate(client_data, 1);
+ mutex_lock(&sapphire_backlight_lock);
+ sapphire_set_backlight_level(0);
+ sapphire_backlight_off = 1;
+ mutex_unlock(&sapphire_backlight_lock);
+ client_data->remote_write(client_data, 0, SYSCLKENA);
+ client_data->remote_write(client_data, 1, DPSUS);
+
+ return ret;
+}
+
+
+/* Initial sequence of sharp panel with Novatek NT35399 MDDI client */
+static const struct mddi_table sharp2_init_table[] = {
+ { 0x02A0, 0x00 },
+ { 0x02A1, 0x00 },
+ { 0x02A2, 0x3F },
+ { 0x02A3, 0x01 },
+ { 0x02B0, 0x00 },
+ { 0x02B1, 0x00 },
+ { 0x02B2, 0xDF },
+ { 0x02B3, 0x01 },
+ { 0x02D0, 0x00 },
+ { 0x02D1, 0x00 },
+ { 0x02D2, 0x00 },
+ { 0x02D3, 0x00 },
+ { 0x0350, 0x80 }, /* Set frame tearing effect(FTE) position */
+ { 0x0351, 0x00 },
+ { 0x0360, 0x30 },
+ { 0x0361, 0xC1 },
+ { 0x0362, 0x00 },
+ { 0x0370, 0x00 },
+ { 0x0371, 0xEF },
+ { 0x0372, 0x01 },
+
+ { 0x0B00, 0x10 },
+
+ { 0x0B10, 0x00 },
+ { 0x0B20, 0x22 },
+ { 0x0B30, 0x46 },
+ { 0x0B40, 0x07 },
+ { 0x0B41, 0x1C },
+ { 0x0B50, 0x0F },
+ { 0x0B51, 0x7A },
+ { 0x0B60, 0x16 },
+ { 0x0B70, 0x0D },
+ { 0x0B80, 0x04 },
+ { 0x0B90, 0x07 },
+ { 0x0BA0, 0x04 },
+ { 0x0BA1, 0x86 },
+ { 0x0BB0, 0xFF },
+ { 0x0BB1, 0x01 },
+ { 0x0BB2, 0xF7 },
+ { 0x0BB3, 0x01 },
+ { 0x0BC0, 0x00 },
+ { 0x0BC1, 0x00 },
+ { 0x0BC2, 0x00 },
+ { 0x0BC3, 0x00 },
+ { 0x0BE0, 0x01 },
+ { 0x0BE1, 0x3F },
+
+ { 0x0BF0, 0x03 },
+
+ { 0x0C10, 0x02 },
+
+ { 0x0C30, 0x22 },
+ { 0x0C31, 0x20 },
+ { 0x0C40, 0x48 },
+ { 0x0C41, 0x06 },
+
+ { 0xE00, 0x0028},
+ { 0xE01, 0x002F},
+ { 0xE02, 0x0032},
+ { 0xE03, 0x000A},
+ { 0xE04, 0x0023},
+ { 0xE05, 0x0024},
+ { 0xE06, 0x0022},
+ { 0xE07, 0x0012},
+ { 0xE08, 0x000D},
+ { 0xE09, 0x0035},
+ { 0xE0A, 0x000E},
+ { 0xE0B, 0x001A},
+ { 0xE0C, 0x003C},
+ { 0xE0D, 0x003A},
+ { 0xE0E, 0x0050},
+ { 0xE0F, 0x0069},
+ { 0xE10, 0x0006},
+ { 0xE11, 0x001F},
+ { 0xE12, 0x0035},
+ { 0xE13, 0x0020},
+ { 0xE14, 0x0043},
+ { 0xE15, 0x0030},
+ { 0xE16, 0x003C},
+ { 0xE17, 0x0010},
+ { 0xE18, 0x0009},
+ { 0xE19, 0x0051},
+ { 0xE1A, 0x001D},
+ { 0xE1B, 0x003C},
+ { 0xE1C, 0x0053},
+ { 0xE1D, 0x0041},
+ { 0xE1E, 0x0045},
+ { 0xE1F, 0x004B},
+ { 0xE20, 0x000A},
+ { 0xE21, 0x0014},
+ { 0xE22, 0x001C},
+ { 0xE23, 0x0013},
+ { 0xE24, 0x002E},
+ { 0xE25, 0x0029},
+ { 0xE26, 0x001B},
+ { 0xE27, 0x0014},
+ { 0xE28, 0x000E},
+ { 0xE29, 0x0032},
+ { 0xE2A, 0x000D},
+ { 0xE2B, 0x001B},
+ { 0xE2C, 0x0033},
+ { 0xE2D, 0x0033},
+ { 0xE2E, 0x005B},
+ { 0xE2F, 0x0069},
+ { 0xE30, 0x0006},
+ { 0xE31, 0x0014},
+ { 0xE32, 0x003D},
+ { 0xE33, 0x0029},
+ { 0xE34, 0x0042},
+ { 0xE35, 0x0032},
+ { 0xE36, 0x003F},
+ { 0xE37, 0x000E},
+ { 0xE38, 0x0008},
+ { 0xE39, 0x0059},
+ { 0xE3A, 0x0015},
+ { 0xE3B, 0x002E},
+ { 0xE3C, 0x0049},
+ { 0xE3D, 0x0058},
+ { 0xE3E, 0x0061},
+ { 0xE3F, 0x006B},
+ { 0xE40, 0x000A},
+ { 0xE41, 0x001A},
+ { 0xE42, 0x0022},
+ { 0xE43, 0x0014},
+ { 0xE44, 0x002F},
+ { 0xE45, 0x002A},
+ { 0xE46, 0x001A},
+ { 0xE47, 0x0014},
+ { 0xE48, 0x000E},
+ { 0xE49, 0x002F},
+ { 0xE4A, 0x000F},
+ { 0xE4B, 0x001B},
+ { 0xE4C, 0x0030},
+ { 0xE4D, 0x002C},
+ { 0xE4E, 0x0051},
+ { 0xE4F, 0x0069},
+ { 0xE50, 0x0006},
+ { 0xE51, 0x001E},
+ { 0xE52, 0x0043},
+ { 0xE53, 0x002F},
+ { 0xE54, 0x0043},
+ { 0xE55, 0x0032},
+ { 0xE56, 0x0043},
+ { 0xE57, 0x000D},
+ { 0xE58, 0x0008},
+ { 0xE59, 0x0059},
+ { 0xE5A, 0x0016},
+ { 0xE5B, 0x0030},
+ { 0xE5C, 0x004B},
+ { 0xE5D, 0x0051},
+ { 0xE5E, 0x005A},
+ { 0xE5F, 0x006B},
+
+ { 0x0290, 0x01 },
+};
+
+#undef TPO2_ONE_GAMMA
+/* Initial sequence of TPO panel with Novatek NT35399 MDDI client */
+
+static const struct mddi_table tpo2_init_table[] = {
+ /* Panel interface control */
+ { 0xB30, 0x44 },
+ { 0xB40, 0x00 },
+ { 0xB41, 0x87 },
+ { 0xB50, 0x06 },
+ { 0xB51, 0x7B },
+ { 0xB60, 0x0E },
+ { 0xB70, 0x0F },
+ { 0xB80, 0x03 },
+ { 0xB90, 0x00 },
+ { 0x350, 0x70 }, /* FTE is at line 0x70 */
+
+ /* Entry Mode */
+ { 0x360, 0x30 },
+ { 0x361, 0xC1 },
+ { 0x362, 0x04 },
+
+/* 0x2 for gray scale gamma correction, 0x12 for RGB gamma correction */
+#ifdef TPO2_ONE_GAMMA
+ { 0xB00, 0x02 },
+#else
+ { 0xB00, 0x12 },
+#endif
+ /* Driver output control */
+ { 0x371, 0xEF },
+ { 0x372, 0x03 },
+
+ /* DCDC on glass control */
+ { 0xC31, 0x10 },
+ { 0xBA0, 0x00 },
+ { 0xBA1, 0x86 },
+
+ /* VCOMH voltage control */
+ { 0xC50, 0x3b },
+
+ /* Special function control */
+ { 0xC10, 0x82 },
+
+ /* Power control */
+ { 0xC40, 0x44 },
+ { 0xC41, 0x02 },
+
+ /* Source output control */
+ { 0xBE0, 0x01 },
+ { 0xBE1, 0x00 },
+
+ /* Windows address setting */
+ { 0x2A0, 0x00 },
+ { 0x2A1, 0x00 },
+ { 0x2A2, 0x3F },
+ { 0x2A3, 0x01 },
+ { 0x2B0, 0x00 },
+ { 0x2B1, 0x00 },
+ { 0x2B2, 0xDF },
+ { 0x2B3, 0x01 },
+
+ /* RAM address setting */
+ { 0x2D0, 0x00 },
+ { 0x2D1, 0x00 },
+ { 0x2D2, 0x00 },
+ { 0x2D3, 0x00 },
+
+ { 0xF20, 0x55 },
+ { 0xF21, 0xAA },
+ { 0xF22, 0x66 },
+ { 0xF57, 0x45 },
+
+/*
+ * The NT35399 provides gray or RGB gamma correction table,
+ * which determinated by register-0xb00, and following table
+ */
+#ifdef TPO2_ONE_GAMMA
+ /* Positive Gamma setting */
+ { 0xE00, 0x04 },
+ { 0xE01, 0x12 },
+ { 0xE02, 0x18 },
+ { 0xE03, 0x10 },
+ { 0xE04, 0x29 },
+ { 0xE05, 0x26 },
+ { 0xE06, 0x1f },
+ { 0xE07, 0x11 },
+ { 0xE08, 0x0c },
+ { 0xE09, 0x3a },
+ { 0xE0A, 0x0d },
+ { 0xE0B, 0x28 },
+ { 0xE0C, 0x40 },
+ { 0xE0D, 0x4e },
+ { 0xE0E, 0x6f },
+ { 0xE0F, 0x5E },
+
+ /* Negative Gamma setting */
+ { 0xE10, 0x0B },
+ { 0xE11, 0x00 },
+ { 0xE12, 0x00 },
+ { 0xE13, 0x1F },
+ { 0xE14, 0x4b },
+ { 0xE15, 0x33 },
+ { 0xE16, 0x13 },
+ { 0xE17, 0x12 },
+ { 0xE18, 0x0d },
+ { 0xE19, 0x2f },
+ { 0xE1A, 0x16 },
+ { 0xE1B, 0x2e },
+ { 0xE1C, 0x49 },
+ { 0xE1D, 0x41 },
+ { 0xE1E, 0x46 },
+ { 0xE1F, 0x55 },
+#else
+ /* Red Positive Gamma */
+ { 0xE00, 0x0f },
+ { 0xE01, 0x19 },
+ { 0xE02, 0x22 },
+ { 0xE03, 0x0b },
+ { 0xE04, 0x23 },
+ { 0xE05, 0x23 },
+ { 0xE06, 0x14 },
+ { 0xE07, 0x13 },
+ { 0xE08, 0x0f },
+ { 0xE09, 0x2a },
+ { 0xE0A, 0x0d },
+ { 0xE0B, 0x26 },
+ { 0xE0C, 0x43 },
+ { 0xE0D, 0x20 },
+ { 0xE0E, 0x2a },
+ { 0xE0F, 0x5c },
+
+ /* Red Negative Gamma */
+ { 0xE10, 0x0d },
+ { 0xE11, 0x45 },
+ { 0xE12, 0x4c },
+ { 0xE13, 0x1c },
+ { 0xE14, 0x4d },
+ { 0xE15, 0x33 },
+ { 0xE16, 0x23 },
+ { 0xE17, 0x0f },
+ { 0xE18, 0x0b },
+ { 0xE19, 0x3a },
+ { 0xE1A, 0x19 },
+ { 0xE1B, 0x32 },
+ { 0xE1C, 0x4e },
+ { 0xE1D, 0x37 },
+ { 0xE1E, 0x38 },
+ { 0xE1F, 0x3b },
+
+ /* Green Positive Gamma */
+ { 0xE20, 0x00 },
+ { 0xE21, 0x09 },
+ { 0xE22, 0x10 },
+ { 0xE23, 0x0f },
+ { 0xE24, 0x29 },
+ { 0xE25, 0x23 },
+ { 0xE26, 0x0b },
+ { 0xE27, 0x14 },
+ { 0xE28, 0x12 },
+ { 0xE29, 0x25 },
+ { 0xE2A, 0x12 },
+ { 0xE2B, 0x2f },
+ { 0xE2C, 0x43 },
+ { 0xE2D, 0x2d },
+ { 0xE2E, 0x52 },
+ { 0xE2F, 0x61 },
+
+ /* Green Negative Gamma */
+ { 0xE30, 0x08 },
+ { 0xE31, 0x1d },
+ { 0xE32, 0x3f },
+ { 0xE33, 0x1c },
+ { 0xE34, 0x44 },
+ { 0xE35, 0x2e },
+ { 0xE36, 0x28 },
+ { 0xE37, 0x0c },
+ { 0xE38, 0x0a },
+ { 0xE39, 0x42 },
+ { 0xE3A, 0x17 },
+ { 0xE3B, 0x30 },
+ { 0xE3C, 0x4b },
+ { 0xE3D, 0x3f },
+ { 0xE3E, 0x43 },
+ { 0xE3F, 0x45 },
+
+ /* Blue Positive Gamma */
+ { 0xE40, 0x32 },
+ { 0xE41, 0x32 },
+ { 0xE42, 0x31 },
+ { 0xE43, 0x06 },
+ { 0xE44, 0x08 },
+ { 0xE45, 0x0d },
+ { 0xE46, 0x04 },
+ { 0xE47, 0x14 },
+ { 0xE48, 0x0f },
+ { 0xE49, 0x1d },
+ { 0xE4A, 0x1a },
+ { 0xE4B, 0x39 },
+ { 0xE4C, 0x4c },
+ { 0xE4D, 0x1e },
+ { 0xE4E, 0x43 },
+ { 0xE4F, 0x61 },
+
+ /* Blue Negative Gamma */
+ { 0xE50, 0x08 },
+ { 0xE51, 0x2c },
+ { 0xE52, 0x4e },
+ { 0xE53, 0x13 },
+ { 0xE54, 0x3a },
+ { 0xE55, 0x26 },
+ { 0xE56, 0x30 },
+ { 0xE57, 0x0f },
+ { 0xE58, 0x0a },
+ { 0xE59, 0x49 },
+ { 0xE5A, 0x34 },
+ { 0xE5B, 0x4a },
+ { 0xE5C, 0x53 },
+ { 0xE5D, 0x28 },
+ { 0xE5E, 0x26 },
+ { 0xE5F, 0x27 },
+
+#endif
+ /* Sleep in mode */
+ { 0x110, 0x00 },
+ { 0x1, 0x23 },
+ /* Display on mode */
+ { 0x290, 0x00 },
+ { 0x1, 0x27 },
+ /* Driver output control */
+ { 0x372, 0x01 },
+ { 0x1, 0x40 },
+ /* Display on mode */
+ { 0x290, 0x01 },
+};
+
+static const struct mddi_table tpo2_display_on[] = {
+ { 0x290, 0x01 },
+};
+
+static const struct mddi_table tpo2_display_off[] = {
+ { 0x110, 0x01 },
+ { 0x290, 0x00 },
+ { 0x1, 100 },
+};
+
+static const struct mddi_table tpo2_power_off[] = {
+ { 0x0110, 0x01 },
+};
+
+static int nt35399_detect_panel(struct msm_mddi_client_data *client_data)
+{
+ int id = -1, i ;
+
+ /* If the MDDI client is failed to report the panel ID,
+ * perform retrial 5 times.
+ */
+ for( i=0; i < 5; i++ ) {
+ client_data->remote_write(client_data, 0, 0x110);
+ msleep(5);
+ id = client_data->remote_read(client_data, userid) ;
+ if( id == 0 || id == 1 ) {
+ if(i==0) {
+ printk(KERN_ERR "%s: got valid panel ID=%d, "
+ "without retry\n",
+ __FUNCTION__, id);
+ }
+ else {
+ printk(KERN_ERR "%s: got valid panel ID=%d, "
+ "after %d retry\n",
+ __FUNCTION__, id, i+1);
+ }
+ break ;
+ }
+ printk(KERN_ERR "%s: got invalid panel ID:%d, trial #%d\n",
+ __FUNCTION__, id, i+1);
+
+ gpio_set_value(MDDI_RST_N, 0);
+ msleep(5);
+
+ gpio_set_value(MDDI_RST_N, 1);
+ msleep(10);
+ gpio_set_value(MDDI_RST_N, 0);
+ udelay(100);
+ gpio_set_value(MDDI_RST_N, 1);
+ mdelay(10);
+ }
+ printk(KERN_INFO "%s: final panel id=%d\n", __FUNCTION__, id);
+
+ switch(id) {
+ case 0:
+ return SAPPHIRE_PANEL_TOPPOLY;
+ case 1:
+ return SAPPHIRE_PANEL_SHARP;
+ default :
+ printk(KERN_ERR "%s(): Invalid panel ID: %d, "
+ "treat as sharp panel.", __FUNCTION__, id);
+ return SAPPHIRE_PANEL_SHARP;
+ }
+}
+
+static int nt35399_client_init(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id;
+
+ if (g_panel_inited == 0) {
+ g_panel_id = panel_id = nt35399_detect_panel(client_data);
+ g_panel_inited = 1 ;
+ } else {
+ gpio_set_value(MDDI_RST_N, 1);
+ msleep(10);
+ gpio_set_value(MDDI_RST_N, 0);
+ udelay(100);
+ gpio_set_value(MDDI_RST_N, 1);
+ mdelay(10);
+
+ g_panel_id = panel_id = nt35399_detect_panel(client_data);
+ if (panel_id == -1) {
+ printk("Invalid panel id\n");
+ return -1;
+ }
+
+ client_data->auto_hibernate(client_data, 0);
+ if (panel_id == SAPPHIRE_PANEL_TOPPOLY) {
+ sapphire_process_mddi_table(client_data, tpo2_init_table,
+ ARRAY_SIZE(tpo2_init_table));
+ } else if(panel_id == SAPPHIRE_PANEL_SHARP) {
+ sapphire_process_mddi_table(client_data, sharp2_init_table,
+ ARRAY_SIZE(sharp2_init_table));
+ }
+
+ client_data->auto_hibernate(client_data, 1);
+ }
+
+ return 0;
+}
+
+static int nt35399_client_uninit(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *cdata)
+{
+ return 0;
+}
+
+static int nt35399_panel_unblank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int ret = 0;
+
+ mdelay(20);
+ sapphire_set_backlight_level(0);
+ client_data->auto_hibernate(client_data, 0);
+
+ mutex_lock(&sapphire_backlight_lock);
+ sapphire_set_backlight_level(sapphire_backlight_brightness);
+ sapphire_backlight_off = 0;
+ mutex_unlock(&sapphire_backlight_lock);
+
+ client_data->auto_hibernate(client_data, 1);
+
+ return ret;
+}
+
+static int nt35399_panel_blank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int ret = 0;
+
+ client_data->auto_hibernate(client_data, 0);
+ sapphire_process_mddi_table(client_data, tpo2_display_off,
+ ARRAY_SIZE(tpo2_display_off));
+ client_data->auto_hibernate(client_data, 1);
+
+ mutex_lock(&sapphire_backlight_lock);
+ sapphire_set_backlight_level(0);
+ sapphire_backlight_off = 1;
+ mutex_unlock(&sapphire_backlight_lock);
+
+ return ret;
+}
+
+static void sapphire_brightness_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ mutex_lock(&sapphire_backlight_lock);
+ sapphire_backlight_brightness = value;
+ if (!sapphire_backlight_off)
+ sapphire_set_backlight_level(sapphire_backlight_brightness);
+ mutex_unlock(&sapphire_backlight_lock);
+}
+
+static struct led_classdev sapphire_backlight_led = {
+ .name = "lcd-backlight",
+ .brightness = SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS,
+ .brightness_set = sapphire_brightness_set,
+};
+
+static int sapphire_backlight_probe(struct platform_device *pdev)
+{
+ led_classdev_register(&pdev->dev, &sapphire_backlight_led);
+ return 0;
+}
+
+static int sapphire_backlight_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&sapphire_backlight_led);
+ return 0;
+}
+
+static struct platform_driver sapphire_backlight_driver = {
+ .probe = sapphire_backlight_probe,
+ .remove = sapphire_backlight_remove,
+ .driver = {
+ .name = "sapphire-backlight",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct resource resources_msm_fb[] = {
+ {
+ .start = SMI64_MSM_FB_BASE,
+ .end = SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct msm_mddi_bridge_platform_data toshiba_client_data = {
+ .init = sapphire_mddi_toshiba_client_init,
+ .uninit = sapphire_mddi_toshiba_client_uninit,
+ .blank = sapphire_mddi_panel_blank,
+ .unblank = sapphire_mddi_panel_unblank,
+ .fb_data = {
+ .xres = 320,
+ .yres = 480,
+ .width = 45,
+ .height = 67,
+ .output_format = 0,
+ },
+};
+
+#define NT35399_MFR_NAME 0x0bda
+#define NT35399_PRODUCT_CODE 0x8a47
+
+static void nt35399_fixup(uint16_t * mfr_name, uint16_t * product_code)
+{
+ printk(KERN_DEBUG "%s: enter.\n", __func__);
+ *mfr_name = NT35399_MFR_NAME ;
+ *product_code= NT35399_PRODUCT_CODE ;
+}
+
+static struct msm_mddi_bridge_platform_data nt35399_client_data = {
+
+ .init = nt35399_client_init,
+ .uninit = nt35399_client_uninit,
+ .blank = nt35399_panel_blank,
+ .unblank = nt35399_panel_unblank,
+ .fb_data = {
+ .xres = 320,
+ .yres = 480,
+ .output_format = 0,
+ },
+};
+
+static struct msm_mddi_platform_data mddi_pdata = {
+ .clk_rate = 122880000,
+ .power_client = sapphire_mddi_power_client,
+ .fixup = nt35399_fixup,
+ .vsync_irq = MSM_GPIO_TO_INT(VSYNC_GPIO),
+ .fb_resource = resources_msm_fb,
+ .num_clients = 2,
+ .client_platform_data = {
+ {
+ .product_id = (0xd263 << 16 | 0),
+ .name = "mddi_c_d263_0000",
+ .id = 0,
+ .client_data = &toshiba_client_data,
+ .clk_rate = 0,
+ },
+ {
+ .product_id =
+ (NT35399_MFR_NAME << 16 | NT35399_PRODUCT_CODE),
+ .name = "mddi_c_simple" ,
+ .id = 0,
+ .client_data = &nt35399_client_data,
+ .clk_rate = 0,
+ },
+ },
+};
+
+static struct platform_device sapphire_backlight = {
+ .name = "sapphire-backlight",
+};
+
+int __init sapphire_init_panel(void)
+{
+ int rc = -1;
+ uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); /* GPIO27 */
+
+ if (!machine_is_sapphire())
+ return 0;
+
+ /* checking board as soon as possible */
+ printk("sapphire_init_panel:machine_is_sapphire=%d, machine_arch_type=%d, MACH_TYPE_SAPPHIRE=%d\r\n", machine_is_sapphire(), machine_arch_type, MACH_TYPE_SAPPHIRE);
+ if (!machine_is_sapphire())
+ return 0;
+
+ vreg_lcm_2v85 = vreg_get(0, "gp4");
+ if (IS_ERR(vreg_lcm_2v85))
+ return PTR_ERR(vreg_lcm_2v85);
+
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0);
+
+ /* setup FB by SMI size */
+ if (sapphire_get_smi_size() == 32) {
+ resources_msm_fb[0].start = SMI32_MSM_FB_BASE;
+ resources_msm_fb[0].end = SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE - 1;
+ }
+
+ rc = gpio_request(VSYNC_GPIO, "vsync");
+ if (rc)
+ return rc;
+ rc = gpio_direction_input(VSYNC_GPIO);
+ if (rc)
+ return rc;
+ rc = platform_device_register(&msm_device_mdp);
+ if (rc)
+ return rc;
+ msm_device_mddi0.dev.platform_data = &mddi_pdata;
+ rc = platform_device_register(&msm_device_mddi0);
+ if (rc)
+ return rc;
+ platform_device_register(&sapphire_backlight);
+ return platform_driver_register(&sapphire_backlight_driver);
+}
+
+device_initcall(sapphire_init_panel);
diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c
new file mode 100644
index 0000000..2fd6ea1
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-rfkill.c
@@ -0,0 +1,105 @@
+/* linux/arch/arm/mach-msm/board-sapphire-rfkill.c
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+*/
+
+/* Control bluetooth power for sapphire platform */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/rfkill.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include "gpio_chip.h"
+#include "board-sapphire.h"
+
+static struct rfkill *bt_rfk;
+static const char bt_name[] = "brf6300";
+
+extern int sapphire_bt_fastclock_power(int on);
+
+static int bluetooth_set_power(void *data, bool blocked)
+{
+ if (!blocked) {
+ sapphire_bt_fastclock_power(1);
+ gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 1);
+ udelay(10);
+ gpio_direction_output(101, 1);
+ } else {
+ gpio_direction_output(101, 0);
+ gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 0);
+ sapphire_bt_fastclock_power(0);
+ }
+ return 0;
+}
+
+static struct rfkill_ops sapphire_rfkill_ops = {
+ .set_block = bluetooth_set_power,
+};
+
+static int sapphire_rfkill_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ bool default_state = true; /* off */
+
+ bluetooth_set_power(NULL, default_state);
+
+ bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH,
+ &sapphire_rfkill_ops, NULL);
+ if (!bt_rfk)
+ return -ENOMEM;
+
+ /* userspace cannot take exclusive control */
+
+ rfkill_set_states(bt_rfk, default_state, false);
+
+ rc = rfkill_register(bt_rfk);
+
+ if (rc)
+ rfkill_destroy(bt_rfk);
+ return rc;
+}
+
+static int sapphire_rfkill_remove(struct platform_device *dev)
+{
+ rfkill_unregister(bt_rfk);
+ rfkill_destroy(bt_rfk);
+
+ return 0;
+}
+
+static struct platform_driver sapphire_rfkill_driver = {
+ .probe = sapphire_rfkill_probe,
+ .remove = sapphire_rfkill_remove,
+ .driver = {
+ .name = "sapphire_rfkill",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sapphire_rfkill_init(void)
+{
+ return platform_driver_register(&sapphire_rfkill_driver);
+}
+
+static void __exit sapphire_rfkill_exit(void)
+{
+ platform_driver_unregister(&sapphire_rfkill_driver);
+}
+
+module_init(sapphire_rfkill_init);
+module_exit(sapphire_rfkill_exit);
+MODULE_DESCRIPTION("sapphire rfkill");
+MODULE_AUTHOR("Nick Pelly <npelly@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-sapphire-wifi.c b/arch/arm/mach-msm/board-sapphire-wifi.c
new file mode 100644
index 0000000..43f827c
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire-wifi.c
@@ -0,0 +1,74 @@
+/* arch/arm/mach-msm/board-sapphire-wifi.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Dmitry Shmidt <dimitrysh@google.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.
+ *
+ */
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/err.h>
+#include <linux/wifi_tiwlan.h>
+
+extern int sapphire_wifi_set_carddetect(int val);
+extern int sapphire_wifi_power(int on);
+extern int sapphire_wifi_reset(int on);
+
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+typedef struct wifi_mem_prealloc_struct {
+ void *mem_ptr;
+ unsigned long size;
+} wifi_mem_prealloc_t;
+
+static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = {
+ { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) },
+ { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) },
+ { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) }
+};
+
+static void *sapphire_wifi_mem_prealloc(int section, unsigned long size)
+{
+ if ((section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS))
+ return NULL;
+ if (wifi_mem_array[section].size < size)
+ return NULL;
+ return wifi_mem_array[section].mem_ptr;
+}
+
+int __init sapphire_init_wifi_mem (void)
+{
+ int i;
+
+ for (i = 0; (i < WMPA_NUMBER_OF_SECTIONS); i++) {
+ wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size);
+ if (wifi_mem_array[i].mem_ptr == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+#endif
+
+struct wifi_platform_data sapphire_wifi_control = {
+ .set_power = sapphire_wifi_power,
+ .set_reset = sapphire_wifi_reset,
+ .set_carddetect = sapphire_wifi_set_carddetect,
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+ .mem_prealloc = sapphire_wifi_mem_prealloc,
+#else
+ .mem_prealloc = NULL,
+#endif
+};
+
+#endif
diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c
index 2bc1b9d..3f05c436 100644
--- a/arch/arm/mach-msm/board-sapphire.c
+++ b/arch/arm/mach-msm/board-sapphire.c
@@ -17,8 +17,17 @@
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/i2c.h>
#include <linux/irq.h>
+#include <linux/keyreset.h>
+#include <linux/leds.h>
+#include <linux/switch.h>
+#include <linux/synaptics_i2c_rmi.h>
+#include <linux/elan_i2c.h>
+#include <linux/akm8976.h>
+#include <mach/htc_headset.h>
#include <linux/sysdev.h>
+#include <linux/android_pmem.h>
#include <linux/delay.h>
@@ -31,40 +40,1156 @@
#include <asm/system.h>
#include <mach/system.h>
#include <mach/vreg.h>
-#include <mach/board.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/setup.h>
+#include <linux/gpio_event.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <asm/mach/mmc.h>
+#include <linux/mmc/sdio_ids.h>
+
+
#include "gpio_chip.h"
#include "board-sapphire.h"
+
+#include <mach/board.h>
+#include <mach/board_htc.h>
+#include <mach/msm_serial_debugger.h>
+#include <mach/msm_serial_hs.h>
+#include <mach/htc_pwrsink.h>
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+extern int sapphire_init_wifi_mem(void);
+#endif
+extern struct wifi_platform_data sapphire_wifi_control;
+#endif
+
#include "proc_comm.h"
#include "devices.h"
void msm_init_irq(void);
void msm_init_gpio(void);
+void msm_init_pmic_vibrator(void);
+
+extern int sapphire_init_mmc(unsigned int);
+
+struct sapphire_axis_info {
+ struct gpio_event_axis_info info;
+ uint16_t in_state;
+ uint16_t out_state;
+ uint16_t temp_state;
+ uint16_t threshold;
+};
+static bool nav_just_on;
+static int nav_on_jiffies;
+static int smi_sz = 64;
+static unsigned int hwid = 0;
+static unsigned int skuid = 0;
+static unsigned engineerid = (0x01 << 1); /* default is 3M sensor */
+
+uint16_t sapphire_axis_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ struct sapphire_axis_info *ai = container_of(info, struct sapphire_axis_info, info);
+ uint16_t out = ai->out_state;
+
+ if (nav_just_on) {
+ if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1)
+ goto ignore;
+ nav_just_on = 0;
+ }
+ if ((ai->in_state ^ in) & 1)
+ out--;
+ if ((ai->in_state ^ in) & 2)
+ out++;
+ ai->out_state = out;
+ignore:
+ ai->in_state = in;
+ if (ai->out_state - ai->temp_state == ai->threshold) {
+ ai->temp_state++;
+ ai->out_state = ai->temp_state;
+ } else if (ai->temp_state - ai->out_state == ai->threshold) {
+ ai->temp_state--;
+ ai->out_state = ai->temp_state;
+ } else if (abs(ai->out_state - ai->temp_state) > ai->threshold)
+ ai->temp_state = ai->out_state;
+
+ return ai->temp_state;
+}
+
+int sapphire_nav_power(const struct gpio_event_platform_data *pdata, bool on)
+{
+ gpio_set_value(SAPPHIRE_GPIO_JOG_EN, on);
+ if (on) {
+ nav_just_on = 1;
+ nav_on_jiffies = jiffies;
+ }
+ return 0;
+}
+
+static uint32_t sapphire_x_axis_gpios[] = {
+ SAPPHIRE_BALL_LEFT_0, SAPPHIRE_BALL_RIGHT_0
+};
+
+static struct sapphire_axis_info sapphire_x_axis = {
+ .threshold = 2,
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(sapphire_x_axis_gpios),
+ .type = EV_REL,
+ .code = REL_X,
+ .decoded_size = 1U << ARRAY_SIZE(sapphire_x_axis_gpios),
+ .map = sapphire_axis_map,
+ .gpio = sapphire_x_axis_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */
+ }
+};
+
+static uint32_t sapphire_y_axis_gpios[] = {
+ SAPPHIRE_BALL_UP_0, SAPPHIRE_BALL_DOWN_0
+};
+
+static struct sapphire_axis_info sapphire_y_axis = {
+ .threshold = 2,
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(sapphire_y_axis_gpios),
+ .type = EV_REL,
+ .code = REL_Y,
+ .decoded_size = 1U << ARRAY_SIZE(sapphire_y_axis_gpios),
+ .map = sapphire_axis_map,
+ .gpio = sapphire_y_axis_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */
+ }
+};
+
+static struct gpio_event_direct_entry sapphire_nav_buttons[] = {
+ { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE },
+};
+
+static struct gpio_event_input_info sapphire_nav_button_info = {
+ .info.func = gpio_event_input_func,
+ .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE,
+ .poll_time.tv.nsec = 40 * NSEC_PER_MSEC,
+ .type = EV_KEY,
+ .keymap = sapphire_nav_buttons,
+ .keymap_size = ARRAY_SIZE(sapphire_nav_buttons)
+};
+
+static struct gpio_event_info *sapphire_nav_info[] = {
+ &sapphire_x_axis.info.info,
+ &sapphire_y_axis.info.info,
+ &sapphire_nav_button_info.info
+};
+
+static struct gpio_event_platform_data sapphire_nav_data = {
+ .name = "sapphire-nav",
+ .info = sapphire_nav_info,
+ .info_count = ARRAY_SIZE(sapphire_nav_info),
+ .power = sapphire_nav_power,
+};
+
+static struct platform_device sapphire_nav_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 2,
+ .dev = {
+ .platform_data = &sapphire_nav_data,
+ },
+};
+
+/* a new search button to be a wake-up source */
+static struct gpio_event_direct_entry sapphire_search_button_v1[] = {
+ { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search*/
+};
+
+static struct gpio_event_direct_entry sapphire_search_button_v2[] = {
+ { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, /* CPLD Key Home */
+};
+
+static struct gpio_event_input_info sapphire_search_button_info = {
+ .info.func = gpio_event_input_func,
+ /* .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, */
+ .flags = 0,
+ .poll_time.tv.nsec = 40 * NSEC_PER_MSEC,
+ .type = EV_KEY,
+ .keymap = sapphire_search_button_v2,
+ .keymap_size = ARRAY_SIZE(sapphire_search_button_v2)
+};
+
+static struct gpio_event_info *sapphire_search_info[] = {
+ &sapphire_search_button_info.info
+};
+
+static struct gpio_event_platform_data sapphire_search_button_data = {
+ .name = "sapphire-nav-button",
+ .info = sapphire_search_info,
+ .info_count = ARRAY_SIZE(sapphire_search_info),
+};
+
+static struct platform_device sapphire_search_button_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 1,
+ .dev = {
+ .platform_data = &sapphire_search_button_data,
+ },
+};
+
+static int sapphire_reset_keys_up[] = {
+ BTN_MOUSE,
+ 0
+};
+
+static struct keyreset_platform_data sapphire_reset_keys_pdata = {
+ .keys_up = sapphire_reset_keys_up,
+ .keys_down = {
+ KEY_SEND,
+ KEY_MENU,
+ KEY_END,
+ 0
+ },
+};
+
+struct platform_device sapphire_reset_keys_device = {
+ .name = KEYRESET_NAME,
+ .dev.platform_data = &sapphire_reset_keys_pdata,
+};
+
+static int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN;
+
+static int sapphire_ts_power(int on)
+{
+ if (on) {
+ sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 1);
+ /* touchscreen must be powered before we enable i2c pullup */
+ msleep(2);
+ /* enable touch panel level shift */
+ gpio_direction_output(gpio_tp_ls_en, 1);
+ msleep(2);
+ } else {
+ gpio_direction_output(gpio_tp_ls_en, 0);
+ udelay(50);
+ sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 0);
+ }
+
+ return 0;
+}
+
+static struct synaptics_i2c_rmi_platform_data sapphire_ts_data[] = {
+{
+ .version = 0x0101,
+ .power = sapphire_ts_power,
+ .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE,
+ .inactive_left = -50 * 0x10000 / 4334,
+ .inactive_right = -50 * 0x10000 / 4334,
+ .inactive_top = -40 * 0x10000 / 6696,
+ .inactive_bottom = -40 * 0x10000 / 6696,
+ .snap_left_on = 50 * 0x10000 / 4334,
+ .snap_left_off = 60 * 0x10000 / 4334,
+ .snap_right_on = 50 * 0x10000 / 4334,
+ .snap_right_off = 60 * 0x10000 / 4334,
+ .snap_top_on = 100 * 0x10000 / 6696,
+ .snap_top_off = 110 * 0x10000 / 6696,
+ .snap_bottom_on = 100 * 0x10000 / 6696,
+ .snap_bottom_off = 110 * 0x10000 / 6696,
+ },
+ {
+ .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE,
+ .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334,
+ .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334,
+ .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696,
+ .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696,
+ }
+};
+
+static struct akm8976_platform_data compass_platform_data = {
+ .reset = SAPPHIRE_GPIO_COMPASS_RST_N,
+ .clk_on = SAPPHIRE_GPIO_COMPASS_32K_EN,
+ .intr = SAPPHIRE_GPIO_COMPASS_IRQ,
+};
+
+static struct elan_i2c_platform_data elan_i2c_data[] = {
+ {
+ .version = 0x104,
+ .abs_x_min = 0,
+ .abs_y_min = 0,
+ .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N,
+ .power = sapphire_ts_power,
+ },
+ {
+ .version = 0x103,
+ .abs_x_min = 0,
+ .abs_x_max = 512 * 2,
+ .abs_y_min = 0,
+ .abs_y_max = 896 * 2,
+ .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N,
+ .power = sapphire_ts_power,
+ },
+ {
+ .version = 0x102,
+ .abs_x_min = 0,
+ .abs_x_max = 384,
+ .abs_y_min = 0,
+ .abs_y_max = 576,
+ .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N,
+ .power = sapphire_ts_power,
+ },
+ {
+ .version = 0x101,
+ .abs_x_min = 32 + 1,
+ .abs_x_max = 352 - 1,
+ .abs_y_min = 32 + 1,
+ .abs_y_max = 544 - 1,
+ .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N,
+ .power = sapphire_ts_power,
+ }
+};
+
+static struct i2c_board_info i2c_devices[] = {
+ {
+ I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20),
+ .platform_data = sapphire_ts_data,
+ .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N)
+ },
+ {
+ I2C_BOARD_INFO(ELAN_8232_I2C_NAME, 0x10),
+ .platform_data = &elan_i2c_data,
+ .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N),
+ },
+ {
+ I2C_BOARD_INFO("akm8976", 0x1C),
+ .platform_data = &compass_platform_data,
+ .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_COMPASS_IRQ),
+ },
+#ifdef CONFIG_MSM_CAMERA
+#ifdef CONFIG_MT9P012
+ {
+ I2C_BOARD_INFO("mt9p012", 0x6C >> 1),
+ },
+#endif
+#ifdef CONFIG_MT9T013
+ {
+ I2C_BOARD_INFO("mt9t013", 0x6C),
+ },
+#endif
+#endif/*CONIFIG_MSM_CAMERA*/
+#ifdef CONFIG_SENSORS_MT9T013
+ {
+ I2C_BOARD_INFO("mt9t013", 0x6C >> 1),
+ },
+#endif
+};
+
+#ifdef CONFIG_LEDS_CPLD
+static struct resource cpldled_resources[] = {
+ {
+ .start = SAPPHIRE_CPLD_LED_BASE,
+ .end = SAPPHIRE_CPLD_LED_BASE + SAPPHIRE_CPLD_LED_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device android_CPLD_leds = {
+ .name = "leds-cpld",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cpldled_resources),
+ .resource = cpldled_resources,
+};
+#endif
+
+static struct gpio_led android_led_list[] = {
+ {
+ .name = "button-backlight",
+ .gpio = SAPPHIRE_GPIO_APKEY_LED_EN,
+ },
+};
+
+static struct gpio_led_platform_data android_leds_data = {
+ .num_leds = ARRAY_SIZE(android_led_list),
+ .leds = android_led_list,
+};
+
+static struct platform_device android_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_leds_data,
+ },
+};
+
+#ifdef CONFIG_HTC_HEADSET
+/* RTS/CTS to GPO/GPI. */
+static uint32_t uart1_on_gpio_table[] = {
+ /* allenou, uart hs test, 2008/11/18 */
+ #ifdef CONFIG_SERIAL_MSM_HS
+ /* RTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 2,
+ GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ /* CTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 2,
+ GPIO_INPUT, GPIO_PULL_UP, GPIO_8MA),
+ #else
+ /* RTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 1,
+ GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA),
+ /* CTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 1,
+ GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA),
+ #endif
+};
+
+/* RTS,CTS to BT. */
+static uint32_t uart1_off_gpio_table[] = {
+ /* RTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 0,
+ GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
+ /* CTS */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 0,
+ GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
+};
+
+/* Sapphire: Switch between UART3 and GPIO */
+static uint32_t uart3_on_gpio_table[] = {
+ /* RX */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_RX, 1,
+ GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
+ /* TX */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_TX, 1,
+ GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
+};
+
+/* set TX,RX to GPI */
+static uint32_t uart3_off_gpi_table[] = {
+ /* RX, H2W DATA */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_DATA, 0,
+ GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA),
+ /* TX, H2W CLK */
+ PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_CLK, 0,
+ GPIO_INPUT, GPIO_KEEPER, GPIO_2MA),
+};
+
+static int sapphire_h2w_path = H2W_GPIO;
+
+static void h2w_config_cpld(int route)
+{
+ switch (route) {
+ case H2W_UART1:
+ /* Make sure uart1 funtion pin opened. */
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+1, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0);
+ sapphire_h2w_path = H2W_UART1;
+ printk(KERN_INFO "H2W route = H2W-UART1, BT-X, UART3-X \n");
+ break;
+ case H2W_BT:
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1);
+ /* UART1 RTS/CTS to GPO/GPI. */
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_off_gpio_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_off_gpio_table+1, 0);
+ sapphire_h2w_path = H2W_BT;
+ printk(KERN_INFO "H2W route = H2W-BT, UART1-X, UART3-X \n");
+ break;
+ case H2W_UART3:
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart3_on_gpio_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart3_on_gpio_table+1, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1);
+ /* Make sure uart1 funtion pin opened. */
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+1, 0);
+ sapphire_h2w_path = H2W_UART3;
+ printk(KERN_INFO "H2W route = H2W-UART3, BT-UART1 \n");
+ break;
+ case H2W_GPIO: /*H2W_UART3 TX,RX are changed to H2W_GPIO */
+ default:
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0);
+ /* Set the CPLD connected H2W GPIO's to input */
+ gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0);
+ /* TX,RX GPI first. */
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart3_off_gpi_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart3_off_gpi_table+1, 0);
+ /* Make sure uart1 funtion pin opened. */
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+0, 0);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
+ uart1_on_gpio_table+1, 0);
+ sapphire_h2w_path = H2W_GPIO;
+ printk(KERN_INFO "H2W route = H2W-GPIO, BT-UART1 \n");
+ break;
+ }
+}
+
+static void h2w_init_cpld(void)
+{
+ h2w_config_cpld(H2W_UART3);
+}
+
+static int h2w_dat_value;
+static void set_h2w_dat(int n)
+{
+ h2w_dat_value = n;
+ gpio_set_value(SAPPHIRE_GPIO_H2W_DATA, n);
+}
+
+static int h2w_clk_value;
+static void set_h2w_clk(int n)
+{
+ h2w_clk_value = n;
+ gpio_set_value(SAPPHIRE_GPIO_H2W_CLK, n);
+}
+
+static void set_h2w_dat_dir(int n)
+{
+ if (n == 0) /* input */
+ gpio_direction_input(SAPPHIRE_GPIO_H2W_DATA);
+ else
+ gpio_direction_output(SAPPHIRE_GPIO_H2W_DATA, h2w_dat_value);
+
+ gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, n);
+
+}
+
+static void set_h2w_clk_dir(int n)
+{
+ if (n == 0) /* input */
+ gpio_direction_input(SAPPHIRE_GPIO_H2W_CLK);
+ else
+ gpio_direction_output(SAPPHIRE_GPIO_H2W_CLK, h2w_clk_value);
+
+ gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, n);
+}
+
+static int get_h2w_dat(void)
+{
+ return gpio_get_value(SAPPHIRE_GPIO_H2W_DATA);
+}
+
+static int get_h2w_clk(void)
+{
+ return gpio_get_value(SAPPHIRE_GPIO_H2W_CLK);
+}
+
+static int set_h2w_path(const char *val, struct kernel_param *kp)
+{
+ int ret = -EINVAL;
+
+ ret = param_set_int(val, kp);
+ if (ret)
+ return ret;
+
+ switch (sapphire_h2w_path) {
+ case H2W_GPIO:
+ case H2W_UART1:
+ case H2W_UART3:
+ case H2W_BT:
+ break;
+ default:
+ sapphire_h2w_path = -1;
+ return -EINVAL;
+ }
+
+ h2w_config_cpld(sapphire_h2w_path);
+ return ret;
+}
+module_param_call(h2w_path, set_h2w_path, param_get_int,
+ &sapphire_h2w_path, S_IWUSR | S_IRUGO);
+
+
+static struct h2w_platform_data sapphire_h2w_data = {
+ .power_name = "wlan",
+ .cable_in1 = SAPPHIRE_GPIO_CABLE_IN1,
+ .cable_in2 = SAPPHIRE_GPIO_CABLE_IN2,
+ .h2w_clk = SAPPHIRE_GPIO_H2W_CLK,
+ .h2w_data = SAPPHIRE_GPIO_H2W_DATA,
+ .headset_mic_35mm = SAPPHIRE_GPIO_AUD_HSMIC_DET_N,
+ .debug_uart = H2W_UART3,
+ .config_cpld = h2w_config_cpld,
+ .init_cpld = h2w_init_cpld,
+ .set_dat = set_h2w_dat,
+ .set_clk = set_h2w_clk,
+ .set_dat_dir = set_h2w_dat_dir,
+ .set_clk_dir = set_h2w_clk_dir,
+ .get_dat = get_h2w_dat,
+ .get_clk = get_h2w_clk,
+};
+
+static struct platform_device sapphire_h2w = {
+ .name = "h2w",
+ .id = -1,
+ .dev = {
+ .platform_data = &sapphire_h2w_data,
+ },
+};
+#endif
+
+static void sapphire_phy_reset(void)
+{
+ gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 0);
+ mdelay(10);
+ gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 1);
+ mdelay(10);
+}
+
+static struct pwr_sink sapphire_pwrsink_table[] = {
+ {
+ .id = PWRSINK_AUDIO,
+ .ua_max = 100000,
+ },
+ {
+ .id = PWRSINK_BACKLIGHT,
+ .ua_max = 125000,
+ },
+ {
+ .id = PWRSINK_LED_BUTTON,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_LED_KEYBOARD,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_GP_CLK,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_BLUETOOTH,
+ .ua_max = 15000,
+ },
+ {
+ .id = PWRSINK_CAMERA,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_SDCARD,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_VIDEO,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_WIFI,
+ .ua_max = 200000,
+ },
+ {
+ .id = PWRSINK_SYSTEM_LOAD,
+ .ua_max = 100000,
+ .percent_util = 38,
+ },
+};
+
+static int sapphire_pwrsink_resume_early(struct platform_device *pdev)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7);
+ return 0;
+}
+
+static void sapphire_pwrsink_resume_late(struct early_suspend *h)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38);
+}
+
+static void sapphire_pwrsink_suspend_early(struct early_suspend *h)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7);
+}
+
+static int sapphire_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1);
+ return 0;
+}
+
+static struct pwr_sink_platform_data sapphire_pwrsink_data = {
+ .num_sinks = ARRAY_SIZE(sapphire_pwrsink_table),
+ .sinks = sapphire_pwrsink_table,
+ .suspend_late = sapphire_pwrsink_suspend_late,
+ .resume_early = sapphire_pwrsink_resume_early,
+ .suspend_early = sapphire_pwrsink_suspend_early,
+ .resume_late = sapphire_pwrsink_resume_late,
+};
+
+static struct platform_device sapphire_pwr_sink = {
+ .name = "htc_pwrsink",
+ .id = -1,
+ .dev = {
+ .platform_data = &sapphire_pwrsink_data,
+ },
+};
+
+static struct platform_device sapphire_rfkill = {
+ .name = "sapphire_rfkill",
+ .id = -1,
+};
+
+static struct msm_pmem_setting pmem_setting_32 = {
+ .pmem_start = SMI32_MSM_PMEM_MDP_BASE,
+ .pmem_size = SMI32_MSM_PMEM_MDP_SIZE,
+ .pmem_adsp_start = SMI32_MSM_PMEM_ADSP_BASE,
+ .pmem_adsp_size = SMI32_MSM_PMEM_ADSP_SIZE,
+ .pmem_gpu0_start = MSM_PMEM_GPU0_BASE,
+ .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE,
+ .pmem_gpu1_start = MSM_PMEM_GPU1_BASE,
+ .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE,
+ .pmem_camera_start = 0,
+ .pmem_camera_size = 0,
+ .ram_console_start = MSM_RAM_CONSOLE_BASE,
+ .ram_console_size = MSM_RAM_CONSOLE_SIZE,
+};
+
+static struct msm_pmem_setting pmem_setting_64 = {
+ .pmem_start = SMI64_MSM_PMEM_MDP_BASE,
+ .pmem_size = SMI64_MSM_PMEM_MDP_SIZE,
+ .pmem_adsp_start = SMI64_MSM_PMEM_ADSP_BASE,
+ .pmem_adsp_size = SMI64_MSM_PMEM_ADSP_SIZE,
+ .pmem_gpu0_start = MSM_PMEM_GPU0_BASE,
+ .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE,
+ .pmem_gpu1_start = MSM_PMEM_GPU1_BASE,
+ .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE,
+ .pmem_camera_start = SMI64_MSM_PMEM_CAMERA_BASE,
+ .pmem_camera_size = SMI64_MSM_PMEM_CAMERA_SIZE,
+ .ram_console_start = MSM_RAM_CONSOLE_BASE,
+ .ram_console_size = MSM_RAM_CONSOLE_SIZE,
+};
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+static struct platform_device sapphire_wifi = {
+ .name = "msm_wifi",
+ .id = 1,
+ .num_resources = 0,
+ .resource = NULL,
+ .dev = {
+ .platform_data = &sapphire_wifi_control,
+ },
+};
+#endif
+
+#define SND(num, desc) { .name = desc, .id = num }
+static struct snd_endpoint snd_endpoints_list[] = {
+ SND(0, "HANDSET"),
+ SND(1, "SPEAKER"),
+ SND(2, "HEADSET"),
+ SND(3, "BT"),
+ SND(44, "BT_EC_OFF"),
+ SND(10, "HEADSET_AND_SPEAKER"),
+ SND(256, "CURRENT"),
+
+ /* Bluetooth accessories. */
+
+ SND(12, "HTC BH S100"),
+ SND(13, "HTC BH M100"),
+ SND(14, "Motorola H500"),
+ SND(15, "Nokia HS-36W"),
+ SND(16, "PLT 510v.D"),
+ SND(17, "M2500 by Plantronics"),
+ SND(18, "Nokia HDW-3"),
+ SND(19, "HBH-608"),
+ SND(20, "HBH-DS970"),
+ SND(21, "i.Tech BlueBAND"),
+ SND(22, "Nokia BH-800"),
+ SND(23, "Motorola H700"),
+ SND(24, "HTC BH M200"),
+ SND(25, "Jabra JX10"),
+ SND(26, "320Plantronics"),
+ SND(27, "640Plantronics"),
+ SND(28, "Jabra BT500"),
+ SND(29, "Motorola HT820"),
+ SND(30, "HBH-IV840"),
+ SND(31, "6XXPlantronics"),
+ SND(32, "3XXPlantronics"),
+ SND(33, "HBH-PV710"),
+ SND(34, "Motorola H670"),
+ SND(35, "HBM-300"),
+ SND(36, "Nokia BH-208"),
+ SND(37, "Samsung WEP410"),
+ SND(38, "Jabra BT8010"),
+ SND(39, "Motorola S9"),
+ SND(40, "Jabra BT620s"),
+ SND(41, "Nokia BH-902"),
+ SND(42, "HBH-DS220"),
+ SND(43, "HBH-DS980"),
+};
+#undef SND
+
+static struct msm_snd_endpoints sapphire_snd_endpoints = {
+ .endpoints = snd_endpoints_list,
+ .num = ARRAY_SIZE(snd_endpoints_list),
+};
+
+static struct platform_device sapphire_snd = {
+ .name = "msm_snd",
+ .id = -1,
+ .dev = {
+ .platform_data = &sapphire_snd_endpoints,
+ },
+};
+
+#ifdef CONFIG_MSM_CAMERA
+void config_sapphire_camera_on_gpios(void);
+void config_sapphire_camera_on_gpios(void);
+static struct msm_camera_device_platform_data msm_camera_device_data = {
+ .camera_gpio_on = config_sapphire_camera_on_gpios,
+ .camera_gpio_off = config_sapphire_camera_off_gpios,
+ .ioext.mdcphy = MSM_MDC_PHYS,
+ .ioext.mdcsz = MSM_MDC_SIZE,
+ .ioext.appphy = MSM_CLK_CTL_PHYS,
+ .ioext.appsz = MSM_CLK_CTL_SIZE,
+};
+
+#ifdef CONFIG_MT9T013
+static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = {
+ .sensor_name = "mt9t013",
+ .sensor_reset = 108,
+ .sensor_pwd = 85,
+ .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9t013 = {
+ .name = "msm_camera_mt9t013",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9t013_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_MT9P012
+static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = {
+ .sensor_name = "mt9p012",
+ .sensor_reset = 108,
+ .sensor_pwd = 85,
+ .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9p012 = {
+ .name = "msm_camera_mt9p012",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9p012_data,
+ },
+};
+#endif
+#endif/*CONFIG_MSM_CAMERA*/
+
+#ifdef CONFIG_SENSORS_MT9T013
+static struct msm_camera_legacy_device_platform_data msm_camera_device_mt9t013 = {
+ .sensor_reset = 108,
+ .sensor_pwd = 85,
+ .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN,
+ .config_gpio_on = config_sapphire_camera_on_gpios,
+ .config_gpio_off = config_sapphire_camera_off_gpios,
+};
+
+static struct platform_device sapphire_camera = {
+ .name = "camera",
+ .dev = {
+ .platform_data = &msm_camera_device_mt9t013,
+ },
+};
+#endif
static struct platform_device *devices[] __initdata = {
&msm_device_smd,
- &msm_device_dmov,
&msm_device_nand,
+ &msm_device_i2c,
&msm_device_uart1,
+#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W)
&msm_device_uart3,
+#endif
+#ifdef CONFIG_SERIAL_MSM_HS
+ &msm_device_uart_dm1,
+#endif
+ &sapphire_nav_device,
+ &sapphire_search_button_device,
+ &sapphire_reset_keys_device,
+ &android_leds,
+#ifdef CONFIG_LEDS_CPLD
+ &android_CPLD_leds,
+#endif
+#ifdef CONFIG_HTC_HEADSET
+ &sapphire_h2w,
+#endif
+#ifdef CONFIG_MT9T013
+ &msm_camera_sensor_mt9t013,
+#endif
+#ifdef CONFIG_MT9P012
+ &msm_camera_sensor_mt9p012,
+#endif
+ &sapphire_rfkill,
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+ &sapphire_wifi,
+#endif
+
+#ifdef CONFIG_HTC_PWRSINK
+ &sapphire_pwr_sink,
+#endif
+ &sapphire_snd,
+#ifdef CONFIG_SENSORS_MT9T013
+ &sapphire_camera,
+#endif
};
extern struct sys_timer msm_timer;
static void __init sapphire_init_irq(void)
{
+ printk(KERN_DEBUG "sapphire_init_irq()\n");
msm_init_irq();
}
+static uint cpld_iset;
+static uint cpld_charger_en;
+static uint cpld_usb_h2w_sw;
+static uint opt_disable_uart3;
+
+module_param_named(iset, cpld_iset, uint, 0);
+module_param_named(charger_en, cpld_charger_en, uint, 0);
+module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0);
+module_param_named(disable_uart3, opt_disable_uart3, uint, 0);
+
+static void sapphire_reset(void)
+{
+ gpio_set_value(SAPPHIRE_GPIO_PS_HOLD, 0);
+}
+
+static uint32_t gpio_table[] = {
+ /* BLUETOOTH */
+#ifdef CONFIG_SERIAL_MSM_HS
+ PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */
+ PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */
+ PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */
+ PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */
+#else
+ PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */
+ PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */
+ PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */
+ PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */
+#endif
+};
+
+
+static uint32_t camera_off_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */
+};
+
+static uint32_t camera_on_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */
+};
+
+static uint32_t camera_off_gpio_12pins_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(0, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */
+};
+
+static uint32_t camera_on_gpio_12pins_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */
+ PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */
+ PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */
+};
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for (n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+void config_sapphire_camera_on_gpios(void)
+{
+ /*Add for judage it's 10 pins or 12 pins platform ----->*/
+ if (is_12pin_camera()) {
+ config_gpio_table(camera_on_gpio_12pins_table,
+ ARRAY_SIZE(camera_on_gpio_12pins_table));
+ } else {
+ config_gpio_table(camera_on_gpio_table,
+ ARRAY_SIZE(camera_on_gpio_table));
+ }
+ /*End Of Add for judage it's 10 pins or 12 pins platform*/
+}
+
+void config_sapphire_camera_off_gpios(void)
+{
+ /*Add for judage it's 10 pins or 12 pins platform ----->*/
+ if (is_12pin_camera()) {
+ config_gpio_table(camera_off_gpio_12pins_table,
+ ARRAY_SIZE(camera_off_gpio_12pins_table));
+ } else {
+ config_gpio_table(camera_off_gpio_table,
+ ARRAY_SIZE(camera_off_gpio_table));
+ }
+ /*End Of Add for judage it's 10 pins or 12 pins platform*/
+}
+
+static void __init config_gpios(void)
+{
+ config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table));
+ config_sapphire_camera_off_gpios();
+}
+
+static struct msm_acpu_clock_platform_data sapphire_clock_data = {
+ .acpu_switch_time_us = 20,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 19200000,
+ .wait_for_irq_khz = 128000000,
+};
+
+#ifdef CONFIG_SERIAL_MSM_HS
+static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = {
+ .rx_wakeup_irq = MSM_GPIO_TO_INT(45),
+ .inject_rx_on_wakeup = 1,
+ .rx_to_inject = 0x32,
+};
+#endif
+
static void __init sapphire_init(void)
{
+ int rc;
+ printk("sapphire_init() revision = 0x%X\n", system_rev);
+
+ /*
+ * Setup common MSM GPIOS
+ */
+ config_gpios();
+
+ msm_hw_reset_hook = sapphire_reset;
+
+ msm_acpu_clock_init(&sapphire_clock_data);
+
+ /* adjust GPIOs based on bootloader request */
+ printk("sapphire_init: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw);
+ gpio_set_value(SAPPHIRE_GPIO_USB_H2W_SW, cpld_usb_h2w_sw);
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ if (!opt_disable_uart3)
+ msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3,
+ &msm_device_uart3.dev, 1,
+ MSM_GPIO_TO_INT(86));
+#endif
+
+ /* gpio_configure(108, IRQF_TRIGGER_LOW); */
+
+ /* H2W pins <-> UART3, Bluetooth <-> UART1 */
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0);
+ gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1);
+ /* put the AF VCM in powerdown mode to avoid noise */
+ if (sapphire_is_5M_camera())
+ sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 0);
+ else
+ sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 1);
+ mdelay(100);
+
+ printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n",
+ sapphire_is_5M_camera());
+ printk(KERN_DEBUG "is_12pin_camera=%d\n", is_12pin_camera());
+#ifdef CONFIG_SERIAL_MSM_HS
+ msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata;
+#endif
+ msm_add_usb_devices(sapphire_phy_reset);
+
+ if (32 == smi_sz)
+ msm_add_mem_devices(&pmem_setting_32);
+ else
+ msm_add_mem_devices(&pmem_setting_64);
+
+ rc = sapphire_init_mmc(system_rev);
+ if (rc)
+ printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc);
+
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+ rc = sapphire_init_wifi_mem();
+ if (rc) {
+ printk(KERN_CRIT "%s: WiFi memory init failure (%d)\n",
+ __func__, rc);
+ }
+#endif
+ msm_init_pmic_vibrator();
+
+ if(system_rev != 0x80)
+ sapphire_search_button_info.keymap = sapphire_search_button_v1;
+
+ if (is_12pin_camera())
+ gpio_tp_ls_en = SAPPHIRE20_TP_LS_EN;
+ gpio_request(gpio_tp_ls_en, "tp_ls_en");
+
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
platform_add_devices(devices, ARRAY_SIZE(devices));
}
@@ -77,22 +1202,88 @@
}
};
+
+unsigned int sapphire_get_hwid(void)
+{
+ return hwid;
+}
+
+unsigned int sapphire_get_skuid(void)
+{
+ return skuid;
+}
+
+unsigned sapphire_engineerid(void)
+{
+ return engineerid;
+}
+
+int sapphire_is_5M_camera(void)
+{
+ int ret = 0;
+ if (sapphire_get_skuid() == 0x1FF00 && !(sapphire_engineerid() & 0x02))
+ ret = 1;
+ else if (sapphire_get_skuid() == 0x20100 && !(sapphire_engineerid() & 0x02))
+ ret = 1;
+ return ret;
+}
+
+/* it can support 3M and 5M sensor */
+unsigned int is_12pin_camera(void)
+{
+ unsigned int ret = 0;
+
+ if (sapphire_get_skuid() == 0x1FF00 || sapphire_get_skuid() == 0x20100)
+ ret = 1;
+ else
+ ret = 0;
+ return ret;
+}
+
+int sapphire_get_smi_size(void)
+{
+ printk(KERN_DEBUG "get_smi_size=%d\n", smi_sz);
+ return smi_sz;
+}
+
static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags,
char **cmdline, struct meminfo *mi)
{
- int smi_sz = parse_tag_smi((const struct tag *)tags);
+ smi_sz = parse_tag_smi((const struct tag *)tags);
+ printk("sapphire_fixup:smisize=%d\n", smi_sz);
+ hwid = parse_tag_hwid((const struct tag *)tags);
+ printk("sapphire_fixup:hwid=0x%x\n", hwid);
+ skuid = parse_tag_skuid((const struct tag *)tags);
+ printk("sapphire_fixup:skuid=0x%x\n", skuid);
+ engineerid = parse_tag_engineerid((const struct tag *)tags);
+ printk("sapphire_fixup:engineerid=0x%x\n", engineerid);
- mi->nr_banks = 1;
- mi->bank[0].start = PHYS_OFFSET;
- mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
if (smi_sz == 32) {
+ mi->nr_banks = 1;
+ mi->bank[0].start = PHYS_OFFSET;
+ mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
mi->bank[0].size = (84*1024*1024);
} else if (smi_sz == 64) {
- mi->bank[0].size = (101*1024*1024);
+ mi->nr_banks = 2;
+ mi->bank[0].start = SMI64_MSM_LINUX_BASE_1;
+ mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1);
+ mi->bank[0].size = (32*1024*1024);
+ mi->bank[1].start = SMI64_MSM_LINUX_BASE_2;
+ mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2);
+ mi->bank[1].size = (84*1024*1024);
} else {
- /* Give a default value when not get smi size */
+ printk(KERN_ERR "can not get smi size\n");
+
+ /*Give a default value when not get smi size*/
smi_sz = 64;
- mi->bank[0].size = (101*1024*1024);
+ mi->nr_banks = 2;
+ mi->bank[0].start = SMI64_MSM_LINUX_BASE_1;
+ mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1);
+ mi->bank[0].size = (32*1024*1024);
+ mi->bank[1].start = SMI64_MSM_LINUX_BASE_2;
+ mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2);
+ mi->bank[1].size = (84*1024*1024);
+ printk(KERN_ERR "use default : smisize=%d\n", smi_sz);
}
}
@@ -100,7 +1291,7 @@
{
msm_map_common_io();
iotable_init(sapphire_io_desc, ARRAY_SIZE(sapphire_io_desc));
- msm_clock_init();
+ msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a);
}
MACHINE_START(SAPPHIRE, "sapphire")
@@ -109,7 +1300,7 @@
.phys_io = MSM_DEBUG_UART_PHYS,
.io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
#endif
- .boot_params = PHYS_OFFSET + 0x100,
+ .boot_params = 0x02000100,
.fixup = sapphire_fixup,
.map_io = sapphire_map_io,
.init_irq = sapphire_init_irq,
diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h
new file mode 100644
index 0000000..d96760a
--- /dev/null
+++ b/arch/arm/mach-msm/board-sapphire.h
@@ -0,0 +1,224 @@
+/* linux/arch/arm/mach-msm/board-sapphire.h
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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 __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H
+#define __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H
+
+#include <mach/board.h>
+
+#define MSM_SMI_BASE 0x00000000
+#define MSM_SMI_SIZE 0x00800000
+
+#define MSM_EBI_BASE 0x10000000
+#define MSM_EBI_SIZE 0x07100000
+
+#define MSM_PMEM_GPU0_BASE 0x00000000
+#define MSM_PMEM_GPU0_SIZE 0x00700000
+
+#define SMI64_MSM_PMEM_MDP_BASE 0x15900000
+#define SMI64_MSM_PMEM_MDP_SIZE 0x00800000
+
+#define SMI64_MSM_PMEM_ADSP_BASE 0x16100000
+#define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000
+
+#define SMI64_MSM_PMEM_CAMERA_BASE 0x15400000
+#define SMI64_MSM_PMEM_CAMERA_SIZE 0x00500000
+
+#define SMI64_MSM_FB_BASE 0x00700000
+#define SMI64_MSM_FB_SIZE 0x00100000
+
+#define SMI64_MSM_LINUX_BASE MSM_EBI_BASE
+#define SMI64_MSM_LINUX_SIZE 0x068e0000
+
+#define SMI64_MSM_LINUX_BASE_1 0x02000000
+#define SMI64_MSM_LINUX_SIZE_1 0x02000000
+
+#define SMI64_MSM_LINUX_BASE_2 MSM_EBI_BASE
+#define SMI64_MSM_LINUX_SIZE_2 0x05400000
+
+#define SMI32_MSM_LINUX_BASE MSM_EBI_BASE
+#define SMI32_MSM_LINUX_SIZE 0x5400000
+
+#define SMI32_MSM_PMEM_MDP_BASE SMI32_MSM_LINUX_BASE + SMI32_MSM_LINUX_SIZE
+#define SMI32_MSM_PMEM_MDP_SIZE 0x800000
+
+#define SMI32_MSM_PMEM_ADSP_BASE SMI32_MSM_PMEM_MDP_BASE + SMI32_MSM_PMEM_MDP_SIZE
+#define SMI32_MSM_PMEM_ADSP_SIZE 0x800000
+
+#define SMI32_MSM_FB_BASE SMI32_MSM_PMEM_ADSP_BASE + SMI32_MSM_PMEM_ADSP_SIZE
+#define SMI32_MSM_FB_SIZE 0x9b000
+
+
+#define MSM_PMEM_GPU1_SIZE 0x800000
+#define MSM_PMEM_GPU1_BASE (MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE)
+
+#define MSM_RAM_CONSOLE_BASE 0x169E0000
+#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K
+
+#if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE)
+#error invalid memory map
+#endif
+
+#if (SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE)
+#error invalid memory map
+#endif
+
+#define DECLARE_MSM_IOMAP
+#include <mach/msm_iomap.h>
+
+/*
+** SOC GPIO
+*/
+#define SAPPHIRE_BALL_UP_0 94
+#define SAPPHIRE_BALL_LEFT_0 18
+#define SAPPHIRE_BALL_DOWN_0 49
+#define SAPPHIRE_BALL_RIGHT_0 19
+
+#define SAPPHIRE_POWER_KEY 20
+#define SAPPHIRE_VOLUME_UP 36
+#define SAPPHIRE_VOLUME_DOWN 39
+
+#define SAPPHIRE_GPIO_PS_HOLD (25)
+#define SAPPHIRE_MDDI_1V5_EN (28)
+#define SAPPHIRE_BL_PWM (27)
+#define SAPPHIRE_TP_LS_EN (1)
+#define SAPPHIRE20_TP_LS_EN (88)
+
+/* H2W */
+#define SAPPHIRE_GPIO_CABLE_IN1 (83)
+#define SAPPHIRE_GPIO_CABLE_IN2 (37)
+#define SAPPHIRE_GPIO_UART3_RX (86)
+#define SAPPHIRE_GPIO_UART3_TX (87)
+#define SAPPHIRE_GPIO_H2W_DATA (86)
+#define SAPPHIRE_GPIO_H2W_CLK (87)
+
+#define SAPPHIRE_GPIO_UART1_RTS (43)
+#define SAPPHIRE_GPIO_UART1_CTS (44)
+
+/*
+** CPLD GPIO
+**
+** Sapphire Altera CPLD can keep the registers value and
+** doesn't need a shadow to backup.
+**/
+#define SAPPHIRE_CPLD_BASE 0xFA000000 /* VA */
+#define SAPPHIRE_CPLD_START 0x98000000 /* PA */
+#define SAPPHIRE_CPLD_SIZE SZ_4K
+
+#define SAPPHIRE_GPIO_START (128) /* Pseudo GPIO number */
+
+/* Sapphire has one INT BANK only. */
+#define SAPPHIRE_GPIO_INT_B0_MASK_REG (0x0c) /*INT3 MASK*/
+#define SAPPHIRE_GPIO_INT_B0_STAT_REG (0x0e) /*INT1 STATUS*/
+
+/* LED control register */
+#define SAPPHIRE_CPLD_LED_BASE (SAPPHIRE_CPLD_BASE + 0x10) /* VA */
+#define SAPPHIRE_CPLD_LED_START (SAPPHIRE_CPLD_START + 0x10) /* PA */
+#define SAPPHIRE_CPLD_LED_SIZE 0x08
+
+/* MISCn: GPO pin to Enable/Disable some functions. */
+#define SAPPHIRE_GPIO_MISC1_BASE (SAPPHIRE_GPIO_START + 0x00)
+#define SAPPHIRE_GPIO_MISC2_BASE (SAPPHIRE_GPIO_START + 0x08)
+#define SAPPHIRE_GPIO_MISC3_BASE (SAPPHIRE_GPIO_START + 0x10)
+#define SAPPHIRE_GPIO_MISC4_BASE (SAPPHIRE_GPIO_START + 0x18)
+#define SAPPHIRE_GPIO_MISC5_BASE (SAPPHIRE_GPIO_START + 0x20)
+
+/* INT BANK0: INT1: int status, INT2: int level, INT3: int Mask */
+#define SAPPHIRE_GPIO_INT_B0_BASE (SAPPHIRE_GPIO_START + 0x28)
+
+/* MISCn GPIO: */
+#define SAPPHIRE_GPIO_CPLD128_VER_0 (SAPPHIRE_GPIO_MISC1_BASE + 4)
+#define SAPPHIRE_GPIO_CPLD128_VER_1 (SAPPHIRE_GPIO_MISC1_BASE + 5)
+#define SAPPHIRE_GPIO_CPLD128_VER_2 (SAPPHIRE_GPIO_MISC1_BASE + 6)
+#define SAPPHIRE_GPIO_CPLD128_VER_3 (SAPPHIRE_GPIO_MISC1_BASE + 7)
+
+#define SAPPHIRE_GPIO_H2W_DAT_DIR (SAPPHIRE_GPIO_MISC2_BASE + 2)
+#define SAPPHIRE_GPIO_H2W_CLK_DIR (SAPPHIRE_GPIO_MISC2_BASE + 3)
+#define SAPPHIRE_GPIO_H2W_SEL0 (SAPPHIRE_GPIO_MISC2_BASE + 6)
+#define SAPPHIRE_GPIO_H2W_SEL1 (SAPPHIRE_GPIO_MISC2_BASE + 7)
+
+#define SAPPHIRE_GPIO_I2C_PULL (SAPPHIRE_GPIO_MISC3_BASE + 2)
+#define SAPPHIRE_GPIO_TP_EN (SAPPHIRE_GPIO_MISC3_BASE + 4)
+#define SAPPHIRE_GPIO_JOG_EN (SAPPHIRE_GPIO_MISC3_BASE + 5)
+#define SAPPHIRE_GPIO_JOG_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 6)
+#define SAPPHIRE_GPIO_APKEY_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 7)
+
+#define SAPPHIRE_GPIO_VCM_PWDN (SAPPHIRE_GPIO_MISC4_BASE + 0)
+#define SAPPHIRE_GPIO_USB_H2W_SW (SAPPHIRE_GPIO_MISC4_BASE + 1)
+#define SAPPHIRE_GPIO_COMPASS_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 2)
+#define SAPPHIRE_GPIO_USB_PHY_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 5)
+#define SAPPHIRE_GPIO_WIFI_PA_RESETX (SAPPHIRE_GPIO_MISC4_BASE + 6)
+#define SAPPHIRE_GPIO_WIFI_EN (SAPPHIRE_GPIO_MISC4_BASE + 7)
+
+#define SAPPHIRE_GPIO_BT_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 0)
+#define SAPPHIRE_GPIO_MAC_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 1)
+#define SAPPHIRE_GPIO_MDDI_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 2)
+#define SAPPHIRE_GPIO_COMPASS_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 3)
+
+/* INT STATUS/LEVEL/MASK : INT GPIO should be the last. */
+#define SAPPHIRE_GPIO_NAVI_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 0)
+#define SAPPHIRE_GPIO_COMPASS_IRQ (SAPPHIRE_GPIO_INT_B0_BASE + 1)
+#define SAPPHIRE_GPIO_SEARCH_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 2)
+#define SAPPHIRE_GPIO_AUD_HSMIC_DET_N (SAPPHIRE_GPIO_INT_B0_BASE + 3)
+#define SAPPHIRE_GPIO_SDMC_CD_N (SAPPHIRE_GPIO_INT_B0_BASE + 4)
+#define SAPPHIRE_GPIO_CAM_BTN_STEP1_N (SAPPHIRE_GPIO_INT_B0_BASE + 5)
+#define SAPPHIRE_GPIO_CAM_BTN_STEP2_N (SAPPHIRE_GPIO_INT_B0_BASE + 6)
+#define SAPPHIRE_GPIO_TP_ATT_N (SAPPHIRE_GPIO_INT_B0_BASE + 7)
+
+#define SAPPHIRE_GPIO_END SAPPHIRE_GPIO_TP_ATT_N
+#define SAPPHIRE_GPIO_LAST_INT (SAPPHIRE_GPIO_TP_ATT_N)
+
+/* Bit position in the CPLD MISCn by the CPLD GPIOn: only bit0-7 is used. */
+#define CPLD_GPIO_BIT_POS_MASK(n) (1U << ((n) & 7))
+#define CPLD_GPIO_REG_OFFSET(n) _g_CPLD_MISCn_Offset[((n)-SAPPHIRE_GPIO_START) >> 3]
+#define CPLD_GPIO_REG(n) (CPLD_GPIO_REG_OFFSET(n) + SAPPHIRE_CPLD_BASE)
+
+/*
+** CPLD INT Start
+*/
+#define SAPPHIRE_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS) /* pseudo number for CPLD INT */
+/* Using INT status/Bank0 for GPIO to INT */
+#define SAPPHIRE_GPIO_TO_INT(n) ((n-SAPPHIRE_GPIO_INT_B0_BASE) + SAPPHIRE_INT_START)
+#define SAPPHIRE_INT_END (SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_END))
+
+/* get the INT reg by GPIO number */
+#define CPLD_INT_GPIO_TO_BANK(n) (((n)-SAPPHIRE_GPIO_INT_B0_BASE) >> 3)
+#define CPLD_INT_STATUS_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][0]
+#define CPLD_INT_LEVEL_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][1]
+#define CPLD_INT_MASK_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][2]
+#define CPLD_INT_STATUS_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET_G(n))
+#define CPLD_INT_LEVEL_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET_G(n))
+#define CPLD_INT_MASK_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET_G(n))
+
+/* get the INT reg by INT number */
+#define CPLD_INT_TO_BANK(i) ((i-SAPPHIRE_INT_START) >> 3)
+#define CPLD_INT_STATUS_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][0]
+#define CPLD_INT_LEVEL_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][1]
+#define CPLD_INT_MASK_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][2]
+#define CPLD_INT_STATUS_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET(i))
+#define CPLD_INT_LEVEL_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET(i))
+#define CPLD_INT_MASK_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET(i) )
+
+/* return the bit mask by INT number */
+#define SAPPHIRE_INT_BIT_MASK(i) (1U << ((i - SAPPHIRE_INT_START) & 7))
+
+void config_sapphire_camera_on_gpios(void);
+void config_sapphire_camera_off_gpios(void);
+int sapphire_get_smi_size(void);
+unsigned int sapphire_get_hwid(void);
+unsigned int sapphire_get_skuid(void);
+unsigned int is_12pin_camera(void);
+int sapphire_is_5M_camera(void);
+int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on);
+
+#endif /* GUARD */
diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c
new file mode 100644
index 0000000..fe52633
--- /dev/null
+++ b/arch/arm/mach-msm/board-surf7x30.c
@@ -0,0 +1,486 @@
+/* linux/arch/arm/mach-msm/board-surf7x30.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio_event.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8058.h>
+#include <linux/platform_device.h>
+#include <linux/usb/android_composite.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/setup.h>
+
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/msm_hsusb.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_ssbi.h>
+
+#include "devices.h"
+#include "proc_comm.h"
+
+#define SURF7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO
+#define SURF7X30_PM8058_GPIO(x) (SURF7X30_PM8058_GPIO_BASE + (x))
+#define SURF7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ
+
+#define SURF7X30_GPIO_PMIC_INT_N 27
+
+#define SURF7X30_USE_PMIC_KEYPAD 1
+
+static struct resource smc91x_resources[] = {
+ [0] = {
+ .start = 0x8A000300,
+ .end = 0x8A0003ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MSM_GPIO_TO_INT(156),
+ .end = MSM_GPIO_TO_INT(156),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc91x_device = {
+ .name = "smc91x",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(smc91x_resources),
+ .resource = smc91x_resources,
+};
+
+static int surf7x30_phy_init_seq[] = {
+ 0x0C, 0x31,
+ 0x31, 0x32,
+ 0x1D, 0x0D,
+ 0x1D, 0x10,
+ -1 };
+
+static void surf7x30_usb_phy_reset(void)
+{
+ u32 id;
+ int ret;
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+
+ msleep(1);
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+}
+
+static void surf7x30_usb_hw_reset(bool enable)
+{
+ u32 id;
+ int ret;
+ u32 func;
+
+ id = PCOM_CLKRGM_APPS_RESET_USBH;
+ if (enable)
+ func = PCOM_CLK_REGIME_SEC_RESET_ASSERT;
+ else
+ func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT;
+ ret = msm_proc_comm(func, &id, NULL);
+ if (ret)
+ pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable,
+ ret);
+}
+
+static struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_init_seq = surf7x30_phy_init_seq,
+ .phy_reset = surf7x30_usb_phy_reset,
+ .hw_reset = surf7x30_usb_hw_reset,
+};
+
+static char *usb_functions[] = {
+ "usb_mass_storage",
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ "rndis",
+#endif
+#ifdef CONFIG_USB_ANDROID_ACM
+ "acm",
+#endif
+};
+
+static char *usb_functions_adb[] = {
+ "usb_mass_storage",
+ "adb",
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ "rndis",
+#endif
+#ifdef CONFIG_USB_ANDROID_ACM
+ "acm",
+#endif
+};
+
+#ifdef CONFIG_USB_ANDROID_DIAG
+static char *usb_functions_adb_diag[] = {
+ "usb_mass_storage",
+ "adb",
+ "diag",
+};
+#endif
+
+static char *usb_functions_all[] = {
+ "usb_mass_storage",
+ "adb",
+#ifdef CONFIG_USB_ANDROID_ACM
+ "acm",
+#endif
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ "rndis",
+#endif
+#ifdef CONFIG_USB_ANDROID_DIAG
+ "diag",
+#endif
+};
+
+static struct android_usb_product usb_products[] = {
+ {
+#ifdef CONFIG_USB_ANDROID_ACM
+ .product_id = 0x4e21,
+#else
+ .product_id = 0x4e11,
+#endif
+ .num_functions = ARRAY_SIZE(usb_functions),
+ .functions = usb_functions,
+ },
+ {
+#ifdef CONFIG_USB_ANDROID_ACM
+ .product_id = 0x4e22,
+#else
+ .product_id = 0x4e12,
+#endif
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+ },
+#ifdef CONFIG_USB_ANDROID_DIAG
+ {
+ .product_id = 0x4e17,
+ .num_functions = ARRAY_SIZE(usb_functions_adb_diag),
+ .functions = usb_functions_adb_diag,
+ },
+#endif
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "Qualcomm, Inc.",
+ .product = "Surf7x30",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x18d1,
+ .product_id = 0x4e11,
+ .version = 0x0100,
+ .product_name = "Surf7x30",
+ .manufacturer_name = "Qualcomm, Inc.",
+ .num_products = ARRAY_SIZE(usb_products),
+ .products = usb_products,
+ .num_functions = ARRAY_SIZE(usb_functions_all),
+ .functions = usb_functions_all,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+#if SURF7X30_USE_PMIC_KEYPAD
+static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = {
+ .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3,
+ .dir = PM8058_GPIO_INPUT,
+ .pull_up = PM8058_GPIO_PULL_UP_31P5,
+ .strength = PM8058_GPIO_STRENGTH_OFF,
+ .func = PM8058_GPIO_FUNC_NORMAL,
+ .flags = PM8058_GPIO_INV_IRQ_POL
+};
+
+static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = {
+ .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3,
+ .dir = PM8058_GPIO_OUTPUT,
+ .pull_up = PM8058_GPIO_PULL_NONE,
+ .strength = PM8058_GPIO_STRENGTH_LOW,
+ .func = PM8058_GPIO_FUNC_1,
+ .flags = (PM8058_GPIO_OPEN_DRAIN |
+ PM8058_GPIO_INV_IRQ_POL),
+};
+
+#else
+
+static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = {
+ .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3,
+ .dir = PM8058_GPIO_INPUT,
+ .pull_up = PM8058_GPIO_PULL_UP_31P5,
+ .strength = PM8058_GPIO_STRENGTH_OFF,
+ .func = PM8058_GPIO_FUNC_NORMAL,
+ .flags = PM8058_GPIO_INV_IRQ_POL
+};
+
+static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = {
+ .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3,
+ .dir = PM8058_GPIO_OUTPUT,
+ .pull_up = PM8058_GPIO_PULL_NONE,
+ .strength = PM8058_GPIO_STRENGTH_LOW,
+ .func = PM8058_GPIO_FUNC_NORMAL,
+ .flags = (PM8058_GPIO_OPEN_DRAIN |
+ PM8058_GPIO_INV_IRQ_POL),
+};
+#endif
+
+static unsigned int surf7x30_pmic_col_gpios[] = {
+ SURF7X30_PM8058_GPIO(0), SURF7X30_PM8058_GPIO(1),
+ SURF7X30_PM8058_GPIO(2), SURF7X30_PM8058_GPIO(3),
+ SURF7X30_PM8058_GPIO(4), SURF7X30_PM8058_GPIO(5),
+ SURF7X30_PM8058_GPIO(6), SURF7X30_PM8058_GPIO(7),
+};
+static unsigned int surf7x30_pmic_row_gpios[] = {
+ SURF7X30_PM8058_GPIO(8), SURF7X30_PM8058_GPIO(9),
+ SURF7X30_PM8058_GPIO(10), SURF7X30_PM8058_GPIO(11),
+ SURF7X30_PM8058_GPIO(12), SURF7X30_PM8058_GPIO(13),
+ SURF7X30_PM8058_GPIO(14), SURF7X30_PM8058_GPIO(15),
+ SURF7X30_PM8058_GPIO(16), SURF7X30_PM8058_GPIO(17),
+ SURF7X30_PM8058_GPIO(18), SURF7X30_PM8058_GPIO(19),
+};
+
+#define KEYMAP_NUM_ROWS ARRAY_SIZE(surf7x30_pmic_row_gpios)
+#define KEYMAP_NUM_COLS ARRAY_SIZE(surf7x30_pmic_col_gpios)
+#define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col))
+#define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS)
+
+static int mux_keypad_gpios(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < KEYMAP_NUM_COLS; ++i)
+ pm8058_gpio_mux_cfg(dev, surf7x30_pmic_col_gpios[i],
+ &surf7x30_kpd_input_gpio_cfg);
+ for (i = 0; i < KEYMAP_NUM_ROWS; ++i)
+ pm8058_gpio_mux_cfg(dev, surf7x30_pmic_row_gpios[i],
+ &surf7x30_kpd_output_gpio_cfg);
+ return 0;
+}
+
+/* if we are using the pmic matrix h/w, we need to do the muxing inside the
+ * keypad probe function once the keypad has been setup. */
+static int surf7x30_pmic_keypad_init(struct device *dev)
+{
+ return mux_keypad_gpios(dev->parent);
+}
+
+static const unsigned short surf7x30_pmic_keymap[KEYMAP_SIZE] = {
+ [KEYMAP_INDEX(0, 6)] = KEY_BACK,
+};
+
+static struct pm8058_keypad_platform_data surf7x30_pmic_keypad_pdata = {
+ .name = "surf7x30-keypad",
+ .num_drv = KEYMAP_NUM_ROWS,
+ .num_sns = KEYMAP_NUM_COLS,
+ .scan_delay_shift = 5,
+ .drv_hold_clks = 4,
+ .debounce_ms = 10,
+ .keymap = surf7x30_pmic_keymap,
+ .init = surf7x30_pmic_keypad_init,
+};
+
+static struct gpio_event_matrix_info surf7x30_keypad_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = surf7x30_pmic_keymap,
+ .output_gpios = surf7x30_pmic_row_gpios,
+ .input_gpios = surf7x30_pmic_col_gpios,
+ .noutputs = KEYMAP_NUM_ROWS,
+ .ninputs = KEYMAP_NUM_COLS,
+ .settle_time.tv.nsec = 40 * NSEC_PER_USEC,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/
+};
+
+static struct gpio_event_info *surf7x30_keypad_info[] = {
+ &surf7x30_keypad_matrix_info.info,
+};
+
+static struct gpio_event_platform_data surf7x30_keypad_data = {
+ .name = "surf7x30-keypad",
+ .info = surf7x30_keypad_info,
+ .info_count = ARRAY_SIZE(surf7x30_keypad_info)
+};
+
+static struct platform_device surf7x30_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 0,
+ .dev = {
+ .platform_data = &surf7x30_keypad_data,
+ },
+};
+
+static int surf7x30_pmic_init(struct device *dev)
+{
+ int ret = 0;
+
+#if !SURF7X30_USE_PMIC_KEYPAD
+ /* if we are not using the keypad matrix h/w, but are using the
+ * gpio_matrix, do the pimuxing here, which is called from pmic's
+ * probe */
+ ret = mux_keypad_gpios(dev);
+#endif
+ return ret;
+}
+
+static struct pm8058_platform_data surf7x30_pm8058_pdata = {
+ .irq_base = SURF7X30_PM8058_IRQ_BASE,
+ .gpio_base = SURF7X30_PM8058_GPIO_BASE,
+ .init = surf7x30_pmic_init,
+
+#if SURF7X30_USE_PMIC_KEYPAD
+ .keypad_pdata = &surf7x30_pmic_keypad_pdata,
+#endif
+};
+
+static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = {
+ .slave = {
+ .name = "pm8058-core",
+ .irq = MSM_GPIO_TO_INT(SURF7X30_GPIO_PMIC_INT_N),
+ .platform_data = &surf7x30_pm8058_pdata,
+ },
+ .rspinlock_name = "D:PMIC_SSBI",
+};
+
+static int __init surf7x30_ssbi_pmic_init(void)
+{
+ int ret;
+ u32 id;
+
+ pr_info("%s()\n", __func__);
+ id = PCOM_GPIO_CFG(SURF7X30_GPIO_PMIC_INT_N, 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ ret = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ if (ret)
+ pr_err("%s: gpio %d cfg failed\n", __func__,
+ SURF7X30_GPIO_PMIC_INT_N);
+
+ ret = gpiochip_reserve(surf7x30_pm8058_pdata.gpio_base,
+ PM8058_NUM_GPIOS);
+ WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n");
+ msm_device_ssbi_pmic.dev.platform_data = &surf7x30_ssbi_pmic_pdata;
+ return platform_device_register(&msm_device_ssbi_pmic);
+}
+
+
+static struct i2c_board_info surf_i2c_devices[] = {
+ /* marimba master is implied at 0x0c */
+ {
+ I2C_BOARD_INFO("marimba-codec", 0x77),
+ },
+};
+
+extern struct sys_timer msm_timer;
+
+void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq);
+
+static struct platform_device *devices[] __initdata = {
+#if !defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ &msm_device_uart1,
+#endif
+ &msm_device_smd,
+ &msm_device_nand,
+ &msm_device_hsusb,
+ &usb_mass_storage_device,
+ &android_usb_device,
+ &smc91x_device,
+ &msm_device_i2c2,
+#if !SURF7X30_USE_PMIC_KEYPAD
+ &surf7x30_keypad_device,
+#endif
+};
+
+static void __init surf7x30_init(void)
+{
+ printk("%s()\n", __func__);
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1,
+ &msm_device_uart1.dev, 1);
+#endif
+ /* do this as early as possible to use pmic gpio/mux facilities */
+ surf7x30_ssbi_pmic_init();
+
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+
+ i2c_register_board_info(1, surf_i2c_devices,
+ ARRAY_SIZE(surf_i2c_devices));
+ msm_hsusb_set_vbus_state(1);
+ msm_hsusb_set_vbus_state(0);
+ msm_hsusb_set_vbus_state(1);
+}
+
+static void __init surf7x30_fixup(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mi->nr_banks = 1;
+ mi->bank[0].start = PHYS_OFFSET;
+ mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
+ mi->bank[0].size = (51*1024*1024);
+}
+
+static void __init surf7x30_map_io(void)
+{
+ msm_map_msm7x30_io();
+ msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30);
+}
+
+MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board")
+#ifdef CONFIG_MSM_DEBUG_UART
+ .phys_io = MSM_DEBUG_UART_PHYS,
+ .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
+#endif
+ .boot_params = 0x00200100,
+ .fixup = surf7x30_fixup,
+ .map_io = surf7x30_map_io,
+ .init_irq = msm_init_irq,
+ .init_machine = surf7x30_init,
+ .timer = &msm_timer,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-swordfish-keypad.c b/arch/arm/mach-msm/board-swordfish-keypad.c
new file mode 100644
index 0000000..f2c2f39
--- /dev/null
+++ b/arch/arm/mach-msm/board-swordfish-keypad.c
@@ -0,0 +1,177 @@
+/* linux/arch/arm/mach-msm/board-swordfish-keypad.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_event.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "board_swordfish."
+static int swordfish_ffa;
+module_param_named(ffa, swordfish_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */
+
+static unsigned int swordfish_row_gpios[] = {
+ 31, 32, 33, 34, 35, 41
+#if SCAN_FUNCTION_KEYS
+ , 42
+#endif
+};
+
+static unsigned int swordfish_col_gpios[] = { 36, 37, 38, 39, 40 };
+
+/* FFA:
+ 36: KEYSENSE_N(0)
+ 37: KEYSENSE_N(1)
+ 38: KEYSENSE_N(2)
+ 39: KEYSENSE_N(3)
+ 40: KEYSENSE_N(4)
+
+ 31: KYPD_17
+ 32: KYPD_15
+ 33: KYPD_13
+ 34: KYPD_11
+ 35: KYPD_9
+ 41: KYPD_MEMO
+*/
+
+#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(swordfish_col_gpios) + (col))
+
+static const unsigned short swordfish_keymap[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_5,
+ [KEYMAP_INDEX(0, 1)] = KEY_9,
+ [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */
+ [KEYMAP_INDEX(0, 3)] = KEY_6,
+ [KEYMAP_INDEX(0, 4)] = KEY_LEFT,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_0,
+ [KEYMAP_INDEX(1, 1)] = KEY_RIGHT,
+ [KEYMAP_INDEX(1, 2)] = KEY_1,
+ [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */
+ [KEYMAP_INDEX(1, 4)] = KEY_SEND,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */
+ [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */
+ [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */
+ [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */
+
+ [KEYMAP_INDEX(3, 0)] = KEY_UP,
+ [KEYMAP_INDEX(3, 1)] = KEY_CLEAR,
+ [KEYMAP_INDEX(3, 2)] = KEY_4,
+ [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */
+ [KEYMAP_INDEX(3, 4)] = KEY_2,
+
+ [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */
+ [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */
+ [KEYMAP_INDEX(4, 2)] = KEY_DOWN,
+ [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */
+ [KEYMAP_INDEX(4, 4)] = KEY_8,
+
+ [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */
+ [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */
+ [KEYMAP_INDEX(5, 3)] = KEY_3,
+ [KEYMAP_INDEX(5, 4)] = KEY_7,
+
+#if SCAN_FUNCTION_KEYS
+ [KEYMAP_INDEX(6, 0)] = KEY_F5,
+ [KEYMAP_INDEX(6, 1)] = KEY_F4,
+ [KEYMAP_INDEX(6, 2)] = KEY_F3,
+ [KEYMAP_INDEX(6, 3)] = KEY_F2,
+ [KEYMAP_INDEX(6, 4)] = KEY_F1
+#endif
+};
+
+static const unsigned short swordfish_keymap_ffa[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = {
+ /*[KEYMAP_INDEX(0, 0)] = ,*/
+ /*[KEYMAP_INDEX(0, 1)] = ,*/
+ [KEYMAP_INDEX(0, 2)] = KEY_1,
+ [KEYMAP_INDEX(0, 3)] = KEY_SEND,
+ [KEYMAP_INDEX(0, 4)] = KEY_LEFT,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_3,
+ [KEYMAP_INDEX(1, 1)] = KEY_RIGHT,
+ [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP,
+ /*[KEYMAP_INDEX(1, 3)] = ,*/
+ [KEYMAP_INDEX(1, 4)] = KEY_6,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */
+ [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */
+ [KEYMAP_INDEX(2, 2)] = KEY_0,
+ [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */
+ [KEYMAP_INDEX(2, 4)] = KEY_9,
+
+ [KEYMAP_INDEX(3, 0)] = KEY_UP,
+ [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */
+ [KEYMAP_INDEX(3, 2)] = KEY_4,
+ /*[KEYMAP_INDEX(3, 3)] = ,*/
+ [KEYMAP_INDEX(3, 4)] = KEY_2,
+
+ [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(4, 1)] = KEY_SOUND,
+ [KEYMAP_INDEX(4, 2)] = KEY_DOWN,
+ [KEYMAP_INDEX(4, 3)] = KEY_8,
+ [KEYMAP_INDEX(4, 4)] = KEY_5,
+
+ /*[KEYMAP_INDEX(5, 0)] = ,*/
+ [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */
+ [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */
+ [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */
+ [KEYMAP_INDEX(5, 4)] = KEY_7,
+};
+
+static struct gpio_event_matrix_info swordfish_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = swordfish_keymap,
+ .output_gpios = swordfish_row_gpios,
+ .input_gpios = swordfish_col_gpios,
+ .noutputs = ARRAY_SIZE(swordfish_row_gpios),
+ .ninputs = ARRAY_SIZE(swordfish_col_gpios),
+ .settle_time.tv.nsec = 0,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/
+};
+
+struct gpio_event_info *swordfish_keypad_info[] = {
+ &swordfish_matrix_info.info
+};
+
+static struct gpio_event_platform_data swordfish_keypad_data = {
+ .name = "swordfish_keypad",
+ .info = swordfish_keypad_info,
+ .info_count = ARRAY_SIZE(swordfish_keypad_info)
+};
+
+static struct platform_device swordfish_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &swordfish_keypad_data,
+ },
+};
+
+static int __init swordfish_init_keypad(void)
+{
+ if (!machine_is_swordfish())
+ return 0;
+ if (swordfish_ffa)
+ swordfish_matrix_info.keymap = swordfish_keymap_ffa;
+ return platform_device_register(&swordfish_keypad_device);
+}
+
+device_initcall(swordfish_init_keypad);
diff --git a/arch/arm/mach-msm/board-swordfish-mmc.c b/arch/arm/mach-msm/board-swordfish-mmc.c
new file mode 100644
index 0000000..bb0b173
--- /dev/null
+++ b/arch/arm/mach-msm/board-swordfish-mmc.c
@@ -0,0 +1,263 @@
+/* linux/arch/arm/mach-msm/board-swordfish-mmc.c
+ *
+ * Copyright (C) 2008 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/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/platform_device.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/mach/mmc.h>
+
+#include <mach/vreg.h>
+
+#include "proc_comm.h"
+#include "devices.h"
+
+#define FPGA_BASE 0x70000000
+#define FPGA_SDIO_STATUS 0x280
+
+static void __iomem *fpga_base;
+
+#define DEBUG_SWORDFISH_MMC 1
+
+extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags);
+
+static int config_gpio_table(unsigned *table, int len, int enable)
+{
+ int n;
+ int rc = 0;
+
+ for (n = 0; n < len; n++) {
+ unsigned dis = !enable;
+ unsigned id = table[n];
+
+ if (msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, &dis)) {
+ pr_err("%s: id=0x%08x dis=%d\n", __func__, table[n],
+ dis);
+ rc = -1;
+ }
+ }
+
+ return rc;
+}
+
+static unsigned sdc1_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
+};
+
+static unsigned sdc2_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
+ PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA),
+};
+
+static unsigned sdc3_gpio_table[] = {
+ PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
+ PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+};
+
+static unsigned sdc4_gpio_table[] = {
+ PCOM_GPIO_CFG(142, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
+ PCOM_GPIO_CFG(143, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(144, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(145, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(146, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+ PCOM_GPIO_CFG(147, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
+};
+
+struct sdc_info {
+ unsigned *table;
+ unsigned len;
+};
+
+static struct sdc_info sdcc_gpio_tables[] = {
+ [0] = {
+ .table = sdc1_gpio_table,
+ .len = ARRAY_SIZE(sdc1_gpio_table),
+ },
+ [1] = {
+ .table = sdc2_gpio_table,
+ .len = ARRAY_SIZE(sdc2_gpio_table),
+ },
+ [2] = {
+ .table = sdc3_gpio_table,
+ .len = ARRAY_SIZE(sdc3_gpio_table),
+ },
+ [3] = {
+ .table = sdc4_gpio_table,
+ .len = ARRAY_SIZE(sdc4_gpio_table),
+ },
+};
+
+static int swordfish_sdcc_setup_gpio(int dev_id, unsigned enable)
+{
+ struct sdc_info *info;
+
+ if (dev_id < 1 || dev_id > 4)
+ return -1;
+
+ info = &sdcc_gpio_tables[dev_id - 1];
+ return config_gpio_table(info->table, info->len, enable);
+}
+
+struct mmc_vdd_xlat {
+ int mask;
+ int level;
+};
+
+static struct mmc_vdd_xlat mmc_vdd_table[] = {
+ { MMC_VDD_165_195, 1800 },
+ { MMC_VDD_20_21, 2050 },
+ { MMC_VDD_21_22, 2150 },
+ { MMC_VDD_22_23, 2250 },
+ { MMC_VDD_23_24, 2350 },
+ { MMC_VDD_24_25, 2450 },
+ { MMC_VDD_25_26, 2550 },
+ { MMC_VDD_26_27, 2650 },
+ { MMC_VDD_27_28, 2750 },
+ { MMC_VDD_28_29, 2850 },
+ { MMC_VDD_29_30, 2950 },
+};
+
+static struct vreg *vreg_sdcc;
+static unsigned int vreg_sdcc_enabled;
+static unsigned int sdcc_vdd = 0xffffffff;
+
+static uint32_t sdcc_translate_vdd(struct device *dev, unsigned int vdd)
+{
+ int i;
+ int rc = 0;
+ struct platform_device *pdev;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ BUG_ON(!vreg_sdcc);
+
+ if (vdd == sdcc_vdd)
+ return 0;
+
+ sdcc_vdd = vdd;
+
+ /* enable/disable the signals to the slot */
+ swordfish_sdcc_setup_gpio(pdev->id, !!vdd);
+
+ /* power down */
+ if (vdd == 0) {
+#if DEBUG_SWORDFISH_MMC
+ pr_info("%s: disable sdcc power\n", __func__);
+#endif
+ vreg_disable(vreg_sdcc);
+ vreg_sdcc_enabled = 0;
+ return 0;
+ }
+
+ if (!vreg_sdcc_enabled) {
+ rc = vreg_enable(vreg_sdcc);
+ if (rc)
+ pr_err("%s: Error enabling vreg (%d)\n", __func__, rc);
+ vreg_sdcc_enabled = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
+ if (mmc_vdd_table[i].mask != (1 << vdd))
+ continue;
+#if DEBUG_SWORDFISH_MMC
+ pr_info("%s: Setting level to %u\n", __func__,
+ mmc_vdd_table[i].level);
+#endif
+ rc = vreg_set_level(vreg_sdcc, mmc_vdd_table[i].level);
+ if (rc)
+ pr_err("%s: Error setting vreg level (%d)\n", __func__, rc);
+ return 0;
+ }
+
+ pr_err("%s: Invalid VDD %d specified\n", __func__, vdd);
+ return 0;
+}
+
+static unsigned int swordfish_sdcc_slot_status (struct device *dev)
+{
+ struct platform_device *pdev;
+ uint32_t sdcc_stat;
+
+ pdev = container_of(dev, struct platform_device, dev);
+
+ sdcc_stat = readl(fpga_base + FPGA_SDIO_STATUS);
+
+ /* bit 0 - sdcc1 crd_det
+ * bit 1 - sdcc1 wr_prt
+ * bit 2 - sdcc2 crd_det
+ * bit 3 - sdcc2 wr_prt
+ * etc...
+ */
+
+ /* crd_det is active low */
+ return !(sdcc_stat & (1 << ((pdev->id - 1) << 1)));
+}
+
+#define SWORDFISH_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \
+ | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \
+ | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \
+ | MMC_VDD_28_29 | MMC_VDD_29_30)
+
+static struct mmc_platform_data swordfish_sdcc_data = {
+ .ocr_mask = SWORDFISH_MMC_VDD/*MMC_VDD_27_28 | MMC_VDD_28_29*/,
+ .status = swordfish_sdcc_slot_status,
+ .translate_vdd = sdcc_translate_vdd,
+};
+
+int __init swordfish_init_mmc(void)
+{
+ vreg_sdcc_enabled = 0;
+ vreg_sdcc = vreg_get(NULL, "gp5");
+ if (IS_ERR(vreg_sdcc)) {
+ pr_err("%s: vreg get failed (%ld)\n",
+ __func__, PTR_ERR(vreg_sdcc));
+ return PTR_ERR(vreg_sdcc);
+ }
+
+ fpga_base = ioremap(FPGA_BASE, SZ_4K);
+ if (!fpga_base) {
+ pr_err("%s: Can't ioremap FPGA base address (0x%08x)\n",
+ __func__, FPGA_BASE);
+ vreg_put(vreg_sdcc);
+ return -EIO;
+ }
+
+ msm_add_sdcc(1, &swordfish_sdcc_data, 0, 0);
+ msm_add_sdcc(4, &swordfish_sdcc_data, 0, 0);
+
+ return 0;
+}
+
diff --git a/arch/arm/mach-msm/board-swordfish-panel.c b/arch/arm/mach-msm/board-swordfish-panel.c
new file mode 100644
index 0000000..cf5f3f6
--- /dev/null
+++ b/arch/arm/mach-msm/board-swordfish-panel.c
@@ -0,0 +1,116 @@
+/* linux/arch/arm/mach-msm/board-swordfish-panel.c
+ *
+ * Copyright (c) 2009 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.
+ *
+ * Author: Dima Zavin <dima@android.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+
+#include "board-swordfish.h"
+#include "devices.h"
+
+#define CLK_NS_TO_RATE(ns) (1000000000UL / (ns))
+
+int swordfish_panel_blank(struct msm_lcdc_panel_ops *ops)
+{
+ /* TODO: Turn backlight off? */
+ return 0;
+}
+
+int swordfish_panel_unblank(struct msm_lcdc_panel_ops *ops)
+{
+ /* TODO: Turn backlight on? */
+ return 0;
+}
+
+int swordfish_panel_init(struct msm_lcdc_panel_ops *ops)
+{
+ return 0;
+}
+
+static struct resource resources_msm_fb[] = {
+ {
+ .start = MSM_FB_BASE,
+ .end = MSM_FB_BASE + MSM_FB_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct msm_lcdc_timing swordfish_lcdc_timing = {
+ .clk_rate = CLK_NS_TO_RATE(26),
+ .hsync_pulse_width = 60,
+ .hsync_back_porch = 81,
+ .hsync_front_porch = 81,
+ .hsync_skew = 0,
+ .vsync_pulse_width = 2,
+ .vsync_back_porch = 20,
+ .vsync_front_porch = 27,
+ .vsync_act_low = 0,
+ .hsync_act_low = 0,
+ .den_act_low = 0,
+};
+
+static struct msm_fb_data swordfish_lcdc_fb_data = {
+ .xres = 800,
+ .yres = 480,
+ .width = 94,
+ .height = 57,
+ .output_format = 0,
+};
+
+static struct msm_lcdc_panel_ops swordfish_lcdc_panel_ops = {
+ .init = swordfish_panel_init,
+ .blank = swordfish_panel_blank,
+ .unblank = swordfish_panel_unblank,
+};
+
+static struct msm_lcdc_platform_data swordfish_lcdc_platform_data = {
+ .panel_ops = &swordfish_lcdc_panel_ops,
+ .timing = &swordfish_lcdc_timing,
+ .fb_id = 0,
+ .fb_data = &swordfish_lcdc_fb_data,
+ .fb_resource = &resources_msm_fb[0],
+};
+
+static struct platform_device swordfish_lcdc_device = {
+ .name = "msm_mdp_lcdc",
+ .id = -1,
+ .dev = {
+ .platform_data = &swordfish_lcdc_platform_data,
+ },
+};
+
+int __init swordfish_init_panel(void)
+{
+ int rc;
+ if (!machine_is_swordfish())
+ return 0;
+
+ if ((rc = platform_device_register(&msm_device_mdp)) != 0)
+ return rc;
+
+ if ((rc = platform_device_register(&swordfish_lcdc_device)) != 0)
+ return rc;
+
+ return 0;
+}
+
+device_initcall(swordfish_init_panel);
diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c
new file mode 100644
index 0000000..70fac49
--- /dev/null
+++ b/arch/arm/mach-msm/board-swordfish.c
@@ -0,0 +1,365 @@
+/* linux/arch/arm/mach-msm/board-swordfish.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_kgsl.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/setup.h>
+
+#include <mach/board.h>
+#include <mach/irqs.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_hsusb.h>
+#include <mach/msm_ts.h>
+#include <linux/usb/android_composite.h>
+
+#include "board-swordfish.h"
+#include "devices.h"
+#include "proc_comm.h"
+
+extern int swordfish_init_mmc(void);
+
+static struct resource smc91x_resources[] = {
+ [0] = {
+ .start = 0x70000300,
+ .end = 0x70000400,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MSM_GPIO_TO_INT(156),
+ .end = MSM_GPIO_TO_INT(156),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc91x_device = {
+ .name = "smc91x",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(smc91x_resources),
+ .resource = smc91x_resources,
+};
+
+static int swordfish_phy_init_seq[] = {
+ 0x0C, 0x31,
+ 0x1D, 0x0D,
+ 0x1D, 0x10,
+ -1
+};
+
+static void swordfish_usb_phy_reset(void)
+{
+ u32 id;
+ int ret;
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+
+ msleep(1);
+
+ id = PCOM_CLKRGM_APPS_RESET_USB_PHY;
+ ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL);
+ if (ret) {
+ pr_err("%s: Cannot assert (%d)\n", __func__, ret);
+ return;
+ }
+}
+
+static void swordfish_usb_hw_reset(bool enable)
+{
+ u32 id;
+ int ret;
+ u32 func;
+
+ id = PCOM_CLKRGM_APPS_RESET_USBH;
+ if (enable)
+ func = PCOM_CLK_REGIME_SEC_RESET_ASSERT;
+ else
+ func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT;
+ ret = msm_proc_comm(func, &id, NULL);
+ if (ret)
+ pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable,
+ ret);
+}
+
+
+static struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_init_seq = swordfish_phy_init_seq,
+ .phy_reset = swordfish_usb_phy_reset,
+ .hw_reset = swordfish_usb_hw_reset,
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "Qualcomm",
+ .product = "Swordfish",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+static struct resource msm_kgsl_resources[] = {
+ {
+ .name = "kgsl_reg_memory",
+ .start = MSM_GPU_REG_PHYS,
+ .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "kgsl_phys_memory",
+ .start = MSM_GPU_MEM_BASE,
+ .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_GRAPHICS,
+ .end = INT_GRAPHICS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device msm_kgsl_device = {
+ .name = "kgsl",
+ .id = -1,
+ .resource = msm_kgsl_resources,
+ .num_resources = ARRAY_SIZE(msm_kgsl_resources),
+};
+
+static struct android_pmem_platform_data mdp_pmem_pdata = {
+ .name = "pmem",
+ .start = MSM_PMEM_MDP_BASE,
+ .size = MSM_PMEM_MDP_SIZE,
+ .no_allocator = 0,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data android_pmem_gpu0_pdata = {
+ .name = "pmem_gpu0",
+ .start = MSM_PMEM_GPU0_BASE,
+ .size = MSM_PMEM_GPU0_SIZE,
+ .no_allocator = 0,
+ .cached = 0,
+};
+
+static struct android_pmem_platform_data android_pmem_gpu1_pdata = {
+ .name = "pmem_gpu1",
+ .start = MSM_PMEM_GPU1_BASE,
+ .size = MSM_PMEM_GPU1_SIZE,
+ .no_allocator = 0,
+ .cached = 0,
+};
+
+static struct android_pmem_platform_data android_pmem_adsp_pdata = {
+ .name = "pmem_adsp",
+ .start = MSM_PMEM_ADSP_BASE,
+ .size = MSM_PMEM_ADSP_SIZE,
+ .no_allocator = 0,
+ .cached = 0,
+};
+
+static struct platform_device android_pmem_mdp_device = {
+ .name = "android_pmem",
+ .id = 0,
+ .dev = {
+ .platform_data = &mdp_pmem_pdata
+ },
+};
+
+static struct platform_device android_pmem_adsp_device = {
+ .name = "android_pmem",
+ .id = 1,
+ .dev = {
+ .platform_data = &android_pmem_adsp_pdata,
+ },
+};
+
+static struct platform_device android_pmem_gpu0_device = {
+ .name = "android_pmem",
+ .id = 2,
+ .dev = {
+ .platform_data = &android_pmem_gpu0_pdata,
+ },
+};
+
+static struct platform_device android_pmem_gpu1_device = {
+ .name = "android_pmem",
+ .id = 3,
+ .dev = {
+ .platform_data = &android_pmem_gpu1_pdata,
+ },
+};
+
+static char *usb_functions[] = { "usb_mass_storage" };
+static char *usb_functions_adb[] = { "usb_mass_storage", "adb" };
+
+static struct android_usb_product usb_products[] = {
+ {
+ .product_id = 0x0c01,
+ .num_functions = ARRAY_SIZE(usb_functions),
+ .functions = usb_functions,
+ },
+ {
+ .product_id = 0x0c02,
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+ },
+};
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x18d1,
+ .product_id = 0x0d01,
+ .version = 0x0100,
+ .serial_number = "42",
+ .product_name = "Swordfishdroid",
+ .manufacturer_name = "Qualcomm",
+ .num_products = ARRAY_SIZE(usb_products),
+ .products = usb_products,
+ .num_functions = ARRAY_SIZE(usb_functions_adb),
+ .functions = usb_functions_adb,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+static struct platform_device fish_battery_device = {
+ .name = "fish_battery",
+};
+
+static struct msm_ts_platform_data swordfish_ts_pdata = {
+ .min_x = 296,
+ .max_x = 3800,
+ .min_y = 296,
+ .max_y = 3800,
+ .min_press = 0,
+ .max_press = 256,
+ .inv_x = 4096,
+ .inv_y = 4096,
+};
+
+static struct platform_device *devices[] __initdata = {
+#if !defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ &msm_device_uart3,
+#endif
+ &msm_device_smd,
+ &msm_device_nand,
+ &msm_device_hsusb,
+ &usb_mass_storage_device,
+ &android_usb_device,
+ &fish_battery_device,
+ &smc91x_device,
+ &msm_device_touchscreen,
+ &android_pmem_mdp_device,
+ &android_pmem_adsp_device,
+ &android_pmem_gpu0_device,
+ &android_pmem_gpu1_device,
+ &msm_kgsl_device,
+};
+
+extern struct sys_timer msm_timer;
+
+static struct msm_acpu_clock_platform_data swordfish_clock_data = {
+ .acpu_switch_time_us = 20,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 128000000,
+ .wait_for_irq_khz = 128000000,
+};
+
+void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq);
+
+static void __init swordfish_init(void)
+{
+ int rc;
+
+ msm_acpu_clock_init(&swordfish_clock_data);
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3,
+ &msm_device_uart3.dev, 1);
+#endif
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ msm_device_touchscreen.dev.platform_data = &swordfish_ts_pdata;
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+ msm_hsusb_set_vbus_state(1);
+ rc = swordfish_init_mmc();
+ if (rc)
+ pr_crit("%s: MMC init failure (%d)\n", __func__, rc);
+}
+
+static void __init swordfish_fixup(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mi->nr_banks = 1;
+ mi->bank[0].start = PHYS_OFFSET;
+ mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
+ mi->bank[0].size = (101*1024*1024);
+}
+
+static void __init swordfish_map_io(void)
+{
+ msm_map_qsd8x50_io();
+ msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50);
+}
+
+MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)")
+#ifdef CONFIG_MSM_DEBUG_UART
+ .phys_io = MSM_DEBUG_UART_PHYS,
+ .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
+#endif
+ .boot_params = 0x20000100,
+ .fixup = swordfish_fixup,
+ .map_io = swordfish_map_io,
+ .init_irq = msm_init_irq,
+ .init_machine = swordfish_init,
+ .timer = &msm_timer,
+MACHINE_END
+
+MACHINE_START(QSD8X50_FFA, "qsd8x50 FFA Board (QCT FFA8250)")
+#ifdef CONFIG_MSM_DEBUG_UART
+ .phys_io = MSM_DEBUG_UART_PHYS,
+ .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
+#endif
+ .boot_params = 0x20000100,
+ .fixup = swordfish_fixup,
+ .map_io = swordfish_map_io,
+ .init_irq = msm_init_irq,
+ .init_machine = swordfish_init,
+ .timer = &msm_timer,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-swordfish.h b/arch/arm/mach-msm/board-swordfish.h
new file mode 100644
index 0000000..b9ea54f
--- /dev/null
+++ b/arch/arm/mach-msm/board-swordfish.h
@@ -0,0 +1,48 @@
+/* arch/arm/mach-msm/board-swordfish.h
+ *
+ * Copyright (C) 2009 Google Inc.
+ * Author: Dima Zavin <dima@android.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 __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H
+#define __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H
+
+#include <mach/board.h>
+
+#define MSM_SMI_BASE 0x02B00000
+#define MSM_SMI_SIZE 0x01500000
+
+#define MSM_PMEM_MDP_BASE 0x03000000
+#define MSM_PMEM_MDP_SIZE 0x01000000
+
+#define MSM_EBI1_BASE 0x20000000
+#define MSM_EBI1_SIZE 0x0E000000
+
+#define MSM_PMEM_ADSP_BASE 0x2A300000
+#define MSM_PMEM_ADSP_SIZE 0x02000000
+
+#define MSM_PMEM_GPU1_BASE 0x2C300000
+#define MSM_PMEM_GPU1_SIZE 0x01400000
+
+#define MSM_PMEM_GPU0_BASE 0x2D700000
+#define MSM_PMEM_GPU0_SIZE 0x00400000
+
+#define MSM_GPU_MEM_BASE 0x2DB00000
+#define MSM_GPU_MEM_SIZE 0x00200000
+
+#define MSM_RAM_CONSOLE_BASE 0x2DD00000
+#define MSM_RAM_CONSOLE_SIZE 0x00040000
+
+#define MSM_FB_BASE 0x2DE00000
+#define MSM_FB_SIZE 0x00200000
+
+#endif /* __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H */
diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c
new file mode 100644
index 0000000..527379e
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-gpio.c
@@ -0,0 +1,305 @@
+/* arch/arm/mach-msm/board-trout-gpio.c
+ *
+ * Copyright (C) 2008 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/errno.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/sysdev.h>
+
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/mach-types.h>
+
+#include <mach/htc_pwrsink.h>
+
+#include "board-trout.h"
+#include "gpio_chip.h"
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "board_trout."
+
+static uint cpld_usb_h2w_sw;
+module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0);
+
+static uint8_t trout_cpld_shadow[4] = {
+#if defined(CONFIG_MSM_DEBUG_UART1)
+ /* H2W pins <-> UART1 */
+ [0] = 0x40, // for serial debug, low current
+#else
+ /* H2W pins <-> UART3, Bluetooth <-> UART1 */
+ [0] = 0x80, // for serial debug, low current
+#endif
+ [1] = 0x04, // I2C_PULL
+ [3] = 0x04, // mmdi 32k en
+};
+static uint8_t trout_int_mask[2] = {
+ [0] = 0xff, /* mask all interrupts */
+ [1] = 0xff,
+};
+static uint8_t trout_sleep_int_mask[] = {
+ [0] = 0xff,
+ [1] = 0xff,
+};
+static int trout_suspended;
+
+static int trout_gpio_read(struct gpio_chip *chip, unsigned n)
+{
+ uint8_t b;
+ int reg;
+ if (n >= TROUT_GPIO_VIRTUAL_BASE)
+ n += TROUT_GPIO_VIRTUAL_TO_REAL_OFFSET;
+ b = 1U << (n & 7);
+ reg = (n & 0x78) >> 2; // assumes base is 128
+ return !!(readb(TROUT_CPLD_BASE + reg) & b);
+}
+
+static void update_pwrsink(unsigned gpio, unsigned on)
+{
+ switch(gpio) {
+ case TROUT_GPIO_UI_LED_EN:
+ htc_pwrsink_set(PWRSINK_LED_BUTTON, on ? 100 : 0);
+ break;
+ case TROUT_GPIO_QTKEY_LED_EN:
+ htc_pwrsink_set(PWRSINK_LED_KEYBOARD, on ? 100 : 0);
+ break;
+ }
+}
+
+static uint8_t trout_gpio_write_shadow(unsigned n, unsigned on)
+{
+ uint8_t b = 1U << (n & 7);
+ int reg = (n & 0x78) >> 2; // assumes base is 128
+
+ if(on)
+ return trout_cpld_shadow[reg >> 1] |= b;
+ else
+ return trout_cpld_shadow[reg >> 1] &= ~b;
+}
+
+static int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
+{
+ int reg = (n & 0x78) >> 2; // assumes base is 128
+ unsigned long flags;
+ uint8_t reg_val;
+
+ if ((reg >> 1) >= ARRAY_SIZE(trout_cpld_shadow)) {
+ printk(KERN_ERR "trout_gpio_write called on input %d\n", n);
+ return -ENOTSUPP;
+ }
+
+ local_irq_save(flags);
+ update_pwrsink(n, on);
+ reg_val = trout_gpio_write_shadow(n, on);
+ writeb(reg_val, TROUT_CPLD_BASE + reg);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int trout_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags)
+{
+ if(flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH))
+ trout_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH);
+ return 0;
+}
+
+static int trout_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp)
+{
+ if ((gpio < TROUT_GPIO_BANK0_FIRST_INT_SOURCE ||
+ gpio > TROUT_GPIO_BANK0_LAST_INT_SOURCE) &&
+ (gpio < TROUT_GPIO_BANK1_FIRST_INT_SOURCE ||
+ gpio > TROUT_GPIO_BANK1_LAST_INT_SOURCE))
+ return -ENOENT;
+ *irqp = TROUT_GPIO_TO_INT(gpio);
+ if(irqnumflagsp)
+ *irqnumflagsp = 0;
+ return 0;
+}
+
+static void trout_gpio_irq_ack(unsigned int irq)
+{
+ int bank = TROUT_INT_TO_BANK(irq);
+ uint8_t mask = TROUT_INT_TO_MASK(irq);
+ int reg = TROUT_BANK_TO_STAT_REG(bank);
+ /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", irq);*/
+ writeb(mask, TROUT_CPLD_BASE + reg);
+}
+
+static void trout_gpio_irq_mask(unsigned int irq)
+{
+ unsigned long flags;
+ uint8_t reg_val;
+ int bank = TROUT_INT_TO_BANK(irq);
+ uint8_t mask = TROUT_INT_TO_MASK(irq);
+ int reg = TROUT_BANK_TO_MASK_REG(bank);
+
+ local_irq_save(flags);
+ reg_val = trout_int_mask[bank] |= mask;
+ /*printk(KERN_INFO "trout_gpio_irq_mask irq %d => %d:%02x\n",
+ irq, bank, reg_val);*/
+ if (!trout_suspended)
+ writeb(reg_val, TROUT_CPLD_BASE + reg);
+ local_irq_restore(flags);
+}
+
+static void trout_gpio_irq_unmask(unsigned int irq)
+{
+ unsigned long flags;
+ uint8_t reg_val;
+ int bank = TROUT_INT_TO_BANK(irq);
+ uint8_t mask = TROUT_INT_TO_MASK(irq);
+ int reg = TROUT_BANK_TO_MASK_REG(bank);
+
+ local_irq_save(flags);
+ reg_val = trout_int_mask[bank] &= ~mask;
+ /*printk(KERN_INFO "trout_gpio_irq_unmask irq %d => %d:%02x\n",
+ irq, bank, reg_val);*/
+ if (!trout_suspended)
+ writeb(reg_val, TROUT_CPLD_BASE + reg);
+ local_irq_restore(flags);
+}
+
+int trout_gpio_irq_set_wake(unsigned int irq, unsigned int on)
+{
+ unsigned long flags;
+ int bank = TROUT_INT_TO_BANK(irq);
+ uint8_t mask = TROUT_INT_TO_MASK(irq);
+
+ local_irq_save(flags);
+ if(on)
+ trout_sleep_int_mask[bank] &= ~mask;
+ else
+ trout_sleep_int_mask[bank] |= mask;
+ local_irq_restore(flags);
+ return 0;
+}
+
+static void trout_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int j, m;
+ unsigned v;
+ int bank;
+ int stat_reg;
+ int int_base = TROUT_INT_START;
+ uint8_t int_mask;
+
+ for (bank = 0; bank < 2; bank++) {
+ stat_reg = TROUT_BANK_TO_STAT_REG(bank);
+ v = readb(TROUT_CPLD_BASE + stat_reg);
+ int_mask = trout_int_mask[bank];
+ if (v & int_mask) {
+ writeb(v & int_mask, TROUT_CPLD_BASE + stat_reg);
+ printk(KERN_ERR "trout_gpio_irq_handler: got masked "
+ "interrupt: %d:%02x\n", bank, v & int_mask);
+ }
+ v &= ~int_mask;
+ while (v) {
+ m = v & -v;
+ j = fls(m) - 1;
+ /*printk(KERN_INFO "msm_gpio_irq_handler %d:%02x %02x b"
+ "it %d irq %d\n", bank, v, m, j, int_base + j);*/
+ v &= ~m;
+ generic_handle_irq(int_base + j);
+ }
+ int_base += TROUT_INT_BANK0_COUNT;
+ }
+ desc->chip->ack(irq);
+}
+
+static int trout_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+ trout_suspended = 1;
+ writeb(trout_sleep_int_mask[0],
+ TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG);
+ writeb(trout_sleep_int_mask[1],
+ TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG);
+ writeb(trout_sleep_int_mask[0],
+ TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT0_REG);
+ writeb(trout_sleep_int_mask[1],
+ TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT1_REG);
+ return 0;
+}
+
+int trout_sysdev_resume(struct sys_device *dev)
+{
+ writeb(trout_int_mask[0], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG);
+ writeb(trout_int_mask[1], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG);
+ trout_suspended = 0;
+ return 0;
+}
+
+static struct irq_chip trout_gpio_irq_chip = {
+ .name = "troutgpio",
+ .ack = trout_gpio_irq_ack,
+ .mask = trout_gpio_irq_mask,
+ .unmask = trout_gpio_irq_unmask,
+ .set_wake = trout_gpio_irq_set_wake,
+ //.set_type = trout_gpio_irq_set_type,
+};
+
+static struct gpio_chip trout_gpio_chip = {
+ .start = TROUT_GPIO_START,
+ .end = TROUT_GPIO_END,
+ .configure = trout_gpio_configure,
+ .get_irq_num = trout_gpio_get_irq_num,
+ .read = trout_gpio_read,
+ .write = trout_gpio_write,
+// .read_detect_status = trout_gpio_read_detect_status,
+// .clear_detect_status = trout_gpio_clear_detect_status
+};
+
+struct sysdev_class trout_sysdev_class = {
+ .name = "troutgpio_irq",
+ .suspend = trout_sysdev_suspend,
+ .resume = trout_sysdev_resume,
+};
+
+static struct sys_device trout_irq_device = {
+ .cls = &trout_sysdev_class,
+};
+
+static int __init trout_init_gpio(void)
+{
+ int i;
+
+ if (!machine_is_trout())
+ return 0;
+
+ /* adjust GPIOs based on bootloader request */
+ pr_info("trout_init_gpio: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw);
+ trout_gpio_write_shadow(TROUT_GPIO_USB_H2W_SW, cpld_usb_h2w_sw);
+
+ for(i = 0; i < ARRAY_SIZE(trout_cpld_shadow); i++)
+ writeb(trout_cpld_shadow[i], TROUT_CPLD_BASE + i * 2);
+
+ for(i = TROUT_INT_START; i <= TROUT_INT_END; i++) {
+ set_irq_chip(i, &trout_gpio_irq_chip);
+ set_irq_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ register_gpio_chip(&trout_gpio_chip);
+
+ set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH);
+ set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler);
+ set_irq_wake(MSM_GPIO_TO_INT(17), 1);
+
+ if(sysdev_class_register(&trout_sysdev_class) == 0)
+ sysdev_register(&trout_irq_device);
+
+ return 0;
+}
+
+postcore_initcall(trout_init_gpio);
diff --git a/arch/arm/mach-msm/board-trout-keypad.c b/arch/arm/mach-msm/board-trout-keypad.c
new file mode 100644
index 0000000..0299d06
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-keypad.c
@@ -0,0 +1,345 @@
+/* arch/arm/mach-msm/board-trout-keypad.c
+ *
+ * Copyright (C) 2008 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/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio_event.h>
+#include <asm/mach-types.h>
+
+#include "board-trout.h"
+
+static char *keycaps = "--qwerty";
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "board_trout."
+module_param_named(keycaps, keycaps, charp, 0);
+
+
+static unsigned int trout_col_gpios[] = { 35, 34, 33, 32, 31, 23, 30, 78 };
+static unsigned int trout_row_gpios[] = { 42, 41, 40, 39, 38, 37, 36 };
+
+#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(trout_row_gpios) + (row))
+
+static const unsigned short trout_keymap[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_BACK,
+ [KEYMAP_INDEX(0, 1)] = KEY_HOME,
+// [KEYMAP_INDEX(0, 2)] = KEY_,
+ [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE,
+ [KEYMAP_INDEX(0, 4)] = KEY_ENTER,
+ [KEYMAP_INDEX(0, 5)] = KEY_RIGHTALT,
+ [KEYMAP_INDEX(0, 6)] = KEY_P,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_MENU,
+// [KEYMAP_INDEX(1, 0)] = 229, // SOFT1
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+ [KEYMAP_INDEX(1, 2)] = KEY_END,
+ [KEYMAP_INDEX(1, 3)] = KEY_LEFTALT,
+ [KEYMAP_INDEX(1, 4)] = KEY_A,
+ [KEYMAP_INDEX(1, 5)] = KEY_LEFTSHIFT,
+ [KEYMAP_INDEX(1, 6)] = KEY_Q,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_U,
+ [KEYMAP_INDEX(2, 1)] = KEY_7,
+ [KEYMAP_INDEX(2, 2)] = KEY_K,
+ [KEYMAP_INDEX(2, 3)] = KEY_J,
+ [KEYMAP_INDEX(2, 4)] = KEY_M,
+ [KEYMAP_INDEX(2, 5)] = KEY_SLASH,
+ [KEYMAP_INDEX(2, 6)] = KEY_8,
+
+ [KEYMAP_INDEX(3, 0)] = KEY_5,
+ [KEYMAP_INDEX(3, 1)] = KEY_6,
+ [KEYMAP_INDEX(3, 2)] = KEY_B,
+ [KEYMAP_INDEX(3, 3)] = KEY_H,
+ [KEYMAP_INDEX(3, 4)] = KEY_N,
+ [KEYMAP_INDEX(3, 5)] = KEY_SPACE,
+ [KEYMAP_INDEX(3, 6)] = KEY_Y,
+
+ [KEYMAP_INDEX(4, 0)] = KEY_4,
+ [KEYMAP_INDEX(4, 1)] = KEY_R,
+ [KEYMAP_INDEX(4, 2)] = KEY_V,
+ [KEYMAP_INDEX(4, 3)] = KEY_G,
+ [KEYMAP_INDEX(4, 4)] = KEY_C,
+ //[KEYMAP_INDEX(4, 5)] = KEY_,
+ [KEYMAP_INDEX(4, 6)] = KEY_T,
+
+ [KEYMAP_INDEX(5, 0)] = KEY_2,
+ [KEYMAP_INDEX(5, 1)] = KEY_W,
+ [KEYMAP_INDEX(5, 2)] = KEY_COMPOSE,
+ [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(5, 4)] = KEY_S,
+ [KEYMAP_INDEX(5, 5)] = KEY_Z,
+ [KEYMAP_INDEX(5, 6)] = KEY_1,
+
+ [KEYMAP_INDEX(6, 0)] = KEY_I,
+ [KEYMAP_INDEX(6, 1)] = KEY_0,
+ [KEYMAP_INDEX(6, 2)] = KEY_O,
+ [KEYMAP_INDEX(6, 3)] = KEY_L,
+ [KEYMAP_INDEX(6, 4)] = KEY_DOT,
+ [KEYMAP_INDEX(6, 5)] = KEY_COMMA,
+ [KEYMAP_INDEX(6, 6)] = KEY_9,
+
+ [KEYMAP_INDEX(7, 0)] = KEY_3,
+ [KEYMAP_INDEX(7, 1)] = KEY_E,
+ [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @
+ [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(7, 4)] = KEY_X,
+ [KEYMAP_INDEX(7, 5)] = KEY_F,
+ [KEYMAP_INDEX(7, 6)] = KEY_D
+};
+
+static unsigned int trout_col_gpios_evt2[] = { 35, 34, 33, 32, 31, 23, 30, 109 };
+static unsigned int trout_row_gpios_evt2[] = { 42, 41, 40, 39, 38, 37, 36 };
+
+static const unsigned short trout_keymap_evt2_1[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_BACK,
+ [KEYMAP_INDEX(0, 1)] = KEY_HOME,
+// [KEYMAP_INDEX(0, 2)] = KEY_,
+ [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE,
+ [KEYMAP_INDEX(0, 4)] = KEY_ENTER,
+ [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT,
+ [KEYMAP_INDEX(0, 6)] = KEY_P,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_MENU,
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+// [KEYMAP_INDEX(1, 2)] = KEY_,
+ [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT,
+ [KEYMAP_INDEX(1, 4)] = KEY_A,
+ [KEYMAP_INDEX(1, 5)] = KEY_COMPOSE,
+ [KEYMAP_INDEX(1, 6)] = KEY_Q,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_U,
+ [KEYMAP_INDEX(2, 1)] = KEY_7,
+ [KEYMAP_INDEX(2, 2)] = KEY_K,
+ [KEYMAP_INDEX(2, 3)] = KEY_J,
+ [KEYMAP_INDEX(2, 4)] = KEY_M,
+ [KEYMAP_INDEX(2, 5)] = KEY_SLASH,
+ [KEYMAP_INDEX(2, 6)] = KEY_8,
+
+ [KEYMAP_INDEX(3, 0)] = KEY_5,
+ [KEYMAP_INDEX(3, 1)] = KEY_6,
+ [KEYMAP_INDEX(3, 2)] = KEY_B,
+ [KEYMAP_INDEX(3, 3)] = KEY_H,
+ [KEYMAP_INDEX(3, 4)] = KEY_N,
+ [KEYMAP_INDEX(3, 5)] = KEY_SPACE,
+ [KEYMAP_INDEX(3, 6)] = KEY_Y,
+
+ [KEYMAP_INDEX(4, 0)] = KEY_4,
+ [KEYMAP_INDEX(4, 1)] = KEY_R,
+ [KEYMAP_INDEX(4, 2)] = KEY_V,
+ [KEYMAP_INDEX(4, 3)] = KEY_G,
+ [KEYMAP_INDEX(4, 4)] = KEY_C,
+// [KEYMAP_INDEX(4, 5)] = KEY_,
+ [KEYMAP_INDEX(4, 6)] = KEY_T,
+
+ [KEYMAP_INDEX(5, 0)] = KEY_2,
+ [KEYMAP_INDEX(5, 1)] = KEY_W,
+ [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT,
+ [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(5, 4)] = KEY_S,
+ [KEYMAP_INDEX(5, 5)] = KEY_Z,
+ [KEYMAP_INDEX(5, 6)] = KEY_1,
+
+ [KEYMAP_INDEX(6, 0)] = KEY_I,
+ [KEYMAP_INDEX(6, 1)] = KEY_0,
+ [KEYMAP_INDEX(6, 2)] = KEY_O,
+ [KEYMAP_INDEX(6, 3)] = KEY_L,
+ [KEYMAP_INDEX(6, 4)] = KEY_COMMA,
+ [KEYMAP_INDEX(6, 5)] = KEY_DOT,
+ [KEYMAP_INDEX(6, 6)] = KEY_9,
+
+ [KEYMAP_INDEX(7, 0)] = KEY_3,
+ [KEYMAP_INDEX(7, 1)] = KEY_E,
+ [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @
+ [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(7, 4)] = KEY_X,
+ [KEYMAP_INDEX(7, 5)] = KEY_F,
+ [KEYMAP_INDEX(7, 6)] = KEY_D
+};
+
+static const unsigned short trout_keymap_evt2_2[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = {
+ [KEYMAP_INDEX(0, 0)] = KEY_BACK,
+ [KEYMAP_INDEX(0, 1)] = KEY_HOME,
+// [KEYMAP_INDEX(0, 2)] = KEY_,
+ [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE,
+ [KEYMAP_INDEX(0, 4)] = KEY_ENTER,
+ [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT,
+ [KEYMAP_INDEX(0, 6)] = KEY_P,
+
+ [KEYMAP_INDEX(1, 0)] = KEY_MENU, /* external menu key */
+ [KEYMAP_INDEX(1, 1)] = KEY_SEND,
+// [KEYMAP_INDEX(1, 2)] = KEY_,
+ [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT,
+ [KEYMAP_INDEX(1, 4)] = KEY_A,
+ [KEYMAP_INDEX(1, 5)] = KEY_F1, /* qwerty menu key */
+ [KEYMAP_INDEX(1, 6)] = KEY_Q,
+
+ [KEYMAP_INDEX(2, 0)] = KEY_U,
+ [KEYMAP_INDEX(2, 1)] = KEY_7,
+ [KEYMAP_INDEX(2, 2)] = KEY_K,
+ [KEYMAP_INDEX(2, 3)] = KEY_J,
+ [KEYMAP_INDEX(2, 4)] = KEY_M,
+ [KEYMAP_INDEX(2, 5)] = KEY_DOT,
+ [KEYMAP_INDEX(2, 6)] = KEY_8,
+
+ [KEYMAP_INDEX(3, 0)] = KEY_5,
+ [KEYMAP_INDEX(3, 1)] = KEY_6,
+ [KEYMAP_INDEX(3, 2)] = KEY_B,
+ [KEYMAP_INDEX(3, 3)] = KEY_H,
+ [KEYMAP_INDEX(3, 4)] = KEY_N,
+ [KEYMAP_INDEX(3, 5)] = KEY_SPACE,
+ [KEYMAP_INDEX(3, 6)] = KEY_Y,
+
+ [KEYMAP_INDEX(4, 0)] = KEY_4,
+ [KEYMAP_INDEX(4, 1)] = KEY_R,
+ [KEYMAP_INDEX(4, 2)] = KEY_V,
+ [KEYMAP_INDEX(4, 3)] = KEY_G,
+ [KEYMAP_INDEX(4, 4)] = KEY_C,
+ [KEYMAP_INDEX(4, 5)] = KEY_EMAIL, // @
+ [KEYMAP_INDEX(4, 6)] = KEY_T,
+
+ [KEYMAP_INDEX(5, 0)] = KEY_2,
+ [KEYMAP_INDEX(5, 1)] = KEY_W,
+ [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT,
+ [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP,
+ [KEYMAP_INDEX(5, 4)] = KEY_S,
+ [KEYMAP_INDEX(5, 5)] = KEY_Z,
+ [KEYMAP_INDEX(5, 6)] = KEY_1,
+
+ [KEYMAP_INDEX(6, 0)] = KEY_I,
+ [KEYMAP_INDEX(6, 1)] = KEY_0,
+ [KEYMAP_INDEX(6, 2)] = KEY_O,
+ [KEYMAP_INDEX(6, 3)] = KEY_L,
+ [KEYMAP_INDEX(6, 4)] = KEY_COMMA,
+ [KEYMAP_INDEX(6, 5)] = KEY_RIGHTALT,
+ [KEYMAP_INDEX(6, 6)] = KEY_9,
+
+ [KEYMAP_INDEX(7, 0)] = KEY_3,
+ [KEYMAP_INDEX(7, 1)] = KEY_E,
+ [KEYMAP_INDEX(7, 2)] = KEY_COMPOSE,
+ [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN,
+ [KEYMAP_INDEX(7, 4)] = KEY_X,
+ [KEYMAP_INDEX(7, 5)] = KEY_F,
+ [KEYMAP_INDEX(7, 6)] = KEY_D
+};
+
+static struct gpio_event_matrix_info trout_keypad_matrix_info = {
+ .info.func = gpio_event_matrix_func,
+ .keymap = trout_keymap,
+ .output_gpios = trout_col_gpios,
+ .input_gpios = trout_row_gpios,
+ .noutputs = ARRAY_SIZE(trout_col_gpios),
+ .ninputs = ARRAY_SIZE(trout_row_gpios),
+ .settle_time.tv.nsec = 40 * NSEC_PER_USEC,
+ .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
+ .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/
+};
+
+static struct gpio_event_direct_entry trout_keypad_nav_map[] = {
+ { TROUT_POWER_KEY, KEY_POWER },
+ { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP
+ { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA },
+};
+
+static struct gpio_event_direct_entry trout_keypad_nav_map_evt2[] = {
+ { TROUT_POWER_KEY, KEY_END },
+ { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP
+ { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA },
+};
+
+static struct gpio_event_input_info trout_keypad_nav_info = {
+ .info.func = gpio_event_input_func,
+ .flags = 0,
+ .type = EV_KEY,
+ .keymap = trout_keypad_nav_map,
+ .keymap_size = ARRAY_SIZE(trout_keypad_nav_map)
+};
+
+static struct gpio_event_direct_entry trout_keypad_switch_map[] = {
+ { TROUT_GPIO_SLIDING_DET, SW_LID }
+};
+
+static struct gpio_event_input_info trout_keypad_switch_info = {
+ .info.func = gpio_event_input_func,
+ .flags = 0,
+ .type = EV_SW,
+ .keymap = trout_keypad_switch_map,
+ .keymap_size = ARRAY_SIZE(trout_keypad_switch_map)
+};
+
+static struct gpio_event_info *trout_keypad_info[] = {
+ &trout_keypad_matrix_info.info,
+ &trout_keypad_nav_info.info,
+ &trout_keypad_switch_info.info,
+};
+
+static struct gpio_event_platform_data trout_keypad_data = {
+ .name = "trout-keypad",
+ .info = trout_keypad_info,
+ .info_count = ARRAY_SIZE(trout_keypad_info)
+};
+
+static struct platform_device trout_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 0,
+ .dev = {
+ .platform_data = &trout_keypad_data,
+ },
+};
+
+static int __init trout_init_keypad(void)
+{
+ if (!machine_is_trout())
+ return 0;
+
+ switch (system_rev) {
+ case 0:
+ /* legacy default keylayout */
+ break;
+ case 1:
+ /* v1 has a new keyboard layout */
+ trout_keypad_matrix_info.keymap = trout_keymap_evt2_1;
+ trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2;
+ trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2;
+
+ /* v1 has new direct keys */
+ trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2;
+ trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2);
+
+ /* userspace needs to know about these changes as well */
+ trout_keypad_data.name = "trout-keypad-v2";
+ break;
+ default: /* 2, 3, 4 currently */
+ /* v2 has a new keyboard layout */
+ trout_keypad_matrix_info.keymap = trout_keymap_evt2_2;
+ trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2;
+ trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2;
+
+ /* v2 has new direct keys */
+ trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2;
+ trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2);
+
+ /* userspace needs to know about these changes as well */
+ if (!strcmp(keycaps, "qwertz")) {
+ trout_keypad_data.name = "trout-keypad-qwertz";
+ } else {
+ trout_keypad_data.name = "trout-keypad-v3";
+ }
+ break;
+ }
+ return platform_device_register(&trout_keypad_device);
+}
+
+device_initcall(trout_init_keypad);
+
diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c
new file mode 100644
index 0000000..13755f5
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-mmc.c
@@ -0,0 +1,438 @@
+/* linux/arch/arm/mach-msm/board-trout-mmc.c
+** Author: Brian Swetland <swetland@google.com>
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+#include <mach/vreg.h>
+#include <mach/htc_pwrsink.h>
+
+#include <asm/mach/mmc.h>
+
+#include "devices.h"
+
+#include "board-trout.h"
+
+#include "proc_comm.h"
+
+#define DEBUG_SDSLOT_VDD 1
+
+extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags);
+
+/* ---- COMMON ---- */
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for(n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+/* ---- SDCARD ---- */
+
+static uint32_t sdcard_on_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+};
+
+static uint32_t sdcard_off_gpio_table[] = {
+ PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+};
+
+static uint opt_disable_sdcard;
+
+static int __init trout_disablesdcard_setup(char *str)
+{
+ int cal = simple_strtol(str, NULL, 0);
+
+ opt_disable_sdcard = cal;
+ return 1;
+}
+
+__setup("board_trout.disable_sdcard=", trout_disablesdcard_setup);
+
+static struct vreg *vreg_sdslot; /* SD slot power */
+
+struct mmc_vdd_xlat {
+ int mask;
+ int level;
+};
+
+static struct mmc_vdd_xlat mmc_vdd_table[] = {
+ { MMC_VDD_165_195, 1800 },
+ { MMC_VDD_20_21, 2050 },
+ { MMC_VDD_21_22, 2150 },
+ { MMC_VDD_22_23, 2250 },
+ { MMC_VDD_23_24, 2350 },
+ { MMC_VDD_24_25, 2450 },
+ { MMC_VDD_25_26, 2550 },
+ { MMC_VDD_26_27, 2650 },
+ { MMC_VDD_27_28, 2750 },
+ { MMC_VDD_28_29, 2850 },
+ { MMC_VDD_29_30, 2950 },
+};
+
+static unsigned int sdslot_vdd = 0xffffffff;
+static unsigned int sdslot_vreg_enabled;
+
+static uint32_t trout_sdslot_switchvdd(struct device *dev, unsigned int vdd)
+{
+ int i, rc;
+
+ BUG_ON(!vreg_sdslot);
+
+ if (vdd == sdslot_vdd)
+ return 0;
+
+ sdslot_vdd = vdd;
+
+ if (vdd == 0) {
+#if DEBUG_SDSLOT_VDD
+ printk("%s: Disabling SD slot power\n", __func__);
+#endif
+ config_gpio_table(sdcard_off_gpio_table,
+ ARRAY_SIZE(sdcard_off_gpio_table));
+ vreg_disable(vreg_sdslot);
+ sdslot_vreg_enabled = 0;
+ return 0;
+ }
+
+ if (!sdslot_vreg_enabled) {
+ rc = vreg_enable(vreg_sdslot);
+ if (rc) {
+ printk(KERN_ERR "%s: Error enabling vreg (%d)\n",
+ __func__, rc);
+ }
+ config_gpio_table(sdcard_on_gpio_table,
+ ARRAY_SIZE(sdcard_on_gpio_table));
+ sdslot_vreg_enabled = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
+ if (mmc_vdd_table[i].mask == (1 << vdd)) {
+#if DEBUG_SDSLOT_VDD
+ printk("%s: Setting level to %u\n",
+ __func__, mmc_vdd_table[i].level);
+#endif
+ rc = vreg_set_level(vreg_sdslot,
+ mmc_vdd_table[i].level);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error setting vreg level (%d)\n",
+ __func__, rc);
+ }
+ return 0;
+ }
+ }
+
+ printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd);
+ return 0;
+}
+
+static unsigned int trout_sdslot_status(struct device *dev)
+{
+ unsigned int status;
+
+ status = (unsigned int) gpio_get_value(TROUT_GPIO_SDMC_CD_N);
+ return (!status);
+}
+
+#define TROUT_MMC_VDD MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \
+ | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \
+ | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \
+ | MMC_VDD_28_29 | MMC_VDD_29_30
+
+static struct mmc_platform_data trout_sdslot_data = {
+ .ocr_mask = TROUT_MMC_VDD,
+ .status = trout_sdslot_status,
+ .translate_vdd = trout_sdslot_switchvdd,
+};
+
+/* ---- WIFI ---- */
+
+static uint32_t wifi_on_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
+ PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
+ PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+static uint32_t wifi_off_gpio_table[] = {
+ PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
+ PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
+ PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
+ PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
+ PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
+};
+
+static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */
+static int trout_wifi_cd = 0; /* WIFI virtual 'card detect' status */
+
+static struct sdio_embedded_func wifi_func = {
+ .f_class = SDIO_CLASS_WLAN,
+ .f_maxblksize = 512,
+};
+
+static struct embedded_sdio_data trout_wifi_emb_data = {
+ .cis = {
+ .vendor = 0x104c,
+ .device = 0x9066,
+ .blksize = 512,
+ /*.max_dtr = 24000000, Max of chip - no worky on Trout */
+ .max_dtr = 20000000,
+ },
+ .cccr = {
+ .multi_block = 0,
+ .low_speed = 0,
+ .wide_bus = 1,
+ .high_power = 0,
+ .high_speed = 0,
+ },
+ .funcs = &wifi_func,
+ .num_funcs = 1,
+};
+
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+
+static int trout_wifi_status_register(void (*callback)(int card_present, void *dev_id), void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static unsigned int trout_wifi_status(struct device *dev)
+{
+ return trout_wifi_cd;
+}
+
+int trout_wifi_set_carddetect(int val)
+{
+ printk("%s: %d\n", __func__, val);
+ trout_wifi_cd = val;
+ if (wifi_status_cb) {
+ wifi_status_cb(val, wifi_status_cb_devid);
+ } else
+ printk(KERN_WARNING "%s: Nobody to notify\n", __func__);
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(trout_wifi_set_carddetect);
+#endif
+
+static int trout_wifi_power_state;
+
+int trout_wifi_power(int on)
+{
+ int rc;
+
+ printk("%s: %d\n", __func__, on);
+
+ if (on) {
+ config_gpio_table(wifi_on_gpio_table,
+ ARRAY_SIZE(wifi_on_gpio_table));
+ rc = vreg_enable(vreg_wifi_osc);
+ if (rc)
+ return rc;
+ htc_pwrsink_set(PWRSINK_WIFI, 70);
+ } else {
+ config_gpio_table(wifi_off_gpio_table,
+ ARRAY_SIZE(wifi_off_gpio_table));
+ htc_pwrsink_set(PWRSINK_WIFI, 0);
+ }
+ gpio_set_value( TROUT_GPIO_MAC_32K_EN, on);
+ mdelay(100);
+ gpio_set_value( TROUT_GPIO_WIFI_EN, on);
+ mdelay(100);
+ if (!on) {
+ vreg_disable(vreg_wifi_osc);
+ }
+ trout_wifi_power_state = on;
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(trout_wifi_power);
+#endif
+
+static int trout_wifi_reset_state;
+int trout_wifi_reset(int on)
+{
+ printk("%s: %d\n", __func__, on);
+ gpio_set_value( TROUT_GPIO_WIFI_PA_RESETX, !on );
+ trout_wifi_reset_state = on;
+ mdelay(50);
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(trout_wifi_reset);
+#endif
+
+static struct mmc_platform_data trout_wifi_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .status = trout_wifi_status,
+ .register_status_notify = trout_wifi_status_register,
+ .embedded_sdio = &trout_wifi_emb_data,
+};
+
+int __init trout_init_mmc(unsigned int sys_rev)
+{
+ wifi_status_cb = NULL;
+
+ sdslot_vreg_enabled = 0;
+
+ vreg_sdslot = vreg_get(0, "gp6");
+ if (IS_ERR(vreg_sdslot))
+ return PTR_ERR(vreg_sdslot);
+ vreg_wifi_osc = vreg_get(0, "mmc");
+ if (IS_ERR(vreg_wifi_osc))
+ return PTR_ERR(vreg_wifi_osc);
+
+ set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 1);
+
+ msm_add_sdcc(1, &trout_wifi_data, 0, 0);
+
+ if (!opt_disable_sdcard)
+ msm_add_sdcc(2, &trout_sdslot_data,
+ TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 0);
+ else
+ printk(KERN_INFO "trout: SD-Card interface disabled\n");
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static int troutmmc_dbg_wifi_reset_set(void *data, u64 val)
+{
+ trout_wifi_reset((int) val);
+ return 0;
+}
+
+static int troutmmc_dbg_wifi_reset_get(void *data, u64 *val)
+{
+ *val = trout_wifi_reset_state;
+ return 0;
+}
+
+static int troutmmc_dbg_wifi_cd_set(void *data, u64 val)
+{
+ trout_wifi_set_carddetect((int) val);
+ return 0;
+}
+
+static int troutmmc_dbg_wifi_cd_get(void *data, u64 *val)
+{
+ *val = trout_wifi_cd;
+ return 0;
+}
+
+static int troutmmc_dbg_wifi_pwr_set(void *data, u64 val)
+{
+ trout_wifi_power((int) val);
+ return 0;
+}
+
+static int troutmmc_dbg_wifi_pwr_get(void *data, u64 *val)
+{
+
+ *val = trout_wifi_power_state;
+ return 0;
+}
+
+static int troutmmc_dbg_sd_pwr_set(void *data, u64 val)
+{
+ trout_sdslot_switchvdd(NULL, (unsigned int) val);
+ return 0;
+}
+
+static int troutmmc_dbg_sd_pwr_get(void *data, u64 *val)
+{
+ *val = sdslot_vdd;
+ return 0;
+}
+
+static int troutmmc_dbg_sd_cd_set(void *data, u64 val)
+{
+ return -ENOSYS;
+}
+
+static int troutmmc_dbg_sd_cd_get(void *data, u64 *val)
+{
+ *val = trout_sdslot_status(NULL);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_reset_fops,
+ troutmmc_dbg_wifi_reset_get,
+ troutmmc_dbg_wifi_reset_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_cd_fops,
+ troutmmc_dbg_wifi_cd_get,
+ troutmmc_dbg_wifi_cd_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_pwr_fops,
+ troutmmc_dbg_wifi_pwr_get,
+ troutmmc_dbg_wifi_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_pwr_fops,
+ troutmmc_dbg_sd_pwr_get,
+ troutmmc_dbg_sd_pwr_set, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_cd_fops,
+ troutmmc_dbg_sd_cd_get,
+ troutmmc_dbg_sd_cd_set, "%llu\n");
+
+static int __init troutmmc_dbg_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("troutmmc_dbg", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("wifi_reset", 0644, dent, NULL,
+ &troutmmc_dbg_wifi_reset_fops);
+ debugfs_create_file("wifi_cd", 0644, dent, NULL,
+ &troutmmc_dbg_wifi_cd_fops);
+ debugfs_create_file("wifi_pwr", 0644, dent, NULL,
+ &troutmmc_dbg_wifi_pwr_fops);
+
+ debugfs_create_file("sd_pwr", 0644, dent, NULL,
+ &troutmmc_dbg_sd_pwr_fops);
+ debugfs_create_file("sd_cd", 0644, dent, NULL,
+ &troutmmc_dbg_sd_cd_fops);
+
+ return 0;
+}
+
+device_initcall(troutmmc_dbg_init);
+
+#endif
diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c
new file mode 100644
index 0000000..7e978b8
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-panel.c
@@ -0,0 +1,650 @@
+/* linux/arch/arm/mach-msm/board-trout-mddi.c
+** Author: Brian Swetland <swetland@google.com>
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+#include <mach/vreg.h>
+#include <mach/htc_pwrsink.h>
+
+#include "board-trout.h"
+#include "proc_comm.h"
+#include "devices.h"
+
+#define TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS 255
+#define VSYNC_GPIO 97
+
+static struct clk *gp_clk;
+static int trout_backlight_off;
+static int trout_backlight_brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS;
+static int trout_new_backlight = 1;
+static uint8_t trout_backlight_last_level = 33;
+static DEFINE_MUTEX(trout_backlight_lock);
+
+static void trout_set_backlight_level(uint8_t level)
+{
+ unsigned percent = ((int)level * 100) / 255;
+
+ if (trout_new_backlight) {
+ unsigned long flags;
+ int i = 0;
+ level = (int)level * 34 / 256;
+
+ if (trout_backlight_last_level == level)
+ return;
+
+ if (level == 0) {
+ gpio_set_value(27, 0);
+ msleep(2);
+ } else {
+ local_irq_save(flags);
+ if (trout_backlight_last_level == 0) {
+ gpio_set_value(27, 1);
+ udelay(40);
+ trout_backlight_last_level = 33;
+ }
+ i = (trout_backlight_last_level - level + 33) % 33;
+ while (i-- > 0) {
+ gpio_set_value(27, 0);
+ udelay(1);
+ gpio_set_value(27, 1);
+ udelay(1);
+ }
+ local_irq_restore(flags);
+ }
+ trout_backlight_last_level = level;
+ }
+ else {
+ if(level) {
+ clk_enable(gp_clk);
+ writel((1U << 16) | (~level & 0xffff),
+ MSM_CLK_CTL_BASE + 0x58);
+ /* Going directly to a 100% duty cycle does not
+ * seem to work */
+ if(level == 255) {
+ writel((~127 << 16) | 0xb20,
+ MSM_CLK_CTL_BASE + 0x5c);
+ udelay(1);
+ }
+ writel((~127 << 16) | 0xb58, MSM_CLK_CTL_BASE + 0x5c);
+ }
+ else {
+ writel(0x0, MSM_CLK_CTL_BASE + 0x5c);
+ clk_disable(gp_clk);
+ }
+ }
+ htc_pwrsink_set(PWRSINK_BACKLIGHT, percent);
+}
+
+#define MDDI_CLIENT_CORE_BASE 0x108000
+#define LCD_CONTROL_BLOCK_BASE 0x110000
+#define SPI_BLOCK_BASE 0x120000
+#define I2C_BLOCK_BASE 0x130000
+#define PWM_BLOCK_BASE 0x140000
+#define GPIO_BLOCK_BASE 0x150000
+#define SYSTEM_BLOCK1_BASE 0x160000
+#define SYSTEM_BLOCK2_BASE 0x170000
+
+
+#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24)
+#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C)
+#define PWM0OFF (PWM_BLOCK_BASE|0x1C)
+
+#define V_VDDE2E_VDD2_GPIO 0
+#define MDDI_RST_N 82
+
+#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00)
+#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04)
+#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08)
+#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C)
+#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10)
+#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14)
+#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18)
+#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C)
+#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20)
+#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24)
+#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28)
+#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C)
+#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30)
+#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34)
+#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38)
+#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C)
+#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40)
+#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44)
+#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48)
+#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C)
+#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50)
+#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54)
+
+#define SRST (LCD_CONTROL_BLOCK_BASE|0x00)
+#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04)
+#define START (LCD_CONTROL_BLOCK_BASE|0x08)
+#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C)
+#define CMN (LCD_CONTROL_BLOCK_BASE|0x10)
+#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14)
+#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18)
+#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C)
+#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20)
+#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24)
+#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28)
+#define PXL (LCD_CONTROL_BLOCK_BASE|0x30)
+#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34)
+#define HSW (LCD_CONTROL_BLOCK_BASE|0x38)
+#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C)
+#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40)
+#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44)
+#define VSW (LCD_CONTROL_BLOCK_BASE|0x48)
+#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C)
+#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50)
+#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54)
+#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58)
+#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C)
+#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60)
+#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64)
+#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68)
+#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C)
+#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70)
+#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74)
+#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78)
+#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C)
+#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80)
+#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84)
+#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88)
+#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C)
+
+#define SSICTL (SPI_BLOCK_BASE|0x00)
+#define SSITIME (SPI_BLOCK_BASE|0x04)
+#define SSITX (SPI_BLOCK_BASE|0x08)
+#define SSIRX (SPI_BLOCK_BASE|0x0C)
+#define SSIINTC (SPI_BLOCK_BASE|0x10)
+#define SSIINTS (SPI_BLOCK_BASE|0x14)
+#define SSIDBG1 (SPI_BLOCK_BASE|0x18)
+#define SSIDBG2 (SPI_BLOCK_BASE|0x1C)
+#define SSIID (SPI_BLOCK_BASE|0x20)
+
+#define WKREQ (SYSTEM_BLOCK1_BASE|0x00)
+#define CLKENB (SYSTEM_BLOCK1_BASE|0x04)
+#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08)
+#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C)
+#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00)
+
+#define GPIODATA (GPIO_BLOCK_BASE|0x00)
+#define GPIODIR (GPIO_BLOCK_BASE|0x04)
+#define GPIOIS (GPIO_BLOCK_BASE|0x08)
+#define GPIOIBE (GPIO_BLOCK_BASE|0x0C)
+#define GPIOIEV (GPIO_BLOCK_BASE|0x10)
+#define GPIOIE (GPIO_BLOCK_BASE|0x14)
+#define GPIORIS (GPIO_BLOCK_BASE|0x18)
+#define GPIOMIS (GPIO_BLOCK_BASE|0x1C)
+#define GPIOIC (GPIO_BLOCK_BASE|0x20)
+#define GPIOOMS (GPIO_BLOCK_BASE|0x24)
+#define GPIOPC (GPIO_BLOCK_BASE|0x28)
+#define GPIOID (GPIO_BLOCK_BASE|0x30)
+
+#define SPI_WRITE(reg, val) \
+ { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \
+ { 0, 5 },
+
+#define SPI_WRITE1(reg) \
+ { SSITX, (reg) & 0xff }, \
+ { 0, 5 },
+
+struct mddi_table {
+ uint32_t reg;
+ uint32_t value;
+};
+static struct mddi_table mddi_toshiba_init_table[] = {
+ { DPSET0, 0x09e90046 },
+ { DPSET1, 0x00000118 },
+ { DPSUS, 0x00000000 },
+ { DPRUN, 0x00000001 },
+ { 1, 14 }, /* msleep 14 */
+ { SYSCKENA, 0x00000001 },
+ //{ CLKENB, 0x000000EF },
+ { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */
+ //{ CLKENB, 0x000025CB }, /* Clock enable register */
+
+ { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */
+ { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */
+ { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */
+ { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */
+ { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */
+
+ { GPIOIBE, 0x000003FF },
+ { GPIOIS, 0x00000000 },
+ { GPIOIC, 0x000003FF },
+ { GPIOIE, 0x00000000 },
+
+ { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */
+ { 1, 1 }, /* msleep 1 */
+ { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */
+ { DRAMPWR, 0x00000001 }, /* eDRAM power */
+};
+
+static struct mddi_table mddi_toshiba_panel_init_table[] = {
+ { SRST, 0x00000003 }, /* FIFO/LCDC not reset */
+ { PORT_ENB, 0x00000001 }, /* Enable sync. Port */
+ { START, 0x00000000 }, /* To stop operation */
+ //{ START, 0x00000001 }, /* To start operation */
+ { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */
+ { CMN, 0x00000000 },
+ { GAMMA, 0x00000000 }, /* No Gamma correction */
+ { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */
+ { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */
+ { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */
+ { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */
+ { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */
+ { PXL, 0x00000001 }, /* 1. RGB666 */
+ /* 2. Data is valid from 1st frame of beginning. */
+ { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */
+ { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */
+ { HSW, 0x00000004 }, /* HSW= 10 PCLK */
+ { VSW, 0x00000001 }, /* VSW=2 HCYCLE */
+ { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */
+ { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */
+ { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */
+ { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { CLKENB, 0x000025CB }, /* Clock enable register */
+
+ { SSICTL, 0x00000170 }, /* SSI control register */
+ { SSITIME, 0x00000250 }, /* SSI timing control register */
+ { SSICTL, 0x00000172 }, /* SSI control register */
+};
+
+
+static struct mddi_table mddi_sharp_init_table[] = {
+ { VCYCLE, 0x000001eb },
+ { HCYCLE, 0x000000ae },
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { 1, 1 }, /* msleep 1 */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { 1, 10 }, /* msleep 10 */
+ SPI_WRITE(0x5f, 0x01)
+ SPI_WRITE1(0x11)
+ { 1, 200 }, /* msleep 200 */
+ SPI_WRITE1(0x29)
+ SPI_WRITE1(0xde)
+ { START, 0x00000001 }, /* To start operation */
+};
+
+static struct mddi_table mddi_sharp_deinit_table[] = {
+ { 1, 200 }, /* msleep 200 */
+ SPI_WRITE(0x10, 0x1)
+ { 1, 100 }, /* msleep 100 */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { 1, 10 }, /* msleep 10 */
+};
+
+static struct mddi_table mddi_tpo_init_table[] = {
+ { VCYCLE, 0x000001e5 },
+ { HCYCLE, 0x000000ac },
+ { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */
+ { 0, 20 }, /* udelay 20 */
+ { GPIODATA, 0x00000004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { 0, 20 }, /* udelay 20 */
+
+ SPI_WRITE(0x08, 0x01)
+ { 0, 500 }, /* udelay 500 */
+ SPI_WRITE(0x08, 0x00)
+ SPI_WRITE(0x02, 0x00)
+ SPI_WRITE(0x03, 0x04)
+ SPI_WRITE(0x04, 0x0e)
+ SPI_WRITE(0x09, 0x02)
+ SPI_WRITE(0x0b, 0x08)
+ SPI_WRITE(0x0c, 0x53)
+ SPI_WRITE(0x0d, 0x01)
+ SPI_WRITE(0x0e, 0xe0)
+ SPI_WRITE(0x0f, 0x01)
+ SPI_WRITE(0x10, 0x58)
+ SPI_WRITE(0x20, 0x1e)
+ SPI_WRITE(0x21, 0x0a)
+ SPI_WRITE(0x22, 0x0a)
+ SPI_WRITE(0x23, 0x1e)
+ SPI_WRITE(0x25, 0x32)
+ SPI_WRITE(0x26, 0x00)
+ SPI_WRITE(0x27, 0xac)
+ SPI_WRITE(0x29, 0x06)
+ SPI_WRITE(0x2a, 0xa4)
+ SPI_WRITE(0x2b, 0x45)
+ SPI_WRITE(0x2c, 0x45)
+ SPI_WRITE(0x2d, 0x15)
+ SPI_WRITE(0x2e, 0x5a)
+ SPI_WRITE(0x2f, 0xff)
+ SPI_WRITE(0x30, 0x6b)
+ SPI_WRITE(0x31, 0x0d)
+ SPI_WRITE(0x32, 0x48)
+ SPI_WRITE(0x33, 0x82)
+ SPI_WRITE(0x34, 0xbd)
+ SPI_WRITE(0x35, 0xe7)
+ SPI_WRITE(0x36, 0x18)
+ SPI_WRITE(0x37, 0x94)
+ SPI_WRITE(0x38, 0x01)
+ SPI_WRITE(0x39, 0x5d)
+ SPI_WRITE(0x3a, 0xae)
+ SPI_WRITE(0x3b, 0xff)
+ SPI_WRITE(0x07, 0x09)
+ { 0, 10 }, /* udelay 10 */
+ { START, 0x00000001 }, /* To start operation */
+};
+
+static struct mddi_table mddi_tpo_deinit_table[] = {
+ SPI_WRITE(0x07, 0x19)
+ { START, 0x00000000 }, /* To stop operation */
+ { GPIODATA, 0x00040004 }, /* GPIO2 high */
+ { GPIODIR, 0x00000004 }, /* GPIO2 out */
+ { GPIODATA, 0x00040000 }, /* GPIO2 low */
+ { 0, 5 }, /* usleep 5 */
+};
+
+
+#define GPIOSEL_VWAKEINT (1U << 0)
+#define INTMASK_VWAKEOUT (1U << 0)
+
+static void trout_process_mddi_table(struct msm_mddi_client_data *client_data,
+ struct mddi_table *table, size_t count)
+{
+ int i;
+ for(i = 0; i < count; i++) {
+ uint32_t reg = table[i].reg;
+ uint32_t value = table[i].value;
+
+ if (reg == 0)
+ udelay(value);
+ else if (reg == 1)
+ msleep(value);
+ else
+ client_data->remote_write(client_data, value, reg);
+ }
+}
+
+static struct vreg *vreg_mddi_1v5;
+static struct vreg *vreg_lcm_2v85;
+
+static void trout_mddi_power_client(struct msm_mddi_client_data *client_data,
+ int on)
+{
+ unsigned id, on_off;
+ if(on) {
+ on_off = 0;
+ id = PM_VREG_PDOWN_MDDI_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ vreg_enable(vreg_mddi_1v5);
+ mdelay(5); // delay time >5ms and <10ms
+ gpio_set_value(V_VDDE2E_VDD2_GPIO, 1);
+ gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 1);
+ msleep(3);
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ vreg_enable(vreg_lcm_2v85);
+ msleep(3);
+ gpio_set_value(MDDI_RST_N, 1);
+ msleep(10);
+ } else {
+ gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 0);
+ gpio_set_value(MDDI_RST_N, 0);
+ msleep(10);
+ vreg_disable(vreg_lcm_2v85);
+ on_off = 1;
+ id = PM_VREG_PDOWN_AUX_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ msleep(5);
+ gpio_set_value(V_VDDE2E_VDD2_GPIO, 0);
+ msleep(200);
+ vreg_disable(vreg_mddi_1v5);
+ id = PM_VREG_PDOWN_MDDI_ID;
+ msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+ }
+}
+
+static int trout_mddi_toshiba_client_init(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id;
+
+ client_data->auto_hibernate(client_data, 0);
+ trout_process_mddi_table(client_data, mddi_toshiba_init_table,
+ ARRAY_SIZE(mddi_toshiba_init_table));
+ client_data->auto_hibernate(client_data, 1);
+ panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ if (panel_id > 1) {
+ printk("unknown panel id at mddi_enable\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int trout_mddi_toshiba_client_uninit(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ return 0;
+}
+
+static int trout_mddi_panel_unblank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+
+ int panel_id, ret = 0;
+
+ trout_set_backlight_level(0);
+ client_data->auto_hibernate(client_data, 0);
+ trout_process_mddi_table(client_data, mddi_toshiba_panel_init_table,
+ ARRAY_SIZE(mddi_toshiba_panel_init_table));
+ panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ switch(panel_id) {
+ case 0:
+ printk("init sharp panel\n");
+ trout_process_mddi_table(client_data,
+ mddi_sharp_init_table,
+ ARRAY_SIZE(mddi_sharp_init_table));
+ break;
+ case 1:
+ printk("init tpo panel\n");
+ trout_process_mddi_table(client_data,
+ mddi_tpo_init_table,
+ ARRAY_SIZE(mddi_tpo_init_table));
+ break;
+ default:
+ printk("unknown panel_id: %d\n", panel_id);
+ ret = -1;
+ };
+ mutex_lock(&trout_backlight_lock);
+ trout_set_backlight_level(trout_backlight_brightness);
+ trout_backlight_off = 0;
+ mutex_unlock(&trout_backlight_lock);
+ client_data->auto_hibernate(client_data, 1);
+ client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL);
+ client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK);
+ return ret;
+
+}
+
+static int trout_mddi_panel_blank(
+ struct msm_mddi_bridge_platform_data *bridge_data,
+ struct msm_mddi_client_data *client_data)
+{
+ int panel_id, ret = 0;
+
+ panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3;
+ client_data->auto_hibernate(client_data, 0);
+ switch(panel_id) {
+ case 0:
+ printk("deinit sharp panel\n");
+ trout_process_mddi_table(client_data,
+ mddi_sharp_deinit_table,
+ ARRAY_SIZE(mddi_sharp_deinit_table));
+ break;
+ case 1:
+ printk("deinit tpo panel\n");
+ trout_process_mddi_table(client_data,
+ mddi_tpo_deinit_table,
+ ARRAY_SIZE(mddi_tpo_deinit_table));
+ break;
+ default:
+ printk("unknown panel_id: %d\n", panel_id);
+ ret = -1;
+ };
+ client_data->auto_hibernate(client_data, 1);
+ mutex_lock(&trout_backlight_lock);
+ trout_set_backlight_level(0);
+ trout_backlight_off = 1;
+ mutex_unlock(&trout_backlight_lock);
+ client_data->remote_write(client_data, 0, SYSCLKENA);
+ client_data->remote_write(client_data, 1, DPSUS);
+ return ret;
+}
+
+static void trout_brightness_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ mutex_lock(&trout_backlight_lock);
+ trout_backlight_brightness = value;
+ if(!trout_backlight_off)
+ trout_set_backlight_level(trout_backlight_brightness);
+ mutex_unlock(&trout_backlight_lock);
+}
+
+static struct led_classdev trout_backlight_led = {
+ .name = "lcd-backlight",
+ .brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS,
+ .brightness_set = trout_brightness_set,
+};
+
+static int trout_backlight_probe(struct platform_device *pdev)
+{
+ led_classdev_register(&pdev->dev, &trout_backlight_led);
+ return 0;
+}
+
+static int trout_backlight_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&trout_backlight_led);
+ return 0;
+}
+
+static struct platform_driver trout_backlight_driver = {
+ .probe = trout_backlight_probe,
+ .remove = trout_backlight_remove,
+ .driver = {
+ .name = "trout-backlight",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct resource resources_msm_fb[] = {
+ {
+ .start = MSM_FB_BASE,
+ .end = MSM_FB_BASE + MSM_FB_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct msm_mddi_bridge_platform_data toshiba_client_data = {
+ .init = trout_mddi_toshiba_client_init,
+ .uninit = trout_mddi_toshiba_client_uninit,
+ .blank = trout_mddi_panel_blank,
+ .unblank = trout_mddi_panel_unblank,
+ .fb_data = {
+ .xres = 320,
+ .yres = 480,
+ .width = 45,
+ .height = 67,
+ .output_format = 0,
+ },
+};
+
+static struct msm_mddi_platform_data mddi_pdata = {
+ .clk_rate = 122880000,
+ .power_client = trout_mddi_power_client,
+ .vsync_irq = MSM_GPIO_TO_INT(VSYNC_GPIO),
+ .fb_resource = resources_msm_fb,
+ .num_clients = 1,
+ .client_platform_data = {
+ {
+ .product_id = (0xd263 << 16 | 0),
+ .name = "mddi_c_d263_0000",
+ //.name = "mddi_c_dummy",
+ .id = 0,
+ .client_data = &toshiba_client_data,
+ //.client_data = &toshiba_client_data.fb_data,
+ .clk_rate = 0,
+ },
+ },
+};
+
+static struct platform_device trout_backlight = {
+ .name = "trout-backlight",
+};
+
+int __init trout_init_panel(void)
+{
+ int rc;
+
+ if (!machine_is_trout())
+ return 0;
+ vreg_mddi_1v5 = vreg_get(0, "gp2");
+ if (IS_ERR(vreg_mddi_1v5))
+ return PTR_ERR(vreg_mddi_1v5);
+ vreg_lcm_2v85 = vreg_get(0, "gp4");
+ if (IS_ERR(vreg_lcm_2v85))
+ return PTR_ERR(vreg_lcm_2v85);
+
+ trout_new_backlight = system_rev >= 5;
+ if (trout_new_backlight) {
+ uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0);
+ }
+ else {
+ uint32_t config = PCOM_GPIO_CFG(27, 1, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0);
+
+ gp_clk = clk_get(NULL, "gp_clk");
+ if (IS_ERR(gp_clk)) {
+ printk(KERN_ERR "trout_init_panel: could not get gp"
+ "clock\n");
+ gp_clk = NULL;
+ }
+ rc = clk_set_rate(gp_clk, 19200000);
+ if (rc)
+ printk(KERN_ERR "trout_init_panel: set clock rate "
+ "failed\n");
+ }
+
+ rc = gpio_request(VSYNC_GPIO, "vsync");
+ if (rc)
+ return rc;
+ rc = gpio_direction_input(VSYNC_GPIO);
+ if (rc)
+ return rc;
+ rc = platform_device_register(&msm_device_mdp);
+ if (rc)
+ return rc;
+ msm_device_mddi0.dev.platform_data = &mddi_pdata;
+ rc = platform_device_register(&msm_device_mddi0);
+ if (rc)
+ return rc;
+ platform_device_register(&trout_backlight);
+ return platform_driver_register(&trout_backlight_driver);
+}
+
+device_initcall(trout_init_panel);
diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c
new file mode 100644
index 0000000..e68eb2a
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-rfkill.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Nick Pelly <npelly@google.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.
+ *
+ */
+
+/* Control bluetooth power for trout platform */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/rfkill.h>
+#include <linux/delay.h>
+#include <asm/gpio.h>
+
+#include "board-trout.h"
+
+static struct rfkill *bt_rfk;
+static const char bt_name[] = "brf6300";
+
+static int bluetooth_set_power(void *data, bool blocked)
+{
+ if (!blocked) {
+ gpio_set_value(TROUT_GPIO_BT_32K_EN, 1);
+ udelay(10);
+ gpio_direction_output(101, 1);
+ } else {
+ gpio_direction_output(101, 0);
+ gpio_set_value(TROUT_GPIO_BT_32K_EN, 0);
+ }
+ return 0;
+}
+
+static struct rfkill_ops trout_rfkill_ops = {
+ .set_block = bluetooth_set_power,
+};
+
+static int trout_rfkill_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ bool default_state = true; /* off */
+
+ bluetooth_set_power(NULL, default_state);
+
+ bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH,
+ &trout_rfkill_ops, NULL);
+ if (!bt_rfk)
+ return -ENOMEM;
+
+ rfkill_set_states(bt_rfk, default_state, false);
+
+ /* userspace cannot take exclusive control */
+
+ rc = rfkill_register(bt_rfk);
+
+ if (rc)
+ rfkill_destroy(bt_rfk);
+ return rc;
+}
+
+static int trout_rfkill_remove(struct platform_device *dev)
+{
+ rfkill_unregister(bt_rfk);
+ rfkill_destroy(bt_rfk);
+
+ return 0;
+}
+
+static struct platform_driver trout_rfkill_driver = {
+ .probe = trout_rfkill_probe,
+ .remove = trout_rfkill_remove,
+ .driver = {
+ .name = "trout_rfkill",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init trout_rfkill_init(void)
+{
+ return platform_driver_register(&trout_rfkill_driver);
+}
+
+static void __exit trout_rfkill_exit(void)
+{
+ platform_driver_unregister(&trout_rfkill_driver);
+}
+
+module_init(trout_rfkill_init);
+module_exit(trout_rfkill_exit);
+MODULE_DESCRIPTION("trout rfkill");
+MODULE_AUTHOR("Nick Pelly <npelly@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/board-trout-wifi.c b/arch/arm/mach-msm/board-trout-wifi.c
new file mode 100644
index 0000000..51b26a4
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-wifi.c
@@ -0,0 +1,74 @@
+/* arch/arm/mach-msm/board-trout-wifi.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Dmitry Shmidt <dimitrysh@google.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.
+ *
+ */
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/err.h>
+#include <linux/wifi_tiwlan.h>
+
+extern int trout_wifi_set_carddetect(int val);
+extern int trout_wifi_power(int on);
+extern int trout_wifi_reset(int on);
+
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+typedef struct wifi_mem_prealloc_struct {
+ void *mem_ptr;
+ unsigned long size;
+} wifi_mem_prealloc_t;
+
+static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = {
+ { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) },
+ { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) },
+ { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) }
+};
+
+static void *trout_wifi_mem_prealloc(int section, unsigned long size)
+{
+ if( (section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS) )
+ return NULL;
+ if( wifi_mem_array[section].size < size )
+ return NULL;
+ return wifi_mem_array[section].mem_ptr;
+}
+
+int __init trout_init_wifi_mem( void )
+{
+ int i;
+
+ for(i=0;( i < WMPA_NUMBER_OF_SECTIONS );i++) {
+ wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size);
+ if( wifi_mem_array[i].mem_ptr == NULL )
+ return -ENOMEM;
+ }
+ return 0;
+}
+#endif
+
+struct wifi_platform_data trout_wifi_control = {
+ .set_power = trout_wifi_power,
+ .set_reset = trout_wifi_reset,
+ .set_carddetect = trout_wifi_set_carddetect,
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+ .mem_prealloc = trout_wifi_mem_prealloc,
+#else
+ .mem_prealloc = NULL,
+#endif
+};
+
+#endif
diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c
index dca5a5f..1ce43e1 100644
--- a/arch/arm/mach-msm/board-trout.c
+++ b/arch/arm/mach-msm/board-trout.c
@@ -1,7 +1,6 @@
-/* linux/arch/arm/mach-msm/board-trout.c
+/* arch/arm/mach-msm/board-trout.c
*
- * Copyright (C) 2009 Google, Inc.
- * Author: Brian Swetland <swetland@google.com>
+ * Copyright (C) 2008 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
@@ -17,46 +16,817 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/keyreset.h>
+#include <linux/leds.h>
+#include <linux/switch.h>
+#include <../../../drivers/staging/android/timed_gpio.h>
+#include <linux/synaptics_i2c_rmi.h>
+#include <linux/akm8976.h>
+#include <linux/sysdev.h>
+#include <linux/android_pmem.h>
+#include <linux/delay.h>
+
+#include <asm/gpio.h>
+#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+#include <asm/system.h>
+#include <mach/system.h>
+#include <mach/vreg.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
#include <asm/setup.h>
-#include <mach/board.h>
-#include <mach/hardware.h>
-#include <mach/msm_iomap.h>
+#include <linux/gpio_event.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
-#include "devices.h"
+#include <asm/mach/mmc.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/msm_audio.h>
+
#include "board-trout.h"
+#include "gpio_chip.h"
+
+#include <mach/board.h>
+#include <mach/board_htc.h>
+#include <mach/msm_serial_debugger.h>
+#include <mach/msm_serial_hs.h>
+#include <mach/htc_pwrsink.h>
+#ifdef CONFIG_HTC_HEADSET
+#include <mach/htc_headset.h>
+#endif
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#include <linux/wifi_tiwlan.h>
+#endif
+
+#include "proc_comm.h"
+#include "devices.h"
+
+void msm_init_irq(void);
+void msm_init_gpio(void);
+
+extern int trout_init_mmc(unsigned int);
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+extern int trout_init_wifi_mem(void);
+#endif
+extern struct wifi_platform_data trout_wifi_control;
+#endif
+
+struct trout_axis_info {
+ struct gpio_event_axis_info info;
+ uint16_t in_state;
+ uint16_t out_state;
+};
+static bool nav_just_on;
+static int nav_on_jiffies;
+
+uint16_t trout_axis_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ struct trout_axis_info *ai = container_of(info, struct trout_axis_info, info);
+ uint16_t out = ai->out_state;
+
+ if (nav_just_on) {
+ if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1)
+ goto ignore;
+ nav_just_on = 0;
+ }
+ if((ai->in_state ^ in) & 1)
+ out--;
+ if((ai->in_state ^ in) & 2)
+ out++;
+ ai->out_state = out;
+ignore:
+ ai->in_state = in;
+ return out;
+}
+
+int trout_nav_power(const struct gpio_event_platform_data *pdata, bool on)
+{
+ gpio_set_value(TROUT_GPIO_JOG_EN, on);
+ if (on) {
+ nav_just_on = 1;
+ nav_on_jiffies = jiffies;
+ }
+ return 0;
+}
+
+static uint32_t trout_4_x_axis_gpios[] = {
+ TROUT_4_BALL_LEFT_0, TROUT_4_BALL_RIGHT_0
+};
+static uint32_t trout_5_x_axis_gpios[] = {
+ TROUT_5_BALL_LEFT_0, TROUT_5_BALL_RIGHT_0
+};
+
+static struct trout_axis_info trout_x_axis = {
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(trout_5_x_axis_gpios),
+ .type = EV_REL,
+ .code = REL_X,
+ .decoded_size = 1U << ARRAY_SIZE(trout_5_x_axis_gpios),
+ .map = trout_axis_map,
+ .gpio = trout_5_x_axis_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */
+ }
+};
+
+static uint32_t trout_4_y_axis_gpios[] = {
+ TROUT_4_BALL_UP_0, TROUT_4_BALL_DOWN_0
+};
+static uint32_t trout_5_y_axis_gpios[] = {
+ TROUT_5_BALL_UP_0, TROUT_5_BALL_DOWN_0
+};
+
+static struct trout_axis_info trout_y_axis = {
+ .info = {
+ .info.func = gpio_event_axis_func,
+ .count = ARRAY_SIZE(trout_5_y_axis_gpios),
+ .type = EV_REL,
+ .code = REL_Y,
+ .decoded_size = 1U << ARRAY_SIZE(trout_5_y_axis_gpios),
+ .map = trout_axis_map,
+ .gpio = trout_5_y_axis_gpios,
+ .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */
+ }
+};
+
+static struct gpio_event_direct_entry trout_nav_buttons[] = {
+ { TROUT_GPIO_NAVI_ACT_N, BTN_MOUSE }
+};
+
+static struct gpio_event_input_info trout_nav_button_info = {
+ .info.func = gpio_event_input_func,
+ .flags = 0,
+ .type = EV_KEY,
+ .keymap = trout_nav_buttons,
+ .keymap_size = ARRAY_SIZE(trout_nav_buttons)
+};
+
+static struct gpio_event_info *trout_nav_info[] = {
+ &trout_x_axis.info.info,
+ &trout_y_axis.info.info,
+ &trout_nav_button_info.info
+};
+
+static struct gpio_event_platform_data trout_nav_data = {
+ .name = "trout-nav",
+ .info = trout_nav_info,
+ .info_count = ARRAY_SIZE(trout_nav_info),
+ .power = trout_nav_power,
+};
+
+static struct platform_device trout_nav_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 2,
+ .dev = {
+ .platform_data = &trout_nav_data,
+ },
+};
+
+static int trout_reset_keys_up[] = {
+ BTN_MOUSE,
+ 0
+};
+
+static struct keyreset_platform_data trout_reset_keys_pdata = {
+ .keys_up = trout_reset_keys_up,
+ .keys_down = {
+ KEY_SEND,
+ KEY_MENU,
+ KEY_END,
+ 0
+ },
+};
+
+struct platform_device trout_reset_keys_device = {
+ .name = KEYRESET_NAME,
+ .dev.platform_data = &trout_reset_keys_pdata,
+};
+
+static int trout_ts_power(int on)
+{
+ int tp_ls_gpio = system_rev < 5 ? TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN;
+ if (on) {
+ gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 1);
+ gpio_set_value(TROUT_GPIO_TP_EN, 1);
+ /* touchscreen must be powered before we enable i2c pullup */
+ msleep(2);
+ /* enable touch panel level shift */
+ gpio_set_value(tp_ls_gpio, 1);
+ msleep(2);
+ }
+ else {
+ gpio_set_value(tp_ls_gpio, 0);
+ udelay(50);
+ gpio_set_value(TROUT_GPIO_TP_EN, 0);
+ gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 0);
+ }
+ return 0;
+}
+
+static struct synaptics_i2c_rmi_platform_data trout_ts_data[] = {
+ {
+ .version = 0x010c,
+ .power = trout_ts_power,
+ .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE,
+ .inactive_left = -100 * 0x10000 / 4334,
+ .inactive_right = -100 * 0x10000 / 4334,
+ .inactive_top = -40 * 0x10000 / 6696,
+ .inactive_bottom = -40 * 0x10000 / 6696,
+ .snap_left_on = 300 * 0x10000 / 4334,
+ .snap_left_off = 310 * 0x10000 / 4334,
+ .snap_right_on = 300 * 0x10000 / 4334,
+ .snap_right_off = 310 * 0x10000 / 4334,
+ .snap_top_on = 100 * 0x10000 / 6696,
+ .snap_top_off = 110 * 0x10000 / 6696,
+ .snap_bottom_on = 100 * 0x10000 / 6696,
+ .snap_bottom_off = 110 * 0x10000 / 6696,
+ },
+ {
+ .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE,
+ .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334,
+ .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334,
+ .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696,
+ .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696,
+ }
+};
+
+static struct akm8976_platform_data compass_platform_data = {
+ .reset = TROUT_GPIO_COMPASS_RST_N,
+ .clk_on = TROUT_GPIO_COMPASS_32K_EN,
+ .intr = TROUT_GPIO_COMPASS_IRQ,
+};
+
+static struct i2c_board_info i2c_devices[] = {
+ {
+ I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20),
+ .platform_data = trout_ts_data,
+ .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N)
+ },
+ {
+ I2C_BOARD_INFO("elan-touch", 0x10),
+ .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N),
+ },
+ {
+ I2C_BOARD_INFO("akm8976", 0x1C),
+ .platform_data = &compass_platform_data,
+ .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_COMPASS_IRQ),
+ },
+ {
+ I2C_BOARD_INFO("pca963x", 0x62),
+ },
+#if defined(CONFIG_MSM_CAMERA) && defined(CONFIG_MT9T013)
+ {
+ I2C_BOARD_INFO("mt9t013", 0x6C),
+ },
+#endif
+#ifdef CONFIG_SENSORS_MT9T013
+ {
+ I2C_BOARD_INFO("mt9t013", 0x6C >> 1),
+ },
+#endif
+};
+
+static struct timed_gpio timed_gpios[] = {
+ {
+ .name = "vibrator",
+ .gpio = TROUT_GPIO_HAPTIC_PWM,
+ .max_timeout = 15000,
+ },
+ {
+ .name = "flash",
+ .gpio = TROUT_GPIO_FLASH_EN,
+ .max_timeout = 400,
+ },
+};
+
+static struct timed_gpio_platform_data timed_gpio_data = {
+ .num_gpios = ARRAY_SIZE(timed_gpios),
+ .gpios = timed_gpios,
+};
+
+static struct platform_device android_timed_gpios = {
+ .name = "timed-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &timed_gpio_data,
+ },
+};
+
+static struct gpio_led android_led_list[] = {
+ {
+ .name = "spotlight",
+ .gpio = TROUT_GPIO_SPOTLIGHT_EN,
+ },
+ {
+ .name = "keyboard-backlight",
+ .gpio = TROUT_GPIO_QTKEY_LED_EN,
+ },
+ {
+ .name = "button-backlight",
+ .gpio = TROUT_GPIO_UI_LED_EN,
+ },
+};
+
+static struct gpio_led_platform_data android_leds_data = {
+ .num_leds = ARRAY_SIZE(android_led_list),
+ .leds = android_led_list,
+};
+
+static struct platform_device android_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_leds_data,
+ },
+};
+
+static struct gpio_switch_platform_data sd_door_switch_data = {
+ .name = "sd-door",
+ .gpio = TROUT_GPIO_SD_DOOR_N,
+ .state_on = "open",
+ .state_off = "closed",
+};
+
+static struct platform_device sd_door_switch = {
+ .name = "switch-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &sd_door_switch_data,
+ },
+};
+
+#ifdef CONFIG_HTC_HEADSET
+static void h2w_config_cpld(int route)
+{
+ switch (route) {
+ case H2W_UART3:
+ gpio_set_value(TROUT_GPIO_H2W_SEL0, 0);
+ gpio_set_value(TROUT_GPIO_H2W_SEL1, 1);
+ break;
+ case H2W_GPIO:
+ gpio_set_value(TROUT_GPIO_H2W_SEL0, 0);
+ gpio_set_value(TROUT_GPIO_H2W_SEL1, 0);
+ break;
+ }
+}
+
+static void h2w_init_cpld(void)
+{
+ h2w_config_cpld(H2W_UART3);
+ gpio_set_value(TROUT_GPIO_H2W_CLK_DIR, 0);
+ gpio_set_value(TROUT_GPIO_H2W_DAT_DIR, 0);
+}
+
+static struct h2w_platform_data trout_h2w_data = {
+ .cable_in1 = TROUT_GPIO_CABLE_IN1,
+ .cable_in2 = TROUT_GPIO_CABLE_IN2,
+ .h2w_clk = TROUT_GPIO_H2W_CLK_GPI,
+ .h2w_data = TROUT_GPIO_H2W_DAT_GPI,
+ .debug_uart = H2W_UART3,
+ .config_cpld = h2w_config_cpld,
+ .init_cpld = h2w_init_cpld,
+};
+
+static struct platform_device trout_h2w = {
+ .name = "h2w",
+ .id = -1,
+ .dev = {
+ .platform_data = &trout_h2w_data,
+ },
+};
+#endif
+
+static void trout_phy_reset(void)
+{
+ gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 0);
+ mdelay(10);
+ gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 1);
+ mdelay(10);
+}
+
+static void config_camera_on_gpios(void);
+static void config_camera_off_gpios(void);
+
+#ifdef CONFIG_MSM_CAMERA
+static struct msm_camera_device_platform_data msm_camera_device_data = {
+ .camera_gpio_on = config_camera_on_gpios,
+ .camera_gpio_off = config_camera_off_gpios,
+ .ioext.mdcphy = MSM_MDC_PHYS,
+ .ioext.mdcsz = MSM_MDC_SIZE,
+ .ioext.appphy = MSM_CLK_CTL_PHYS,
+ .ioext.appsz = MSM_CLK_CTL_SIZE,
+};
+
+#ifdef CONFIG_MT9T013
+static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = {
+ .sensor_name = "mt9t013",
+ .sensor_reset = 108,
+ .sensor_pwd = 85,
+ .vcm_pwd = TROUT_GPIO_VCM_PWDN,
+ .pdata = &msm_camera_device_data,
+};
+
+static struct platform_device msm_camera_sensor_mt9t013 = {
+ .name = "msm_camera_mt9t013",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9t013_data,
+ },
+};
+#endif
+#endif
+
+#ifdef CONFIG_SENSORS_MT9T013
+static struct msm_camera_legacy_device_platform_data msm_camera_device_mt9t013 = {
+ .sensor_reset = 108,
+ .sensor_pwd = 85,
+ .vcm_pwd = TROUT_GPIO_VCM_PWDN,
+ .config_gpio_on = config_camera_on_gpios,
+ .config_gpio_off = config_camera_off_gpios,
+};
+
+static struct platform_device trout_camera = {
+ .name = "camera",
+ .dev = {
+ .platform_data = &msm_camera_device_mt9t013,
+ },
+};
+#endif
+
+static struct pwr_sink trout_pwrsink_table[] = {
+ {
+ .id = PWRSINK_AUDIO,
+ .ua_max = 90000,
+ },
+ {
+ .id = PWRSINK_BACKLIGHT,
+ .ua_max = 128000,
+ },
+ {
+ .id = PWRSINK_LED_BUTTON,
+ .ua_max = 17000,
+ },
+ {
+ .id = PWRSINK_LED_KEYBOARD,
+ .ua_max = 22000,
+ },
+ {
+ .id = PWRSINK_GP_CLK,
+ .ua_max = 30000,
+ },
+ {
+ .id = PWRSINK_BLUETOOTH,
+ .ua_max = 15000,
+ },
+ {
+ .id = PWRSINK_CAMERA,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_SDCARD,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_VIDEO,
+ .ua_max = 0,
+ },
+ {
+ .id = PWRSINK_WIFI,
+ .ua_max = 200000,
+ },
+ {
+ .id = PWRSINK_SYSTEM_LOAD,
+ .ua_max = 100000,
+ .percent_util = 38,
+ },
+};
+
+static struct pwr_sink_platform_data trout_pwrsink_data = {
+ .num_sinks = ARRAY_SIZE(trout_pwrsink_table),
+ .sinks = trout_pwrsink_table,
+ .suspend_late = NULL,
+ .resume_early = NULL,
+ .suspend_early = NULL,
+ .resume_late = NULL,
+};
+
+static struct platform_device trout_pwr_sink = {
+ .name = "htc_pwrsink",
+ .id = -1,
+ .dev = {
+ .platform_data = &trout_pwrsink_data,
+ },
+};
+
+static struct platform_device trout_rfkill = {
+ .name = "trout_rfkill",
+ .id = -1,
+};
+
+static struct msm_pmem_setting pmem_setting = {
+ .pmem_start = MSM_PMEM_MDP_BASE,
+ .pmem_size = MSM_PMEM_MDP_SIZE,
+ .pmem_adsp_start = MSM_PMEM_ADSP_BASE,
+ .pmem_adsp_size = MSM_PMEM_ADSP_SIZE,
+ .pmem_gpu0_start = MSM_PMEM_GPU0_BASE,
+ .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE,
+ .pmem_gpu1_start = MSM_PMEM_GPU1_BASE,
+ .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE,
+ .pmem_camera_start = MSM_PMEM_CAMERA_BASE,
+ .pmem_camera_size = MSM_PMEM_CAMERA_SIZE,
+ .ram_console_start = MSM_RAM_CONSOLE_BASE,
+ .ram_console_size = MSM_RAM_CONSOLE_SIZE,
+};
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+static struct platform_device trout_wifi = {
+ .name = "msm_wifi",
+ .id = 1,
+ .num_resources = 0,
+ .resource = NULL,
+ .dev = {
+ .platform_data = &trout_wifi_control,
+ },
+};
+#endif
+
+#define SND(num, desc) { .name = desc, .id = num }
+static struct snd_endpoint snd_endpoints_list[] = {
+ SND(0, "HANDSET"),
+ SND(1, "SPEAKER"),
+ SND(2, "HEADSET"),
+ SND(3, "BT"),
+ SND(44, "BT_EC_OFF"),
+ SND(10, "HEADSET_AND_SPEAKER"),
+ SND(256, "CURRENT"),
+
+ /* Bluetooth accessories. */
+
+ SND(12, "HTC BH S100"),
+ SND(13, "HTC BH M100"),
+ SND(14, "Motorola H500"),
+ SND(15, "Nokia HS-36W"),
+ SND(16, "PLT 510v.D"),
+ SND(17, "M2500 by Plantronics"),
+ SND(18, "Nokia HDW-3"),
+ SND(19, "HBH-608"),
+ SND(20, "HBH-DS970"),
+ SND(21, "i.Tech BlueBAND"),
+ SND(22, "Nokia BH-800"),
+ SND(23, "Motorola H700"),
+ SND(24, "HTC BH M200"),
+ SND(25, "Jabra JX10"),
+ SND(26, "320Plantronics"),
+ SND(27, "640Plantronics"),
+ SND(28, "Jabra BT500"),
+ SND(29, "Motorola HT820"),
+ SND(30, "HBH-IV840"),
+ SND(31, "6XXPlantronics"),
+ SND(32, "3XXPlantronics"),
+ SND(33, "HBH-PV710"),
+ SND(34, "Motorola H670"),
+ SND(35, "HBM-300"),
+ SND(36, "Nokia BH-208"),
+ SND(37, "Samsung WEP410"),
+ SND(38, "Jabra BT8010"),
+ SND(39, "Motorola S9"),
+ SND(40, "Jabra BT620s"),
+ SND(41, "Nokia BH-902"),
+ SND(42, "HBH-DS220"),
+ SND(43, "HBH-DS980"),
+};
+#undef SND
+
+static struct msm_snd_endpoints trout_snd_endpoints = {
+ .endpoints = snd_endpoints_list,
+ .num = ARRAY_SIZE(snd_endpoints_list),
+};
+
+static struct platform_device trout_snd = {
+ .name = "msm_snd",
+ .id = -1,
+ .dev = {
+ .platform_data = &trout_snd_endpoints,
+ },
+};
+
static struct platform_device *devices[] __initdata = {
- &msm_device_uart3,
&msm_device_smd,
&msm_device_nand,
- &msm_device_hsusb,
&msm_device_i2c,
+ &msm_device_uart1,
+#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W)
+ &msm_device_uart3,
+#endif
+#ifdef CONFIG_SERIAL_MSM_HS
+ &msm_device_uart_dm1,
+#endif
+ &trout_nav_device,
+ &trout_reset_keys_device,
+ &android_leds,
+ &sd_door_switch,
+ &android_timed_gpios,
+#ifdef CONFIG_MT9T013
+ &msm_camera_sensor_mt9t013,
+#endif
+#ifdef CONFIG_SENSORS_MT9T013
+ &trout_camera,
+#endif
+ &trout_rfkill,
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+ &trout_wifi,
+#endif
+#ifdef CONFIG_HTC_HEADSET
+ &trout_h2w,
+#endif
+#ifdef CONFIG_HTC_PWRSINK
+ &trout_pwr_sink,
+#endif
+ &trout_snd,
};
extern struct sys_timer msm_timer;
static void __init trout_init_irq(void)
{
+ printk("trout_init_irq()\n");
msm_init_irq();
}
-static void __init trout_fixup(struct machine_desc *desc, struct tag *tags,
- char **cmdline, struct meminfo *mi)
+static uint opt_disable_uart3;
+
+module_param_named(disable_uart3, opt_disable_uart3, uint, 0);
+
+static void trout_reset(void)
{
- mi->nr_banks = 1;
- mi->bank[0].start = PHYS_OFFSET;
- mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
- mi->bank[0].size = (101*1024*1024);
+ gpio_set_value(TROUT_GPIO_PS_HOLD, 0);
}
+static uint32_t gpio_table[] = {
+ /* BLUETOOTH */
+#ifdef CONFIG_SERIAL_MSM_HS
+ PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */
+ PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */
+ PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */
+ PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */
+#else
+ PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */
+ PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */
+ PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */
+ PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */
+#endif
+};
+
+
+static uint32_t camera_off_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */
+};
+
+static uint32_t camera_on_gpio_table[] = {
+ /* CAMERA */
+ PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */
+ PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */
+ PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */
+ PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */
+ PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */
+ PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */
+ PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */
+ PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */
+ PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */
+ PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */
+ PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */
+ PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */
+ PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */
+ PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */
+};
+
+static void config_gpio_table(uint32_t *table, int len)
+{
+ int n;
+ unsigned id;
+ for(n = 0; n < len; n++) {
+ id = table[n];
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static void config_camera_on_gpios(void)
+{
+ config_gpio_table(camera_on_gpio_table,
+ ARRAY_SIZE(camera_on_gpio_table));
+}
+
+static void config_camera_off_gpios(void)
+{
+ config_gpio_table(camera_off_gpio_table,
+ ARRAY_SIZE(camera_off_gpio_table));
+}
+
+static void __init config_gpios(void)
+{
+ config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table));
+ config_camera_off_gpios();
+}
+
+static struct msm_acpu_clock_platform_data trout_clock_data = {
+ .acpu_switch_time_us = 20,
+ .max_speed_delta_khz = 256000,
+ .vdd_switch_time_us = 62,
+ .power_collapse_khz = 19200000,
+ .wait_for_irq_khz = 128000000,
+};
+
+#ifdef CONFIG_SERIAL_MSM_HS
+static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = {
+ .rx_wakeup_irq = MSM_GPIO_TO_INT(45),
+ .inject_rx_on_wakeup = 1,
+ .rx_to_inject = 0x32,
+};
+#endif
+
static void __init trout_init(void)
{
+ int rc;
+
+ printk("trout_init() revision=%d\n", system_rev);
+
+ /*
+ * Setup common MSM GPIOS
+ */
+ config_gpios();
+
+ msm_hw_reset_hook = trout_reset;
+
+ gpio_direction_output(system_rev < 5 ?
+ TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN, 0);
+
+ msm_acpu_clock_init(&trout_clock_data);
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+ if (!opt_disable_uart3)
+ msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3,
+ &msm_device_uart3.dev, 1,
+ MSM_GPIO_TO_INT(86));
+#endif
+
+ /* gpio_configure(108, IRQF_TRIGGER_LOW); */
+
+ /* put the AF VCM in powerdown mode to avoid noise */
+ gpio_set_value(TROUT_GPIO_VCM_PWDN, 1);
+ mdelay(100);
+
+ if (system_rev < 5) {
+ trout_x_axis.info.gpio = trout_4_x_axis_gpios;
+ trout_y_axis.info.gpio = trout_4_y_axis_gpios;
+ }
+
+#ifdef CONFIG_SERIAL_MSM_HS
+ msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata;
+#endif
+ msm_add_usb_devices(trout_phy_reset);
+
+ msm_add_mem_devices(&pmem_setting);
+
+ rc = trout_init_mmc(system_rev);
+ if (rc)
+ printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc);
+
+#ifdef CONFIG_WIFI_MEM_PREALLOC
+ rc = trout_init_wifi_mem();
+ if (rc)
+ printk(KERN_CRIT "%s: WiFi Memory init failure (%d)\n", __func__, rc);
+#endif
+
platform_add_devices(devices, ARRAY_SIZE(devices));
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* SD card door should wake the device */
+ set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SD_DOOR_N), 1);
}
static struct map_desc trout_io_desc[] __initdata = {
@@ -68,28 +838,32 @@
}
};
+static void __init trout_fixup(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mi->nr_banks=1;
+ mi->bank[0].start = PHYS_OFFSET;
+ mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET);
+ mi->bank[0].size = (101*1024*1024);
+}
+
static void __init trout_map_io(void)
{
msm_map_common_io();
iotable_init(trout_io_desc, ARRAY_SIZE(trout_io_desc));
-
-#ifdef CONFIG_MSM_DEBUG_UART3
- /* route UART3 to the "H2W" extended usb connector */
- writeb(0x80, TROUT_CPLD_BASE + 0x00);
-#endif
-
msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a);
}
-MACHINE_START(TROUT, "HTC Dream")
+MACHINE_START(TROUT, "trout")
+/* Maintainer: Brian Swetland <swetland@google.com> */
#ifdef CONFIG_MSM_DEBUG_UART
- .phys_io = MSM_DEBUG_UART_PHYS,
- .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
+ .phys_io = MSM_DEBUG_UART_PHYS,
+ .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc,
#endif
- .boot_params = 0x10000100,
- .fixup = trout_fixup,
- .map_io = trout_map_io,
- .init_irq = trout_init_irq,
- .init_machine = trout_init,
- .timer = &msm_timer,
+ .boot_params = 0x10000100,
+ .fixup = trout_fixup,
+ .map_io = trout_map_io,
+ .init_irq = trout_init_irq,
+ .init_machine = trout_init,
+ .timer = &msm_timer,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-trout.h b/arch/arm/mach-msm/board-trout.h
index 4f345a5..0a7d274 100644
--- a/arch/arm/mach-msm/board-trout.h
+++ b/arch/arm/mach-msm/board-trout.h
@@ -1,5 +1,162 @@
+/* linux/arch/arm/mach-msm/board-trout.h
+** Author: Brian Swetland <swetland@google.com>
+*/
+#ifndef __ARCH_ARM_MACH_MSM_BOARD_TROUT_H
+#define __ARCH_ARM_MACH_MSM_BOARD_TROUT_H
-#define TROUT_CPLD_BASE 0xE8100000
+#include <mach/board.h>
+
+#define MSM_SMI_BASE 0x00000000
+#define MSM_SMI_SIZE 0x00800000
+
+#define MSM_EBI_BASE 0x10000000
+#define MSM_EBI_SIZE 0x06e00000
+
+#define MSM_PMEM_GPU0_BASE 0x00000000
+#define MSM_PMEM_GPU0_SIZE 0x00700000
+
+#define MSM_PMEM_MDP_BASE 0x02000000
+#define MSM_PMEM_MDP_SIZE 0x00800000
+
+#define MSM_PMEM_ADSP_BASE 0x02800000
+#define MSM_PMEM_ADSP_SIZE 0x00800000
+
+#define MSM_PMEM_CAMERA_BASE 0x03000000
+#define MSM_PMEM_CAMERA_SIZE 0x00800000
+
+#define MSM_FB_BASE 0x03800000
+#define MSM_FB_SIZE 0x00100000
+
+#define MSM_LINUX_BASE MSM_EBI_BASE
+#define MSM_LINUX_SIZE 0x06500000
+
+#define MSM_PMEM_GPU1_SIZE 0x800000
+#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE
+
+#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000
+#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K
+
+#if (MSM_FB_BASE + MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE)
+#error invalid memory map
+#endif
+
+#define DECLARE_MSM_IOMAP
+#include <mach/msm_iomap.h>
+
+#define TROUT_4_BALL_UP_0 1
+#define TROUT_4_BALL_LEFT_0 18
+#define TROUT_4_BALL_DOWN_0 57
+#define TROUT_4_BALL_RIGHT_0 91
+
+#define TROUT_5_BALL_UP_0 94
+#define TROUT_5_BALL_LEFT_0 18
+#define TROUT_5_BALL_DOWN_0 90
+#define TROUT_5_BALL_RIGHT_0 19
+
+#define TROUT_POWER_KEY 20
+
+#define TROUT_4_TP_LS_EN 19
+#define TROUT_5_TP_LS_EN 1
+
+#define TROUT_CPLD_BASE 0xFA000000
#define TROUT_CPLD_START 0x98000000
#define TROUT_CPLD_SIZE SZ_4K
+#define TROUT_GPIO_CABLE_IN1 (83)
+#define TROUT_GPIO_CABLE_IN2 (49)
+
+#define TROUT_GPIO_START (128)
+
+#define TROUT_GPIO_INT_MASK0_REG (0x0c)
+#define TROUT_GPIO_INT_STAT0_REG (0x0e)
+#define TROUT_GPIO_INT_MASK1_REG (0x14)
+#define TROUT_GPIO_INT_STAT1_REG (0x10)
+
+#define TROUT_GPIO_HAPTIC_PWM (28)
+#define TROUT_GPIO_PS_HOLD (25)
+
+#define TROUT_GPIO_MISC2_BASE (TROUT_GPIO_START + 0x00)
+#define TROUT_GPIO_MISC3_BASE (TROUT_GPIO_START + 0x08)
+#define TROUT_GPIO_MISC4_BASE (TROUT_GPIO_START + 0x10)
+#define TROUT_GPIO_MISC5_BASE (TROUT_GPIO_START + 0x18)
+#define TROUT_GPIO_INT2_BASE (TROUT_GPIO_START + 0x20)
+#define TROUT_GPIO_MISC1_BASE (TROUT_GPIO_START + 0x28)
+#define TROUT_GPIO_VIRTUAL_BASE (TROUT_GPIO_START + 0x30)
+#define TROUT_GPIO_INT5_BASE (TROUT_GPIO_START + 0x48)
+
+#define TROUT_GPIO_CHARGER_EN (TROUT_GPIO_MISC2_BASE + 0)
+#define TROUT_GPIO_ISET (TROUT_GPIO_MISC2_BASE + 1)
+#define TROUT_GPIO_H2W_DAT_DIR (TROUT_GPIO_MISC2_BASE + 2)
+#define TROUT_GPIO_H2W_CLK_DIR (TROUT_GPIO_MISC2_BASE + 3)
+#define TROUT_GPIO_H2W_DAT_GPO (TROUT_GPIO_MISC2_BASE + 4)
+#define TROUT_GPIO_H2W_CLK_GPO (TROUT_GPIO_MISC2_BASE + 5)
+#define TROUT_GPIO_H2W_SEL0 (TROUT_GPIO_MISC2_BASE + 6)
+#define TROUT_GPIO_H2W_SEL1 (TROUT_GPIO_MISC2_BASE + 7)
+
+#define TROUT_GPIO_SPOTLIGHT_EN (TROUT_GPIO_MISC3_BASE + 0)
+#define TROUT_GPIO_FLASH_EN (TROUT_GPIO_MISC3_BASE + 1)
+#define TROUT_GPIO_I2C_PULL (TROUT_GPIO_MISC3_BASE + 2)
+#define TROUT_GPIO_TP_I2C_PULL (TROUT_GPIO_MISC3_BASE + 3)
+#define TROUT_GPIO_TP_EN (TROUT_GPIO_MISC3_BASE + 4)
+#define TROUT_GPIO_JOG_EN (TROUT_GPIO_MISC3_BASE + 5)
+#define TROUT_GPIO_UI_LED_EN (TROUT_GPIO_MISC3_BASE + 6)
+#define TROUT_GPIO_QTKEY_LED_EN (TROUT_GPIO_MISC3_BASE + 7)
+
+#define TROUT_GPIO_VCM_PWDN (TROUT_GPIO_MISC4_BASE + 0)
+#define TROUT_GPIO_USB_H2W_SW (TROUT_GPIO_MISC4_BASE + 1)
+#define TROUT_GPIO_COMPASS_RST_N (TROUT_GPIO_MISC4_BASE + 2)
+#define TROUT_GPIO_HAPTIC_EN_UP (TROUT_GPIO_MISC4_BASE + 3)
+#define TROUT_GPIO_HAPTIC_EN_MAIN (TROUT_GPIO_MISC4_BASE + 4)
+#define TROUT_GPIO_USB_PHY_RST_N (TROUT_GPIO_MISC4_BASE + 5)
+#define TROUT_GPIO_WIFI_PA_RESETX (TROUT_GPIO_MISC4_BASE + 6)
+#define TROUT_GPIO_WIFI_EN (TROUT_GPIO_MISC4_BASE + 7)
+
+#define TROUT_GPIO_BT_32K_EN (TROUT_GPIO_MISC5_BASE + 0)
+#define TROUT_GPIO_MAC_32K_EN (TROUT_GPIO_MISC5_BASE + 1)
+#define TROUT_GPIO_MDDI_32K_EN (TROUT_GPIO_MISC5_BASE + 2)
+#define TROUT_GPIO_COMPASS_32K_EN (TROUT_GPIO_MISC5_BASE + 3)
+
+#define TROUT_GPIO_NAVI_ACT_N (TROUT_GPIO_INT2_BASE + 0)
+#define TROUT_GPIO_COMPASS_IRQ (TROUT_GPIO_INT2_BASE + 1)
+#define TROUT_GPIO_SLIDING_DET (TROUT_GPIO_INT2_BASE + 2)
+#define TROUT_GPIO_AUD_HSMIC_DET_N (TROUT_GPIO_INT2_BASE + 3)
+#define TROUT_GPIO_SD_DOOR_N (TROUT_GPIO_INT2_BASE + 4)
+#define TROUT_GPIO_CAM_BTN_STEP1_N (TROUT_GPIO_INT2_BASE + 5)
+#define TROUT_GPIO_CAM_BTN_STEP2_N (TROUT_GPIO_INT2_BASE + 6)
+#define TROUT_GPIO_TP_ATT_N (TROUT_GPIO_INT2_BASE + 7)
+#define TROUT_GPIO_BANK0_FIRST_INT_SOURCE (TROUT_GPIO_NAVI_ACT_N)
+#define TROUT_GPIO_BANK0_LAST_INT_SOURCE (TROUT_GPIO_TP_ATT_N)
+
+#define TROUT_GPIO_H2W_DAT_GPI (TROUT_GPIO_MISC1_BASE + 0)
+#define TROUT_GPIO_H2W_CLK_GPI (TROUT_GPIO_MISC1_BASE + 1)
+#define TROUT_GPIO_CPLD128_VER_0 (TROUT_GPIO_MISC1_BASE + 4)
+#define TROUT_GPIO_CPLD128_VER_1 (TROUT_GPIO_MISC1_BASE + 5)
+#define TROUT_GPIO_CPLD128_VER_2 (TROUT_GPIO_MISC1_BASE + 6)
+#define TROUT_GPIO_CPLD128_VER_3 (TROUT_GPIO_MISC1_BASE + 7)
+
+#define TROUT_GPIO_SDMC_CD_N (TROUT_GPIO_VIRTUAL_BASE + 0)
+#define TROUT_GPIO_END (TROUT_GPIO_SDMC_CD_N)
+#define TROUT_GPIO_BANK1_FIRST_INT_SOURCE (TROUT_GPIO_SDMC_CD_N)
+#define TROUT_GPIO_BANK1_LAST_INT_SOURCE (TROUT_GPIO_SDMC_CD_N)
+
+#define TROUT_GPIO_VIRTUAL_TO_REAL_OFFSET \
+ (TROUT_GPIO_INT5_BASE - TROUT_GPIO_VIRTUAL_BASE)
+
+#define TROUT_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS)
+#define TROUT_INT_BANK0_COUNT (8)
+#define TROUT_INT_BANK1_START (TROUT_INT_START + TROUT_INT_BANK0_COUNT)
+#define TROUT_INT_BANK1_COUNT (1)
+#define TROUT_INT_END (TROUT_INT_START + TROUT_INT_BANK0_COUNT + \
+ TROUT_INT_BANK1_COUNT - 1)
+#define TROUT_GPIO_TO_INT(n) (((n) <= TROUT_GPIO_BANK0_LAST_INT_SOURCE) ? \
+ (TROUT_INT_START - TROUT_GPIO_BANK0_FIRST_INT_SOURCE + (n)) : \
+ (TROUT_INT_BANK1_START - TROUT_GPIO_BANK1_FIRST_INT_SOURCE + (n)))
+
+#define TROUT_INT_TO_BANK(n) ((n - TROUT_INT_START) / TROUT_INT_BANK0_COUNT)
+#define TROUT_INT_TO_MASK(n) (1U << ((n - TROUT_INT_START) & 7))
+#define TROUT_BANK_TO_MASK_REG(bank) \
+ (bank ? TROUT_GPIO_INT_MASK1_REG : TROUT_GPIO_INT_MASK0_REG)
+#define TROUT_BANK_TO_STAT_REG(bank) \
+ (bank ? TROUT_GPIO_INT_STAT1_REG : TROUT_GPIO_INT_STAT0_REG)
+
+#endif /* GUARD */
diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h
index 17d027b..04f7332 100644
--- a/arch/arm/mach-msm/clock-pcom.h
+++ b/arch/arm/mach-msm/clock-pcom.h
@@ -72,8 +72,13 @@
#define P_USB_HS_P_CLK 37 /* High speed USB pbus clock */
#define P_USB_OTG_CLK 38 /* Full speed USB clock */
#define P_VDC_CLK 39 /* Video controller clock */
-#define P_VFE_MDC_CLK 40 /* Camera / Video Front End clock */
-#define P_VFE_CLK 41 /* VFE MDDI client clock */
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+#define P_VFE_MDC_CLK 40 /* VFE MDDI client clock */
+#define P_VFE_CLK 41 /* Camera / Video Front End clock */
+#else/* For radio code base others */
+#define P_VFE_MDC_CLK 41 /* VFE MDDI client clock */
+#define P_VFE_CLK 40 /* Camera / Video Front End clock */
+#endif
#define P_MDP_LCDC_PCLK_CLK 42
#define P_MDP_LCDC_PAD_PCLK_CLK 43
#define P_MDP_VSYNC_CLK 44
@@ -89,7 +94,7 @@
#define P_USB_HS2_CORE_CLK 54 /* High speed USB 2 core clock */
#define P_USB_HS3_CORE_CLK 55 /* High speed USB 3 core clock */
#define P_CAM_M_CLK 56
-#define P_CAMIF_PAD_P_CLK 57
+#define P_QUP_I2C_P_CLK 57
#define P_GRP_2D_CLK 58
#define P_GRP_2D_P_CLK 59
#define P_I2S_CLK 60
@@ -137,6 +142,7 @@
struct clk_ops;
extern struct clk_ops clk_ops_pcom;
+enum clk_reset_action;
int pc_clk_reset(unsigned id, enum clk_reset_action action);
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 9cb1276..9d09658 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -21,10 +21,13 @@
#include <linux/list.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
#include <linux/pm_qos_params.h>
+#include <linux/device.h>
+#include <linux/seq_file.h>
#include <mach/clk.h>
#include "clock.h"
@@ -33,7 +36,7 @@
static DEFINE_MUTEX(clocks_mutex);
static DEFINE_SPINLOCK(clocks_lock);
-static LIST_HEAD(clocks);
+static HLIST_HEAD(clocks);
struct clk *msm_clocks;
unsigned msm_num_clocks;
@@ -44,25 +47,54 @@
static DECLARE_BITMAP(clock_map_enabled, NR_CLKS);
static DEFINE_SPINLOCK(clock_map_lock);
+static struct clk *clk_allocate_handle(struct clk *sclk)
+{
+ unsigned long flags;
+ struct clk_handle *clkh = kzalloc(sizeof(*clkh), GFP_KERNEL);
+ if (!clkh)
+ return ERR_PTR(ENOMEM);
+ clkh->clk.flags = CLKFLAG_HANDLE;
+ clkh->source = sclk;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ hlist_add_head(&clkh->clk.list, &sclk->handles);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ return &clkh->clk;
+}
+
+static struct clk *source_clk(struct clk *clk)
+{
+ struct clk_handle *clkh;
+
+ if (clk->flags & CLKFLAG_HANDLE) {
+ clkh = container_of(clk, struct clk_handle, clk);
+ clk = clkh->source;
+ }
+ return clk;
+}
+
/*
* Standard clock functions defined in include/linux/clk.h
*/
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *clk;
+ struct hlist_node *pos;
mutex_lock(&clocks_mutex);
- list_for_each_entry(clk, &clocks, list)
+ hlist_for_each_entry(clk, pos, &clocks, list)
if (!strcmp(id, clk->name) && clk->dev == dev)
goto found_it;
- list_for_each_entry(clk, &clocks, list)
+ hlist_for_each_entry(clk, pos, &clocks, list)
if (!strcmp(id, clk->name) && clk->dev == NULL)
goto found_it;
clk = ERR_PTR(-ENOENT);
found_it:
+ if (!IS_ERR(clk) && (clk->flags & CLKFLAG_SHARED))
+ clk = clk_allocate_handle(clk);
mutex_unlock(&clocks_mutex);
return clk;
}
@@ -70,6 +102,22 @@
void clk_put(struct clk *clk)
{
+ struct clk_handle *clkh;
+ unsigned long flags;
+
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
+ if (!(clk->flags & CLKFLAG_HANDLE))
+ return;
+
+ clk_set_rate(clk, 0);
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ clkh = container_of(clk, struct clk_handle, clk);
+ hlist_del(&clk->list);
+ kfree(clkh);
+ spin_unlock_irqrestore(&clocks_lock, flags);
}
EXPORT_SYMBOL(clk_put);
@@ -77,6 +125,7 @@
{
unsigned long flags;
spin_lock_irqsave(&clocks_lock, flags);
+ clk = source_clk(clk);
clk->count++;
if (clk->count == 1) {
clk->ops->enable(clk->id);
@@ -93,6 +142,7 @@
{
unsigned long flags;
spin_lock_irqsave(&clocks_lock, flags);
+ clk = source_clk(clk);
BUG_ON(clk->count == 0);
clk->count--;
if (clk->count == 0) {
@@ -115,13 +165,53 @@
unsigned long clk_get_rate(struct clk *clk)
{
+ clk = source_clk(clk);
return clk->ops->get_rate(clk->id);
}
EXPORT_SYMBOL(clk_get_rate);
+static unsigned long clk_find_min_rate_locked(struct clk *clk)
+{
+ unsigned long rate = 0;
+ struct clk_handle *clkh;
+ struct hlist_node *pos;
+
+ hlist_for_each_entry(clkh, pos, &clk->handles, clk.list)
+ if (clkh->rate > rate)
+ rate = clkh->rate;
+ return rate;
+}
+
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- return clk->ops->set_rate(clk->id, rate);
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ if (clk->flags & CLKFLAG_HANDLE) {
+ struct clk_handle *clkh;
+ clkh = container_of(clk, struct clk_handle, clk);
+ clkh->rate = rate;
+ clk = clkh->source;
+ rate = clk_find_min_rate_locked(clk);
+ }
+
+ if (clk->flags & CLKFLAG_MAX) {
+ ret = clk->ops->set_max_rate(clk->id, rate);
+ if (ret)
+ goto err;
+ }
+ if (clk->flags & CLKFLAG_MIN) {
+ ret = clk->ops->set_min_rate(clk->id, rate);
+ if (ret)
+ goto err;
+ }
+
+ if (!(clk->flags & (CLKFLAG_MAX | CLKFLAG_MIN)))
+ ret = clk->ops->set_rate(clk->id, rate);
+err:
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ return ret;
}
EXPORT_SYMBOL(clk_set_rate);
@@ -159,16 +249,49 @@
{
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
+ clk = source_clk(clk);
return clk->ops->set_flags(clk->id, flags);
}
EXPORT_SYMBOL(clk_set_flags);
-/* EBI1 is the only shared clock that several clients want to vote on as of
- * this commit. If this changes in the future, then it might be better to
- * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
- * generic to support different clocks.
- */
-static struct clk *ebi1_clk;
+void clk_enter_sleep(int from_idle)
+{
+}
+
+void clk_exit_sleep(void)
+{
+}
+
+int clks_print_running(void)
+{
+ struct clk *clk;
+ int clk_on_count = 0;
+ struct hlist_node *pos;
+ char buf[100];
+ char *pbuf = buf;
+ int size = sizeof(buf);
+ int wr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+
+ hlist_for_each_entry(clk, pos, &clocks, list) {
+ if (clk->count) {
+ clk_on_count++;
+ wr = snprintf(pbuf, size, " %s", clk->name);
+ if (wr >= size)
+ break;
+ pbuf += wr;
+ size -= wr;
+ }
+ }
+ if (clk_on_count)
+ pr_info("clocks on:%s\n", buf);
+
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ return !clk_on_count;
+}
+EXPORT_SYMBOL(clks_print_running);
static void __init set_clock_ops(struct clk *clk)
{
@@ -188,13 +311,9 @@
msm_num_clocks = num_clocks;
for (n = 0; n < msm_num_clocks; n++) {
set_clock_ops(&msm_clocks[n]);
- list_add_tail(&msm_clocks[n].list, &clocks);
+ hlist_add_head(&msm_clocks[n].list, &clocks);
}
mutex_unlock(&clocks_mutex);
-
- ebi1_clk = clk_get(NULL, "ebi1_clk");
- BUG_ON(ebi1_clk == NULL);
-
}
#if defined(CONFIG_DEBUG_FS)
@@ -263,6 +382,75 @@
return 0;
}
+static void *clk_info_seq_start(struct seq_file *seq, loff_t *ppos)
+{
+ struct hlist_node *pos;
+ int i = *ppos;
+ mutex_lock(&clocks_mutex);
+ hlist_for_each(pos, &clocks)
+ if (i-- == 0)
+ return hlist_entry(pos, struct clk, list);
+ return NULL;
+}
+
+static void *clk_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct clk *clk = v;
+ ++*pos;
+ return hlist_entry(clk->list.next, struct clk, list);
+}
+
+static void clk_info_seq_stop(struct seq_file *seq, void *v)
+{
+ mutex_unlock(&clocks_mutex);
+}
+
+static int clk_info_seq_show(struct seq_file *seq, void *v)
+{
+ struct clk *clk = v;
+ unsigned long flags;
+ struct clk_handle *clkh;
+ struct hlist_node *pos;
+
+ seq_printf(seq, "Clock %s\n", clk->name);
+ seq_printf(seq, " Id %d\n", clk->id);
+ seq_printf(seq, " Count %d\n", clk->count);
+ seq_printf(seq, " Flags %x\n", clk->flags);
+ seq_printf(seq, " Dev %p %s\n",
+ clk->dev, clk->dev ? dev_name(clk->dev) : "");
+ seq_printf(seq, " Handles %p\n", clk->handles.first);
+ spin_lock_irqsave(&clocks_lock, flags);
+ hlist_for_each_entry(clkh, pos, &clk->handles, clk.list)
+ seq_printf(seq, " Requested rate %ld\n", clkh->rate);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ seq_printf(seq, " Enabled %d\n", clk->ops->is_enabled(clk->id));
+ seq_printf(seq, " Rate %ld\n", clk_get_rate(clk));
+
+ seq_printf(seq, "\n");
+ return 0;
+}
+
+static struct seq_operations clk_info_seqops = {
+ .start = clk_info_seq_start,
+ .next = clk_info_seq_next,
+ .stop = clk_info_seq_stop,
+ .show = clk_info_seq_show,
+};
+
+static int clk_info_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &clk_info_seqops);
+}
+
+static const struct file_operations clk_info_fops = {
+ .owner = THIS_MODULE,
+ .open = clk_info_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
clock_debug_rate_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
@@ -274,7 +462,7 @@
{
struct dentry *dent_rate, *dent_enable, *dent_local;
struct clk *clock;
- unsigned n = 0;
+ struct hlist_node *pos;
char temp[50], *ptr;
dent_rate = debugfs_create_dir("clk_rate", 0);
@@ -289,7 +477,10 @@
if (IS_ERR(dent_local))
return PTR_ERR(dent_local);
- while ((clock = msm_clock_get_nth(n++)) != 0) {
+ debugfs_create_file("clk_info", 0x444, 0, NULL, &clk_info_fops);
+
+ mutex_lock(&clocks_mutex);
+ hlist_for_each_entry(clock, pos, &clocks, list) {
strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1);
for (ptr = temp; *ptr; ptr++)
*ptr = tolower(*ptr);
@@ -300,10 +491,11 @@
debugfs_create_file(temp, S_IRUGO, dent_local,
clock, &clock_local_fops);
}
+ mutex_unlock(&clocks_mutex);
return 0;
}
-device_initcall(clock_debug_init);
+late_initcall(clock_debug_init);
#endif
/* The bootloader and/or AMSS may have left various clocks enabled.
@@ -314,10 +506,11 @@
{
unsigned long flags;
struct clk *clk;
+ struct hlist_node *pos;
unsigned count = 0;
mutex_lock(&clocks_mutex);
- list_for_each_entry(clk, &clocks, list) {
+ hlist_for_each_entry(clk, pos, &clocks, list) {
if (clk->flags & CLKFLAG_AUTO_OFF) {
spin_lock_irqsave(&clocks_lock, flags);
if (!clk->count) {
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index c270b55..10d9458 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -27,11 +27,13 @@
#define CLKFLAG_NOINVERT 0x00000002
#define CLKFLAG_NONEST 0x00000004
#define CLKFLAG_NORESET 0x00000008
+#define CLKFLAG_HANDLE 0x00000010
#define CLK_FIRST_AVAILABLE_FLAG 0x00000100
#define CLKFLAG_AUTO_OFF 0x00000200
#define CLKFLAG_MIN 0x00000400
#define CLKFLAG_MAX 0x00000800
+#define CLKFLAG_SHARED 0x00001000
struct clk_ops {
int (*enable)(unsigned id);
@@ -55,8 +57,15 @@
const char *name;
struct clk_ops *ops;
const char *dbg_name;
- struct list_head list;
+ struct hlist_node list;
struct device *dev;
+ struct hlist_head handles;
+};
+
+struct clk_handle {
+ struct clk clk;
+ struct clk *source;
+ unsigned long rate;
};
#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
@@ -105,5 +114,8 @@
int ebi1_clk_set_min_rate(enum clkvote_client client, unsigned long rate);
unsigned long clk_get_max_axi_khz(void);
+void clk_enter_sleep(int from_idle);
+void clk_exit_sleep(void);
+
#endif
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
new file mode 100644
index 0000000..aaa30bb
--- /dev/null
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -0,0 +1,119 @@
+/* arch/arm/mach-msm/cpufreq.c
+ *
+ * MSM architecture cpufreq driver
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007 QUALCOMM Incorporated
+ * Author: Mike A. Chan <mikechan@google.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.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/earlysuspend.h>
+#include <linux/init.h>
+#include "acpuclock.h"
+
+#ifdef CONFIG_MSM_CPU_FREQ_SCREEN
+static void msm_early_suspend(struct early_suspend *handler) {
+ acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, 0);
+}
+
+static void msm_late_resume(struct early_suspend *handler) {
+ acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_ON * 1000, 0);
+}
+
+static struct early_suspend msm_power_suspend = {
+ .suspend = msm_early_suspend,
+ .resume = msm_late_resume,
+};
+
+static int __init clock_late_init(void)
+{
+ register_early_suspend(&msm_power_suspend);
+ return 0;
+}
+
+late_initcall(clock_late_init);
+#else
+
+static int msm_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ int index;
+ struct cpufreq_freqs freqs;
+ struct cpufreq_frequency_table *table =
+ cpufreq_frequency_get_table(policy->cpu);
+
+ if (cpufreq_frequency_table_target(policy, table, target_freq, relation,
+ &index)) {
+ pr_err("cpufreq: invalid target_freq: %d\n", target_freq);
+ return -EINVAL;
+ }
+
+ if (policy->cur == table[index].frequency)
+ return 0;
+
+#ifdef CONFIG_CPU_FREQ_DEBUG
+ printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq,
+ relation, policy->min, policy->max, table[index].frequency);
+#endif
+ freqs.old = policy->cur;
+ freqs.new = table[index].frequency;
+ freqs.cpu = policy->cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ acpuclk_set_rate(table[index].frequency * 1000, 0);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ return 0;
+}
+
+static int msm_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+ return 0;
+}
+
+static int msm_cpufreq_init(struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *table =
+ cpufreq_frequency_get_table(policy->cpu);
+
+ BUG_ON(cpufreq_frequency_table_cpuinfo(policy, table));
+ policy->cur = acpuclk_get_rate();
+ policy->cpuinfo.transition_latency =
+ acpuclk_get_switch_time() * NSEC_PER_USEC;
+ return 0;
+}
+
+static struct freq_attr *msm_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver msm_cpufreq_driver = {
+ /* lps calculations are handled here. */
+ .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS,
+ .init = msm_cpufreq_init,
+ .verify = msm_cpufreq_verify,
+ .target = msm_cpufreq_target,
+ .name = "msm",
+ .attr = msm_cpufreq_attr,
+};
+
+static int __init msm_cpufreq_register(void)
+{
+ return cpufreq_register_driver(&msm_cpufreq_driver);
+}
+
+device_initcall(msm_cpufreq_register);
+#endif
diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c
new file mode 100644
index 0000000..7d37efc
--- /dev/null
+++ b/arch/arm/mach-msm/dal.c
@@ -0,0 +1,603 @@
+/* arch/arm/mach-msm/qdsp6/dal.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+
+#include <linux/delay.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_qdsp6_audio.h>
+
+#include "dal.h"
+
+#define DAL_TRACE 0
+
+struct dal_hdr {
+ uint32_t length:16; /* message length (header inclusive) */
+ uint32_t version:8; /* DAL protocol version */
+ uint32_t priority:7;
+ uint32_t async:1;
+ uint32_t ddi:16; /* DDI method number */
+ uint32_t prototype:8; /* DDI serialization format */
+ uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */
+ void *from;
+ void *to;
+} __attribute__((packed));
+
+#define TRACE_DATA_MAX 128
+#define TRACE_LOG_MAX 32
+#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1)
+
+struct dal_trace {
+ unsigned timestamp;
+ struct dal_hdr hdr;
+ uint32_t data[TRACE_DATA_MAX];
+};
+
+#define DAL_HDR_SIZE (sizeof(struct dal_hdr))
+#define DAL_DATA_MAX 512
+#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX)
+
+#define DAL_VERSION 0x11
+
+#define DAL_MSGID_DDI 0x00
+#define DAL_MSGID_ATTACH 0x01
+#define DAL_MSGID_DETACH 0x02
+#define DAL_MSGID_ASYNCH 0xC0
+#define DAL_MSGID_REPLY 0x80
+
+struct dal_channel {
+ struct list_head list;
+ struct list_head clients;
+
+ /* synchronization for changing channel state,
+ * adding/removing clients, smd callbacks, etc
+ */
+ spinlock_t lock;
+
+ struct smd_channel *sch;
+ char *name;
+
+ /* events are delivered at IRQ context immediately, so
+ * we only need one assembly buffer for the entire channel
+ */
+ struct dal_hdr hdr;
+ unsigned char data[DAL_DATA_MAX];
+
+ unsigned count;
+ void *ptr;
+
+ /* client which the current inbound message is for */
+ struct dal_client *active;
+};
+
+struct dal_client {
+ struct list_head list;
+ struct dal_channel *dch;
+ void *cookie;
+ dal_event_func_t event;
+
+ /* opaque handle for the far side */
+ void *remote;
+
+ /* dal rpc calls are fully synchronous -- only one call may be
+ * active per client at a time
+ */
+ struct mutex write_lock;
+ wait_queue_head_t wait;
+
+ unsigned char data[DAL_DATA_MAX];
+
+ void *reply;
+ int reply_max;
+ int status;
+ unsigned msgid; /* msgid of expected reply */
+
+ spinlock_t tr_lock;
+ unsigned tr_head;
+ unsigned tr_tail;
+ struct dal_trace *tr_log;
+};
+
+static unsigned now(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000);
+}
+
+void dal_trace(struct dal_client *c)
+{
+ if (c->tr_log)
+ return;
+ c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX,
+ GFP_KERNEL);
+}
+
+void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when)
+{
+ int i;
+ printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d",
+ (unsigned) hdr->from, (unsigned) hdr->to,
+ hdr->length, hdr->async,
+ hdr->ddi, hdr->prototype, hdr->msgid,
+ when);
+ len /= 4;
+ for (i = 0; i < len; i++) {
+ if (!(i & 7))
+ printk("\n%03x", i * 4);
+ printk(" %08x", data[i]);
+ }
+ printk("\n");
+}
+
+void dal_trace_dump(struct dal_client *c)
+{
+ struct dal_trace *dt;
+ unsigned n, len;
+
+ if (!c->tr_log)
+ return;
+
+ for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) {
+ dt = c->tr_log + n;
+ len = dt->hdr.length - sizeof(dt->hdr);
+ if (len > TRACE_DATA_MAX)
+ len = TRACE_DATA_MAX;
+ dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp);
+ }
+}
+
+static void dal_trace_log(struct dal_client *c,
+ struct dal_hdr *hdr, void *data, unsigned len)
+{
+ unsigned long flags;
+ unsigned t, n;
+ struct dal_trace *dt;
+
+ t = now();
+ if (len > TRACE_DATA_MAX)
+ len = TRACE_DATA_MAX;
+
+ spin_lock_irqsave(&c->tr_lock, flags);
+ n = (c->tr_head + 1) & TRACE_LOG_MASK;
+ if (c->tr_tail == n)
+ c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK;
+ dt = c->tr_log + n;
+ dt->timestamp = t;
+ memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr));
+ memcpy(dt->data, data, len);
+ c->tr_head = n;
+
+ spin_unlock_irqrestore(&c->tr_lock, flags);
+}
+
+
+static void dal_channel_notify(void *priv, unsigned event)
+{
+ struct dal_channel *dch = priv;
+ struct dal_hdr *hdr = &dch->hdr;
+ struct dal_client *client;
+ unsigned long flags;
+ int len;
+ int r;
+
+ spin_lock_irqsave(&dch->lock, flags);
+
+again:
+ if (dch->count == 0) {
+ if (smd_read_avail(dch->sch) < DAL_HDR_SIZE)
+ goto done;
+
+ smd_read(dch->sch, hdr, DAL_HDR_SIZE);
+
+ if (hdr->length < DAL_HDR_SIZE)
+ goto done;
+
+ if (hdr->length > DAL_MSG_MAX)
+ panic("oversize message");
+
+ dch->count = hdr->length - DAL_HDR_SIZE;
+
+ /* locate the client this message is targeted to */
+ list_for_each_entry(client, &dch->clients, list) {
+ if (dch->hdr.to == client) {
+ dch->active = client;
+ dch->ptr = client->data;
+ goto check_data;
+ }
+ }
+ pr_err("$$$ receiving unknown message len = %d $$$\n",
+ dch->count);
+ dch->active = 0;
+ dch->ptr = dch->data;
+ }
+
+check_data:
+ len = dch->count;
+ if (len > 0) {
+ if (smd_read_avail(dch->sch) < len)
+ goto done;
+
+ r = smd_read(dch->sch, dch->ptr, len);
+ if (r != len)
+ panic("invalid read");
+
+#if DAL_TRACE
+ pr_info("dal recv %p <- %p %02x:%04x:%02x %d\n",
+ hdr->to, hdr->from, hdr->msgid, hdr->ddi,
+ hdr->prototype, hdr->length - sizeof(*hdr));
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len);
+#endif
+ dch->count = 0;
+
+ client = dch->active;
+ if (!client) {
+ pr_err("dal: message to %p discarded\n", dch->hdr.to);
+ goto again;
+ }
+
+ if (client->tr_log)
+ dal_trace_log(client, hdr, dch->ptr, len);
+
+ if (hdr->msgid == DAL_MSGID_ASYNCH) {
+ if (client->event)
+ client->event(dch->ptr, len, client->cookie);
+ else
+ pr_err("dal: client %p has no event handler\n",
+ client);
+ goto again;
+ }
+
+ if (hdr->msgid == client->msgid) {
+ if (!client->remote)
+ client->remote = hdr->from;
+ if (len > client->reply_max)
+ len = client->reply_max;
+ memcpy(client->reply, client->data, len);
+ client->status = len;
+ wake_up(&client->wait);
+ goto again;
+ }
+
+ pr_err("dal: cannot find client %p\n", dch->hdr.to);
+ goto again;
+ }
+
+done:
+ spin_unlock_irqrestore(&dch->lock, flags);
+}
+
+static LIST_HEAD(dal_channel_list);
+static DEFINE_MUTEX(dal_channel_list_lock);
+
+static struct dal_channel *dal_open_channel(const char *name)
+{
+ struct dal_channel *dch;
+
+ /* quick sanity check to avoid trying to talk to
+ * some non-DAL channel...
+ */
+ if (strncmp(name, "DSP_DAL", 7) && strncmp(name, "SMD_DAL", 7))
+ return 0;
+
+ mutex_lock(&dal_channel_list_lock);
+
+ list_for_each_entry(dch, &dal_channel_list, list) {
+ if (!strcmp(dch->name, name))
+ goto found_it;
+ }
+
+ dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL);
+ if (!dch)
+ goto fail;
+
+ dch->name = (char *) (dch + 1);
+ strcpy(dch->name, name);
+ spin_lock_init(&dch->lock);
+ INIT_LIST_HEAD(&dch->clients);
+
+ list_add(&dch->list, &dal_channel_list);
+
+found_it:
+ if (!dch->sch) {
+ if (smd_open(name, &dch->sch, dch, dal_channel_notify))
+ dch = NULL;
+ /* FIXME: wait for channel to open before returning */
+ msleep(100);
+ }
+
+fail:
+ mutex_unlock(&dal_channel_list_lock);
+
+ return dch;
+}
+
+int dal_call_raw(struct dal_client *client,
+ struct dal_hdr *hdr,
+ void *data, int data_len,
+ void *reply, int reply_max)
+{
+ struct dal_channel *dch = client->dch;
+ unsigned long flags;
+
+ client->reply = reply;
+ client->reply_max = reply_max;
+ client->msgid = hdr->msgid | DAL_MSGID_REPLY;
+ client->status = -EBUSY;
+
+#if DAL_TRACE
+ pr_info("dal send %p -> %p %02x:%04x:%02x %d\n",
+ hdr->from, hdr->to, hdr->msgid, hdr->ddi,
+ hdr->prototype, hdr->length - sizeof(*hdr));
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len);
+#endif
+
+ if (client->tr_log)
+ dal_trace_log(client, hdr, data, data_len);
+
+ spin_lock_irqsave(&dch->lock, flags);
+ /* FIXME: ensure entire message is written or none. */
+ smd_write(dch->sch, hdr, sizeof(*hdr));
+ smd_write(dch->sch, data, data_len);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) {
+ dal_trace_dump(client);
+ pr_err("dal: call timed out. dsp is probably dead.\n");
+ dal_trace_print(hdr, data, data_len, 0);
+#if defined(CONFIG_MSM_QDSP6)
+ q6audio_dsp_not_responding();
+#endif
+ }
+
+ return client->status;
+}
+
+int dal_call(struct dal_client *client,
+ unsigned ddi, unsigned prototype,
+ void *data, int data_len,
+ void *reply, int reply_max)
+{
+ struct dal_hdr hdr;
+ int r;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ hdr.length = data_len + sizeof(hdr);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_DDI;
+ hdr.ddi = ddi;
+ hdr.prototype = prototype;
+ hdr.from = client;
+ hdr.to = client->remote;
+
+ if (hdr.length > DAL_MSG_MAX)
+ return -EINVAL;
+
+ mutex_lock(&client->write_lock);
+ r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max);
+ mutex_unlock(&client->write_lock);
+#if 0
+ if ((r > 3) && (((uint32_t*) reply)[0] == 0)) {
+ pr_info("dal call OK\n");
+ } else {
+ pr_info("dal call ERROR\n");
+ }
+#endif
+ return r;
+}
+
+struct dal_msg_attach {
+ uint32_t device_id;
+ char attach[64];
+ char service_name[32];
+} __attribute__((packed));
+
+struct dal_reply_attach {
+ uint32_t status;
+ char name[64];
+};
+
+struct dal_client *dal_attach(uint32_t device_id, const char *name,
+ dal_event_func_t func, void *cookie)
+{
+ struct dal_hdr hdr;
+ struct dal_msg_attach msg;
+ struct dal_reply_attach reply;
+ struct dal_channel *dch;
+ struct dal_client *client;
+ unsigned long flags;
+ int r;
+
+ dch = dal_open_channel(name);
+ if (!dch)
+ return 0;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return 0;
+
+ client->dch = dch;
+ client->event = func;
+ client->cookie = cookie;
+ mutex_init(&client->write_lock);
+ spin_lock_init(&client->tr_lock);
+ init_waitqueue_head(&client->wait);
+
+ spin_lock_irqsave(&dch->lock, flags);
+ list_add(&client->list, &dch->clients);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ memset(&hdr, 0, sizeof(hdr));
+ memset(&msg, 0, sizeof(msg));
+
+ hdr.length = sizeof(hdr) + sizeof(msg);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_ATTACH;
+ hdr.from = client;
+ msg.device_id = device_id;
+
+ r = dal_call_raw(client, &hdr, &msg, sizeof(msg),
+ &reply, sizeof(reply));
+
+ if ((r == sizeof(reply)) && (reply.status == 0)) {
+ reply.name[63] = 0;
+ pr_info("dal_attach: status = %d, name = '%s'\n",
+ reply.status, reply.name);
+ return client;
+ }
+
+ pr_err("dal_attach: failure\n");
+
+ dal_detach(client);
+ return 0;
+}
+
+int dal_detach(struct dal_client *client)
+{
+ struct dal_channel *dch;
+ unsigned long flags;
+
+ mutex_lock(&client->write_lock);
+ if (client->remote) {
+ struct dal_hdr hdr;
+ uint32_t data;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.length = sizeof(hdr) + sizeof(data);
+ hdr.version = DAL_VERSION;
+ hdr.msgid = DAL_MSGID_DETACH;
+ hdr.from = client;
+ hdr.to = client->remote;
+ data = (uint32_t) client;
+
+ dal_call_raw(client, &hdr, &data, sizeof(data),
+ &data, sizeof(data));
+ }
+
+ dch = client->dch;
+ spin_lock_irqsave(&dch->lock, flags);
+ if (dch->active == client) {
+ /* We have received a message header for this client
+ * but not the body of the message. Ensure that when
+ * the body arrives we don't write it into the now-closed
+ * client. In *theory* this should never happen.
+ */
+ dch->active = 0;
+ dch->ptr = dch->data;
+ }
+ list_del(&client->list);
+ spin_unlock_irqrestore(&dch->lock, flags);
+
+ mutex_unlock(&client->write_lock);
+
+ kfree(client);
+ return 0;
+}
+
+void *dal_get_remote_handle(struct dal_client *client)
+{
+ return client->remote;
+}
+
+/* convenience wrappers */
+
+int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1)
+{
+ uint32_t tmp = arg1;
+ int res;
+ res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp));
+ if (res >= 4)
+ return (int) tmp;
+ return res;
+}
+
+int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t arg2)
+{
+ uint32_t tmp[2];
+ int res;
+ tmp[0] = arg1;
+ tmp[1] = arg2;
+ res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t));
+ if (res >= 4)
+ return (int) tmp[0];
+ return res;
+}
+
+int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen)
+{
+ uint32_t tmp[128];
+ int res;
+ int param_idx = 0;
+
+ if (ilen + 4 > DAL_DATA_MAX)
+ return -EINVAL;
+
+ tmp[param_idx] = ilen;
+ param_idx++;
+
+ memcpy(&tmp[param_idx], ibuf, ilen);
+ param_idx += DIV_ROUND_UP(ilen, 4);
+
+ res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ return (int) tmp[0];
+ return res;
+}
+
+int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
+ uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf,
+ uint32_t olen)
+{
+ uint32_t tmp[128];
+ int res;
+ int param_idx = 0;
+
+ if (ilen1 + ilen2 + 8 > DAL_DATA_MAX)
+ return -EINVAL;
+
+ tmp[param_idx] = ilen1;
+ param_idx++;
+
+ memcpy(&tmp[param_idx], ibuf1, ilen1);
+ param_idx += DIV_ROUND_UP(ilen1, 4);
+
+ tmp[param_idx++] = ilen2;
+ memcpy(&tmp[param_idx], ibuf2, ilen2);
+ param_idx += DIV_ROUND_UP(ilen2, 4);
+
+ tmp[param_idx++] = olen;
+ res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp, sizeof(tmp));
+
+ if (res >= 4)
+ res = (int)tmp[0];
+
+ if (!res) {
+ if (tmp[1] > olen)
+ return -EIO;
+ memcpy(obuf, &tmp[2], tmp[1]);
+ }
+ return res;
+}
diff --git a/arch/arm/mach-msm/dal.h b/arch/arm/mach-msm/dal.h
new file mode 100644
index 0000000..c02f5c7
--- /dev/null
+++ b/arch/arm/mach-msm/dal.h
@@ -0,0 +1,65 @@
+/* arch/arm/mach-msm/qdsp6/dal.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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 _MACH_MSM_DAL_
+#define _MACH_MSM_DAL_
+
+struct dal_client;
+
+typedef void (*dal_event_func_t)(void *data, int len, void *cookie);
+
+struct dal_client *dal_attach(uint32_t device_id, const char *name,
+ dal_event_func_t func, void *cookie);
+
+int dal_detach(struct dal_client *client);
+
+int dal_call(struct dal_client *client,
+ unsigned ddi, unsigned prototype,
+ void *data, int data_len,
+ void *reply, int reply_max);
+
+void dal_trace(struct dal_client *client);
+void dal_trace_dump(struct dal_client *client);
+
+/* function to call before panic on stalled dal calls */
+void dal_set_oops(struct dal_client *client, void (*oops)(void));
+
+/* convenience wrappers */
+int dal_call_f0(struct dal_client *client, uint32_t ddi,
+ uint32_t arg1);
+int dal_call_f1(struct dal_client *client, uint32_t ddi,
+ uint32_t arg1, uint32_t arg2);
+int dal_call_f5(struct dal_client *client, uint32_t ddi,
+ void *ibuf, uint32_t ilen);
+int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
+ uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf,
+ uint32_t olen);
+
+/* common DAL operations */
+enum {
+ DAL_OP_ATTACH = 0,
+ DAL_OP_DETACH,
+ DAL_OP_INIT,
+ DAL_OP_DEINIT,
+ DAL_OP_OPEN,
+ DAL_OP_CLOSE,
+ DAL_OP_INFO,
+ DAL_OP_POWEREVENT,
+ DAL_OP_SYSREQUEST,
+ DAL_OP_FIRST_DEVICE_API,
+};
+
+#endif
diff --git a/arch/arm/mach-msm/devices-msm7x00.c b/arch/arm/mach-msm/devices-msm7x00.c
index fde9d8f..a49c3ff 100644
--- a/arch/arm/mach-msm/devices-msm7x00.c
+++ b/arch/arm/mach-msm/devices-msm7x00.c
@@ -15,10 +15,14 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
#include <mach/irqs.h>
#include <mach/msm_iomap.h>
+#include <mach/dma.h>
#include "devices.h"
+#include "proc_comm.h"
#include <asm/mach/flash.h>
#include <linux/mtd/nand.h>
@@ -88,6 +92,92 @@
.resource = resources_uart3,
};
+static struct resource msm_uart1_dm_resources[] = {
+ {
+ .start = MSM_UART1DM_PHYS,
+ .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART1DM_IRQ,
+ .end = INT_UART1DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART1DM_RX,
+ .end = INT_UART1DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CHAN,
+ .end = DMOV_HSUART1_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CRCI,
+ .end = DMOV_HSUART1_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm1 = {
+ .name = "msm_serial_hs",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_uart1_dm_resources),
+ .resource = msm_uart1_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm1_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource msm_uart2_dm_resources[] = {
+ {
+ .start = MSM_UART2DM_PHYS,
+ .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART2DM_IRQ,
+ .end = INT_UART2DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART2DM_RX,
+ .end = INT_UART2DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CHAN,
+ .end = DMOV_HSUART2_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CRCI,
+ .end = DMOV_HSUART2_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm2 = {
+ .name = "msm_serial_hs",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(msm_uart2_dm_resources),
+ .resource = msm_uart2_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm2_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
static struct resource resources_i2c[] = {
{
.start = MSM_I2C_PHYS,
@@ -108,6 +198,30 @@
.resource = resources_i2c,
};
+#define GPIO_I2C_CLK 60
+#define GPIO_I2C_DAT 61
+void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat)
+{
+ unsigned id;
+ if (gpio) {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ *gpio_clk = GPIO_I2C_CLK;
+ *gpio_dat = GPIO_I2C_DAT;
+ } else {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
static struct resource resources_hsusb[] = {
{
.start = MSM_HSUSB_PHYS,
@@ -346,24 +460,119 @@
return platform_device_register(pdev);
}
+static struct resource resources_mddi0[] = {
+ {
+ .start = MSM_PMDH_PHYS,
+ .end = MSM_PMDH_PHYS + MSM_PMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_PRI,
+ .end = INT_MDDI_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource resources_mddi1[] = {
+ {
+ .start = MSM_EMDH_PHYS,
+ .end = MSM_EMDH_PHYS + MSM_EMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_EXT,
+ .end = INT_MDDI_EXT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mddi0 = {
+ .name = "msm_mddi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mddi0),
+ .resource = resources_mddi0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_mddi1 = {
+ .name = "msm_mddi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_mddi1),
+ .resource = resources_mddi1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct resource resources_mdp[] = {
+ {
+ .start = MSM_MDP_PHYS,
+ .end = MSM_MDP_PHYS + MSM_MDP_SIZE - 1,
+ .name = "mdp",
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = INT_MDP,
+ .end = INT_MDP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mdp = {
+ .name = "msm_mdp",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mdp),
+ .resource = resources_mdp,
+};
+
+static struct resource resources_tssc[] = {
+ {
+ .start = MSM_TSSC_PHYS,
+ .end = MSM_TSSC_PHYS + MSM_TSSC_SIZE - 1,
+ .name = "tssc",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_TCHSCRN1,
+ .end = INT_TCHSCRN1,
+ .name = "tssc1",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+ {
+ .start = INT_TCHSCRN2,
+ .end = INT_TCHSCRN2,
+ .name = "tssc2",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+};
+
+struct platform_device msm_device_touchscreen = {
+ .name = "msm_touchscreen",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_tssc),
+ .resource = resources_tssc,
+};
+
struct clk msm_clocks_7x01a[] = {
CLK_PCOM("adm_clk", ADM_CLK, NULL, 0),
CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0),
- CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, 0),
+ CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN),
CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0),
CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0),
- CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF),
- CLK_PCOM("gp_clk", GP_CLK, NULL, 0),
+ CLK_PCOM("mddi_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF),
+ CLK_PCOM("gp_clk", GP_CLK, NULL, 0),
CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, OFF),
CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0),
CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0),
CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0),
CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF),
CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0),
- CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF),
+ CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF),
CLK_PCOM("pbus_clk", PBUS_CLK, NULL, 0),
CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0),
- CLK_PCOM("pmdh_clk", PMDH_CLK, NULL, OFF ),
+ CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX),
CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF),
CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF),
CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF),
@@ -378,14 +587,14 @@
CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0),
CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0),
CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF),
- CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0),
+ CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF),
CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF),
- CLK_PCOM("uart1dm_clk", UART1DM_CLK, NULL, OFF),
- CLK_PCOM("uart2dm_clk", UART2DM_CLK, NULL, 0),
+ CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF),
+ CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF),
CLK_PCOM("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF),
CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, &msm_device_hsusb.dev, OFF),
CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0),
- CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF ),
+ CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MINMAX),
CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF),
CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF),
};
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index b449e8a..995d966 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -23,6 +23,7 @@
#include <mach/board.h>
#include "devices.h"
+#include "proc_comm.h"
#include "smd_private.h"
#include <asm/mach/flash.h>
@@ -31,6 +32,19 @@
#include <mach/mmc.h>
+static struct resource resources_uart1[] = {
+ {
+ .start = INT_UART1,
+ .end = INT_UART1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART1_PHYS,
+ .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
static struct resource resources_uart2[] = {
{
.start = INT_UART2,
@@ -44,6 +58,26 @@
},
};
+static struct resource resources_uart3[] = {
+ {
+ .start = INT_UART3,
+ .end = INT_UART3,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART3_PHYS,
+ .end = MSM_UART3_PHYS + MSM_UART3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm_device_uart1 = {
+ .name = "msm_serial",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_uart1),
+ .resource = resources_uart1,
+};
+
struct platform_device msm_device_uart2 = {
.name = "msm_serial",
.id = 1,
@@ -51,15 +85,585 @@
.resource = resources_uart2,
};
+struct platform_device msm_device_uart3 = {
+ .name = "msm_serial",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(resources_uart3),
+ .resource = resources_uart3,
+};
+
+static struct resource msm_uart1_dm_resources[] = {
+ {
+ .start = MSM_UART1DM_PHYS,
+ .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART1DM_IRQ,
+ .end = INT_UART1DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART1DM_RX,
+ .end = INT_UART1DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CHAN,
+ .end = DMOV_HSUART1_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CRCI,
+ .end = DMOV_HSUART1_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm1 = {
+ .name = "msm_serial_hs",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_uart1_dm_resources),
+ .resource = msm_uart1_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm1_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource msm_uart2_dm_resources[] = {
+ {
+ .start = MSM_UART2DM_PHYS,
+ .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART2DM_IRQ,
+ .end = INT_UART2DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART2DM_RX,
+ .end = INT_UART2DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CHAN,
+ .end = DMOV_HSUART2_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CRCI,
+ .end = DMOV_HSUART2_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm2 = {
+ .name = "msm_serial_hs",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(msm_uart2_dm_resources),
+ .resource = msm_uart2_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm2_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource resources_i2c[] = {
+ {
+ .start = MSM_I2C_PHYS,
+ .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_PWB_I2C,
+ .end = INT_PWB_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_i2c = {
+ .name = "msm_i2c",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_i2c),
+ .resource = resources_i2c,
+};
+
+#define GPIO_I2C_CLK 70
+#define GPIO_I2C_DAT 71
+void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat)
+{
+ unsigned id;
+ if (gpio) {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ *gpio_clk = GPIO_I2C_CLK;
+ *gpio_dat = GPIO_I2C_DAT;
+ } else {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static struct resource resources_i2c2[] = {
+ {
+ .start = MSM_I2C_2_PHYS,
+ .end = MSM_I2C_2_PHYS + MSM_I2C_2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_PWB_I2C_2,
+ .end = INT_PWB_I2C_2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_i2c2 = {
+ .name = "msm_i2c",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_i2c2),
+ .resource = resources_i2c2,
+};
+
+static struct resource resources_qup[] = {
+ {
+ .name = "qup_phys_addr",
+ .start = MSM_QUP_PHYS,
+ .end = MSM_QUP_PHYS + MSM_QUP_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "gsbi_qup_i2c_addr",
+ .start = MSM_GSBI_QUP_I2C_PHYS,
+ .end = MSM_GSBI_QUP_I2C_PHYS + MSM_GSBI_QUP_I2C_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "qup_in_intr",
+ .start = INT_PWB_QUP_IN,
+ .end = INT_PWB_QUP_IN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "qup_out_intr",
+ .start = INT_PWB_QUP_OUT,
+ .end = INT_PWB_QUP_OUT,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "qup_err_intr",
+ .start = INT_PWB_QUP_ERR,
+ .end = INT_PWB_QUP_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_qup_i2c = {
+ .name = "qup_i2c",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(resources_qup),
+ .resource = resources_qup,
+};
+
+static struct resource resources_hsusb[] = {
+ {
+ .start = MSM_HSUSB_PHYS,
+ .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_USB_HS,
+ .end = INT_USB_HS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_hsusb = {
+ .name = "msm_hsusb",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_hsusb),
+ .resource = resources_hsusb,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct flash_platform_data msm_nand_data = {
+ .parts = NULL,
+ .nr_parts = 0,
+};
+
+static struct resource resources_nand[] = {
+ [0] = {
+ .start = 7,
+ .end = 7,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device msm_device_nand = {
+ .name = "msm_nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nand),
+ .resource = resources_nand,
+ .dev = {
+ .platform_data = &msm_nand_data,
+ },
+};
+
+struct platform_device msm_device_smd = {
+ .name = "msm_smd",
+ .id = -1,
+};
+
+static struct resource resources_sdc1[] = {
+ {
+ .start = MSM_SDC1_PHYS,
+ .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC1_0,
+ .end = INT_SDC1_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC1_1,
+ .end = INT_SDC1_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc2[] = {
+ {
+ .start = MSM_SDC2_PHYS,
+ .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC2_0,
+ .end = INT_SDC2_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC2_1,
+ .end = INT_SDC2_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc3[] = {
+ {
+ .start = MSM_SDC3_PHYS,
+ .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC3_0,
+ .end = INT_SDC3_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC3_1,
+ .end = INT_SDC3_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc4[] = {
+ {
+ .start = MSM_SDC4_PHYS,
+ .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC4_0,
+ .end = INT_SDC4_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC4_1,
+ .end = INT_SDC4_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device msm_device_sdc1 = {
+ .name = "msm_sdcc",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_sdc1),
+ .resource = resources_sdc1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc2 = {
+ .name = "msm_sdcc",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(resources_sdc2),
+ .resource = resources_sdc2,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc3 = {
+ .name = "msm_sdcc",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(resources_sdc3),
+ .resource = resources_sdc3,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc4 = {
+ .name = "msm_sdcc",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(resources_sdc4),
+ .resource = resources_sdc4,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct platform_device *msm_sdcc_devices[] __initdata = {
+ &msm_device_sdc1,
+ &msm_device_sdc2,
+ &msm_device_sdc3,
+ &msm_device_sdc4,
+};
+
+int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+
+ if (controller < 1 || controller > 4)
+ return -EINVAL;
+
+ pdev = msm_sdcc_devices[controller-1];
+ pdev->dev.platform_data = plat;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq");
+ if (!res)
+ return -EINVAL;
+ else if (stat_irq) {
+ res->start = res->end = stat_irq;
+ res->flags &= ~IORESOURCE_DISABLED;
+ res->flags |= stat_irq_flags;
+ }
+
+ return platform_device_register(pdev);
+}
+
+static struct resource resources_mddi0[] = {
+ {
+ .start = MSM_PMDH_PHYS,
+ .end = MSM_PMDH_PHYS + MSM_PMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_PRI,
+ .end = INT_MDDI_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource resources_mddi1[] = {
+ {
+ .start = MSM_EMDH_PHYS,
+ .end = MSM_EMDH_PHYS + MSM_EMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_EXT,
+ .end = INT_MDDI_EXT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mddi0 = {
+ .name = "msm_mddi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mddi0),
+ .resource = resources_mddi0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_mddi1 = {
+ .name = "msm_mddi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_mddi1),
+ .resource = resources_mddi1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct resource resources_mdp[] = {
+ {
+ .start = MSM_MDP_PHYS,
+ .end = MSM_MDP_PHYS + MSM_MDP_SIZE - 1,
+ .name = "mdp",
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = INT_MDP,
+ .end = INT_MDP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mdp = {
+ .name = "msm_mdp",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mdp),
+ .resource = resources_mdp,
+};
+
+
+static struct resource resources_ssbi_pmic[] = {
+ {
+ .start = MSM_PMIC_SSBI_PHYS,
+ .end = MSM_PMIC_SSBI_PHYS + MSM_PMIC_SSBI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm_device_ssbi_pmic = {
+ .name = "msm_ssbi",
+ .id = -1,
+ .resource = resources_ssbi_pmic,
+ .num_resources = ARRAY_SIZE(resources_ssbi_pmic),
+};
+
+static struct resource resources_spi[] = {
+ {
+ .start = MSM_SPI_PHYS,
+ .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SPI_INPUT,
+ .end = INT_SPI_INPUT,
+ .name = "irq_in",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_SPI_OUTPUT,
+ .end = INT_SPI_OUTPUT,
+ .name = "irq_out",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_SPI_ERROR,
+ .end = INT_SPI_ERROR,
+ .name = "irq_err",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_spi = {
+ .name = "msm_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_spi),
+ .resource = resources_spi,
+};
+
+static struct resource msm_vidc_720p_resources[] = {
+ {
+ .start = 0xA3B00000,
+ .end = 0xA3B00000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MFC720,
+ .end = INT_MFC720,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_vidc_720p = {
+ .name = "msm_vidc_720p",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_vidc_720p_resources),
+ .resource = msm_vidc_720p_resources,
+};
+
struct clk msm_clocks_7x30[] = {
CLK_PCOM("adm_clk", ADM_CLK, NULL, 0),
CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0),
CLK_PCOM("cam_m_clk", CAM_M_CLK, NULL, 0),
- CLK_PCOM("camif_pad_pclk", CAMIF_PAD_P_CLK, NULL, OFF),
- CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN),
+ CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN | CLKFLAG_SHARED),
CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0),
- CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX),
- CLK_PCOM("emdh_pclk", EMDH_P_CLK, NULL, OFF),
+ CLK_PCOM("emdh_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX),
+ CLK_PCOM("emdh_pclk", EMDH_P_CLK, &msm_device_mddi1.dev, OFF),
CLK_PCOM("gp_clk", GP_CLK, NULL, 0),
CLK_PCOM("grp_2d_clk", GRP_2D_CLK, NULL, 0),
CLK_PCOM("grp_2d_pclk", GRP_2D_P_CLK, NULL, 0),
@@ -68,43 +672,57 @@
CLK_7X30S("grp_src_clk", GRP_3D_SRC_CLK, GRP_3D_CLK, NULL, 0),
CLK_PCOM("hdmi_clk", HDMI_CLK, NULL, 0),
CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF),
+ CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, OFF),
+ CLK_PCOM("i2c_clk", I2C_2_CLK, &msm_device_i2c2.dev, OFF),
CLK_PCOM("jpeg_clk", JPEG_CLK, NULL, OFF),
CLK_PCOM("jpeg_pclk", JPEG_P_CLK, NULL, OFF),
CLK_PCOM("lpa_codec_clk", LPA_CODEC_CLK, NULL, 0),
CLK_PCOM("lpa_core_clk", LPA_CORE_CLK, NULL, 0),
CLK_PCOM("lpa_pclk", LPA_P_CLK, NULL, 0),
CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0),
- CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX),
- CLK_PCOM("mddi_pclk", PMDH_P_CLK, NULL, 0),
- CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF),
- CLK_PCOM("mdp_pclk", MDP_P_CLK, NULL, 0),
- CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0),
- CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0),
- CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0),
+ CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX),
+ CLK_PCOM("mddi_pclk", PMDH_P_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX),
+ CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF),
+ CLK_PCOM("mdp_pclk", MDP_P_CLK, &msm_device_mdp.dev, OFF),
+ CLK_PCOM("lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, &msm_device_mdp.dev, 0),
+ CLK_PCOM("lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, &msm_device_mdp.dev, 0),
+ CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, &msm_device_mdp.dev, 0),
CLK_PCOM("mfc_clk", MFC_CLK, NULL, 0),
CLK_PCOM("mfc_div2_clk", MFC_DIV2_CLK, NULL, 0),
CLK_PCOM("mfc_pclk", MFC_P_CLK, NULL, 0),
CLK_PCOM("mi2s_m_clk", MI2S_M_CLK, NULL, 0),
CLK_PCOM("mi2s_s_clk", MI2S_S_CLK, NULL, 0),
- CLK_PCOM("mi2s_codec_rx_m_clk", MI2S_CODEC_RX_M_CLK, NULL, 0),
- CLK_PCOM("mi2s_codec_rx_s_clk", MI2S_CODEC_RX_S_CLK, NULL, 0),
- CLK_PCOM("mi2s_codec_tx_m_clk", MI2S_CODEC_TX_M_CLK, NULL, 0),
- CLK_PCOM("mi2s_codec_tx_s_clk", MI2S_CODEC_TX_S_CLK, NULL, 0),
+ CLK_PCOM("mi2s_codec_rx_mclk", MI2S_CODEC_RX_M_CLK, NULL, 0),
+ CLK_PCOM("mi2s_codec_rx_sclk", MI2S_CODEC_RX_S_CLK, NULL, 0),
+ CLK_PCOM("mi2s_codec_tx_mclk", MI2S_CODEC_TX_M_CLK, NULL, 0),
+ CLK_PCOM("mi2s_codec_tx_sclk", MI2S_CODEC_TX_S_CLK, NULL, 0),
CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN),
CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0),
CLK_PCOM("rotator_clk", AXI_ROTATOR_CLK, NULL, 0),
CLK_PCOM("rotator_imem_clk", ROTATOR_IMEM_CLK, NULL, OFF),
CLK_PCOM("rotator_pclk", ROTATOR_P_CLK, NULL, OFF),
CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF),
- CLK_PCOM("spi_clk", SPI_CLK, NULL, 0),
- CLK_PCOM("spi_pclk", SPI_P_CLK, NULL, 0),
+ CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC2_P_CLK, &msm_device_sdc2.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC3_P_CLK, &msm_device_sdc3.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC4_P_CLK, &msm_device_sdc4.dev, OFF),
+ CLK_PCOM("spi_clk", SPI_CLK, &msm_device_spi.dev, 0),
+ CLK_PCOM("spi_pclk", SPI_P_CLK, &msm_device_spi.dev, 0),
CLK_7X30S("tv_src_clk", TV_CLK, TV_ENC_CLK, NULL, 0),
CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0),
CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0),
- CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0),
+ CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF),
+ CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF),
+ CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF),
+ CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF),
+ CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF),
CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF),
CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF),
- CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, NULL, OFF),
+ CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, &msm_device_hsusb.dev, OFF),
CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF),
CLK_PCOM("usb_hs2_pclk", USB_HS2_P_CLK, NULL, OFF),
CLK_PCOM("usb_hs2_core_clk", USB_HS2_CORE_CLK, NULL, OFF),
@@ -117,6 +735,8 @@
CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, 0),
CLK_PCOM("vfe_pclk", VFE_P_CLK, NULL, OFF),
CLK_PCOM("vpe_clk", VPE_CLK, NULL, 0),
+ CLK_PCOM("qup_clk", QUP_I2C_CLK, &msm_device_qup_i2c.dev, OFF),
+ CLK_PCOM("qup_pclk", QUP_I2C_P_CLK, &msm_device_qup_i2c.dev, OFF),
/* 7x30 v2 hardware only. */
CLK_PCOM("csi_clk", CSI0_CLK, NULL, 0),
diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c
index 4d4a507..0cb7e8d 100644
--- a/arch/arm/mach-msm/devices-qsd8x50.c
+++ b/arch/arm/mach-msm/devices-qsd8x50.c
@@ -13,8 +13,10 @@
*
*/
+#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <mach/irqs.h>
@@ -23,11 +25,40 @@
#include <mach/board.h>
#include "devices.h"
+#include "proc_comm.h"
#include <asm/mach/flash.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
#include <mach/mmc.h>
+static struct resource resources_uart1[] = {
+ {
+ .start = INT_UART1,
+ .end = INT_UART1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART1_PHYS,
+ .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource resources_uart2[] = {
+ {
+ .start = INT_UART2,
+ .end = INT_UART2,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART2_PHYS,
+ .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
static struct resource resources_uart3[] = {
{
.start = INT_UART3,
@@ -41,6 +72,20 @@
},
};
+struct platform_device msm_device_uart1 = {
+ .name = "msm_serial",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_uart1),
+ .resource = resources_uart1,
+};
+
+struct platform_device msm_device_uart2 = {
+ .name = "msm_serial",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_uart2),
+ .resource = resources_uart2,
+};
+
struct platform_device msm_device_uart3 = {
.name = "msm_serial",
.id = 2,
@@ -48,34 +93,555 @@
.resource = resources_uart3,
};
+static struct resource msm_uart1_dm_resources[] = {
+ {
+ .start = MSM_UART1DM_PHYS,
+ .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART1DM_IRQ,
+ .end = INT_UART1DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART1DM_RX,
+ .end = INT_UART1DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CHAN,
+ .end = DMOV_HSUART1_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART1_TX_CRCI,
+ .end = DMOV_HSUART1_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm1 = {
+ .name = "msm_serial_hs",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_uart1_dm_resources),
+ .resource = msm_uart1_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm1_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource msm_uart2_dm_resources[] = {
+ {
+ .start = MSM_UART2DM_PHYS,
+ .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_UART2DM_IRQ,
+ .end = INT_UART2DM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_UART2DM_RX,
+ .end = INT_UART2DM_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CHAN,
+ .end = DMOV_HSUART2_RX_CHAN,
+ .name = "uartdm_channels",
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DMOV_HSUART2_TX_CRCI,
+ .end = DMOV_HSUART2_RX_CRCI,
+ .name = "uartdm_crci",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device msm_device_uart_dm2 = {
+ .name = "msm_serial_hs",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(msm_uart2_dm_resources),
+ .resource = msm_uart2_dm_resources,
+ .dev = {
+ .dma_mask = &msm_uart_dm2_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource resources_i2c[] = {
+ {
+ .start = MSM_I2C_PHYS,
+ .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_PWB_I2C,
+ .end = INT_PWB_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_i2c = {
+ .name = "msm_i2c",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_i2c),
+ .resource = resources_i2c,
+};
+
+#define GPIO_I2C_CLK 95
+#define GPIO_I2C_DAT 96
+static int gpio_i2c_clk = -1;
+static int gpio_i2c_dat = -1;
+void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat)
+{
+ unsigned id;
+
+ if (gpio_i2c_clk < 0) {
+ gpio_request(GPIO_I2C_CLK, "i2c-clk");
+ gpio_i2c_clk = GPIO_I2C_CLK;
+ }
+ if (gpio_i2c_dat < 0) {
+ gpio_request(GPIO_I2C_DAT, "i2c-dat");
+ gpio_i2c_dat = GPIO_I2C_DAT;
+ }
+
+ if (gpio) {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT,
+ GPIO_NO_PULL, GPIO_2MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ *gpio_clk = GPIO_I2C_CLK;
+ *gpio_dat = GPIO_I2C_DAT;
+ } else {
+ id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT,
+ GPIO_NO_PULL, GPIO_8MA);
+ msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+ }
+}
+
+static struct resource resources_hsusb[] = {
+ {
+ .start = MSM_HSUSB_PHYS,
+ .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_USB_HS,
+ .end = INT_USB_HS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_hsusb = {
+ .name = "msm_hsusb",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_hsusb),
+ .resource = resources_hsusb,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct flash_platform_data msm_nand_data = {
+ .parts = NULL,
+ .nr_parts = 0,
+};
+
+static struct resource resources_nand[] = {
+ [0] = {
+ .start = 7,
+ .end = 7,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device msm_device_nand = {
+ .name = "msm_nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_nand),
+ .resource = resources_nand,
+ .dev = {
+ .platform_data = &msm_nand_data,
+ },
+};
+
+struct platform_device msm_device_smd = {
+ .name = "msm_smd",
+ .id = -1,
+};
+
+static struct resource resources_sdc1[] = {
+ {
+ .start = MSM_SDC1_PHYS,
+ .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC1_0,
+ .end = INT_SDC1_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC1_1,
+ .end = INT_SDC1_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc2[] = {
+ {
+ .start = MSM_SDC2_PHYS,
+ .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC2_0,
+ .end = INT_SDC2_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC2_1,
+ .end = INT_SDC2_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc3[] = {
+ {
+ .start = MSM_SDC3_PHYS,
+ .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC3_0,
+ .end = INT_SDC3_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC3_1,
+ .end = INT_SDC3_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct resource resources_sdc4[] = {
+ {
+ .start = MSM_SDC4_PHYS,
+ .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SDC4_0,
+ .end = INT_SDC4_0,
+ .flags = IORESOURCE_IRQ,
+ .name = "cmd_irq",
+ },
+ {
+ .start = INT_SDC4_1,
+ .end = INT_SDC4_1,
+ .flags = IORESOURCE_IRQ,
+ .name = "pio_irq",
+ },
+ {
+ .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
+ .name = "status_irq"
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device msm_device_sdc1 = {
+ .name = "msm_sdcc",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_sdc1),
+ .resource = resources_sdc1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc2 = {
+ .name = "msm_sdcc",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(resources_sdc2),
+ .resource = resources_sdc2,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc3 = {
+ .name = "msm_sdcc",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(resources_sdc3),
+ .resource = resources_sdc3,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_sdc4 = {
+ .name = "msm_sdcc",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(resources_sdc4),
+ .resource = resources_sdc4,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct platform_device *msm_sdcc_devices[] __initdata = {
+ &msm_device_sdc1,
+ &msm_device_sdc2,
+ &msm_device_sdc3,
+ &msm_device_sdc4,
+};
+
+int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
+ unsigned int stat_irq, unsigned long stat_irq_flags)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+
+ if (controller < 1 || controller > 4)
+ return -EINVAL;
+
+ pdev = msm_sdcc_devices[controller-1];
+ pdev->dev.platform_data = plat;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq");
+ if (!res)
+ return -EINVAL;
+ else if (stat_irq) {
+ res->start = res->end = stat_irq;
+ res->flags &= ~IORESOURCE_DISABLED;
+ res->flags |= stat_irq_flags;
+ }
+
+ return platform_device_register(pdev);
+}
+
+static struct resource resources_mddi0[] = {
+ {
+ .start = MSM_PMDH_PHYS,
+ .end = MSM_PMDH_PHYS + MSM_PMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_PRI,
+ .end = INT_MDDI_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource resources_mddi1[] = {
+ {
+ .start = MSM_EMDH_PHYS,
+ .end = MSM_EMDH_PHYS + MSM_EMDH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_MDDI_EXT,
+ .end = INT_MDDI_EXT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mddi0 = {
+ .name = "msm_mddi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mddi0),
+ .resource = resources_mddi0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+struct platform_device msm_device_mddi1 = {
+ .name = "msm_mddi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_mddi1),
+ .resource = resources_mddi1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ },
+};
+
+static struct resource resources_mdp[] = {
+ {
+ .start = MSM_MDP_PHYS,
+ .end = MSM_MDP_PHYS + MSM_MDP_SIZE - 1,
+ .name = "mdp",
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = INT_MDP,
+ .end = INT_MDP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_mdp = {
+ .name = "msm_mdp",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_mdp),
+ .resource = resources_mdp,
+};
+
+static struct resource resources_tssc[] = {
+ {
+ .start = MSM_TSSC_PHYS,
+ .end = MSM_TSSC_PHYS + MSM_TSSC_SIZE - 1,
+ .name = "tssc",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_TCHSCRN1,
+ .end = INT_TCHSCRN1,
+ .name = "tssc1",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+ {
+ .start = INT_TCHSCRN2,
+ .end = INT_TCHSCRN2,
+ .name = "tssc2",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+};
+
+struct platform_device msm_device_touchscreen = {
+ .name = "msm_touchscreen",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_tssc),
+ .resource = resources_tssc,
+};
+
+static struct resource resources_spi[] = {
+ {
+ .start = MSM_SPI_PHYS,
+ .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = INT_SPI_INPUT,
+ .end = INT_SPI_INPUT,
+ .name = "irq_in",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_SPI_OUTPUT,
+ .end = INT_SPI_OUTPUT,
+ .name = "irq_out",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = INT_SPI_ERROR,
+ .end = INT_SPI_ERROR,
+ .name = "irq_err",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_spi = {
+ .name = "msm_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_spi),
+ .resource = resources_spi,
+};
+
struct clk msm_clocks_8x50[] = {
CLK_PCOM("adm_clk", ADM_CLK, NULL, 0),
- CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN),
+ CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN | CLKFLAG_SHARED),
CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0),
CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0),
- CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX),
+ CLK_PCOM("mddi_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX),
CLK_PCOM("gp_clk", GP_CLK, NULL, 0),
CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, 0),
+ CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0),
CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0),
CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0),
CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF),
CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0),
- CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX),
- CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF),
- CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0),
- CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0),
- CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0),
+ CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX),
+ CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF),
+ CLK_PCOM("lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, &msm_device_mdp.dev, 0),
+ CLK_PCOM("lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, &msm_device_mdp.dev, 0),
+ CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, &msm_device_mdp.dev, 0),
CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN),
CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0),
CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF),
- CLK_PCOM("spi_clk", SPI_CLK, NULL, 0),
+ CLK_PCOM("spi_clk", SPI_CLK, &msm_device_spi.dev, 0),
+ CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC2_P_CLK, &msm_device_sdc2.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC3_P_CLK, &msm_device_sdc3.dev, OFF),
+ CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF),
+ CLK_PCOM("sdc_pclk", SDC4_P_CLK, &msm_device_sdc4.dev, OFF),
CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0),
CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0),
CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0),
CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0),
+ CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF),
+ CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF),
CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF),
- CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF),
- CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF),
+ CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF),
+ CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF),
+ CLK_PCOM("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF),
+ CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, &msm_device_hsusb.dev, OFF),
CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0),
CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN),
CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF),
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 568443e..e86be3ad 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -22,6 +22,9 @@
extern struct platform_device msm_device_uart2;
extern struct platform_device msm_device_uart3;
+extern struct platform_device msm_device_uart_dm1;
+extern struct platform_device msm_device_uart_dm2;
+
extern struct platform_device msm_device_sdc1;
extern struct platform_device msm_device_sdc2;
extern struct platform_device msm_device_sdc3;
@@ -30,10 +33,19 @@
extern struct platform_device msm_device_hsusb;
extern struct platform_device msm_device_i2c;
+extern struct platform_device msm_device_i2c2;
+
+extern struct platform_device msm_device_qup_i2c;
extern struct platform_device msm_device_smd;
extern struct platform_device msm_device_nand;
+extern struct platform_device msm_device_mddi0;
+extern struct platform_device msm_device_mddi1;
+extern struct platform_device msm_device_mdp;
+extern struct platform_device msm_device_touchscreen;
+extern struct platform_device msm_device_spi;
+extern struct platform_device msm_device_ssbi_pmic;
extern struct clk msm_clocks_7x01a[];
extern unsigned msm_num_clocks_7x01a;
@@ -43,5 +55,6 @@
extern struct clk msm_clocks_8x50[];
extern unsigned msm_num_clocks_8x50;
+extern struct platform_device msm_device_vidc_720p;
#endif
diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c
new file mode 100644
index 0000000..e5f65e7
--- /dev/null
+++ b/arch/arm/mach-msm/devices_htc.c
@@ -0,0 +1,500 @@
+/* linux/arch/arm/mach-msm/devices.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/dma-mapping.h>
+#include <mach/msm_iomap.h>
+#include <mach/dma.h>
+#include "gpio_chip.h"
+#include "devices.h"
+#include <mach/board.h>
+#include <mach/board_htc.h>
+#include <mach/msm_hsusb.h>
+#include <linux/usb/android_composite.h>
+
+#include <asm/mach/flash.h>
+#include <asm/setup.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <linux/android_pmem.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_iomap.h>
+#include <asm/mach/mmc.h>
+
+static char *df_serialno = "000000000000";
+
+#if 0
+struct platform_device *devices[] __initdata = {
+ &msm_device_nand,
+ &msm_device_smd,
+ &msm_device_i2c,
+};
+
+void __init msm_add_devices(void)
+{
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+#endif
+
+#define HSUSB_API_INIT_PHY_PROC 2
+#define HSUSB_API_PROG 0x30000064
+#define HSUSB_API_VERS MSM_RPC_VERS(1,1)
+
+static void internal_phy_reset(void)
+{
+ struct msm_rpc_endpoint *usb_ep;
+ int rc;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ printk(KERN_INFO "msm_hsusb_phy_reset\n");
+
+ usb_ep = msm_rpc_connect(HSUSB_API_PROG, HSUSB_API_VERS, 0);
+ if (IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: init rpc failed! error: %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ goto close;
+ }
+ rc = msm_rpc_call(usb_ep, HSUSB_API_INIT_PHY_PROC,
+ &req, sizeof(req), 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: rpc call failed! (%d)\n", __func__, rc);
+
+close:
+ msm_rpc_close(usb_ep);
+}
+
+/* adjust eye diagram, disable vbusvalid interrupts */
+static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 };
+
+struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_reset = internal_phy_reset,
+ .phy_init_seq = hsusb_phy_init_seq,
+ .usb_connected = notify_usb_connected,
+};
+
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .vendor = "HTC ",
+ .product = "Android Phone ",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+static struct usb_ether_platform_data rndis_pdata = {
+ /* ethaddr is filled by board_serialno_setup */
+ .vendorID = 0x0bb4,
+ .vendorDescr = "HTC",
+};
+
+static struct platform_device rndis_device = {
+ .name = "rndis",
+ .id = -1,
+ .dev = {
+ .platform_data = &rndis_pdata,
+ },
+};
+#endif
+
+static char *usb_functions_ums[] = {
+ "usb_mass_storage",
+};
+
+static char *usb_functions_ums_adb[] = {
+ "usb_mass_storage",
+ "adb",
+};
+
+static char *usb_functions_rndis[] = {
+ "rndis",
+};
+
+static char *usb_functions_rndis_adb[] = {
+ "rndis",
+ "adb",
+};
+
+static char *usb_functions_all[] = {
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ "rndis",
+#endif
+ "usb_mass_storage",
+ "adb",
+#ifdef CONFIG_USB_ANDROID_ACM
+ "acm",
+#endif
+};
+
+static struct android_usb_product usb_products[] = {
+ {
+ .product_id = 0x0c01,
+ .num_functions = ARRAY_SIZE(usb_functions_ums),
+ .functions = usb_functions_ums,
+ },
+ {
+ .product_id = 0x0c02,
+ .num_functions = ARRAY_SIZE(usb_functions_ums_adb),
+ .functions = usb_functions_ums_adb,
+ },
+ {
+ .product_id = 0x0ffe,
+ .num_functions = ARRAY_SIZE(usb_functions_rndis),
+ .functions = usb_functions_rndis,
+ },
+ {
+ .product_id = 0x0ffc,
+ .num_functions = ARRAY_SIZE(usb_functions_rndis_adb),
+ .functions = usb_functions_rndis_adb,
+ },
+};
+
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x0bb4,
+ .product_id = 0x0c01,
+ .version = 0x0100,
+ .product_name = "Android Phone",
+ .manufacturer_name = "HTC",
+ .num_products = ARRAY_SIZE(usb_products),
+ .products = usb_products,
+ .num_functions = ARRAY_SIZE(usb_functions_all),
+ .functions = usb_functions_all,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+
+void __init msm_add_usb_devices(void (*phy_reset) (void))
+{
+ /* setup */
+ if (phy_reset)
+ msm_hsusb_pdata.phy_reset = phy_reset;
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ platform_device_register(&msm_device_hsusb);
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ platform_device_register(&rndis_device);
+#endif
+ platform_device_register(&usb_mass_storage_device);
+ platform_device_register(&android_usb_device);
+}
+
+static struct android_pmem_platform_data pmem_pdata = {
+ .name = "pmem",
+ .no_allocator = 1,
+ .cached = 1,
+};
+
+static struct android_pmem_platform_data pmem_adsp_pdata = {
+ .name = "pmem_adsp",
+ .no_allocator = 0,
+ .cached = 0,
+};
+
+static struct android_pmem_platform_data pmem_camera_pdata = {
+ .name = "pmem_camera",
+ .no_allocator = 1,
+ .cached = 0,
+};
+
+static struct platform_device pmem_device = {
+ .name = "android_pmem",
+ .id = 0,
+ .dev = { .platform_data = &pmem_pdata },
+};
+
+static struct platform_device pmem_adsp_device = {
+ .name = "android_pmem",
+ .id = 1,
+ .dev = { .platform_data = &pmem_adsp_pdata },
+};
+
+static struct platform_device pmem_camera_device = {
+ .name = "android_pmem",
+ .id = 2,
+ .dev = { .platform_data = &pmem_camera_pdata },
+};
+
+static struct resource ram_console_resource[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ram_console_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ram_console_resource),
+ .resource = ram_console_resource,
+};
+
+static struct resource resources_hw3d[] = {
+ {
+ .start = 0xA0000000,
+ .end = 0xA00fffff,
+ .flags = IORESOURCE_MEM,
+ .name = "regs",
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ .name = "smi",
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ .name = "ebi",
+ },
+ {
+ .start = INT_GRAPHICS,
+ .end = INT_GRAPHICS,
+ .flags = IORESOURCE_IRQ,
+ .name = "gfx",
+ },
+};
+
+static struct platform_device hw3d_device = {
+ .name = "msm_hw3d",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_hw3d),
+ .resource = resources_hw3d,
+};
+
+void __init msm_add_mem_devices(struct msm_pmem_setting *setting)
+{
+ if (setting->pmem_size) {
+ pmem_pdata.start = setting->pmem_start;
+ pmem_pdata.size = setting->pmem_size;
+ platform_device_register(&pmem_device);
+ }
+
+ if (setting->pmem_adsp_size) {
+ pmem_adsp_pdata.start = setting->pmem_adsp_start;
+ pmem_adsp_pdata.size = setting->pmem_adsp_size;
+ platform_device_register(&pmem_adsp_device);
+ }
+
+ if (setting->pmem_gpu0_size && setting->pmem_gpu1_size) {
+ struct resource *res;
+
+ res = platform_get_resource_byname(&hw3d_device, IORESOURCE_MEM,
+ "smi");
+ res->start = setting->pmem_gpu0_start;
+ res->end = res->start + setting->pmem_gpu0_size - 1;
+
+ res = platform_get_resource_byname(&hw3d_device, IORESOURCE_MEM,
+ "ebi");
+ res->start = setting->pmem_gpu1_start;
+ res->end = res->start + setting->pmem_gpu1_size - 1;
+ platform_device_register(&hw3d_device);
+ }
+
+ if (setting->pmem_camera_size) {
+ pmem_camera_pdata.start = setting->pmem_camera_start;
+ pmem_camera_pdata.size = setting->pmem_camera_size;
+ platform_device_register(&pmem_camera_device);
+ }
+
+ if (setting->ram_console_size) {
+ ram_console_resource[0].start = setting->ram_console_start;
+ ram_console_resource[0].end = setting->ram_console_start
+ + setting->ram_console_size - 1;
+ platform_device_register(&ram_console_device);
+ }
+}
+
+#define PM_LIBPROG 0x30000061
+#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
+#define PM_LIBVERS 0xfb837d0b
+#else
+#define PM_LIBVERS 0x10001
+#endif
+
+#if 0
+static struct platform_device *msm_serial_devices[] __initdata = {
+ &msm_device_uart1,
+ &msm_device_uart2,
+ &msm_device_uart3,
+ #ifdef CONFIG_SERIAL_MSM_HS
+ &msm_device_uart_dm1,
+ &msm_device_uart_dm2,
+ #endif
+};
+
+int __init msm_add_serial_devices(unsigned num)
+{
+ if (num > MSM_SERIAL_NUM)
+ return -EINVAL;
+
+ return platform_device_register(msm_serial_devices[num]);
+}
+#endif
+
+#define ATAG_SMI 0x4d534D71
+/* setup calls mach->fixup, then parse_tags, parse_cmdline
+ * We need to setup meminfo in mach->fixup, so this function
+ * will need to traverse each tag to find smi tag.
+ */
+int __init parse_tag_smi(const struct tag *tags)
+{
+ int smi_sz = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_SMI) {
+ printk(KERN_DEBUG "find the smi tag\n");
+ find = 1;
+ break;
+ }
+ }
+ if (!find)
+ return -1;
+
+ printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size);
+ smi_sz = t->u.mem.size;
+ return smi_sz;
+}
+__tagtable(ATAG_SMI, parse_tag_smi);
+
+
+#define ATAG_HWID 0x4d534D72
+int __init parse_tag_hwid(const struct tag *tags)
+{
+ int hwid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_HWID) {
+ printk(KERN_DEBUG "find the hwid tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ hwid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid);
+ return hwid;
+}
+__tagtable(ATAG_HWID, parse_tag_hwid);
+
+#define ATAG_SKUID 0x4d534D73
+int __init parse_tag_skuid(const struct tag *tags)
+{
+ int skuid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_SKUID) {
+ printk(KERN_DEBUG "find the skuid tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ skuid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid);
+ return skuid;
+}
+__tagtable(ATAG_SKUID, parse_tag_skuid);
+
+#define ATAG_ENGINEERID 0x4d534D75
+int __init parse_tag_engineerid(const struct tag *tags)
+{
+ int engineerid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_ENGINEERID) {
+ printk(KERN_DEBUG "find the engineer tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ engineerid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid);
+ return engineerid;
+}
+__tagtable(ATAG_ENGINEERID, parse_tag_engineerid);
+
+static int mfg_mode;
+int __init board_mfg_mode_init(char *s)
+{
+ if (!strcmp(s, "normal"))
+ mfg_mode = 0;
+ else if (!strcmp(s, "factory2"))
+ mfg_mode = 1;
+ else if (!strcmp(s, "recovery"))
+ mfg_mode = 2;
+ else if (!strcmp(s, "charge"))
+ mfg_mode = 3;
+
+ return 1;
+}
+__setup("androidboot.mode=", board_mfg_mode_init);
+
+
+int board_mfg_mode(void)
+{
+ return mfg_mode;
+}
+
+static int __init board_serialno_setup(char *serialno)
+{
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ int i;
+ char *src;
+#endif
+ char *str;
+
+ /* use default serial number when mode is factory2 */
+ if (mfg_mode == 1 || !strlen(serialno))
+ str = df_serialno;
+ else
+ str = serialno;
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ /* create a fake MAC address from our serial number.
+ * first byte is 0x02 to signify locally administered.
+ */
+ rndis_pdata.ethaddr[0] = 0x02;
+ src = str;
+ for (i = 0; *src; i++) {
+ /* XOR the USB serial across the remaining bytes */
+ rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++;
+ }
+#endif
+ android_usb_pdata.serial_number = str;
+ return 1;
+}
+
+__setup("androidboot.serialno=", board_serialno_setup);
diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c
index 02cae5e..cfb78c4 100644
--- a/arch/arm/mach-msm/dma.c
+++ b/arch/arm/mach-msm/dma.c
@@ -1,6 +1,8 @@
/* linux/arch/arm/mach-msm/dma.c
*
* Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,6 +20,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
+#include <linux/module.h>
#include <mach/dma.h>
#define MSM_DMOV_CHANNEL_COUNT 16
@@ -51,6 +54,7 @@
{
writel((graceful << 31), DMOV_FLUSH0(id));
}
+EXPORT_SYMBOL(msm_dmov_stop_cmd);
void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
{
@@ -89,6 +93,20 @@
}
spin_unlock_irqrestore(&msm_dmov_lock, irq_flags);
}
+EXPORT_SYMBOL(msm_dmov_enqueue_cmd);
+
+void msm_dmov_flush(unsigned int id)
+{
+ unsigned long irq_flags;
+ spin_lock_irqsave(&msm_dmov_lock, irq_flags);
+ /* XXX not checking if flush cmd sent already */
+ if (!list_empty(&active_commands[id])) {
+ PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id);
+ writel(DMOV_FLUSH_TYPE, DMOV_FLUSH0(id));
+ }
+ spin_unlock_irqrestore(&msm_dmov_lock, irq_flags);
+}
+EXPORT_SYMBOL(msm_dmov_flush);
struct msm_dmov_exec_cmdptr_cmd {
struct msm_dmov_cmd dmov_cmd;
@@ -268,4 +286,3 @@
}
arch_initcall(msm_init_datamover);
-
diff --git a/arch/arm/mach-msm/fiq_glue.S b/arch/arm/mach-msm/fiq_glue.S
new file mode 100644
index 0000000..df1c708
--- /dev/null
+++ b/arch/arm/mach-msm/fiq_glue.S
@@ -0,0 +1,112 @@
+/* arch/arm/mach-msm/fiq_glue.S
+ *
+ * Copyright (C) 2008 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/linkage.h>
+#include <asm/assembler.h>
+
+ .text
+
+ .global fiq_glue_end
+
+ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */
+
+ENTRY(fiq_glue)
+ /* store pc, cpsr from previous mode */
+ mrs r12, spsr
+ sub r11, lr, #4
+ subs r10, #1
+ bne nested_fiq
+
+ stmfd sp!, {r11-r12, lr}
+
+ /* store r8-r14 from previous mode */
+ sub sp, sp, #(7 * 4)
+ stmia sp, {r8-r14}^
+ nop
+
+ /* store r0-r7 from previous mode */
+ stmfd sp!, {r0-r7}
+
+ /* setup func(data,regs) arguments */
+ mov r0, r9
+ mov r1, sp
+ mov r3, r8
+
+ mov r7, sp
+
+ /* Get sp and lr from non-user modes */
+ and r4, r12, #MODE_MASK
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode
+
+ mov r7, sp
+ orr r4, r4, #(PSR_I_BIT | PSR_F_BIT)
+ msr cpsr_c, r4
+ str sp, [r7, #(4 * 13)]
+ str lr, [r7, #(4 * 14)]
+ mrs r5, spsr
+ str r5, [r7, #(4 * 17)]
+
+ cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ /* use fiq stack if we reenter this mode */
+ subne sp, r7, #(4 * 3)
+
+fiq_from_usr_mode:
+ msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ mov r2, sp
+ sub sp, r7, #12
+ stmfd sp!, {r2, ip, lr}
+ /* call func(data,regs) */
+ blx r3
+ ldmfd sp, {r2, ip, lr}
+ mov sp, r2
+
+ /* restore/discard saved state */
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode_exit
+
+ msr cpsr_c, r4
+ ldr sp, [r7, #(4 * 13)]
+ ldr lr, [r7, #(4 * 14)]
+ msr spsr_cxsf, r5
+
+fiq_from_usr_mode_exit:
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+
+ ldmfd sp!, {r0-r7}
+ add sp, sp, #(7 * 4)
+ ldmfd sp!, {r11-r12, lr}
+exit_fiq:
+ msr spsr_cxsf, r12
+ add r10, #1
+ movs pc, r11
+
+nested_fiq:
+ orr r12, r12, #(PSR_F_BIT)
+ b exit_fiq
+
+fiq_glue_end:
+
+ENTRY(fiq_glue_setup) /* func, data, sp */
+ mrs r3, cpsr
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+ movs r8, r0
+ mov r9, r1
+ mov sp, r2
+ moveq r10, #0
+ movne r10, #1
+ msr cpsr_c, r3
+ bx lr
+
diff --git a/arch/arm/mach-msm/fish_battery.c b/arch/arm/mach-msm/fish_battery.c
new file mode 100644
index 0000000..19fbb91
--- /dev/null
+++ b/arch/arm/mach-msm/fish_battery.c
@@ -0,0 +1,145 @@
+/* arch/arm/mach-msm/fish_battery.c
+ *
+ * Copyright (C) 2008 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.
+ *
+ * based on: arch/arm/mach-msm/htc_battery.c
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+
+static enum power_supply_property fish_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property fish_power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+ "battery",
+};
+
+static int fish_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static int fish_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static struct power_supply fish_power_supplies[] = {
+ {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = fish_battery_properties,
+ .num_properties = ARRAY_SIZE(fish_battery_properties),
+ .get_property = fish_battery_get_property,
+ },
+ {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = fish_power_properties,
+ .num_properties = ARRAY_SIZE(fish_power_properties),
+ .get_property = fish_power_get_property,
+ },
+};
+
+static int fish_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fish_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = 100;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fish_battery_probe(struct platform_device *pdev)
+{
+ int i;
+ int rc;
+
+ /* init power supplier framework */
+ for (i = 0; i < ARRAY_SIZE(fish_power_supplies); i++) {
+ rc = power_supply_register(&pdev->dev, &fish_power_supplies[i]);
+ if (rc)
+ pr_err("%s: Failed to register power supply (%d)\n",
+ __func__, rc);
+ }
+
+ return 0;
+}
+
+static struct platform_driver fish_battery_driver = {
+ .probe = fish_battery_probe,
+ .driver = {
+ .name = "fish_battery",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init fish_battery_init(void)
+{
+ platform_driver_register(&fish_battery_driver);
+ return 0;
+}
+
+module_init(fish_battery_init);
+MODULE_DESCRIPTION("Qualcomm fish battery driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-msm/generic_gpio.c b/arch/arm/mach-msm/generic_gpio.c
new file mode 100644
index 0000000..46630a2
--- /dev/null
+++ b/arch/arm/mach-msm/generic_gpio.c
@@ -0,0 +1,113 @@
+/* arch/arm/mach-msm/generic_gpio.c
+ *
+ * Copyright (C) 2007 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/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include "gpio_chip.h"
+
+#undef gpio_chip
+static int gpio_chip_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct old_gpio_chip *old_chip;
+ unsigned long irq_flags;
+ int ret = -ENOTSUPP;
+
+ old_chip = container_of(chip, struct old_gpio_chip, gpio_chip);
+ spin_lock_irqsave(&old_chip->lock, irq_flags);
+ ret = old_chip->configure(old_chip, chip->base + offset, GPIOF_INPUT);
+ spin_unlock_irqrestore(&old_chip->lock, irq_flags);
+ return ret;
+}
+
+static int gpio_chip_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct old_gpio_chip *old_chip;
+ unsigned long irq_flags;
+ int ret = -ENOTSUPP;
+
+ old_chip = container_of(chip, struct old_gpio_chip, gpio_chip);
+ spin_lock_irqsave(&old_chip->lock, irq_flags);
+ if (old_chip->read)
+ ret = old_chip->read(old_chip, chip->base + offset);
+ spin_unlock_irqrestore(&old_chip->lock, irq_flags);
+ return ret;
+}
+
+static int
+gpio_chip_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct old_gpio_chip *old_chip;
+ unsigned long irq_flags;
+ int ret = -ENOTSUPP;
+
+ old_chip = container_of(chip, struct old_gpio_chip, gpio_chip);
+ spin_lock_irqsave(&old_chip->lock, irq_flags);
+ if (old_chip->write)
+ old_chip->write(old_chip, chip->base + offset, value);
+ ret = old_chip->configure(old_chip, chip->base + offset,
+ GPIOF_DRIVE_OUTPUT);
+ spin_unlock_irqrestore(&old_chip->lock, irq_flags);
+ return ret;
+}
+
+static void gpio_chip_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct old_gpio_chip *old_chip;
+ unsigned long irq_flags;
+
+ old_chip = container_of(chip, struct old_gpio_chip, gpio_chip);
+ spin_lock_irqsave(&old_chip->lock, irq_flags);
+ if (old_chip->write)
+ old_chip->write(old_chip, chip->base + offset, value);
+ spin_unlock_irqrestore(&old_chip->lock, irq_flags);
+}
+
+static int gpio_chip_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct old_gpio_chip *old_chip;
+ unsigned long irq_flags;
+ int ret = -ENOTSUPP;
+ int irq;
+
+ old_chip = container_of(chip, struct old_gpio_chip, gpio_chip);
+ spin_lock_irqsave(&old_chip->lock, irq_flags);
+ if (old_chip->get_irq_num)
+ ret = old_chip->get_irq_num(old_chip, chip->base + offset,
+ &irq, NULL);
+ spin_unlock_irqrestore(&old_chip->lock, irq_flags);
+ if (ret)
+ return ret;
+ return irq;
+}
+
+int register_gpio_chip(struct old_gpio_chip *new_gpio_chip)
+{
+ spin_lock_init(&new_gpio_chip->lock);
+ new_gpio_chip->gpio_chip.direction_input = gpio_chip_direction_input;
+ new_gpio_chip->gpio_chip.get = gpio_chip_get;
+ new_gpio_chip->gpio_chip.direction_output = gpio_chip_direction_output;
+ new_gpio_chip->gpio_chip.set = gpio_chip_set;
+ new_gpio_chip->gpio_chip.to_irq = gpio_chip_to_irq;
+ new_gpio_chip->gpio_chip.base = new_gpio_chip->start;
+ new_gpio_chip->gpio_chip.ngpio =
+ new_gpio_chip->end - new_gpio_chip->start + 1;
+
+ return gpiochip_add(&new_gpio_chip->gpio_chip);
+}
+
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c
index bc32c84..41b7e4d 100644
--- a/arch/arm/mach-msm/gpio.c
+++ b/arch/arm/mach-msm/gpio.c
@@ -14,9 +14,655 @@
*
*/
+#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <mach/gpio.h>
+#include "gpio_chip.h"
+#include "gpio_hw.h"
#include "proc_comm.h"
+#include "smd_private.h"
+
+enum {
+ GPIO_DEBUG_SLEEP = 1U << 0,
+};
+static int msm_gpio_debug_mask;
+module_param_named(debug_mask, msm_gpio_debug_mask, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define MSM_GPIO_BROKEN_INT_CLEAR 1
+
+/* private gpio_configure flags */
+#define MSM_GPIOF_ENABLE_INTERRUPT 0x10000000
+#define MSM_GPIOF_DISABLE_INTERRUPT 0x20000000
+#define MSM_GPIOF_ENABLE_WAKE 0x40000000
+#define MSM_GPIOF_DISABLE_WAKE 0x80000000
+
+static int msm_gpio_configure(struct gpio_chip *chip, unsigned int gpio,
+ unsigned long flags);
+static int msm_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio,
+ unsigned int *irqp,
+ unsigned long *irqnumflagsp);
+static int msm_gpio_read(struct gpio_chip *chip, unsigned n);
+static int msm_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on);
+static int msm_gpio_read_detect_status(struct gpio_chip *chip,
+ unsigned int gpio);
+static int msm_gpio_clear_detect_status(struct gpio_chip *chip,
+ unsigned int gpio);
+
+struct msm_gpio_regs {
+ void __iomem *out;
+ void __iomem *in;
+ void __iomem *int_status;
+ void __iomem *int_clear;
+ void __iomem *int_en;
+ void __iomem *int_edge;
+ void __iomem *int_pos;
+ void __iomem *oe;
+};
+
+struct msm_gpio_chip {
+ struct gpio_chip chip;
+ struct msm_gpio_regs regs;
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ unsigned int_status_copy;
+#endif
+ unsigned int both_edge_detect;
+ unsigned int int_enable[2]; /* 0: awake, 1: sleep */
+};
+
+struct msm_gpio_chip msm_gpio_chips[] = {
+ {
+ .regs = {
+ .out = GPIO_OUT_0,
+ .in = GPIO_IN_0,
+ .int_status = GPIO_INT_STATUS_0,
+ .int_clear = GPIO_INT_CLEAR_0,
+ .int_en = GPIO_INT_EN_0,
+ .int_edge = GPIO_INT_EDGE_0,
+ .int_pos = GPIO_INT_POS_0,
+ .oe = GPIO_OE_0,
+ },
+ .chip = {
+ .start = 0,
+ .end = 15,
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_1,
+ .in = GPIO_IN_1,
+ .int_status = GPIO_INT_STATUS_1,
+ .int_clear = GPIO_INT_CLEAR_1,
+ .int_en = GPIO_INT_EN_1,
+ .int_edge = GPIO_INT_EDGE_1,
+ .int_pos = GPIO_INT_POS_1,
+ .oe = GPIO_OE_1,
+ },
+ .chip = {
+ .start = 16,
+#if defined(CONFIG_ARCH_MSM7X30)
+ .end = 43,
+#else
+ .end = 42,
+#endif
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_2,
+ .in = GPIO_IN_2,
+ .int_status = GPIO_INT_STATUS_2,
+ .int_clear = GPIO_INT_CLEAR_2,
+ .int_en = GPIO_INT_EN_2,
+ .int_edge = GPIO_INT_EDGE_2,
+ .int_pos = GPIO_INT_POS_2,
+ .oe = GPIO_OE_2,
+ },
+ .chip = {
+#if defined(CONFIG_ARCH_MSM7X30)
+ .start = 44,
+#else
+ .start = 43,
+#endif
+ .end = 67,
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_3,
+ .in = GPIO_IN_3,
+ .int_status = GPIO_INT_STATUS_3,
+ .int_clear = GPIO_INT_CLEAR_3,
+ .int_en = GPIO_INT_EN_3,
+ .int_edge = GPIO_INT_EDGE_3,
+ .int_pos = GPIO_INT_POS_3,
+ .oe = GPIO_OE_3,
+ },
+ .chip = {
+ .start = 68,
+ .end = 94,
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_4,
+ .in = GPIO_IN_4,
+ .int_status = GPIO_INT_STATUS_4,
+ .int_clear = GPIO_INT_CLEAR_4,
+ .int_en = GPIO_INT_EN_4,
+ .int_edge = GPIO_INT_EDGE_4,
+ .int_pos = GPIO_INT_POS_4,
+ .oe = GPIO_OE_4,
+ },
+ .chip = {
+ .start = 95,
+#if defined(CONFIG_ARCH_QSD8X50)
+ .end = 103,
+#else
+ .end = 106,
+#endif
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_5,
+ .in = GPIO_IN_5,
+ .int_status = GPIO_INT_STATUS_5,
+ .int_clear = GPIO_INT_CLEAR_5,
+ .int_en = GPIO_INT_EN_5,
+ .int_edge = GPIO_INT_EDGE_5,
+ .int_pos = GPIO_INT_POS_5,
+ .oe = GPIO_OE_5,
+ },
+ .chip = {
+#if defined(CONFIG_ARCH_QSD8X50)
+ .start = 104,
+ .end = 121,
+#elif defined(CONFIG_ARCH_MSM7X30)
+ .start = 107,
+ .end = 133,
+#else
+ .start = 107,
+ .end = 121,
+#endif
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30)
+ {
+ .regs = {
+ .out = GPIO_OUT_6,
+ .in = GPIO_IN_6,
+ .int_status = GPIO_INT_STATUS_6,
+ .int_clear = GPIO_INT_CLEAR_6,
+ .int_en = GPIO_INT_EN_6,
+ .int_edge = GPIO_INT_EDGE_6,
+ .int_pos = GPIO_INT_POS_6,
+ .oe = GPIO_OE_6,
+ },
+ .chip = {
+#if defined(CONFIG_ARCH_MSM7X30)
+ .start = 134,
+ .end = 150,
+#else
+ .start = 122,
+ .end = 152,
+#endif
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+ {
+ .regs = {
+ .out = GPIO_OUT_7,
+ .in = GPIO_IN_7,
+ .int_status = GPIO_INT_STATUS_7,
+ .int_clear = GPIO_INT_CLEAR_7,
+ .int_en = GPIO_INT_EN_7,
+ .int_edge = GPIO_INT_EDGE_7,
+ .int_pos = GPIO_INT_POS_7,
+ .oe = GPIO_OE_7,
+ },
+ .chip = {
+#if defined(CONFIG_ARCH_MSM7X30)
+ .start = 151,
+ .end = 181,
+#else
+ .start = 153,
+ .end = 164,
+#endif
+ .configure = msm_gpio_configure,
+ .get_irq_num = msm_gpio_get_irq_num,
+ .read = msm_gpio_read,
+ .write = msm_gpio_write,
+ .read_detect_status = msm_gpio_read_detect_status,
+ .clear_detect_status = msm_gpio_clear_detect_status
+ }
+ },
+#endif
+};
+
+static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip)
+{
+ int loop_limit = 100;
+ unsigned pol, val, val2, intstat;
+ do {
+ val = readl(msm_chip->regs.in);
+ pol = readl(msm_chip->regs.int_pos);
+ pol = (pol & ~msm_chip->both_edge_detect) |
+ (~val & msm_chip->both_edge_detect);
+ writel(pol, msm_chip->regs.int_pos);
+ intstat = readl(msm_chip->regs.int_status);
+ val2 = readl(msm_chip->regs.in);
+ if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
+ return;
+ } while (loop_limit-- > 0);
+ printk(KERN_ERR "msm_gpio_update_both_edge_detect, "
+ "failed to reach stable state %x != %x\n", val, val2);
+}
+
+static int msm_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
+{
+ struct msm_gpio_chip *msm_chip =
+ container_of(chip, struct msm_gpio_chip, chip);
+ unsigned b = 1U << (n - chip->start);
+ unsigned v;
+
+ v = readl(msm_chip->regs.out);
+ if (on)
+ writel(v | b, msm_chip->regs.out);
+ else
+ writel(v & (~b), msm_chip->regs.out);
+ return 0;
+}
+
+static int msm_gpio_read(struct gpio_chip *chip, unsigned n)
+{
+ struct msm_gpio_chip *msm_chip =
+ container_of(chip, struct msm_gpio_chip, chip);
+ unsigned b = 1U << (n - chip->start);
+
+ return (readl(msm_chip->regs.in) & b) ? 1 : 0;
+}
+
+static int msm_gpio_read_detect_status(struct gpio_chip *chip,
+ unsigned int gpio)
+{
+ struct msm_gpio_chip *msm_chip =
+ container_of(chip, struct msm_gpio_chip, chip);
+ unsigned b = 1U << (gpio - chip->start);
+ unsigned v;
+
+ v = readl(msm_chip->regs.int_status);
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ v |= msm_chip->int_status_copy;
+#endif
+ return (v & b) ? 1 : 0;
+}
+
+static int msm_gpio_clear_detect_status(struct gpio_chip *chip,
+ unsigned int gpio)
+{
+ struct msm_gpio_chip *msm_chip =
+ container_of(chip, struct msm_gpio_chip, chip);
+ unsigned b = 1U << (gpio - chip->start);
+
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ /* Save interrupts that already triggered before we loose them. */
+ /* Any interrupt that triggers between the read of int_status */
+ /* and the write to int_clear will still be lost though. */
+ msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
+ msm_chip->int_status_copy &= ~b;
+#endif
+ writel(b, msm_chip->regs.int_clear);
+ msm_gpio_update_both_edge_detect(msm_chip);
+ return 0;
+}
+
+int msm_gpio_configure(struct gpio_chip *chip, unsigned int gpio,
+ unsigned long flags)
+{
+ struct msm_gpio_chip *msm_chip =
+ container_of(chip, struct msm_gpio_chip, chip);
+ unsigned b = 1U << (gpio - chip->start);
+ unsigned v;
+
+ if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH))
+ msm_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH);
+
+ if (flags & (GPIOF_INPUT | GPIOF_DRIVE_OUTPUT)) {
+ v = readl(msm_chip->regs.oe);
+ if (flags & GPIOF_DRIVE_OUTPUT)
+ writel(v | b, msm_chip->regs.oe);
+ else
+ writel(v & (~b), msm_chip->regs.oe);
+ }
+
+ if (flags & (IRQF_TRIGGER_MASK | GPIOF_IRQF_TRIGGER_NONE)) {
+ v = readl(msm_chip->regs.int_edge);
+ if (flags & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) {
+ writel(v | b, msm_chip->regs.int_edge);
+ irq_desc[MSM_GPIO_TO_INT(gpio)].handle_irq =
+ handle_edge_irq;
+ } else {
+ writel(v & (~b), msm_chip->regs.int_edge);
+ irq_desc[MSM_GPIO_TO_INT(gpio)].handle_irq =
+ handle_level_irq;
+ }
+ if ((flags & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) ==
+ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) {
+ msm_chip->both_edge_detect |= b;
+ msm_gpio_update_both_edge_detect(msm_chip);
+ } else {
+ msm_chip->both_edge_detect &= ~b;
+ v = readl(msm_chip->regs.int_pos);
+ if (flags &
+ (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) {
+ writel(v | b, msm_chip->regs.int_pos);
+ } else {
+ writel(v & (~b), msm_chip->regs.int_pos);
+ }
+ }
+ }
+
+ /* used by msm_gpio_irq_mask and msm_gpio_irq_unmask */
+ if (flags &
+ (MSM_GPIOF_ENABLE_INTERRUPT | MSM_GPIOF_DISABLE_INTERRUPT)) {
+ v = readl(msm_chip->regs.int_edge);
+ /* level triggered interrupts are also latched */
+ if (!(v & b))
+ msm_gpio_clear_detect_status(chip, gpio);
+ if (flags & MSM_GPIOF_ENABLE_INTERRUPT)
+ msm_chip->int_enable[0] |= b;
+ else
+ msm_chip->int_enable[0] &= ~b;
+ writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+ }
+
+ if (flags & (MSM_GPIOF_ENABLE_WAKE | MSM_GPIOF_DISABLE_WAKE)) {
+ if (flags & MSM_GPIOF_ENABLE_WAKE)
+ msm_chip->int_enable[1] |= b;
+ else
+ msm_chip->int_enable[1] &= ~b;
+ }
+
+ return 0;
+}
+
+static int msm_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio,
+ unsigned int *irqp, unsigned long *irqnumflagsp)
+{
+ *irqp = MSM_GPIO_TO_INT(gpio);
+ if (irqnumflagsp)
+ *irqnumflagsp = 0;
+ return 0;
+}
+
+
+static void msm_gpio_irq_ack(unsigned int irq)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq);
+ spin_lock_irqsave(&msm_chip->chip.lock, irq_flags);
+ msm_gpio_clear_detect_status(&msm_chip->chip, irq - FIRST_GPIO_IRQ);
+ spin_unlock_irqrestore(&msm_chip->chip.lock, irq_flags);
+}
+
+static void msm_gpio_irq_mask(unsigned int irq)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq);
+ spin_lock_irqsave(&msm_chip->chip.lock, irq_flags);
+ msm_gpio_configure(&msm_chip->chip,
+ irq - FIRST_GPIO_IRQ, MSM_GPIOF_DISABLE_INTERRUPT);
+ spin_unlock_irqrestore(&msm_chip->chip.lock, irq_flags);
+}
+
+static void msm_gpio_irq_unmask(unsigned int irq)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq);
+ spin_lock_irqsave(&msm_chip->chip.lock, irq_flags);
+ msm_gpio_configure(&msm_chip->chip,
+ irq - FIRST_GPIO_IRQ, MSM_GPIOF_ENABLE_INTERRUPT);
+ spin_unlock_irqrestore(&msm_chip->chip.lock, irq_flags);
+}
+
+static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on)
+{
+ int ret;
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq);
+ spin_lock_irqsave(&msm_chip->chip.lock, irq_flags);
+ ret = msm_gpio_configure(&msm_chip->chip, irq - FIRST_GPIO_IRQ,
+ on ? MSM_GPIOF_ENABLE_WAKE : MSM_GPIOF_DISABLE_WAKE);
+ spin_unlock_irqrestore(&msm_chip->chip.lock, irq_flags);
+ return ret;
+}
+
+
+static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+ int ret;
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq);
+ spin_lock_irqsave(&msm_chip->chip.lock, irq_flags);
+ ret = msm_gpio_configure(&msm_chip->chip, irq - FIRST_GPIO_IRQ,
+ flow_type);
+ spin_unlock_irqrestore(&msm_chip->chip.lock, irq_flags);
+ return ret;
+}
+
+static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int i, j, m;
+ unsigned v;
+
+ for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+ struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
+ v = readl(msm_chip->regs.int_status);
+ v &= msm_chip->int_enable[0];
+ while (v) {
+ m = v & -v;
+ j = fls(m) - 1;
+ /* printk("%s %08x %08x bit %d gpio %d irq %d\n",
+ __func__, v, m, j, msm_chip->chip.start + j,
+ FIRST_GPIO_IRQ + msm_chip->chip.start + j); */
+ v &= ~m;
+ generic_handle_irq(FIRST_GPIO_IRQ +
+ msm_chip->chip.start + j);
+ }
+ }
+ desc->chip->ack(irq);
+}
+
+static struct irq_chip msm_gpio_irq_chip = {
+ .name = "msmgpio",
+ .ack = msm_gpio_irq_ack,
+ .mask = msm_gpio_irq_mask,
+ .unmask = msm_gpio_irq_unmask,
+ .set_wake = msm_gpio_irq_set_wake,
+ .set_type = msm_gpio_irq_set_type,
+};
+
+#define NUM_GPIO_SMEM_BANKS 6
+#define GPIO_SMEM_NUM_GROUPS 2
+#define GPIO_SMEM_MAX_PC_INTERRUPTS 8
+struct tramp_gpio_smem {
+ uint16_t num_fired[GPIO_SMEM_NUM_GROUPS];
+ uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS];
+ uint32_t enabled[NUM_GPIO_SMEM_BANKS];
+ uint32_t detection[NUM_GPIO_SMEM_BANKS];
+ uint32_t polarity[NUM_GPIO_SMEM_BANKS];
+};
+
+static void msm_gpio_sleep_int(unsigned long arg)
+{
+ int i, j;
+ struct tramp_gpio_smem *smem_gpio;
+
+ BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32);
+
+ smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+ if (smem_gpio == NULL)
+ return;
+
+ local_irq_disable();
+ for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) {
+ int count = smem_gpio->num_fired[i];
+ for (j = 0; j < count; j++) {
+ /* TODO: Check mask */
+ generic_handle_irq(
+ MSM_GPIO_TO_INT(smem_gpio->fired[i][j]));
+ }
+ }
+ local_irq_enable();
+}
+
+static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0);
+
+void msm_gpio_enter_sleep(int from_idle)
+{
+ int i;
+ struct tramp_gpio_smem *smem_gpio;
+
+ smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+ if (smem_gpio) {
+ for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+ smem_gpio->enabled[i] = 0;
+ smem_gpio->detection[i] = 0;
+ smem_gpio->polarity[i] = 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+ writel(msm_gpio_chips[i].int_enable[!from_idle],
+ msm_gpio_chips[i].regs.int_en);
+ if (smem_gpio) {
+ uint32_t tmp;
+ int start, index, shiftl, shiftr;
+ start = msm_gpio_chips[i].chip.start;
+ index = start / 32;
+ shiftl = start % 32;
+ shiftr = 32 - shiftl;
+ tmp = msm_gpio_chips[i].int_enable[!from_idle];
+ smem_gpio->enabled[index] |= tmp << shiftl;
+ smem_gpio->enabled[index+1] |= tmp >> shiftr;
+ smem_gpio->detection[index] |=
+ readl(msm_gpio_chips[i].regs.int_edge) <<
+ shiftl;
+ smem_gpio->detection[index+1] |=
+ readl(msm_gpio_chips[i].regs.int_edge) >>
+ shiftr;
+ smem_gpio->polarity[index] |=
+ readl(msm_gpio_chips[i].regs.int_pos) << shiftl;
+ smem_gpio->polarity[index+1] |=
+ readl(msm_gpio_chips[i].regs.int_pos) >> shiftr;
+ }
+ }
+
+ if (smem_gpio) {
+ if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+ for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+ printk("msm_gpio_enter_sleep gpio %d-%d: enable"
+ " %08x, edge %08x, polarity %08x\n",
+ i * 32, i * 32 + 31,
+ smem_gpio->enabled[i],
+ smem_gpio->detection[i],
+ smem_gpio->polarity[i]);
+ }
+ for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++)
+ smem_gpio->num_fired[i] = 0;
+ }
+}
+
+void msm_gpio_exit_sleep(void)
+{
+ int i;
+ struct tramp_gpio_smem *smem_gpio;
+
+ smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+ for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+ writel(msm_gpio_chips[i].int_enable[0],
+ msm_gpio_chips[i].regs.int_en);
+ }
+
+ if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) {
+ if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+ printk(KERN_INFO "gpio: fired %x %x\n",
+ smem_gpio->num_fired[0], smem_gpio->num_fired[1]);
+ tasklet_schedule(&msm_gpio_sleep_int_tasklet);
+ }
+}
+
+static int __init msm_init_gpio(void)
+{
+ int i, j = 0;
+
+ for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
+ if (i - FIRST_GPIO_IRQ > msm_gpio_chips[j].chip.end)
+ j++;
+ set_irq_chip_data(i, &msm_gpio_chips[j]);
+ set_irq_chip(i, &msm_gpio_irq_chip);
+ set_irq_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+ writel(0, msm_gpio_chips[i].regs.int_en);
+ register_gpio_chip(&msm_gpio_chips[i].chip);
+ }
+
+ set_irq_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
+ set_irq_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
+ set_irq_wake(INT_GPIO_GROUP1, 1);
+ set_irq_wake(INT_GPIO_GROUP2, 2);
+ return 0;
+}
+
+postcore_initcall(msm_init_gpio);
int gpio_tlmm_config(unsigned config, unsigned disable)
{
diff --git a/arch/arm/mach-msm/gpio_chip.h b/arch/arm/mach-msm/gpio_chip.h
new file mode 100644
index 0000000..b5165c3
--- /dev/null
+++ b/arch/arm/mach-msm/gpio_chip.h
@@ -0,0 +1,51 @@
+/* arch/arm/mach-msm/gpio_chip.h
+ *
+ * Copyright (C) 2007 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.
+ *
+ */
+
+#ifndef _LINUX_GPIO_CHIP_H
+#define _LINUX_GPIO_CHIP_H
+
+#include <linux/list.h>
+#include <linux/gpio.h>
+
+/* use to specify edge detection without */
+#define GPIOF_IRQF_MASK 0x0000ffff
+/* IRQF_TRIGGER_NONE is 0 which also means "as already configured" */
+#define GPIOF_IRQF_TRIGGER_NONE 0x00010000
+#define GPIOF_INPUT 0x00020000
+#define GPIOF_DRIVE_OUTPUT 0x00040000
+#define GPIOF_OUTPUT_LOW 0x00080000
+#define GPIOF_OUTPUT_HIGH 0x00100000
+
+struct old_gpio_chip {
+ struct gpio_chip gpio_chip;
+#define gpio_chip old_gpio_chip
+
+ spinlock_t lock;
+ unsigned int start;
+ unsigned int end;
+
+ int (*configure)(struct gpio_chip *chip, unsigned int gpio,
+ unsigned long flags);
+ int (*get_irq_num)(struct gpio_chip *chip, unsigned int gpio,
+ unsigned int *irqp, unsigned long *irqnumflagsp);
+ int (*read)(struct gpio_chip *chip, unsigned int gpio);
+ int (*write)(struct gpio_chip *chip, unsigned int gpio, unsigned on);
+ int (*read_detect_status)(struct gpio_chip *chip, unsigned int gpio);
+ int (*clear_detect_status)(struct gpio_chip *chip, unsigned int gpio);
+};
+
+int register_gpio_chip(struct gpio_chip *gpio_chip);
+
+#endif
diff --git a/arch/arm/mach-msm/gpio_hw.h b/arch/arm/mach-msm/gpio_hw.h
new file mode 100644
index 0000000..4634463
--- /dev/null
+++ b/arch/arm/mach-msm/gpio_hw.h
@@ -0,0 +1,276 @@
+/* arch/arm/mach-msm/gpio_hw.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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 __ARCH_ARM_MACH_MSM_GPIO_HW_H
+#define __ARCH_ARM_MACH_MSM_GPIO_HW_H
+
+#include <mach/msm_iomap.h>
+
+/* see 80-VA736-2 Rev C pp 695-751
+**
+** These are actually the *shadow* gpio registers, since the
+** real ones (which allow full access) are only available to the
+** ARM9 side of the world.
+**
+** Since the _BASE need to be page-aligned when we're mapping them
+** to virtual addresses, adjust for the additional offset in these
+** macros.
+*/
+
+#if defined(CONFIG_ARCH_MSM7X30)
+#define GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
+#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
+#else
+#define GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
+#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+#endif
+
+#if defined(CONFIG_ARCH_MSM7X00A)
+
+/* output value */
+#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */
+#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */
+#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */
+#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */
+#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */
+#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 107-121 */
+
+/* same pin map as above, output enable */
+#define GPIO_OE_0 GPIO1_REG(0x10)
+#define GPIO_OE_1 GPIO2_REG(0x08)
+#define GPIO_OE_2 GPIO1_REG(0x14)
+#define GPIO_OE_3 GPIO1_REG(0x18)
+#define GPIO_OE_4 GPIO1_REG(0x1C)
+#define GPIO_OE_5 GPIO1_REG(0x54)
+
+/* same pin map as above, input read */
+#define GPIO_IN_0 GPIO1_REG(0x34)
+#define GPIO_IN_1 GPIO2_REG(0x20)
+#define GPIO_IN_2 GPIO1_REG(0x38)
+#define GPIO_IN_3 GPIO1_REG(0x3C)
+#define GPIO_IN_4 GPIO1_REG(0x40)
+#define GPIO_IN_5 GPIO1_REG(0x44)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define GPIO_INT_EDGE_0 GPIO1_REG(0x60)
+#define GPIO_INT_EDGE_1 GPIO2_REG(0x50)
+#define GPIO_INT_EDGE_2 GPIO1_REG(0x64)
+#define GPIO_INT_EDGE_3 GPIO1_REG(0x68)
+#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C)
+#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0)
+
+/* same pin map as above, 1=positive 0=negative */
+#define GPIO_INT_POS_0 GPIO1_REG(0x70)
+#define GPIO_INT_POS_1 GPIO2_REG(0x58)
+#define GPIO_INT_POS_2 GPIO1_REG(0x74)
+#define GPIO_INT_POS_3 GPIO1_REG(0x78)
+#define GPIO_INT_POS_4 GPIO1_REG(0x7C)
+#define GPIO_INT_POS_5 GPIO1_REG(0xBC)
+
+/* same pin map as above, interrupt enable */
+#define GPIO_INT_EN_0 GPIO1_REG(0x80)
+#define GPIO_INT_EN_1 GPIO2_REG(0x60)
+#define GPIO_INT_EN_2 GPIO1_REG(0x84)
+#define GPIO_INT_EN_3 GPIO1_REG(0x88)
+#define GPIO_INT_EN_4 GPIO1_REG(0x8C)
+#define GPIO_INT_EN_5 GPIO1_REG(0xB8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90)
+#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68)
+#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94)
+#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98)
+#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C)
+#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4)
+
+/* same pin map as above, 1=interrupt pending */
+#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0)
+#define GPIO_INT_STATUS_1 GPIO2_REG(0x70)
+#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4)
+#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8)
+#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC)
+#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0)
+
+#endif
+
+#if defined(CONFIG_ARCH_QSD8X50)
+/* output value */
+#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */
+#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */
+#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */
+#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */
+#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 103-95 */
+#define GPIO_OUT_5 GPIO1_REG(0x10) /* gpio 121-104 */
+#define GPIO_OUT_6 GPIO1_REG(0x14) /* gpio 152-122 */
+#define GPIO_OUT_7 GPIO1_REG(0x18) /* gpio 164-153 */
+
+/* same pin map as above, output enable */
+#define GPIO_OE_0 GPIO1_REG(0x20)
+#define GPIO_OE_1 GPIO2_REG(0x08)
+#define GPIO_OE_2 GPIO1_REG(0x24)
+#define GPIO_OE_3 GPIO1_REG(0x28)
+#define GPIO_OE_4 GPIO1_REG(0x2C)
+#define GPIO_OE_5 GPIO1_REG(0x30)
+#define GPIO_OE_6 GPIO1_REG(0x34)
+#define GPIO_OE_7 GPIO1_REG(0x38)
+
+/* same pin map as above, input read */
+#define GPIO_IN_0 GPIO1_REG(0x50)
+#define GPIO_IN_1 GPIO2_REG(0x20)
+#define GPIO_IN_2 GPIO1_REG(0x54)
+#define GPIO_IN_3 GPIO1_REG(0x58)
+#define GPIO_IN_4 GPIO1_REG(0x5C)
+#define GPIO_IN_5 GPIO1_REG(0x60)
+#define GPIO_IN_6 GPIO1_REG(0x64)
+#define GPIO_IN_7 GPIO1_REG(0x68)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define GPIO_INT_EDGE_0 GPIO1_REG(0x70)
+#define GPIO_INT_EDGE_1 GPIO2_REG(0x50)
+#define GPIO_INT_EDGE_2 GPIO1_REG(0x74)
+#define GPIO_INT_EDGE_3 GPIO1_REG(0x78)
+#define GPIO_INT_EDGE_4 GPIO1_REG(0x7C)
+#define GPIO_INT_EDGE_5 GPIO1_REG(0x80)
+#define GPIO_INT_EDGE_6 GPIO1_REG(0x84)
+#define GPIO_INT_EDGE_7 GPIO1_REG(0x88)
+
+/* same pin map as above, 1=positive 0=negative */
+#define GPIO_INT_POS_0 GPIO1_REG(0x90)
+#define GPIO_INT_POS_1 GPIO2_REG(0x58)
+#define GPIO_INT_POS_2 GPIO1_REG(0x94)
+#define GPIO_INT_POS_3 GPIO1_REG(0x98)
+#define GPIO_INT_POS_4 GPIO1_REG(0x9C)
+#define GPIO_INT_POS_5 GPIO1_REG(0xA0)
+#define GPIO_INT_POS_6 GPIO1_REG(0xA4)
+#define GPIO_INT_POS_7 GPIO1_REG(0xA8)
+
+/* same pin map as above, interrupt enable */
+#define GPIO_INT_EN_0 GPIO1_REG(0xB0)
+#define GPIO_INT_EN_1 GPIO2_REG(0x60)
+#define GPIO_INT_EN_2 GPIO1_REG(0xB4)
+#define GPIO_INT_EN_3 GPIO1_REG(0xB8)
+#define GPIO_INT_EN_4 GPIO1_REG(0xBC)
+#define GPIO_INT_EN_5 GPIO1_REG(0xC0)
+#define GPIO_INT_EN_6 GPIO1_REG(0xC4)
+#define GPIO_INT_EN_7 GPIO1_REG(0xC8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define GPIO_INT_CLEAR_0 GPIO1_REG(0xD0)
+#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68)
+#define GPIO_INT_CLEAR_2 GPIO1_REG(0xD4)
+#define GPIO_INT_CLEAR_3 GPIO1_REG(0xD8)
+#define GPIO_INT_CLEAR_4 GPIO1_REG(0xDC)
+#define GPIO_INT_CLEAR_5 GPIO1_REG(0xE0)
+#define GPIO_INT_CLEAR_6 GPIO1_REG(0xE4)
+#define GPIO_INT_CLEAR_7 GPIO1_REG(0xE8)
+
+/* same pin map as above, 1=interrupt pending */
+#define GPIO_INT_STATUS_0 GPIO1_REG(0xF0)
+#define GPIO_INT_STATUS_1 GPIO2_REG(0x70)
+#define GPIO_INT_STATUS_2 GPIO1_REG(0xF4)
+#define GPIO_INT_STATUS_3 GPIO1_REG(0xF8)
+#define GPIO_INT_STATUS_4 GPIO1_REG(0xFC)
+#define GPIO_INT_STATUS_5 GPIO1_REG(0x100)
+#define GPIO_INT_STATUS_6 GPIO1_REG(0x104)
+#define GPIO_INT_STATUS_7 GPIO1_REG(0x108)
+
+#endif
+
+#if defined(CONFIG_ARCH_MSM7X30)
+
+/* output value */
+#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */
+#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 43-16 */
+#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-44 */
+#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */
+#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */
+#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 133-107 */
+#define GPIO_OUT_6 GPIO1_REG(0xC4) /* gpio 150-134 */
+#define GPIO_OUT_7 GPIO1_REG(0x214) /* gpio 181-151 */
+
+/* same pin map as above, output enable */
+#define GPIO_OE_0 GPIO1_REG(0x10)
+#define GPIO_OE_1 GPIO2_REG(0x08)
+#define GPIO_OE_2 GPIO1_REG(0x14)
+#define GPIO_OE_3 GPIO1_REG(0x18)
+#define GPIO_OE_4 GPIO1_REG(0x1C)
+#define GPIO_OE_5 GPIO1_REG(0x54)
+#define GPIO_OE_6 GPIO1_REG(0xC8)
+#define GPIO_OE_7 GPIO1_REG(0x218)
+
+/* same pin map as above, input read */
+#define GPIO_IN_0 GPIO1_REG(0x34)
+#define GPIO_IN_1 GPIO2_REG(0x20)
+#define GPIO_IN_2 GPIO1_REG(0x38)
+#define GPIO_IN_3 GPIO1_REG(0x3C)
+#define GPIO_IN_4 GPIO1_REG(0x40)
+#define GPIO_IN_5 GPIO1_REG(0x44)
+#define GPIO_IN_6 GPIO1_REG(0xCC)
+#define GPIO_IN_7 GPIO1_REG(0x21C)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define GPIO_INT_EDGE_0 GPIO1_REG(0x60)
+#define GPIO_INT_EDGE_1 GPIO2_REG(0x50)
+#define GPIO_INT_EDGE_2 GPIO1_REG(0x64)
+#define GPIO_INT_EDGE_3 GPIO1_REG(0x68)
+#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C)
+#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0)
+#define GPIO_INT_EDGE_6 GPIO1_REG(0xD0)
+#define GPIO_INT_EDGE_7 GPIO1_REG(0x240)
+
+/* same pin map as above, 1=positive 0=negative */
+#define GPIO_INT_POS_0 GPIO1_REG(0x70)
+#define GPIO_INT_POS_1 GPIO2_REG(0x58)
+#define GPIO_INT_POS_2 GPIO1_REG(0x74)
+#define GPIO_INT_POS_3 GPIO1_REG(0x78)
+#define GPIO_INT_POS_4 GPIO1_REG(0x7C)
+#define GPIO_INT_POS_5 GPIO1_REG(0xBC)
+#define GPIO_INT_POS_6 GPIO1_REG(0xD4)
+#define GPIO_INT_POS_7 GPIO1_REG(0x228)
+
+/* same pin map as above, interrupt enable */
+#define GPIO_INT_EN_0 GPIO1_REG(0x80)
+#define GPIO_INT_EN_1 GPIO2_REG(0x60)
+#define GPIO_INT_EN_2 GPIO1_REG(0x84)
+#define GPIO_INT_EN_3 GPIO1_REG(0x88)
+#define GPIO_INT_EN_4 GPIO1_REG(0x8C)
+#define GPIO_INT_EN_5 GPIO1_REG(0xB8)
+#define GPIO_INT_EN_6 GPIO1_REG(0xD8)
+#define GPIO_INT_EN_7 GPIO1_REG(0x22C)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90)
+#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68)
+#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94)
+#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98)
+#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C)
+#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4)
+#define GPIO_INT_CLEAR_6 GPIO1_REG(0xDC)
+#define GPIO_INT_CLEAR_7 GPIO1_REG(0x230)
+
+/* same pin map as above, 1=interrupt pending */
+#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0)
+#define GPIO_INT_STATUS_1 GPIO2_REG(0x70)
+#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4)
+#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8)
+#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC)
+#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0)
+#define GPIO_INT_STATUS_6 GPIO1_REG(0xE0)
+#define GPIO_INT_STATUS_7 GPIO1_REG(0x234)
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/htc_35mm_jack.c b/arch/arm/mach-msm/htc_35mm_jack.c
new file mode 100644
index 0000000..a956472
--- /dev/null
+++ b/arch/arm/mach-msm/htc_35mm_jack.c
@@ -0,0 +1,398 @@
+/* arch/arm/mach-msm/htc_35mm_jack.c
+ *
+ * Copyright (C) 2009 HTC, Inc.
+ * Author: Arec Kao <Arec_Kao@htc.com>
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Eric Olsen <eolsen@android.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/wakelock.h>
+#include <asm/gpio.h>
+#include <asm/atomic.h>
+#include <mach/board.h>
+#include <mach/vreg.h>
+#include <asm/mach-types.h>
+#include <mach/htc_acoustic_qsd.h>
+#include <mach/htc_35mm_jack.h>
+#include <mach/htc_headset.h>
+
+#ifdef CONFIG_HTC_AUDIOJACK
+#include <mach/audio_jack.h>
+#endif
+
+/* #define CONFIG_DEBUG_H2W */
+
+#define H2WI(fmt, arg...) \
+ printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
+#define H2WE(fmt, arg...) \
+ printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg)
+
+#ifdef CONFIG_DEBUG_H2W
+#define H2W_DBG(fmt, arg...) \
+ printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
+#else
+#define H2W_DBG(fmt, arg...) do {} while (0)
+#endif
+
+void detect_h2w_do_work(struct work_struct *w);
+
+static struct workqueue_struct *detect_wq;
+static struct workqueue_struct *button_wq;
+
+static DECLARE_DELAYED_WORK(detect_h2w_work, detect_h2w_do_work);
+
+static void insert_35mm_do_work(struct work_struct *work);
+static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work);
+static void remove_35mm_do_work(struct work_struct *work);
+static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work);
+static void button_35mm_do_work(struct work_struct *work);
+static DECLARE_WORK(button_35mm_work, button_35mm_do_work);
+
+struct h35_info {
+ struct mutex mutex_lock;
+ struct switch_dev hs_change;
+ unsigned long insert_jiffies;
+ int ext_35mm_status;
+ int is_ext_insert;
+ int key_code;
+ int mic_bias_state;
+ int *is_hpin_stable;
+ struct input_dev *input;
+
+ struct wake_lock headset_wake_lock;
+};
+
+static struct h35mm_platform_data *pd;
+static struct h35_info *hi;
+
+static ssize_t h35mm_print_name(struct switch_dev *sdev, char *buf)
+{
+ return sprintf(buf, "Headset\n");
+}
+
+static void button_35mm_do_work(struct work_struct *work)
+{
+ int key = 0;
+ int pressed = 0;
+
+ if (!hi->is_ext_insert) {
+ /* no headset ignor key event */
+ H2WI("3.5mm headset is plugged out, skip report key event");
+ return;
+ }
+
+ switch (hi->key_code) {
+ case 0x1: /* Play/Pause */
+ H2WI("3.5mm RC: Play Pressed");
+ key = KEY_MEDIA;
+ pressed = 1;
+ break;
+ case 0x2:
+ H2WI("3.5mm RC: BACKWARD Pressed");
+ key = KEY_PREVIOUSSONG;
+ pressed = 1;
+ break;
+ case 0x3:
+ H2WI("3.5mm RC: FORWARD Pressed");
+ key = KEY_NEXTSONG;
+ pressed = 1;
+ break;
+ case 0x81: /* Play/Pause */
+ H2WI("3.5mm RC: Play Released");
+ key = KEY_MEDIA;
+ pressed = 0;
+ break;
+ case 0x82:
+ H2WI("3.5mm RC: BACKWARD Released");
+ key = KEY_PREVIOUSSONG;
+ pressed = 0;
+ break;
+ case 0x83:
+ H2WI("3.5mm RC: FORWARD Released");
+ key = KEY_NEXTSONG;
+ pressed = 0;
+ break;
+ default:
+ H2WI("3.5mm RC: Unknown Button (0x%x) Pressed", hi->key_code);
+ return;
+ }
+ input_report_key(hi->input, key, pressed);
+ input_sync(hi->input);
+
+ wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ);
+}
+
+static void remove_35mm_do_work(struct work_struct *work)
+{
+ wake_lock_timeout(&hi->headset_wake_lock, 2.5*HZ);
+
+ H2W_DBG("");
+ /*To solve the insert, remove, insert headset problem*/
+ if (time_before_eq(jiffies, hi->insert_jiffies))
+ msleep(800);
+
+ if (hi->is_ext_insert) {
+ H2WI("Skip 3.5mm headset plug out!!!");
+ if (hi->is_hpin_stable)
+ *(hi->is_hpin_stable) = 1;
+ return;
+ }
+
+ pr_info("3.5mm_headset plug out\n");
+
+ if (pd->key_event_disable != NULL)
+ pd->key_event_disable();
+
+ if (hi->mic_bias_state) {
+ turn_mic_bias_on(0);
+ hi->mic_bias_state = 0;
+ }
+ hi->ext_35mm_status = 0;
+ if (hi->is_hpin_stable)
+ *(hi->is_hpin_stable) = 0;
+
+ /* Notify framework via switch class */
+ mutex_lock(&hi->mutex_lock);
+ switch_set_state(&hi->hs_change, hi->ext_35mm_status);
+ mutex_unlock(&hi->mutex_lock);
+}
+
+static void insert_35mm_do_work(struct work_struct *work)
+{
+ H2W_DBG("");
+ hi->insert_jiffies = jiffies + 1*HZ;
+
+ wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ);
+
+ if (hi->is_ext_insert) {
+ pr_info("3.5mm_headset plug in\n");
+
+ if (pd->key_event_enable != NULL)
+ pd->key_event_enable();
+
+ /* Turn On Mic Bias */
+ if (!hi->mic_bias_state) {
+ turn_mic_bias_on(1);
+ hi->mic_bias_state = 1;
+ /* Wait for pin stable */
+ msleep(300);
+ }
+
+ /* Detect headset with or without microphone */
+ if(pd->headset_has_mic) {
+ if (pd->headset_has_mic() == 0) {
+ /* without microphone */
+ pr_info("3.5mm without microphone\n");
+ hi->ext_35mm_status = BIT_HEADSET_NO_MIC;
+ } else { /* with microphone */
+ pr_info("3.5mm with microphone\n");
+ hi->ext_35mm_status = BIT_HEADSET;
+ }
+ } else {
+ /* Assume no mic */
+ pr_info("3.5mm without microphone\n");
+ hi->ext_35mm_status = BIT_HEADSET_NO_MIC;
+ }
+ hi->ext_35mm_status |= BIT_35MM_HEADSET;
+
+ /* Notify framework via switch class */
+ mutex_lock(&hi->mutex_lock);
+ switch_set_state(&hi->hs_change, hi->ext_35mm_status);
+ mutex_unlock(&hi->mutex_lock);
+
+ if (hi->is_hpin_stable)
+ *(hi->is_hpin_stable) = 1;
+ }
+}
+
+int htc_35mm_key_event(int keycode, int *hpin_stable)
+{
+ hi->key_code = keycode;
+ hi->is_hpin_stable = hpin_stable;
+
+ if ((hi->ext_35mm_status & BIT_HEADSET) == 0) {
+ *(hi->is_hpin_stable) = 0;
+
+ pr_info("Key press with no mic. Retrying detection\n");
+ queue_work(detect_wq, &insert_35mm_work);
+ } else
+ queue_work(button_wq, &button_35mm_work);
+
+ return 0;
+}
+
+int htc_35mm_jack_plug_event(int insert, int *hpin_stable)
+{
+ if (!hi) {
+ pr_err("Plug event before driver init\n");
+ return -1;
+ }
+
+ mutex_lock(&hi->mutex_lock);
+ hi->is_ext_insert = insert;
+ hi->is_hpin_stable = hpin_stable;
+ mutex_unlock(&hi->mutex_lock);
+
+ H2WI(" %d", hi->is_ext_insert);
+ if (!hi->is_ext_insert)
+ queue_work(detect_wq, &remove_35mm_work);
+ else
+ queue_work(detect_wq, &insert_35mm_work);
+ return 1;
+}
+
+static int htc_35mm_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ pd = pdev->dev.platform_data;
+
+ pr_info("H2W: htc_35mm_jack driver register\n");
+
+ hi = kzalloc(sizeof(struct h35_info), GFP_KERNEL);
+ if (!hi)
+ return -ENOMEM;
+
+ hi->ext_35mm_status = 0;
+ hi->is_ext_insert = 0;
+ hi->mic_bias_state = 0;
+
+ mutex_init(&hi->mutex_lock);
+
+ wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset");
+
+ hi->hs_change.name = "h2w";
+ hi->hs_change.print_name = h35mm_print_name;
+ ret = switch_dev_register(&hi->hs_change);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ detect_wq = create_workqueue("detection");
+ if (detect_wq == NULL) {
+ ret = -ENOMEM;
+ goto err_create_detect_work_queue;
+ }
+
+ button_wq = create_workqueue("button");
+ if (button_wq == NULL) {
+ ret = -ENOMEM;
+ goto err_create_button_work_queue;
+ }
+
+ hi->input = input_allocate_device();
+ if (!hi->input) {
+ ret = -ENOMEM;
+ goto err_request_input_dev;
+ }
+
+ hi->input->name = "h2w headset";
+ set_bit(EV_SYN, hi->input->evbit);
+ set_bit(EV_KEY, hi->input->evbit);
+ set_bit(KEY_MEDIA, hi->input->keybit);
+ set_bit(KEY_NEXTSONG, hi->input->keybit);
+ set_bit(KEY_PLAYPAUSE, hi->input->keybit);
+ set_bit(KEY_PREVIOUSSONG, hi->input->keybit);
+ set_bit(KEY_MUTE, hi->input->keybit);
+ set_bit(KEY_VOLUMEUP, hi->input->keybit);
+ set_bit(KEY_VOLUMEDOWN, hi->input->keybit);
+ set_bit(KEY_END, hi->input->keybit);
+ set_bit(KEY_SEND, hi->input->keybit);
+
+ ret = input_register_device(hi->input);
+ if (ret < 0)
+ goto err_register_input_dev;
+
+ /* Enable plug events*/
+ if (pd->plug_event_enable == NULL) {
+ ret = -ENOMEM;
+ goto err_enable_plug_event;
+ }
+ if (pd->plug_event_enable() != 1) {
+ ret = -ENOMEM;
+ goto err_enable_plug_event;
+ }
+
+ return 0;
+
+err_enable_plug_event:
+err_register_input_dev:
+ input_free_device(hi->input);
+err_request_input_dev:
+ destroy_workqueue(button_wq);
+err_create_button_work_queue:
+ destroy_workqueue(detect_wq);
+err_create_detect_work_queue:
+ switch_dev_unregister(&hi->hs_change);
+err_switch_dev_register:
+ kzfree(hi);
+ pr_err("H2W: Failed to register driver\n");
+
+ return ret;
+}
+
+static int htc_35mm_remove(struct platform_device *pdev)
+{
+ H2W_DBG("");
+ switch_dev_unregister(&hi->hs_change);
+ kzfree(hi);
+
+#if 0 /* Add keys later */
+ input_unregister_device(hi->input);
+#endif
+ return 0;
+}
+
+static struct platform_driver htc_35mm_driver = {
+ .probe = htc_35mm_probe,
+ .remove = htc_35mm_remove,
+ .driver = {
+ .name = "htc_headset",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init htc_35mm_init(void)
+{
+ H2W_DBG("");
+ return platform_driver_register(&htc_35mm_driver);
+}
+
+static void __exit htc_35mm_exit(void)
+{
+ platform_driver_unregister(&htc_35mm_driver);
+}
+
+module_init(htc_35mm_init);
+module_exit(htc_35mm_exit);
+
+MODULE_AUTHOR("Eric Olsen <eolsen@android.com>");
+MODULE_DESCRIPTION("HTC 3.5MM Driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c
new file mode 100644
index 0000000..2360b37
--- /dev/null
+++ b/arch/arm/mach-msm/htc_acoustic.c
@@ -0,0 +1,266 @@
+/* arch/arm/mach-msm/htc_acoustic.c
+ *
+ * Copyright (C) 2007-2008 HTC Corporation
+ * Author: Laurence Chen <Laurence_Chen@htc.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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_iomap.h>
+
+#include "smd_private.h"
+
+#define ACOUSTIC_IOCTL_MAGIC 'p'
+#define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int)
+
+#define HTCRPOG 0x30100002
+#define HTCVERS MSM_RPC_VERS(0,0)
+#define ONCRPC_SET_MIC_BIAS_PROC (1)
+#define ONCRPC_ACOUSTIC_INIT_PROC (5)
+#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6)
+
+#define HTC_ACOUSTIC_TABLE_SIZE (0x10000)
+
+#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args)
+#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args)
+
+struct set_smem_req {
+ struct rpc_request_hdr hdr;
+ uint32_t size;
+};
+
+struct set_smem_rep {
+ struct rpc_reply_hdr hdr;
+ int n;
+};
+
+struct set_acoustic_req {
+ struct rpc_request_hdr hdr;
+};
+
+struct set_acoustic_rep {
+ struct rpc_reply_hdr hdr;
+ int n;
+};
+
+static uint32_t htc_acoustic_vir_addr;
+static struct msm_rpc_endpoint *endpoint;
+static struct mutex api_lock;
+static struct mutex rpc_connect_mutex;
+
+static int is_rpc_connect(void)
+{
+ mutex_lock(&rpc_connect_mutex);
+ if (endpoint == NULL) {
+ endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0);
+ if (IS_ERR(endpoint)) {
+ pr_err("%s: init rpc failed! rc = %ld\n",
+ __func__, PTR_ERR(endpoint));
+ mutex_unlock(&rpc_connect_mutex);
+ return 0;
+ }
+ }
+ mutex_unlock(&rpc_connect_mutex);
+ return 1;
+}
+
+int turn_mic_bias_on(int on)
+{
+ struct mic_bias_req {
+ struct rpc_request_hdr hdr;
+ uint32_t on;
+ } req;
+
+ if (!is_rpc_connect())
+ return -1;
+
+ req.on = cpu_to_be32(on);
+ return msm_rpc_call(endpoint, ONCRPC_SET_MIC_BIAS_PROC,
+ &req, sizeof(req), 5 * HZ);
+}
+EXPORT_SYMBOL(turn_mic_bias_on);
+
+static int acoustic_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long pgoff, delta;
+ int rc = -EINVAL;
+ size_t size;
+
+ D("mmap\n");
+
+ mutex_lock(&api_lock);
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (vma->vm_pgoff != 0) {
+ E("mmap failed: page offset %lx\n", vma->vm_pgoff);
+ goto done;
+ }
+
+ if (!htc_acoustic_vir_addr) {
+ E("mmap failed: smem region not allocated\n");
+ rc = -EIO;
+ goto done;
+ }
+
+ pgoff = MSM_SHARED_RAM_PHYS +
+ (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE);
+ delta = PAGE_ALIGN(pgoff) - pgoff;
+
+ if (size + delta > HTC_ACOUSTIC_TABLE_SIZE) {
+ E("mmap failed: size %d\n", size);
+ goto done;
+ }
+
+ pgoff += delta;
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ rc = io_remap_pfn_range(vma, vma->vm_start, pgoff >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+
+ if (rc < 0)
+ E("mmap failed: remap error %d\n", rc);
+
+done: mutex_unlock(&api_lock);
+ return rc;
+}
+
+static int acoustic_open(struct inode *inode, struct file *file)
+{
+ int rc = -EIO;
+ struct set_smem_req req_smem;
+ struct set_smem_rep rep_smem;
+
+ D("open\n");
+
+ mutex_lock(&api_lock);
+
+ if (!htc_acoustic_vir_addr) {
+ if (!is_rpc_connect())
+ goto done;
+
+ req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE);
+ rc = msm_rpc_call_reply(endpoint,
+ ONCRPC_ALLOC_ACOUSTIC_MEM_PROC,
+ &req_smem, sizeof(req_smem),
+ &rep_smem, sizeof(rep_smem),
+ 5 * HZ);
+
+ if (rep_smem.n != 0 || rc < 0) {
+ E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n",
+ rc);
+ goto done;
+ }
+ htc_acoustic_vir_addr =
+ (uint32_t)smem_alloc(SMEM_ID_VENDOR1,
+ HTC_ACOUSTIC_TABLE_SIZE);
+ if (!htc_acoustic_vir_addr) {
+ E("open failed: smem_alloc error\n");
+ goto done;
+ }
+ }
+
+ rc = 0;
+done:
+ mutex_unlock(&api_lock);
+ return rc;
+}
+
+static int acoustic_release(struct inode *inode, struct file *file)
+{
+ D("release\n");
+ return 0;
+}
+
+static long acoustic_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc, reply_value;
+ struct set_acoustic_req req;
+ struct set_acoustic_rep rep;
+
+ D("ioctl\n");
+
+ mutex_lock(&api_lock);
+
+ switch (cmd) {
+ case ACOUSTIC_ARM11_DONE:
+ D("ioctl: ACOUSTIC_ARM11_DONE called %d.\n", current->pid);
+ rc = msm_rpc_call_reply(endpoint,
+ ONCRPC_ACOUSTIC_INIT_PROC, &req,
+ sizeof(req), &rep, sizeof(rep),
+ 5 * HZ);
+
+ reply_value = be32_to_cpu(rep.n);
+ if (reply_value != 0 || rc < 0) {
+ E("ioctl failed: ONCRPC_ACOUSTIC_INIT_PROC "\
+ "error %d.\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ break;
+ }
+ D("ioctl: ONCRPC_ACOUSTIC_INIT_PROC success.\n");
+ break;
+ default:
+ E("ioctl: invalid command\n");
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&api_lock);
+ return 0;
+}
+
+
+static struct file_operations acoustic_fops = {
+ .owner = THIS_MODULE,
+ .open = acoustic_open,
+ .release = acoustic_release,
+ .mmap = acoustic_mmap,
+ .unlocked_ioctl = acoustic_ioctl,
+};
+
+static struct miscdevice acoustic_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "htc-acoustic",
+ .fops = &acoustic_fops,
+};
+
+static int __init acoustic_init(void)
+{
+ mutex_init(&api_lock);
+ mutex_init(&rpc_connect_mutex);
+ return misc_register(&acoustic_misc);
+}
+
+static void __exit acoustic_exit(void)
+{
+ misc_deregister(&acoustic_misc);
+}
+
+module_init(acoustic_init);
+module_exit(acoustic_exit);
+
+MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>");
+MODULE_DESCRIPTION("HTC acoustic driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c
new file mode 100644
index 0000000..ce3c3a0
--- /dev/null
+++ b/arch/arm/mach-msm/htc_acoustic_qsd.c
@@ -0,0 +1,315 @@
+/* arch/arm/mach-msm/htc_acoustic_qsd.c
+ *
+ * Copyright (C) 2009 HTC 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/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_iomap.h>
+#include <mach/htc_acoustic_qsd.h>
+#include <mach/msm_qdsp6_audio.h>
+
+#include "smd_private.h"
+
+#define ACOUSTIC_IOCTL_MAGIC 'p'
+#define ACOUSTIC_UPDATE_ADIE \
+ _IOW(ACOUSTIC_IOCTL_MAGIC, 24, unsigned int)
+
+#define HTCACOUSTICPROG 0x30100003
+#define HTCACOUSTICVERS 0
+#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (1)
+#define ONCRPC_UPDATE_ADIE_PROC (2)
+#define ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC (3)
+#define ONCRPC_FORCE_HEADSET_SPEAKER_PROC (4)
+
+#define HTC_ACOUSTIC_TABLE_SIZE (0x20000)
+
+#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args)
+#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args)
+
+static uint32_t htc_acoustic_vir_addr;
+static struct msm_rpc_endpoint *endpoint;
+static struct mutex api_lock;
+static struct mutex rpc_connect_lock;
+static struct qsd_acoustic_ops *the_ops;
+
+void acoustic_register_ops(struct qsd_acoustic_ops *ops)
+{
+ the_ops = ops;
+}
+
+static int is_rpc_connect(void)
+{
+ mutex_lock(&rpc_connect_lock);
+ if (endpoint == NULL) {
+ endpoint = msm_rpc_connect(HTCACOUSTICPROG,
+ HTCACOUSTICVERS, 0);
+ if (IS_ERR(endpoint)) {
+ pr_err("%s: init rpc failed! rc = %ld\n",
+ __func__, PTR_ERR(endpoint));
+ mutex_unlock(&rpc_connect_lock);
+ return -1;
+ }
+ }
+ mutex_unlock(&rpc_connect_lock);
+ return 0;
+}
+
+int turn_mic_bias_on(int on)
+{
+ D("%s called %d\n", __func__, on);
+ if (the_ops->enable_mic_bias)
+ the_ops->enable_mic_bias(on);
+
+ return 0;
+}
+EXPORT_SYMBOL(turn_mic_bias_on);
+
+int force_headset_speaker_on(int enable)
+{
+ struct speaker_headset_req {
+ struct rpc_request_hdr hdr;
+ uint32_t enable;
+ } spkr_req;
+
+ D("%s called %d\n", __func__, enable);
+
+ if (is_rpc_connect() == -1)
+ return -1;
+
+ spkr_req.enable = cpu_to_be32(enable);
+ return msm_rpc_call(endpoint,
+ ONCRPC_FORCE_HEADSET_SPEAKER_PROC,
+ &spkr_req, sizeof(spkr_req), 5 * HZ);
+}
+EXPORT_SYMBOL(force_headset_speaker_on);
+
+int enable_aux_loopback(uint32_t enable)
+{
+ struct aux_loopback_req {
+ struct rpc_request_hdr hdr;
+ uint32_t enable;
+ } aux_req;
+
+ D("%s called %d\n", __func__, enable);
+
+ if (is_rpc_connect() == -1)
+ return -1;
+
+ aux_req.enable = cpu_to_be32(enable);
+ return msm_rpc_call(endpoint,
+ ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC,
+ &aux_req, sizeof(aux_req), 5 * HZ);
+}
+EXPORT_SYMBOL(enable_aux_loopback);
+
+static int acoustic_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long pgoff;
+ int rc = -EINVAL;
+ size_t size;
+
+ D("mmap\n");
+
+ mutex_lock(&api_lock);
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (vma->vm_pgoff != 0) {
+ E("mmap failed: page offset %lx\n", vma->vm_pgoff);
+ goto done;
+ }
+
+ if (!htc_acoustic_vir_addr) {
+ E("mmap failed: smem region not allocated\n");
+ rc = -EIO;
+ goto done;
+ }
+
+ pgoff = MSM_SHARED_RAM_PHYS +
+ (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE);
+ pgoff = ((pgoff + 4095) & ~4095);
+ htc_acoustic_vir_addr = ((htc_acoustic_vir_addr + 4095) & ~4095);
+
+ if (pgoff <= 0) {
+ E("pgoff wrong. %ld\n", pgoff);
+ goto done;
+ }
+
+ if (size <= HTC_ACOUSTIC_TABLE_SIZE) {
+ pgoff = pgoff >> PAGE_SHIFT;
+ } else {
+ E("size > HTC_ACOUSTIC_TABLE_SIZE %d\n", size);
+ goto done;
+ }
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ rc = io_remap_pfn_range(vma, vma->vm_start, pgoff,
+ size, vma->vm_page_prot);
+
+ if (rc < 0)
+ E("mmap failed: remap error %d\n", rc);
+
+done: mutex_unlock(&api_lock);
+ return rc;
+}
+
+static int acoustic_open(struct inode *inode, struct file *file)
+{
+ int reply_value;
+ int rc = -EIO;
+ struct set_smem_req {
+ struct rpc_request_hdr hdr;
+ uint32_t size;
+ } req_smem;
+
+ struct set_smem_rep {
+ struct rpc_reply_hdr hdr;
+ int n;
+ } rep_smem;
+
+ D("open\n");
+
+ mutex_lock(&api_lock);
+
+ if (!htc_acoustic_vir_addr) {
+ if (is_rpc_connect() == -1)
+ goto done;
+
+ req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE);
+ rc = msm_rpc_call_reply(endpoint,
+ ONCRPC_ALLOC_ACOUSTIC_MEM_PROC,
+ &req_smem, sizeof(req_smem),
+ &rep_smem, sizeof(rep_smem),
+ 5 * HZ);
+
+ reply_value = be32_to_cpu(rep_smem.n);
+ if (reply_value != 0 || rc < 0) {
+ E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n",
+ rc);
+ goto done;
+ }
+ htc_acoustic_vir_addr =
+ (uint32_t)smem_alloc(SMEM_ID_VENDOR1,
+ HTC_ACOUSTIC_TABLE_SIZE);
+ if (!htc_acoustic_vir_addr) {
+ E("open failed: smem_alloc error\n");
+ goto done;
+ }
+ }
+
+ rc = 0;
+done:
+ mutex_unlock(&api_lock);
+ return rc;
+}
+
+static int acoustic_release(struct inode *inode, struct file *file)
+{
+ D("release\n");
+ return 0;
+}
+
+static long acoustic_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc, reply_value;
+
+ D("ioctl\n");
+
+ mutex_lock(&api_lock);
+
+ switch (cmd) {
+ case ACOUSTIC_UPDATE_ADIE: {
+ struct update_adie_req {
+ struct rpc_request_hdr hdr;
+ int id;
+ } adie_req;
+
+ struct update_adie_rep {
+ struct rpc_reply_hdr hdr;
+ int ret;
+ } adie_rep;
+
+ D("ioctl: ACOUSTIC_UPDATE_ADIE called %d.\n", current->pid);
+
+ adie_req.id = cpu_to_be32(-1); /* update all codecs */
+ rc = msm_rpc_call_reply(endpoint,
+ ONCRPC_UPDATE_ADIE_PROC, &adie_req,
+ sizeof(adie_req), &adie_rep,
+ sizeof(adie_rep), 5 * HZ);
+
+ reply_value = be32_to_cpu(adie_rep.ret);
+ if (reply_value != 0 || rc < 0) {
+ E("ioctl failed: ONCRPC_UPDATE_ADIE_PROC "\
+ "error %d.\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ break;
+ }
+ D("ioctl: ONCRPC_UPDATE_ADIE_PROC success.\n");
+ break;
+ }
+ default:
+ E("ioctl: invalid command\n");
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&api_lock);
+ return rc;
+}
+
+struct rpc_set_uplink_mute_args {
+ int mute;
+};
+
+static struct file_operations acoustic_fops = {
+ .owner = THIS_MODULE,
+ .open = acoustic_open,
+ .release = acoustic_release,
+ .mmap = acoustic_mmap,
+ .unlocked_ioctl = acoustic_ioctl,
+};
+
+static struct miscdevice acoustic_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "htc-acoustic",
+ .fops = &acoustic_fops,
+};
+
+static int __init acoustic_init(void)
+{
+ mutex_init(&api_lock);
+ mutex_init(&rpc_connect_lock);
+ return misc_register(&acoustic_misc);
+}
+
+static void __exit acoustic_exit(void)
+{
+ misc_deregister(&acoustic_misc);
+}
+
+module_init(acoustic_init);
+module_exit(acoustic_exit);
+
diff --git a/arch/arm/mach-msm/htc_akm_cal.c b/arch/arm/mach-msm/htc_akm_cal.c
new file mode 100644
index 0000000..943083f
--- /dev/null
+++ b/arch/arm/mach-msm/htc_akm_cal.c
@@ -0,0 +1,64 @@
+/* arch/arm/mach-msm/htc_akm_cal.c
+ *
+ * Code to extract compass calibration information from ATAG set up
+ * by the bootloader.
+ *
+ * Copyright (C) 2007-2008 HTC Corporation
+ * Author: Farmer Tseng <farmer_tseng@htc.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/setup.h>
+
+/* configuration tags specific to AKM8976 */
+#define ATAG_AKM8976 0x89768976 /* AKM8976 */
+
+#define MAX_CALI_SIZE 0x1000U
+
+static char akm_cal_ram[MAX_CALI_SIZE];
+
+char *get_akm_cal_ram(void)
+{
+ return(akm_cal_ram);
+}
+EXPORT_SYMBOL(get_akm_cal_ram);
+
+static int __init parse_tag_akm(const struct tag *tag)
+{
+ unsigned char *dptr = (unsigned char *)(&tag->u);
+ unsigned size;
+
+ size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_CALI_SIZE);
+
+ printk(KERN_INFO "AKM Data size = %d , 0x%x, size = %d\n",
+ tag->hdr.size, tag->hdr.tag, size);
+
+#ifdef ATAG_COMPASS_DEBUG
+ unsigned i;
+ unsigned char *ptr;
+
+ ptr = dptr;
+ printk(KERN_INFO
+ "AKM Data size = %d , 0x%x\n",
+ tag->hdr.size, tag->hdr.tag);
+ for (i = 0; i < size; i++)
+ printk(KERN_INFO "%02x ", *ptr++);
+#endif
+ memcpy((void *)akm_cal_ram, (void *)dptr, size);
+ return 0;
+}
+
+__tagtable(ATAG_AKM8976, parse_tag_akm);
diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c
new file mode 100644
index 0000000..e1dbbf5
--- /dev/null
+++ b/arch/arm/mach-msm/htc_battery.c
@@ -0,0 +1,796 @@
+/* arch/arm/mach-msm/htc_battery.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2008 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/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/wakelock.h>
+#include <asm/gpio.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/board.h>
+#include <asm/mach-types.h>
+#include <mach/board_htc.h>
+
+static struct wake_lock vbus_wake_lock;
+
+#define TRACE_BATT 0
+
+#if TRACE_BATT
+#include <linux/rtc.h>
+
+#define BATT(x...) do { \
+struct timespec ts; \
+struct rtc_time tm; \
+getnstimeofday(&ts); \
+rtc_time_to_tm(ts.tv_sec, &tm); \
+printk(KERN_INFO "[BATT] " x); \
+printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \
+ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+} while (0)
+#else
+#define BATT(x...) do {} while (0)
+#endif
+
+/* rpc related */
+#define APP_BATT_PDEV_NAME "rs30100001:00000000"
+#define APP_BATT_PROG 0x30100001
+#define APP_BATT_VER MSM_RPC_VERS(0,0)
+#define HTC_PROCEDURE_BATTERY_NULL 0
+#define HTC_PROCEDURE_GET_BATT_LEVEL 1
+#define HTC_PROCEDURE_GET_BATT_INFO 2
+#define HTC_PROCEDURE_GET_CABLE_STATUS 3
+#define HTC_PROCEDURE_SET_BATT_DELTA 4
+
+/* module debugger */
+#define HTC_BATTERY_DEBUG 1
+#define BATTERY_PREVENTION 1
+
+/* Enable this will shut down if no battery */
+#define ENABLE_BATTERY_DETECTION 0
+/* Sapphire pin changes:
+ * USB_ID (GPIO 90) is renamed to AC_IN (GPIO 30)
+ * CHARGER_EN (CPLD MISC2 bit[0]) is move to PMIC (MPP_14).
+ * ISET (CPLD MISC2 bit[1]) is move to PMIC (MPP_13). */
+#define GPIO_SAPPHIRE_USB_ID 30
+
+#define GPIO_BATTERY_DETECTION 21
+#define GPIO_BATTERY_CHARGER_EN 128
+
+/* Charge current selection */
+#define GPIO_BATTERY_CHARGER_CURRENT 129
+
+typedef enum {
+ DISABLE = 0,
+ ENABLE_SLOW_CHG,
+ ENABLE_FAST_CHG
+} batt_ctl_t;
+
+/* This order is the same as htc_power_supplies[]
+ * And it's also the same as htc_cable_status_update()
+ */
+typedef enum {
+ CHARGER_BATTERY = 0,
+ CHARGER_USB,
+ CHARGER_AC
+} charger_type_t;
+
+const char *charger_tags[] = {"none", "USB", "AC"};
+
+struct battery_info_reply {
+ u32 batt_id; /* Battery ID from ADC */
+ u32 batt_vol; /* Battery voltage from ADC */
+ u32 batt_temp; /* Battery Temperature (C) from formula and ADC */
+ u32 batt_current; /* Battery current from ADC */
+ u32 level; /* formula */
+ u32 charging_source; /* 0: no cable, 1:usb, 2:AC */
+ u32 charging_enabled; /* 0: Disable, 1: Enable */
+ u32 full_bat; /* Full capacity of battery (mAh) */
+};
+
+struct htc_battery_info {
+ int present;
+ unsigned long update_time;
+
+ /* lock to protect the battery info */
+ struct mutex lock;
+
+ /* lock held while calling the arm9 to query the battery info */
+ struct mutex rpc_lock;
+ struct battery_info_reply rep;
+};
+
+static struct msm_rpc_endpoint *endpoint;
+
+static struct htc_battery_info htc_batt_info;
+
+static unsigned int cache_time = 1000;
+
+static int htc_battery_initial = 0;
+
+static enum power_supply_property htc_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property htc_power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+ "battery",
+};
+
+/* HTC dedicated attributes */
+static ssize_t htc_battery_show_property(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static int htc_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static int htc_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static struct power_supply htc_power_supplies[] = {
+ {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = htc_battery_properties,
+ .num_properties = ARRAY_SIZE(htc_battery_properties),
+ .get_property = htc_battery_get_property,
+ },
+ {
+ .name = "usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = htc_power_properties,
+ .num_properties = ARRAY_SIZE(htc_power_properties),
+ .get_property = htc_power_get_property,
+ },
+ {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = htc_power_properties,
+ .num_properties = ARRAY_SIZE(htc_power_properties),
+ .get_property = htc_power_get_property,
+ },
+};
+
+static int g_usb_online;
+
+/* -------------------------------------------------------------------------- */
+
+#if defined(CONFIG_DEBUG_FS)
+int htc_battery_set_charging(batt_ctl_t ctl);
+static int batt_debug_set(void *data, u64 val)
+{
+ return htc_battery_set_charging((batt_ctl_t) val);
+}
+
+static int batt_debug_get(void *data, u64 *val)
+{
+ return -ENOSYS;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n");
+static int __init batt_debug_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("htc_battery", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops);
+
+ return 0;
+}
+
+device_initcall(batt_debug_init);
+#endif
+
+static int init_batt_gpio(void)
+{
+ if (!machine_is_trout())
+ return 0;
+
+ if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0)
+ goto gpio_failed;
+ if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0)
+ goto gpio_failed;
+ if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0)
+ goto gpio_failed;
+
+ return 0;
+
+gpio_failed:
+ return -EINVAL;
+
+}
+
+/*
+ * battery_charging_ctrl - battery charing control.
+ * @ctl: battery control command
+ *
+ */
+static int battery_charging_ctrl(batt_ctl_t ctl)
+{
+ int result = 0;
+
+ /* The charing operations are move to A9 in Sapphire. */
+ if (!machine_is_trout())
+ return result;
+
+ switch (ctl) {
+ case DISABLE:
+ BATT("charger OFF");
+ /* 0 for enable; 1 disable */
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1);
+ break;
+ case ENABLE_SLOW_CHG:
+ BATT("charger ON (SLOW)");
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0);
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+ break;
+ case ENABLE_FAST_CHG:
+ BATT("charger ON (FAST)");
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1);
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+ break;
+ default:
+ printk(KERN_ERR "Not supported battery ctr called.!\n");
+ result = -EINVAL;
+ break;
+ }
+
+ return result;
+}
+
+int htc_battery_set_charging(batt_ctl_t ctl)
+{
+ int rc;
+
+ if ((rc = battery_charging_ctrl(ctl)) < 0)
+ goto result;
+
+ if (!htc_battery_initial) {
+ htc_batt_info.rep.charging_enabled = ctl & 0x3;
+ } else {
+ mutex_lock(&htc_batt_info.lock);
+ htc_batt_info.rep.charging_enabled = ctl & 0x3;
+ mutex_unlock(&htc_batt_info.lock);
+ }
+result:
+ return rc;
+}
+
+int htc_battery_status_update(u32 curr_level)
+{
+ int notify;
+ if (!htc_battery_initial)
+ return 0;
+
+ mutex_lock(&htc_batt_info.lock);
+ notify = (htc_batt_info.rep.level != curr_level);
+ htc_batt_info.rep.level = curr_level;
+ mutex_unlock(&htc_batt_info.lock);
+
+ if (notify)
+ power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+ return 0;
+}
+
+int htc_cable_status_update(int status)
+{
+ int rc = 0;
+ unsigned last_source;
+
+ if (!htc_battery_initial)
+ return 0;
+
+ if (status < CHARGER_BATTERY || status > CHARGER_AC) {
+ BATT("%s: Not supported cable status received!", __func__);
+ return -EINVAL;
+ }
+ mutex_lock(&htc_batt_info.lock);
+ /* A9 reports USB charging when helf AC cable in and China AC charger. */
+ /* Work arround: notify userspace AC charging first,
+ and notify USB charging again when receiving usb connected notificaiton from usb driver. */
+ last_source = htc_batt_info.rep.charging_source;
+ if (status == CHARGER_USB && g_usb_online == 0)
+ htc_batt_info.rep.charging_source = CHARGER_AC;
+ else {
+ htc_batt_info.rep.charging_source = status;
+ /* usb driver will not notify usb offline. */
+ if (status == CHARGER_BATTERY && g_usb_online == 1)
+ g_usb_online = 0;
+ }
+
+ /* TODO: Don't call usb driver again with the same cable status. */
+ msm_hsusb_set_vbus_state(status == CHARGER_USB);
+
+ if (htc_batt_info.rep.charging_source != last_source) {
+ if (htc_batt_info.rep.charging_source == CHARGER_USB ||
+ htc_batt_info.rep.charging_source == CHARGER_AC) {
+ wake_lock(&vbus_wake_lock);
+ } else {
+ /* give userspace some time to see the uevent and update
+ * LED state or whatnot...
+ */
+ wake_lock_timeout(&vbus_wake_lock, HZ / 2);
+ }
+ if (htc_batt_info.rep.charging_source == CHARGER_BATTERY || last_source == CHARGER_BATTERY)
+ power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+ if (htc_batt_info.rep.charging_source == CHARGER_USB || last_source == CHARGER_USB)
+ power_supply_changed(&htc_power_supplies[CHARGER_USB]);
+ if (htc_batt_info.rep.charging_source == CHARGER_AC || last_source == CHARGER_AC)
+ power_supply_changed(&htc_power_supplies[CHARGER_AC]);
+ }
+ mutex_unlock(&htc_batt_info.lock);
+
+ return rc;
+}
+
+/* A9 reports USB charging when helf AC cable in and China AC charger. */
+/* Work arround: notify userspace AC charging first,
+and notify USB charging again when receiving usb connected notification from usb driver. */
+void notify_usb_connected(int online)
+{
+ mutex_lock(&htc_batt_info.lock);
+
+ BATT("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online);
+
+ if (g_usb_online != online) {
+ g_usb_online = online;
+ if (online && htc_batt_info.rep.charging_source == CHARGER_AC) {
+ mutex_unlock(&htc_batt_info.lock);
+ htc_cable_status_update(CHARGER_USB);
+ mutex_lock(&htc_batt_info.lock);
+ } else if (online) {
+ BATT("warning: usb connected but charging source=%d", htc_batt_info.rep.charging_source);
+ }
+ }
+ mutex_unlock(&htc_batt_info.lock);
+}
+
+static int htc_get_batt_info(struct battery_info_reply *buffer)
+{
+ struct rpc_request_hdr req;
+
+ struct htc_get_batt_info_rep {
+ struct rpc_reply_hdr hdr;
+ struct battery_info_reply info;
+ } rep;
+
+ int rc;
+
+ if (buffer == NULL)
+ return -EINVAL;
+
+ rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO,
+ &req, sizeof(req),
+ &rep, sizeof(rep),
+ 5 * HZ);
+ if ( rc < 0 )
+ return rc;
+
+ mutex_lock(&htc_batt_info.lock);
+ buffer->batt_id = be32_to_cpu(rep.info.batt_id);
+ buffer->batt_vol = be32_to_cpu(rep.info.batt_vol);
+ buffer->batt_temp = be32_to_cpu(rep.info.batt_temp);
+ buffer->batt_current = be32_to_cpu(rep.info.batt_current);
+ buffer->level = be32_to_cpu(rep.info.level);
+ /* Move the rules of charging_source to cable_status_update. */
+ /* buffer->charging_source = be32_to_cpu(rep.info.charging_source); */
+ buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled);
+ buffer->full_bat = be32_to_cpu(rep.info.full_bat);
+ mutex_unlock(&htc_batt_info.lock);
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+static int htc_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ charger_type_t charger;
+
+ mutex_lock(&htc_batt_info.lock);
+ charger = htc_batt_info.rep.charging_source;
+ mutex_unlock(&htc_batt_info.lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = (charger == CHARGER_AC ? 1 : 0);
+ else if (psy->type == POWER_SUPPLY_TYPE_USB)
+ val->intval = (charger == CHARGER_USB ? 1 : 0);
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int htc_battery_get_charging_status(void)
+{
+ u32 level;
+ charger_type_t charger;
+ int ret;
+
+ mutex_lock(&htc_batt_info.lock);
+ charger = htc_batt_info.rep.charging_source;
+
+ switch (charger) {
+ case CHARGER_BATTERY:
+ ret = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case CHARGER_USB:
+ case CHARGER_AC:
+ level = htc_batt_info.rep.level;
+ if (level == 100)
+ ret = POWER_SUPPLY_STATUS_FULL;
+ else
+ ret = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ default:
+ ret = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ mutex_unlock(&htc_batt_info.lock);
+ return ret;
+}
+
+static int htc_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = htc_battery_get_charging_status();
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = htc_batt_info.present;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ mutex_lock(&htc_batt_info.lock);
+ val->intval = htc_batt_info.rep.level;
+ mutex_unlock(&htc_batt_info.lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define HTC_BATTERY_ATTR(_name) \
+{ \
+ .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \
+ .show = htc_battery_show_property, \
+ .store = NULL, \
+}
+
+static struct device_attribute htc_battery_attrs[] = {
+ HTC_BATTERY_ATTR(batt_id),
+ HTC_BATTERY_ATTR(batt_vol),
+ HTC_BATTERY_ATTR(batt_temp),
+ HTC_BATTERY_ATTR(batt_current),
+ HTC_BATTERY_ATTR(charging_source),
+ HTC_BATTERY_ATTR(charging_enabled),
+ HTC_BATTERY_ATTR(full_bat),
+};
+
+enum {
+ BATT_ID = 0,
+ BATT_VOL,
+ BATT_TEMP,
+ BATT_CURRENT,
+ CHARGING_SOURCE,
+ CHARGING_ENABLED,
+ FULL_BAT,
+};
+
+static int htc_rpc_set_delta(unsigned delta)
+{
+ struct set_batt_delta_req {
+ struct rpc_request_hdr hdr;
+ uint32_t data;
+ } req;
+
+ req.data = cpu_to_be32(delta);
+ return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA,
+ &req, sizeof(req), 5 * HZ);
+}
+
+
+static ssize_t htc_battery_set_delta(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ unsigned long delta = 0;
+
+ delta = simple_strtoul(buf, NULL, 10);
+
+ if (delta > 100)
+ return -EINVAL;
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ rc = htc_rpc_set_delta(delta);
+ mutex_unlock(&htc_batt_info.rpc_lock);
+ if (rc < 0)
+ return rc;
+ return count;
+}
+
+static struct device_attribute htc_set_delta_attrs[] = {
+ __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta),
+};
+
+static int htc_battery_create_attrs(struct device * dev)
+{
+ int i, j, rc;
+
+ for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) {
+ rc = device_create_file(dev, &htc_battery_attrs[i]);
+ if (rc)
+ goto htc_attrs_failed;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) {
+ rc = device_create_file(dev, &htc_set_delta_attrs[j]);
+ if (rc)
+ goto htc_delta_attrs_failed;
+ }
+
+ goto succeed;
+
+htc_attrs_failed:
+ while (i--)
+ device_remove_file(dev, &htc_battery_attrs[i]);
+htc_delta_attrs_failed:
+ while (j--)
+ device_remove_file(dev, &htc_set_delta_attrs[i]);
+succeed:
+ return rc;
+}
+
+static ssize_t htc_battery_show_property(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int i = 0;
+ const ptrdiff_t off = attr - htc_battery_attrs;
+
+ /* rpc lock is used to prevent two threads from calling
+ * into the get info rpc at the same time
+ */
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ /* check cache time to decide if we need to update */
+ if (htc_batt_info.update_time &&
+ time_before(jiffies, htc_batt_info.update_time +
+ msecs_to_jiffies(cache_time)))
+ goto dont_need_update;
+
+ if (htc_get_batt_info(&htc_batt_info.rep) < 0) {
+ printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__);
+ } else {
+ htc_batt_info.update_time = jiffies;
+ }
+dont_need_update:
+ mutex_unlock(&htc_batt_info.rpc_lock);
+
+ mutex_lock(&htc_batt_info.lock);
+ switch (off) {
+ case BATT_ID:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_id);
+ break;
+ case BATT_VOL:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_vol);
+ break;
+ case BATT_TEMP:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_temp);
+ break;
+ case BATT_CURRENT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_current);
+ break;
+ case CHARGING_SOURCE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.charging_source);
+ break;
+ case CHARGING_ENABLED:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.charging_enabled);
+ break;
+ case FULL_BAT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.full_bat);
+ break;
+ default:
+ i = -EINVAL;
+ }
+ mutex_unlock(&htc_batt_info.lock);
+
+ return i;
+}
+
+static int htc_battery_probe(struct platform_device *pdev)
+{
+ int i, rc;
+
+ /* init battery gpio */
+ if ((rc = init_batt_gpio()) < 0) {
+ printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__);
+ return rc;
+ }
+
+ /* init structure data member */
+ htc_batt_info.update_time = jiffies;
+ /* A9 will shutdown the phone if battery is pluged out, so this value is always 1.
+ htc_batt_info.present = gpio_get_value(GPIO_TROUT_MBAT_IN);
+ */
+ htc_batt_info.present = 1;
+
+ /* init rpc */
+ endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0);
+ if (IS_ERR(endpoint)) {
+ printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+ __FUNCTION__, PTR_ERR(endpoint));
+ return rc;
+ }
+
+ /* init power supplier framework */
+ for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) {
+ rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]);
+ if (rc)
+ printk(KERN_ERR "Failed to register power supply (%d)\n", rc);
+ }
+
+ /* create htc detail attributes */
+ htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev);
+
+ /* After battery driver gets initialized, send rpc request to inquiry
+ * the battery status in case of we lost some info
+ */
+ htc_battery_initial = 1;
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ htc_batt_info.rep.charging_source = CHARGER_BATTERY;
+ if (htc_get_batt_info(&htc_batt_info.rep) < 0)
+ printk(KERN_ERR "%s: get info failed\n", __FUNCTION__);
+
+ if (htc_rpc_set_delta(1) < 0)
+ printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__);
+ htc_batt_info.update_time = jiffies;
+ mutex_unlock(&htc_batt_info.rpc_lock);
+
+ return 0;
+}
+
+static struct platform_driver htc_battery_driver = {
+ .probe = htc_battery_probe,
+ .driver = {
+ .name = APP_BATT_PDEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* batt_mtoa server definitions */
+#define BATT_MTOA_PROG 0x30100000
+#define BATT_MTOA_VERS 0
+#define RPC_BATT_MTOA_NULL 0
+#define RPC_BATT_MTOA_SET_CHARGING_PROC 1
+#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2
+#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3
+
+struct rpc_batt_mtoa_set_charging_args {
+ int enable;
+};
+
+struct rpc_batt_mtoa_cable_status_update_args {
+ int status;
+};
+
+struct rpc_dem_battery_update_args {
+ uint32_t level;
+};
+
+static int handle_battery_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_BATT_MTOA_NULL:
+ return 0;
+
+ case RPC_BATT_MTOA_SET_CHARGING_PROC: {
+ struct rpc_batt_mtoa_set_charging_args *args;
+ args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1);
+ args->enable = be32_to_cpu(args->enable);
+ BATT("set_charging: enable=%d",args->enable);
+ htc_battery_set_charging(args->enable);
+ return 0;
+ }
+ case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: {
+ struct rpc_batt_mtoa_cable_status_update_args *args;
+ args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1);
+ args->status = be32_to_cpu(args->status);
+ BATT("cable_status_update: status=%d",args->status);
+ htc_cable_status_update(args->status);
+ return 0;
+ }
+ case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: {
+ struct rpc_dem_battery_update_args *args;
+ args = (struct rpc_dem_battery_update_args *)(req + 1);
+ args->level = be32_to_cpu(args->level);
+ BATT("dem_battery_update: level=%d",args->level);
+ htc_battery_status_update(args->level);
+ return 0;
+ }
+ default:
+ printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n",
+ __FUNCTION__, req->prog, req->vers, req->procedure);
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server battery_server = {
+ .prog = BATT_MTOA_PROG,
+ .vers = BATT_MTOA_VERS,
+ .rpc_call = handle_battery_call,
+};
+
+static int __init htc_battery_init(void)
+{
+ wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
+ mutex_init(&htc_batt_info.lock);
+ mutex_init(&htc_batt_info.rpc_lock);
+ msm_rpc_create_server(&battery_server);
+ platform_driver_register(&htc_battery_driver);
+ return 0;
+}
+
+module_init(htc_battery_init);
+MODULE_DESCRIPTION("HTC Battery Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-msm/htc_headset.c b/arch/arm/mach-msm/htc_headset.c
new file mode 100644
index 0000000..f9a00b7
--- /dev/null
+++ b/arch/arm/mach-msm/htc_headset.c
@@ -0,0 +1,1332 @@
+/*
+ * H2W device detection driver.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC, Inc.
+ *
+ * Authors:
+ * Laurence Chen <Laurence_Chen@htc.com>
+ * Nick Pelly <npelly@google.com>
+ * Thomas Tsai <thomas_tsai@htc.com>
+ * Farmer Tseng <farmer_tseng@htc.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; version 2 of the License.
+ */
+
+/* For detecting HTC 2 Wire devices, such as wired headset.
+
+ Logically, the H2W driver is always present, and H2W state (hi->state)
+ indicates what is currently plugged into the H2W interface.
+
+ When the headset is plugged in, CABLE_IN1 is pulled low. When the headset
+ button is pressed, CABLE_IN2 is pulled low. These two lines are shared with
+ the TX and RX (respectively) of UART3 - used for serial debugging.
+
+ This headset driver keeps the CPLD configured as UART3 for as long as
+ possible, so that we can do serial FIQ debugging even when the kernel is
+ locked and this driver no longer runs. So it only configures the CPLD to
+ GPIO while the headset is plugged in, and for 10ms during detection work.
+
+ Unfortunately we can't leave the CPLD as UART3 while a headset is plugged
+ in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA
+ drain on trout.
+
+ The headset detection work involves setting CPLD to GPIO, and then pulling
+ CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still
+ pull this line low, whereas other attachments such as a serial console
+ would get pulled up by this stronger pullup.
+
+ Headset insertion/removal causes UEvent's to be sent, and
+ /sys/class/switch/h2w/state to be updated.
+
+ Button presses are interpreted as input event (KEY_MEDIA). Button presses
+ are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm
+ jack adapters do not work until a headset is plugged into the adapter. This
+ is to avoid serial RX traffic causing spurious button press events.
+
+ We tend to check the status of CABLE_IN1 a few more times than strictly
+ necessary during headset detection, to avoid spurious headset insertion
+ events caused by serial debugger TX traffic.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysdev.h>
+#include <linux/fs.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/mutex.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <asm/gpio.h>
+#include <asm/atomic.h>
+#include <mach/board.h>
+#include <mach/vreg.h>
+#include <asm/mach-types.h>
+
+#include <mach/htc_headset.h>
+
+#define H2WI(fmt, arg...) \
+ printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
+#define H2WE(fmt, arg...) \
+ printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg)
+
+#ifdef CONFIG_DEBUG_H2W
+#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
+#else
+#define H2W_DBG(fmt, arg...) do {} while (0)
+#endif
+
+static struct workqueue_struct *g_detection_work_queue;
+static void detection_work(struct work_struct *work);
+static DECLARE_WORK(g_detection_work, detection_work);
+
+struct h2w_info {
+ struct switch_dev sdev;
+ struct input_dev *input;
+ struct mutex mutex_lock;
+
+ atomic_t btn_state;
+ int ignore_btn;
+
+ unsigned int irq;
+ unsigned int irq_btn;
+ unsigned int irq_btn_35mm;
+
+ int cable_in1;
+ int cable_in2;
+ int h2w_clk;
+ int h2w_data;
+ int debug_uart;
+ int headset_mic_35mm;
+
+ void (*config_cpld) (int);
+ void (*init_cpld) (void);
+ /* for h2w */
+ void (*set_dat)(int);
+ void (*set_clk)(int);
+ void (*set_dat_dir)(int);
+ void (*set_clk_dir)(int);
+ int (*get_dat)(void);
+ int (*get_clk)(void);
+
+ int htc_headset_flag;
+ int btn_11pin_35mm_flag;
+
+ struct hrtimer timer;
+ ktime_t debounce_time;
+
+ struct hrtimer btn_timer;
+ ktime_t btn_debounce_time;
+
+ struct hrtimer btn35mm_timer;
+ ktime_t btn35mm_debounce_time;
+
+ H2W_INFO h2w_info;
+ H2W_SPEED speed;
+ struct vreg *vreg_h2w;
+};
+static struct h2w_info *hi;
+
+static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf)
+{
+ switch (switch_get_state(&hi->sdev)) {
+ case H2W_NO_DEVICE:
+ return sprintf(buf, "No Device\n");
+ case H2W_HTC_HEADSET:
+ return sprintf(buf, "Headset\n");
+ }
+ return -EINVAL;
+}
+
+static void button_pressed(void)
+{
+ printk(KERN_INFO "[H2W] button_pressed\n");
+ atomic_set(&hi->btn_state, 1);
+ input_report_key(hi->input, KEY_MEDIA, 1);
+ input_sync(hi->input);
+}
+
+static void button_released(void)
+{
+ printk(KERN_INFO "[H2W] button_released\n");
+ atomic_set(&hi->btn_state, 0);
+ input_report_key(hi->input, KEY_MEDIA, 0);
+ input_sync(hi->input);
+}
+
+/*****************
+ * H2W proctocol *
+ *****************/
+static inline void h2w_begin_command(void)
+{
+ /* Disable H2W interrupt */
+ set_irq_type(hi->irq_btn, IRQF_TRIGGER_HIGH);
+ disable_irq(hi->irq);
+ disable_irq(hi->irq_btn);
+
+ /* Set H2W_CLK as output low */
+ hi->set_clk(0);
+ hi->set_clk_dir(1);
+}
+
+static inline void h2w_end_command(void)
+{
+ /* Set H2W_CLK as input */
+ hi->set_clk_dir(0);
+
+ /* Enable H2W interrupt */
+ enable_irq(hi->irq);
+ enable_irq(hi->irq_btn);
+ set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING);
+}
+
+static inline void one_clock_write(unsigned short flag)
+{
+ if (flag)
+ hi->set_dat(1);
+ else
+ hi->set_dat(0);
+
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(hi->speed);
+ hi->set_clk(0);
+}
+
+static inline void one_clock_write_RWbit(unsigned short flag)
+{
+ if (flag)
+ hi->set_dat(1);
+ else
+ hi->set_dat(0);
+
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(hi->speed);
+ hi->set_clk(0);
+ hi->set_dat_dir(0);
+ udelay(hi->speed);
+}
+
+static inline void h2w_reset(void)
+{
+ /* Set H2W_DAT as output low */
+ hi->set_dat(0);
+ hi->set_dat_dir(1);
+
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(4 * hi->speed);
+ hi->set_dat(1);
+ udelay(hi->speed);
+ hi->set_dat(0);
+ udelay(hi->speed);
+ hi->set_clk(0);
+ udelay(hi->speed);
+}
+
+static inline void h2w_start(void)
+{
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(2 * hi->speed);
+ hi->set_clk(0);
+ udelay(hi->speed);
+}
+
+static inline int h2w_ack(void)
+{
+ int retry_times = 0;
+
+ack_resend:
+ if (retry_times == MAX_ACK_RESEND_TIMES)
+ return -1;
+
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(2 * hi->speed);
+
+ if (!hi->get_dat()) {
+ retry_times++;
+ hi->set_clk(0);
+ udelay(hi->speed);
+ goto ack_resend;
+ }
+
+ hi->set_clk(0);
+ udelay(hi->speed);
+ return 0;
+}
+
+static unsigned char h2w_readc(void)
+{
+ unsigned char h2w_read_data = 0x0;
+ int index;
+
+ for (index = 0; index < 8; index++) {
+ hi->set_clk(0);
+ udelay(hi->speed);
+ hi->set_clk(1);
+ udelay(hi->speed);
+ if (hi->get_dat())
+ h2w_read_data |= (1 << (7 - index));
+ }
+ hi->set_clk(0);
+ udelay(hi->speed);
+
+ return h2w_read_data;
+}
+
+static int h2w_readc_cmd(H2W_ADDR address)
+{
+ int ret = -1, retry_times = 0;
+ unsigned char read_data;
+
+read_resend:
+ if (retry_times == MAX_HOST_RESEND_TIMES)
+ goto err_read;
+
+ h2w_reset();
+ h2w_start();
+ /* Write address */
+ one_clock_write(address & 0x1000);
+ one_clock_write(address & 0x0800);
+ one_clock_write(address & 0x0400);
+ one_clock_write(address & 0x0200);
+ one_clock_write(address & 0x0100);
+ one_clock_write(address & 0x0080);
+ one_clock_write(address & 0x0040);
+ one_clock_write(address & 0x0020);
+ one_clock_write(address & 0x0010);
+ one_clock_write(address & 0x0008);
+ one_clock_write(address & 0x0004);
+ one_clock_write(address & 0x0002);
+ one_clock_write(address & 0x0001);
+ one_clock_write_RWbit(1);
+ if (h2w_ack() < 0) {
+ H2W_DBG("Addr NO ACK(%d).\n", retry_times);
+ retry_times++;
+ hi->set_clk(0);
+ mdelay(RESEND_DELAY);
+ goto read_resend;
+ }
+
+ read_data = h2w_readc();
+
+ if (h2w_ack() < 0) {
+ H2W_DBG("Data NO ACK(%d).\n", retry_times);
+ retry_times++;
+ hi->set_clk(0);
+ mdelay(RESEND_DELAY);
+ goto read_resend;
+ }
+ ret = (int)read_data;
+
+err_read:
+ if (ret < 0)
+ H2WE("NO ACK.\n");
+
+ return ret;
+}
+
+static int h2w_writec_cmd(H2W_ADDR address, unsigned char data)
+{
+ int ret = -1;
+ int retry_times = 0;
+
+write_resend:
+ if (retry_times == MAX_HOST_RESEND_TIMES)
+ goto err_write;
+
+ h2w_reset();
+ h2w_start();
+
+ /* Write address */
+ one_clock_write(address & 0x1000);
+ one_clock_write(address & 0x0800);
+ one_clock_write(address & 0x0400);
+ one_clock_write(address & 0x0200);
+ one_clock_write(address & 0x0100);
+ one_clock_write(address & 0x0080);
+ one_clock_write(address & 0x0040);
+ one_clock_write(address & 0x0020);
+ one_clock_write(address & 0x0010);
+ one_clock_write(address & 0x0008);
+ one_clock_write(address & 0x0004);
+ one_clock_write(address & 0x0002);
+ one_clock_write(address & 0x0001);
+ one_clock_write_RWbit(0);
+ if (h2w_ack() < 0) {
+ H2W_DBG("Addr NO ACK(%d).\n", retry_times);
+ retry_times++;
+ hi->set_clk(0);
+ mdelay(RESEND_DELAY);
+ goto write_resend;
+ }
+
+ /* Write data */
+ hi->set_dat_dir(1);
+ one_clock_write(data & 0x0080);
+ one_clock_write(data & 0x0040);
+ one_clock_write(data & 0x0020);
+ one_clock_write(data & 0x0010);
+ one_clock_write(data & 0x0008);
+ one_clock_write(data & 0x0004);
+ one_clock_write(data & 0x0002);
+ one_clock_write_RWbit(data & 0x0001);
+ if (h2w_ack() < 0) {
+ H2W_DBG("Data NO ACK(%d).\n", retry_times);
+ retry_times++;
+ hi->set_clk(0);
+ mdelay(RESEND_DELAY);
+ goto write_resend;
+ }
+ ret = 0;
+
+err_write:
+ if (ret < 0)
+ H2WE("NO ACK.\n");
+
+ return ret;
+}
+
+static int h2w_get_fnkey(void)
+{
+ int ret;
+ h2w_begin_command();
+ ret = h2w_readc_cmd(H2W_FNKEY_UPDOWN);
+ h2w_end_command();
+ return ret;
+}
+
+static int h2w_dev_init(H2W_INFO *ph2w_info)
+{
+ int ret = -1;
+ unsigned char ascr0 = 0;
+ int h2w_sys = 0, maxgpadd = 0, maxadd = 0, key = 0;
+
+ hi->speed = H2W_50KHz;
+ h2w_begin_command();
+
+ /* read H2W_SYSTEM */
+ h2w_sys = h2w_readc_cmd(H2W_SYSTEM);
+ if (h2w_sys == -1) {
+ H2WE("read H2W_SYSTEM(0x0000) failed.\n");
+ goto err_plugin;
+ }
+ ph2w_info->ACC_CLASS = (h2w_sys & 0x03);
+ ph2w_info->AUDIO_DEVICE = (h2w_sys & 0x04) > 0 ? 1 : 0;
+ ph2w_info->HW_REV = (h2w_sys & 0x18) >> 3;
+ ph2w_info->SLEEP_PR = (h2w_sys & 0x20) >> 5;
+ ph2w_info->CLK_SP = (h2w_sys & 0xC0) >> 6;
+
+ /* enter init mode */
+ if (h2w_writec_cmd(H2W_ASCR0, H2W_ASCR_DEVICE_INI) < 0) {
+ H2WE("write H2W_ASCR0(0x0002) failed.\n");
+ goto err_plugin;
+ }
+ udelay(10);
+
+ /* read H2W_MAX_GP_ADD */
+ maxgpadd = h2w_readc_cmd(H2W_MAX_GP_ADD);
+ if (maxgpadd == -1) {
+ H2WE("write H2W_MAX_GP_ADD(0x0001) failed.\n");
+ goto err_plugin;
+ }
+ ph2w_info->CLK_SP += (maxgpadd & 0x60) >> 3;
+ ph2w_info->MAX_GP_ADD = (maxgpadd & 0x1F);
+
+ /* read key group */
+ if (ph2w_info->MAX_GP_ADD >= 1) {
+ ph2w_info->KEY_MAXADD = h2w_readc_cmd(H2W_KEY_MAXADD);
+ if (ph2w_info->KEY_MAXADD == -1)
+ goto err_plugin;
+ if (ph2w_info->KEY_MAXADD >= 1) {
+ key = h2w_readc_cmd(H2W_ASCII_DOWN);
+ if (key < 0)
+ goto err_plugin;
+ ph2w_info->ASCII_DOWN = (key == 0xFF) ? 1 : 0;
+ }
+ if (ph2w_info->KEY_MAXADD >= 2) {
+ key = h2w_readc_cmd(H2W_ASCII_UP);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->ASCII_UP = (key == 0xFF) ? 1 : 0;
+ }
+ if (ph2w_info->KEY_MAXADD >= 3) {
+ key = h2w_readc_cmd(H2W_FNKEY_UPDOWN);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->FNKEY_UPDOWN = (key == 0xFF) ? 1 : 0;
+ }
+ if (ph2w_info->KEY_MAXADD >= 4) {
+ key = h2w_readc_cmd(H2W_KD_STATUS);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->KD_STATUS = (key == 0x01) ? 1 : 0;
+ }
+ }
+
+ /* read led group */
+ if (ph2w_info->MAX_GP_ADD >= 2) {
+ ph2w_info->LED_MAXADD = h2w_readc_cmd(H2W_LED_MAXADD);
+ if (ph2w_info->LED_MAXADD == -1)
+ goto err_plugin;
+ if (ph2w_info->LED_MAXADD >= 1) {
+ key = h2w_readc_cmd(H2W_LEDCT0);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->LEDCT0 = (key == 0x02) ? 1 : 0;
+ }
+ }
+
+ /* read group 3, 4, 5 */
+ if (ph2w_info->MAX_GP_ADD >= 3) {
+ maxadd = h2w_readc_cmd(H2W_CRDL_MAXADD);
+ if (maxadd == -1)
+ goto err_plugin;
+ }
+ if (ph2w_info->MAX_GP_ADD >= 4) {
+ maxadd = h2w_readc_cmd(H2W_CARKIT_MAXADD);
+ if (maxadd == -1)
+ goto err_plugin;
+ }
+ if (ph2w_info->MAX_GP_ADD >= 5) {
+ maxadd = h2w_readc_cmd(H2W_USBHOST_MAXADD);
+ if (maxadd == -1)
+ goto err_plugin;
+ }
+
+ /* read medical group */
+ if (ph2w_info->MAX_GP_ADD >= 6) {
+ ph2w_info->MED_MAXADD = h2w_readc_cmd(H2W_MED_MAXADD);
+ if (ph2w_info->MED_MAXADD == -1)
+ goto err_plugin;
+ if (ph2w_info->MED_MAXADD >= 1) {
+ key = h2w_readc_cmd(H2W_MED_CONTROL);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->DATA_EN = (key & 0x01);
+ ph2w_info->AP_EN = (key & 0x02) >> 1;
+ ph2w_info->AP_ID = (key & 0x1c) >> 2;
+ }
+ if (ph2w_info->MED_MAXADD >= 2) {
+ key = h2w_readc_cmd(H2W_MED_IN_DATA);
+ if (key == -1)
+ goto err_plugin;
+ }
+ }
+
+ if (ph2w_info->AUDIO_DEVICE)
+ ascr0 = H2W_ASCR_AUDIO_IN | H2W_ASCR_ACT_EN;
+ else
+ ascr0 = H2W_ASCR_ACT_EN;
+
+ if (h2w_writec_cmd(H2W_ASCR0, ascr0) < 0)
+ goto err_plugin;
+ udelay(10);
+
+ ret = 0;
+
+ /* adjust speed */
+ if (ph2w_info->MAX_GP_ADD == 2) {
+ /* Remote control */
+ hi->speed = H2W_250KHz;
+ } else if (ph2w_info->MAX_GP_ADD == 6) {
+ if (ph2w_info->MED_MAXADD >= 1) {
+ key = h2w_readc_cmd(H2W_MED_CONTROL);
+ if (key == -1)
+ goto err_plugin;
+ ph2w_info->DATA_EN = (key & 0x01);
+ ph2w_info->AP_EN = (key & 0x02) >> 1;
+ ph2w_info->AP_ID = (key & 0x1c) >> 2;
+ }
+ }
+
+err_plugin:
+ h2w_end_command();
+
+ return ret;
+}
+
+static inline void h2w_dev_power_on(int on)
+{
+ if (!hi->vreg_h2w)
+ return;
+
+ if (on)
+ vreg_enable(hi->vreg_h2w);
+ else
+ vreg_disable(hi->vreg_h2w);
+}
+
+static int h2w_dev_detect(void)
+{
+ int ret = -1;
+ int retry_times;
+
+ for (retry_times = 5; retry_times; retry_times--) {
+ /* Enable H2W Power */
+ h2w_dev_power_on(1);
+ msleep(100);
+ memset(&hi->h2w_info, 0, sizeof(H2W_INFO));
+ if (h2w_dev_init(&hi->h2w_info) < 0) {
+ h2w_dev_power_on(0);
+ msleep(100);
+ } else if (hi->h2w_info.MAX_GP_ADD == 2) {
+ ret = 0;
+ break;
+ } else {
+ printk(KERN_INFO "h2w_detect: detect error(%d)\n"
+ , hi->h2w_info.MAX_GP_ADD);
+ h2w_dev_power_on(0);
+ msleep(100);
+ }
+ printk(KERN_INFO "h2w_detect(%d)\n"
+ , hi->h2w_info.MAX_GP_ADD);
+ }
+ H2W_DBG("h2w_detect:(%d)\n", retry_times);
+ return ret;
+}
+
+static void remove_headset(void)
+{
+ unsigned long irq_flags;
+
+ H2W_DBG("");
+
+ mutex_lock(&hi->mutex_lock);
+ switch_set_state(&hi->sdev, switch_get_state(&hi->sdev) &
+ ~(BIT_HEADSET | BIT_HEADSET_NO_MIC));
+ mutex_unlock(&hi->mutex_lock);
+ hi->init_cpld();
+
+ /* Disable button */
+ switch (hi->htc_headset_flag) {
+ case H2W_HTC_HEADSET:
+ local_irq_save(irq_flags);
+ disable_irq(hi->irq_btn);
+ local_irq_restore(irq_flags);
+
+ if (atomic_read(&hi->btn_state))
+ button_released();
+ printk(KERN_INFO "remove htc headset\n");
+ break;
+ case NORMAL_HEARPHONE:
+ if (hi->btn_11pin_35mm_flag) {
+ disable_irq(hi->irq_btn_35mm);
+ turn_mic_bias_on(0);
+ hi->btn_11pin_35mm_flag = 0;
+ if (atomic_read(&hi->btn_state))
+ button_released();
+ }
+ printk(KERN_INFO "remove 11pin 3.5mm headset\n");
+ break;
+ case H2W_DEVICE:
+ h2w_dev_power_on(0);
+ set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW);
+ disable_irq(hi->irq_btn);
+ /* 10ms (5-15 with 10ms tick) */
+ hi->btn_debounce_time = ktime_set(0, 10000000);
+ hi->set_clk_dir(0);
+ hi->set_dat_dir(0);
+ printk(KERN_INFO "remove h2w device\n");
+ break;
+ }
+
+ hi->htc_headset_flag = 0;
+ hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */
+
+}
+
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER
+extern void msm_serial_debug_enable(int);
+#endif
+
+static void insert_headset(int type)
+{
+ unsigned long irq_flags;
+ int state;
+
+ H2W_DBG("");
+
+ hi->htc_headset_flag = type;
+ state = BIT_HEADSET | BIT_HEADSET_NO_MIC;
+
+ state = switch_get_state(&hi->sdev);
+ state &= ~(BIT_HEADSET_NO_MIC | BIT_HEADSET);
+ switch (type) {
+ case H2W_HTC_HEADSET:
+ printk(KERN_INFO "insert_headset H2W_HTC_HEADSET\n");
+ state |= BIT_HEADSET;
+ hi->ignore_btn = !gpio_get_value(hi->cable_in2);
+ /* Enable button irq */
+ local_irq_save(irq_flags);
+ enable_irq(hi->irq_btn);
+ local_irq_restore(irq_flags);
+ hi->debounce_time = ktime_set(0, 200000000); /* 20 ms */
+ break;
+ case NORMAL_HEARPHONE:
+ if (hi->headset_mic_35mm) {
+ /* support 3.5mm earphone with mic */
+ printk(KERN_INFO "11pin_3.5mm_headset plug in\n");
+ /* Turn On Mic Bias */
+ turn_mic_bias_on(1);
+ /* Wait pin be stable */
+ msleep(200);
+ /* Detect headset with or without microphone */
+ if (gpio_get_value(hi->headset_mic_35mm)) {
+ /* without microphone */
+ turn_mic_bias_on(0);
+ state |= BIT_HEADSET_NO_MIC;
+ printk(KERN_INFO
+ "11pin_3.5mm without microphone\n");
+ } else { /* with microphone */
+ state |= BIT_HEADSET;
+ /* Enable button irq */
+ if (!hi->btn_11pin_35mm_flag) {
+ set_irq_type(hi->irq_btn_35mm,
+ IRQF_TRIGGER_HIGH);
+ enable_irq(hi->irq_btn_35mm);
+ hi->btn_11pin_35mm_flag = 1;
+ }
+ printk(KERN_INFO
+ "11pin_3.5mm with microphone\n");
+ }
+ } else /* not support 3.5mm earphone with mic */
+ state |= BIT_HEADSET_NO_MIC;
+ hi->debounce_time = ktime_set(0, 500000000); /* 500 ms */
+ break;
+ case H2W_DEVICE:
+ printk(KERN_INFO "insert_headset H2W_DEVICE\n");
+ if (!hi->set_dat) {
+ printk(KERN_INFO "Don't support H2W_DEVICE\n");
+ hi->htc_headset_flag = 0;
+ return;
+ }
+ if (h2w_dev_detect() < 0) {
+ printk(KERN_INFO "H2W_DEVICE -- Non detect\n");
+ remove_headset();
+ } else {
+ printk(KERN_INFO "H2W_DEVICE -- detect\n");
+ hi->btn_debounce_time = ktime_set(0, 0);
+ local_irq_save(irq_flags);
+ enable_irq(hi->irq_btn);
+ set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING);
+ local_irq_restore(irq_flags);
+ state |= BIT_HEADSET;
+ }
+ break;
+ case H2W_USB_CRADLE:
+ printk(KERN_INFO "insert_headset USB_CRADLE\n");
+ state |= BIT_HEADSET_NO_MIC;
+ break;
+ case H2W_UART_DEBUG:
+ printk(KERN_INFO "switch to H2W_UART_DEBUG\n");
+ hi->config_cpld(hi->debug_uart);
+ default:
+ return;
+ }
+ mutex_lock(&hi->mutex_lock);
+ switch_set_state(&hi->sdev, state);
+ mutex_unlock(&hi->mutex_lock);
+
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER
+ msm_serial_debug_enable(false);
+#endif
+
+}
+#if 0
+static void remove_headset(void)
+{
+ unsigned long irq_flags;
+
+ H2W_DBG("");
+
+ switch_set_state(&hi->sdev, H2W_NO_DEVICE);
+
+ hi->init_cpld();
+
+ /* Disable button */
+ local_irq_save(irq_flags);
+ disable_irq(hi->irq_btn);
+ local_irq_restore(irq_flags);
+
+ if (atomic_read(&hi->btn_state))
+ button_released();
+
+ hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */
+}
+#endif
+static int is_accessary_pluged_in(void)
+{
+ int type = 0;
+ int clk1 = 0, dat1 = 0, clk2 = 0, dat2 = 0, clk3 = 0, dat3 = 0;
+
+ /* Step1: save H2W_CLK and H2W_DAT */
+ /* Delay 10ms for pin stable. */
+ msleep(10);
+ clk1 = gpio_get_value(hi->h2w_clk);
+ dat1 = gpio_get_value(hi->h2w_data);
+
+ /*
+ * Step2: set GPIO_CABLE_IN1 as output high and GPIO_CABLE_IN2 as
+ * input
+ */
+ gpio_direction_output(hi->cable_in1, 1);
+ gpio_direction_input(hi->cable_in2);
+ /* Delay 10ms for pin stable. */
+ msleep(10);
+ /* Step 3: save H2W_CLK and H2W_DAT */
+ clk2 = gpio_get_value(hi->h2w_clk);
+ dat2 = gpio_get_value(hi->h2w_data);
+
+ /*
+ * Step 4: set GPIO_CABLE_IN1 as input and GPIO_CABLE_IN2 as output
+ * high
+ */
+ gpio_direction_input(hi->cable_in1);
+ gpio_direction_output(hi->cable_in2, 1);
+ /* Delay 10ms for pin stable. */
+ msleep(10);
+ /* Step 5: save H2W_CLK and H2W_DAT */
+ clk3 = gpio_get_value(hi->h2w_clk);
+ dat3 = gpio_get_value(hi->h2w_data);
+
+ /* Step 6: set both GPIO_CABLE_IN1 and GPIO_CABLE_IN2 as input */
+ gpio_direction_input(hi->cable_in1);
+ gpio_direction_input(hi->cable_in2);
+
+ H2WI("(%d,%d) (%d,%d) (%d,%d)",
+ clk1, dat1, clk2, dat2, clk3, dat3);
+
+ if ((clk1 == 0) && (dat1 == 1) &&
+ (clk2 == 0) && (dat2 == 1) &&
+ (clk3 == 0) && (dat3 == 1))
+ type = H2W_HTC_HEADSET;
+ else if ((clk1 == 0) && (dat1 == 0) &&
+ (clk2 == 0) && (dat2 == 0) &&
+ (clk3 == 0) && (dat3 == 0))
+ type = NORMAL_HEARPHONE;
+ else if ((clk1 == 0) && (dat1 == 0) &&
+ (clk2 == 1) && (dat2 == 0) &&
+ (clk3 == 0) && (dat3 == 1))
+ type = H2W_DEVICE;
+ else if ((clk1 == 0) && (dat1 == 0) &&
+ (clk2 == 1) && (dat2 == 1) &&
+ (clk3 == 1) && (dat3 == 1))
+ type = H2W_USB_CRADLE;
+ else if ((clk1 == 0) && (dat1 == 1) &&
+ (clk2 == 1) && (dat2 == 1) &&
+ (clk3 == 0) && (dat3 == 1))
+ type = H2W_UART_DEBUG;
+ else
+ type = H2W_NO_DEVICE;
+
+ return type;
+}
+
+
+static void detection_work(struct work_struct *work)
+{
+ unsigned long irq_flags;
+ int type;
+
+ H2W_DBG("");
+
+ if (gpio_get_value(hi->cable_in1) != 0) {
+ /* Headset not plugged in */
+ if (switch_get_state(&hi->sdev) != H2W_NO_DEVICE)
+ remove_headset();
+ return;
+ }
+
+ /* Something plugged in, lets make sure its a headset */
+
+ /* Switch CPLD to GPIO to do detection */
+ hi->config_cpld(H2W_GPIO);
+
+ /* Disable headset interrupt while detecting.*/
+ local_irq_save(irq_flags);
+ disable_irq(hi->irq);
+ local_irq_restore(irq_flags);
+
+ /* Something plugged in, lets make sure its a headset */
+ type = is_accessary_pluged_in();
+
+ /* Restore IRQs */
+ local_irq_save(irq_flags);
+ enable_irq(hi->irq);
+ local_irq_restore(irq_flags);
+
+ insert_headset(type);
+}
+
+void headset_button_event(int is_press)
+{
+ if (!is_press) {
+ if (hi->ignore_btn)
+ hi->ignore_btn = 0;
+ else if (atomic_read(&hi->btn_state))
+ button_released();
+ } else {
+ if (!hi->ignore_btn && !atomic_read(&hi->btn_state))
+ button_pressed();
+ }
+}
+
+static enum hrtimer_restart button_35mm_event_timer_func(struct hrtimer *data)
+{
+ if (gpio_get_value(hi->headset_mic_35mm)) {
+ headset_button_event(1);
+ /* 10 ms */
+ hi->btn35mm_debounce_time = ktime_set(0, 10000000);
+ } else {
+ headset_button_event(0);
+ /* 100 ms */
+ hi->btn35mm_debounce_time = ktime_set(0, 100000000);
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart button_event_timer_func(struct hrtimer *data)
+{
+ int key, press, keyname, h2w_key = 1;
+
+ H2W_DBG("");
+
+ if (switch_get_state(&hi->sdev) & BIT_HEADSET) {
+ switch (hi->htc_headset_flag) {
+ case H2W_HTC_HEADSET:
+ if (!gpio_get_value(hi->cable_in2))
+ headset_button_event(1); /* press */
+ else
+ headset_button_event(0);
+ break;
+ case H2W_DEVICE:
+ if ((hi->get_dat() == 1) && (hi->get_clk() == 1)) {
+ /* Don't do anything because H2W pull out. */
+ H2WE("Remote Control pull out.\n");
+ } else {
+ key = h2w_get_fnkey();
+ press = (key > 0x7F) ? 0 : 1;
+ keyname = key & 0x7F;
+ /* H2WI("key = %d, press = %d,
+ keyname = %d \n",
+ key, press, keyname); */
+ switch (keyname) {
+ case H2W_KEY_PLAY:
+ H2WI("H2W_KEY_PLAY");
+ key = KEY_PLAYPAUSE;
+ break;
+ case H2W_KEY_FORWARD:
+ H2WI("H2W_KEY_FORWARD");
+ key = KEY_NEXTSONG;
+ break;
+ case H2W_KEY_BACKWARD:
+ H2WI("H2W_KEY_BACKWARD");
+ key = KEY_PREVIOUSSONG;
+ break;
+ case H2W_KEY_VOLUP:
+ H2WI("H2W_KEY_VOLUP");
+ key = KEY_VOLUMEUP;
+ break;
+ case H2W_KEY_VOLDOWN:
+ H2WI("H2W_KEY_VOLDOWN");
+ key = KEY_VOLUMEDOWN;
+ break;
+ case H2W_KEY_PICKUP:
+ H2WI("H2W_KEY_PICKUP");
+ key = KEY_SEND;
+ break;
+ case H2W_KEY_HANGUP:
+ H2WI("H2W_KEY_HANGUP");
+ key = KEY_END;
+ break;
+ case H2W_KEY_MUTE:
+ H2WI("H2W_KEY_MUTE");
+ key = KEY_MUTE;
+ break;
+ case H2W_KEY_HOLD:
+ H2WI("H2W_KEY_HOLD");
+ break;
+ default:
+ H2WI("default");
+ h2w_key = 0;
+ }
+ if (h2w_key) {
+ if (press)
+ H2WI("Press\n");
+ else
+ H2WI("Release\n");
+ input_report_key(hi->input, key, press);
+ }
+ }
+ break;
+ } /* end switch */
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data)
+{
+ H2W_DBG("");
+
+ queue_work(g_detection_work_queue, &g_detection_work);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t detect_irq_handler(int irq, void *dev_id)
+{
+ int value1, value2;
+ int retry_limit = 10;
+
+ H2W_DBG("");
+ set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW);
+ do {
+ value1 = gpio_get_value(hi->cable_in1);
+ set_irq_type(hi->irq, value1 ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ value2 = gpio_get_value(hi->cable_in1);
+ } while (value1 != value2 && retry_limit-- > 0);
+
+ H2W_DBG("value2 = %d (%d retries), device=%d",
+ value2, (10-retry_limit), switch_get_state(&hi->sdev));
+
+ if ((switch_get_state(&hi->sdev) == H2W_NO_DEVICE) ^ value2) {
+ if (switch_get_state(&hi->sdev) & BIT_HEADSET)
+ hi->ignore_btn = 1;
+ /* Do the rest of the work in timer context */
+ hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t button_irq_handler(int irq, void *dev_id)
+{
+ int value1, value2;
+ int retry_limit = 10;
+
+ H2W_DBG("");
+ do {
+ value1 = gpio_get_value(hi->cable_in2);
+ if (hi->htc_headset_flag != H2W_DEVICE)
+ set_irq_type(hi->irq_btn, value1 ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ value2 = gpio_get_value(hi->cable_in2);
+ } while (value1 != value2 && retry_limit-- > 0);
+
+ H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit));
+
+ hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t button_35mm_irq_handler(int irq, void *dev_id)
+{
+ int value1, value2;
+ int retry_limit = 10;
+
+ H2W_DBG("");
+ do {
+ value1 = gpio_get_value(hi->headset_mic_35mm);
+ set_irq_type(hi->irq_btn_35mm, value1 ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ value2 = gpio_get_value(hi->headset_mic_35mm);
+ } while (value1 != value2 && retry_limit-- > 0);
+
+ H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit));
+
+ hrtimer_start(&hi->btn35mm_timer,
+ hi->btn35mm_debounce_time,
+ HRTIMER_MODE_REL);
+
+ return IRQ_HANDLED;
+
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static int h2w_debug_set(void *data, u64 val)
+{
+ mutex_lock(&hi->mutex_lock);
+ switch_set_state(&hi->sdev, (int)val);
+ mutex_unlock(&hi->mutex_lock);
+ return 0;
+}
+
+static int h2w_debug_get(void *data, u64 *val)
+{
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n");
+static int __init h2w_debug_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("h2w", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops);
+
+ return 0;
+}
+
+device_initcall(h2w_debug_init);
+#endif
+
+static int h2w_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct h2w_platform_data *pdata = pdev->dev.platform_data;
+
+ printk(KERN_INFO "H2W: Registering H2W (headset) driver\n");
+ hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL);
+ if (!hi)
+ return -ENOMEM;
+
+ atomic_set(&hi->btn_state, 0);
+ hi->ignore_btn = 0;
+
+ hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */
+ hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */
+ hi->btn35mm_debounce_time = ktime_set(0, 50000000); /* 50 ms */
+
+ hi->htc_headset_flag = 0;
+ hi->btn_11pin_35mm_flag = 0;
+ hi->cable_in1 = pdata->cable_in1;
+ hi->cable_in2 = pdata->cable_in2;
+ hi->h2w_clk = pdata->h2w_clk;
+ hi->h2w_data = pdata->h2w_data;
+ hi->debug_uart = pdata->debug_uart;
+ hi->headset_mic_35mm = pdata->headset_mic_35mm;
+ hi->config_cpld = pdata->config_cpld;
+ hi->init_cpld = pdata->init_cpld;
+ hi->set_dat = pdata->set_dat;
+ hi->set_clk = pdata->set_clk;
+ hi->set_dat_dir = pdata->set_dat_dir;
+ hi->set_clk_dir = pdata->set_clk_dir;
+ hi->get_dat = pdata->get_dat;
+ hi->get_clk = pdata->get_clk;
+ hi->speed = H2W_50KHz;
+ /* obtain needed VREGs */
+ if (pdata->power_name)
+ hi->vreg_h2w = vreg_get(0, pdata->power_name);
+
+ mutex_init(&hi->mutex_lock);
+
+ hi->sdev.name = "h2w";
+ hi->sdev.print_name = h2w_print_name;
+
+ ret = switch_dev_register(&hi->sdev);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ g_detection_work_queue = create_workqueue("detection");
+ if (g_detection_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto err_create_work_queue;
+ }
+
+ if (hi->headset_mic_35mm) {
+ ret = gpio_request(hi->headset_mic_35mm, "3.5mm_mic_detect");
+ if (ret < 0)
+ goto err_request_35mm_mic_detect_gpio;
+
+ ret = gpio_direction_input(hi->headset_mic_35mm);
+ if (ret < 0)
+ goto err_set_35mm_mic_detect_gpio;
+
+ hi->irq_btn_35mm = gpio_to_irq(hi->headset_mic_35mm);
+ if (hi->irq_btn_35mm < 0) {
+ ret = hi->irq_btn_35mm;
+ goto err_request_btn_35mm_irq;
+ }
+ set_irq_flags(hi->irq_btn_35mm, IRQF_VALID | IRQF_NOAUTOEN);
+ ret = request_irq(hi->irq_btn_35mm,
+ button_35mm_irq_handler,
+ IRQF_TRIGGER_HIGH, "35mm_button", NULL);
+ if (ret < 0)
+ goto err_request_btn_35mm_irq;
+ }
+
+ ret = gpio_request(hi->cable_in1, "h2w_detect");
+ if (ret < 0)
+ goto err_request_detect_gpio;
+
+ ret = gpio_request(hi->cable_in2, "h2w_button");
+ if (ret < 0)
+ goto err_request_button_gpio;
+
+ ret = gpio_direction_input(hi->cable_in1);
+ if (ret < 0)
+ goto err_set_detect_gpio;
+
+ ret = gpio_direction_input(hi->cable_in2);
+ if (ret < 0)
+ goto err_set_button_gpio;
+
+ hi->irq = gpio_to_irq(hi->cable_in1);
+ if (hi->irq < 0) {
+ ret = hi->irq;
+ goto err_get_h2w_detect_irq_num_failed;
+ }
+
+ hi->irq_btn = gpio_to_irq(hi->cable_in2);
+ if (hi->irq_btn < 0) {
+ ret = hi->irq_btn;
+ goto err_get_button_irq_num_failed;
+ }
+
+ /* Set CPLD MUX to H2W <-> CPLD GPIO */
+ hi->init_cpld();
+
+ hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hi->timer.function = detect_event_timer_func;
+ hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hi->btn_timer.function = button_event_timer_func;
+ hrtimer_init(&hi->btn35mm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hi->btn35mm_timer.function = button_35mm_event_timer_func;
+
+ ret = request_irq(hi->irq, detect_irq_handler,
+ IRQF_TRIGGER_LOW, "h2w_detect", NULL);
+ if (ret < 0)
+ goto err_request_detect_irq;
+
+ /* Disable button until plugged in */
+ set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN);
+ ret = request_irq(hi->irq_btn, button_irq_handler,
+ IRQF_TRIGGER_LOW, "h2w_button", NULL);
+ if (ret < 0)
+ goto err_request_h2w_headset_button_irq;
+
+ ret = set_irq_wake(hi->irq, 1);
+ if (ret < 0)
+ goto err_request_input_dev;
+
+ ret = set_irq_wake(hi->irq_btn, 1);
+ if (ret < 0)
+ goto err_request_input_dev;
+
+
+
+ hi->input = input_allocate_device();
+ if (!hi->input) {
+ ret = -ENOMEM;
+ goto err_request_input_dev;
+ }
+
+ hi->input->name = "h2w headset";
+ set_bit(EV_SYN, hi->input->evbit);
+ set_bit(EV_KEY, hi->input->evbit);
+ set_bit(KEY_MEDIA, hi->input->keybit);
+ set_bit(KEY_NEXTSONG, hi->input->keybit);
+ set_bit(KEY_PLAYPAUSE, hi->input->keybit);
+ set_bit(KEY_PREVIOUSSONG, hi->input->keybit);
+ set_bit(KEY_MUTE, hi->input->keybit);
+ set_bit(KEY_VOLUMEUP, hi->input->keybit);
+ set_bit(KEY_VOLUMEDOWN, hi->input->keybit);
+ set_bit(KEY_END, hi->input->keybit);
+ set_bit(KEY_SEND, hi->input->keybit);
+
+ ret = input_register_device(hi->input);
+ if (ret < 0)
+ goto err_register_input_dev;
+
+ return 0;
+
+err_register_input_dev:
+ input_free_device(hi->input);
+err_request_input_dev:
+ free_irq(hi->irq_btn, 0);
+err_request_h2w_headset_button_irq:
+ free_irq(hi->irq, 0);
+err_request_detect_irq:
+err_get_button_irq_num_failed:
+err_get_h2w_detect_irq_num_failed:
+err_set_button_gpio:
+err_set_detect_gpio:
+ gpio_free(hi->cable_in2);
+err_request_button_gpio:
+ gpio_free(hi->cable_in1);
+err_request_detect_gpio:
+ if (hi->headset_mic_35mm)
+ free_irq(hi->irq_btn_35mm, 0);
+err_request_btn_35mm_irq:
+err_set_35mm_mic_detect_gpio:
+ if (hi->headset_mic_35mm)
+ gpio_free(hi->headset_mic_35mm);
+err_request_35mm_mic_detect_gpio:
+ destroy_workqueue(g_detection_work_queue);
+err_create_work_queue:
+ switch_dev_unregister(&hi->sdev);
+err_switch_dev_register:
+ printk(KERN_ERR "H2W: Failed to register driver\n");
+
+ return ret;
+}
+
+static int h2w_remove(struct platform_device *pdev)
+{
+ H2W_DBG("");
+ if (switch_get_state(&hi->sdev))
+ remove_headset();
+ input_unregister_device(hi->input);
+ gpio_free(hi->cable_in2);
+ gpio_free(hi->cable_in1);
+ free_irq(hi->irq_btn, 0);
+ free_irq(hi->irq, 0);
+ if (hi->headset_mic_35mm) {
+ gpio_free(hi->headset_mic_35mm);
+ free_irq(hi->irq_btn_35mm, 0);
+ }
+ destroy_workqueue(g_detection_work_queue);
+ switch_dev_unregister(&hi->sdev);
+
+ return 0;
+}
+
+
+static struct platform_driver h2w_driver = {
+ .probe = h2w_probe,
+ .remove = h2w_remove,
+ .driver = {
+ .name = "h2w",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init h2w_init(void)
+{
+ H2W_DBG("");
+ return platform_driver_register(&h2w_driver);
+}
+
+static void __exit h2w_exit(void)
+{
+ platform_driver_unregister(&h2w_driver);
+}
+
+module_init(h2w_init);
+module_exit(h2w_exit);
+
+MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>");
+MODULE_DESCRIPTION("HTC 2 Wire detection driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c
new file mode 100644
index 0000000..9cfe2cc
--- /dev/null
+++ b/arch/arm/mach-msm/htc_power_supply.c
@@ -0,0 +1,618 @@
+/* arch/arm/mach-msm/htc_battery.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2008 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/power_supply.h>
+#include <linux/platform_device.h>
+#include <mach/msm_fast_timer.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/board.h>
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/switch.h>
+#include <linux/wakelock.h>
+
+#include "board-mahimahi.h"
+
+extern void notify_usb_connected(int);
+
+static char *supply_list[] = {
+ "battery",
+};
+
+static struct switch_dev dock_switch = {
+ .name = "dock",
+};
+
+static int vbus_present;
+static int usb_status;
+static bool dock_mains;
+
+struct dock_state {
+ struct mutex lock;
+ u32 t;
+ u32 last_edge_t[2];
+ u32 last_edge_i[2];
+ bool level;
+ bool dock_connected_unknown;
+};
+
+static struct workqueue_struct *dock_wq;
+static struct work_struct dock_work;
+static struct wake_lock dock_work_wake_lock;
+static struct dock_state ds = {
+ .lock = __MUTEX_INITIALIZER(ds.lock),
+};
+
+#define _GPIO_DOCK MAHIMAHI_GPIO_DOCK
+
+#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 MFM_DELAY_NS 10000
+
+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 = msm_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 = msm_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] + 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] + MFM_DELAY_NS * i;
+ u32 timeout = target + MFM_DELAY_NS / 2;
+ u32 tmin = target - MFM_DELAY_NS / 4;
+ u32 tmax = target + 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 void dock_delay(u32 timeout)
+{
+ timeout += msm_read_fast_timer();
+ while (((s32)(msm_read_fast_timer() - timeout)) < 0)
+ ;
+}
+
+static int dock_send_bits(struct dock_state *s, u32 data, int count, int period)
+{
+ u32 t, t0, to;
+
+ dock_out2(s->level);
+ t = to = 0;
+ t0 = msm_read_fast_timer();
+
+ while (count--) {
+ if (data & 1)
+ dock_out2((s->level = !s->level));
+
+ t = msm_read_fast_timer() - t0;
+ if (t - to > period / 2) {
+ pr_info("dock: to = %d, t = %d\n", to, t);
+ return -EIO;
+ }
+
+ to += MFM_DELAY_NS;
+ do {
+ t = msm_read_fast_timer() - t0;
+ } while (t < to);
+ if (t - to > period / 4) {
+ pr_info("dock: to = %d, t = %d\n", to, t);
+ return -EIO;
+ }
+ data >>= 1;
+ }
+ 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 ret;
+ int err = -1;
+ unsigned long flags;
+
+ data = data << 2 | 3; /* add 0101 mfm data*/
+ mfm = mfm_encode(data, len, false);
+ count = len * 2 + 2;
+
+ msm_enable_fast_timer();
+ local_irq_save(flags);
+ ret = dock_send_bits(s, mfm, count, MFM_DELAY_NS);
+ if (!ret) {
+ dock_in();
+ if (dock_sync(s, MFM_DELAY_NS * 5))
+ ret = dock_get_bits(s, retlen * 2, &err);
+ else
+ ret = -1;
+ dock_out(s->level);
+ }
+ local_irq_restore(flags);
+
+ dock_delay((ret < 0) ? MFM_DELAY_NS * 6 : MFM_DELAY_NS * 2);
+ msm_disable_fast_timer();
+ if (ret < 0) {
+ pr_warning("dock_command: %x: no response\n", cmd);
+ return ret;
+ }
+ data = mfm_decode(ret);
+ mfm = mfm_encode(data, retlen, true);
+ if (mfm != ret || err) {
+ pr_warning("dock_command: %x: bad response, "
+ "data %x, mfm %x %x, err %d\n",
+ cmd, data, mfm, ret, err);
+ return -EIO;
+ }
+ return data;
+}
+
+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)
+ msleep(10);
+ }
+ s->dock_connected_unknown = true;
+ return -EIO;
+}
+
+static int dock_read_single(struct dock_state *s, int addr)
+{
+ int ret = -1, last;
+ int retry = 20;
+ while (retry--) {
+ last = ret;
+ ret = dock_command_retry(s, addr << 1, 6, 8);
+ if (ret < 0 || ret == last)
+ return ret;
+ }
+ return -EIO;
+}
+
+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_command_retry(s, (addr + i) << 1, 6, 8);
+ 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_byte(struct dock_state *s, int addr, u8 data)
+{
+ return dock_command_retry(s, 1 | addr << 1 | data << 4, 6 + 8, 1);
+}
+
+static int dock_write_multi(struct dock_state *s, int addr, u8 *data, size_t len)
+{
+ int ret;
+ int i;
+ u8 sum;
+ int retry = 2;
+ while (retry--) {
+ sum = 0;
+ for (i = 0; i < len; i++) {
+ sum += data[i];
+ ret = dock_write_byte(s, addr + i, data[i]);
+ if (ret < 0)
+ return ret;
+ }
+ ret = dock_write_byte(s, addr + len, sum);
+ if (ret <= 0)
+ return ret;
+ }
+ return -EIO;
+}
+
+static int dock_acquire(struct dock_state *s)
+{
+ mutex_lock(&s->lock);
+ dock_in();
+ if (dock_read()) {
+ /* Allow some time for the dock pull-down resistor to discharge
+ * the capasitor.
+ */
+ msleep(20);
+ if (dock_read()) {
+ mutex_unlock(&s->lock);
+ return -ENOENT;
+ }
+ }
+ dock_out(0);
+ s->level = false;
+ return 0;
+}
+
+static void dock_release(struct dock_state *s)
+{
+ dock_in();
+ mutex_unlock(&s->lock);
+}
+
+enum {
+ DOCK_TYPE = 0x0,
+ DOCK_BT_ADDR = 0x1, /* - 0x7 */
+
+ DOCK_PIN_CODE = 0x0,
+};
+
+static ssize_t bt_addr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 bt_addr[6];
+
+ ret = dock_acquire(&ds);
+ if (ret < 0)
+ return ret;
+ ret = dock_read_multi(&ds, DOCK_BT_ADDR, bt_addr, 6);
+ dock_release(&ds);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ bt_addr[0], bt_addr[1], bt_addr[2],
+ bt_addr[3], bt_addr[4], bt_addr[5]);
+}
+static DEVICE_ATTR(bt_addr, S_IRUGO | S_IWUSR, bt_addr_show, NULL);
+
+static ssize_t bt_pin_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret, i;
+ u8 pin[4];
+
+ if (size < 4)
+ return -EINVAL;
+
+ for (i = 0; i < sizeof(pin); i++) {
+ if ((pin[i] = buf[i] - '0') > 10)
+ return -EINVAL;
+ }
+
+ ret = dock_acquire(&ds);
+ if (ret < 0)
+ return ret;
+ ret = dock_write_multi(&ds, DOCK_PIN_CODE, pin, 4);
+ dock_release(&ds);
+ if (ret < 0)
+ return ret;
+
+ return size;
+}
+static DEVICE_ATTR(bt_pin, S_IRUGO | S_IWUSR, NULL, bt_pin_store);
+
+
+static int power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS) {
+ val->intval = (vbus_present && (usb_status == 2 || dock_mains));
+ } else {
+ val->intval = vbus_present;
+ }
+ return 0;
+}
+
+static enum power_supply_property power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static struct power_supply ac_supply = {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = power_properties,
+ .num_properties = ARRAY_SIZE(power_properties),
+ .get_property = power_get_property,
+};
+
+static struct power_supply usb_supply = {
+ .name = "usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = power_properties,
+ .num_properties = ARRAY_SIZE(power_properties),
+ .get_property = power_get_property,
+};
+
+/* rpc related */
+#define APP_BATT_PDEV_NAME "rs30100001:00000000"
+#define APP_BATT_PROG 0x30100001
+#define APP_BATT_VER MSM_RPC_VERS(0,0)
+#define HTC_PROCEDURE_BATTERY_NULL 0
+#define HTC_PROCEDURE_GET_BATT_LEVEL 1
+#define HTC_PROCEDURE_GET_BATT_INFO 2
+#define HTC_PROCEDURE_GET_CABLE_STATUS 3
+#define HTC_PROCEDURE_SET_BATT_DELTA 4
+
+static struct msm_rpc_endpoint *endpoint;
+
+struct battery_info_reply {
+ u32 batt_id; /* Battery ID from ADC */
+ u32 batt_vol; /* Battery voltage from ADC */
+ u32 batt_temp; /* Battery Temperature (C) from formula and ADC */
+ u32 batt_current; /* Battery current from ADC */
+ u32 level; /* formula */
+ u32 charging_source; /* 0: no cable, 1:usb, 2:AC */
+ u32 charging_enabled; /* 0: Disable, 1: Enable */
+ u32 full_bat; /* Full capacity of battery (mAh) */
+};
+
+static void dock_work_proc(struct work_struct *work)
+{
+ int dockid;
+
+ if (!vbus_present || dock_acquire(&ds))
+ goto no_dock;
+
+ if (ds.dock_connected_unknown) {
+ /* force a new dock notification if a command failed */
+ switch_set_state(&dock_switch, 0);
+ ds.dock_connected_unknown = false;
+ }
+
+ dockid = dock_read_single(&ds, DOCK_TYPE);
+ dock_release(&ds);
+
+ pr_info("Detected dock with ID %02x\n", dockid);
+ if (dockid >= 0) {
+ msm_hsusb_set_vbus_state(0);
+ dock_mains = !!(dockid & 0x80);
+ switch_set_state(&dock_switch, (dockid & 1) ? 2 : 1);
+ goto done;
+ }
+no_dock:
+ dock_mains = false;
+ switch_set_state(&dock_switch, 0);
+ msm_hsusb_set_vbus_state(vbus_present);
+done:
+ power_supply_changed(&ac_supply);
+ power_supply_changed(&usb_supply);
+ wake_unlock(&dock_work_wake_lock);
+}
+
+static int htc_battery_probe(struct platform_device *pdev)
+{
+ struct rpc_request_hdr req;
+ struct htc_get_batt_info_rep {
+ struct rpc_reply_hdr hdr;
+ struct battery_info_reply info;
+ } rep;
+
+ int rc;
+
+ endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0);
+ if (IS_ERR(endpoint)) {
+ printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+ __FUNCTION__, PTR_ERR(endpoint));
+ return PTR_ERR(endpoint);
+ }
+
+ /* must do this or we won't get cable status updates */
+ rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO,
+ &req, sizeof(req),
+ &rep, sizeof(rep),
+ 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: get info failed\n", __FUNCTION__);
+
+ power_supply_register(&pdev->dev, &ac_supply);
+ power_supply_register(&pdev->dev, &usb_supply);
+
+ INIT_WORK(&dock_work, dock_work_proc);
+ dock_wq = create_singlethread_workqueue("dock");
+
+ return 0;
+}
+
+static struct platform_driver htc_battery_driver = {
+ .probe = htc_battery_probe,
+ .driver = {
+ .name = APP_BATT_PDEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* batt_mtoa server definitions */
+#define BATT_MTOA_PROG 0x30100000
+#define BATT_MTOA_VERS 0
+#define RPC_BATT_MTOA_NULL 0
+#define RPC_BATT_MTOA_SET_CHARGING_PROC 1
+#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2
+#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3
+
+struct rpc_batt_mtoa_cable_status_update_args {
+ int status;
+};
+
+static int handle_battery_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ struct rpc_batt_mtoa_cable_status_update_args *args;
+
+ if (req->procedure != RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC)
+ return 0;
+
+ args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1);
+ args->status = be32_to_cpu(args->status);
+ pr_info("cable_status_update: status=%d\n",args->status);
+
+ args->status = !!args->status;
+
+ vbus_present = args->status;
+ wake_lock(&dock_work_wake_lock);
+ queue_work(dock_wq, &dock_work);
+ return 0;
+}
+
+void notify_usb_connected(int status)
+{
+ printk("### notify_usb_connected(%d) ###\n", status);
+ usb_status = status;
+ power_supply_changed(&ac_supply);
+ power_supply_changed(&usb_supply);
+}
+
+int is_ac_power_supplied(void)
+{
+ return vbus_present && (usb_status == 2 || dock_mains);
+}
+
+static struct msm_rpc_server battery_server = {
+ .prog = BATT_MTOA_PROG,
+ .vers = BATT_MTOA_VERS,
+ .rpc_call = handle_battery_call,
+};
+
+static int __init htc_battery_init(void)
+{
+ int ret;
+ gpio_request(_GPIO_DOCK, "dock");
+ dock_in();
+ wake_lock_init(&dock_work_wake_lock, WAKE_LOCK_SUSPEND, "dock");
+ platform_driver_register(&htc_battery_driver);
+ msm_rpc_create_server(&battery_server);
+ if (switch_dev_register(&dock_switch) == 0) {
+ ret = device_create_file(dock_switch.dev, &dev_attr_bt_addr);
+ WARN_ON(ret);
+ ret = device_create_file(dock_switch.dev, &dev_attr_bt_pin);
+ WARN_ON(ret);
+ }
+
+ return 0;
+}
+
+module_init(htc_battery_init);
+MODULE_DESCRIPTION("HTC Battery Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c
new file mode 100644
index 0000000..12d6182
--- /dev/null
+++ b/arch/arm/mach-msm/htc_pwrsink.c
@@ -0,0 +1,289 @@
+/* arch/arm/mach-msm/htc_pwrsink.c
+ *
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (C) 2008 Google, Inc.
+ * Author: San Mehat <san@google.com>
+ * Kant Kang <kant_kang@htc.com>
+ * Eiven Peng <eiven_peng@htc.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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/earlysuspend.h>
+#include <mach/msm_smd.h>
+#include <mach/htc_pwrsink.h>
+
+#include "smd_private.h"
+
+enum {
+ PWRSINK_DEBUG_CURR_CHANGE = 1U << 0,
+ PWRSINK_DEBUG_CURR_CHANGE_AUDIO = 1U << 1,
+};
+static int pwrsink_debug_mask;
+module_param_named(debug_mask, pwrsink_debug_mask, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+static int initialized;
+static unsigned audio_path = 1; /* HTC_SND_DEVICE_SPEAKER = 1 */
+static struct pwr_sink_audio audio_sink_array[PWRSINK_AUDIO_LAST + 1];
+static struct pwr_sink *sink_array[PWRSINK_LAST + 1];
+static DEFINE_SPINLOCK(sink_lock);
+static DEFINE_SPINLOCK(audio_sink_lock);
+static unsigned long total_sink;
+static uint32_t *smem_total_sink;
+
+int htc_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized)
+{
+ unsigned long flags;
+
+ if (!smem_total_sink)
+ smem_total_sink = smem_alloc(SMEM_ID_VENDOR0, sizeof(uint32_t));
+
+ if (!initialized)
+ return -EAGAIN;
+
+ if (id < 0 || id > PWRSINK_LAST)
+ return -EINVAL;
+
+ spin_lock_irqsave(&sink_lock, flags);
+
+ if (!sink_array[id]) {
+ spin_unlock_irqrestore(&sink_lock, flags);
+ return -ENOENT;
+ }
+
+ if (sink_array[id]->percent_util == percent_utilized) {
+ spin_unlock_irqrestore(&sink_lock, flags);
+ return 0;
+ }
+
+ total_sink -= (sink_array[id]->ua_max *
+ sink_array[id]->percent_util / 100);
+ sink_array[id]->percent_util = percent_utilized;
+ total_sink += (sink_array[id]->ua_max *
+ sink_array[id]->percent_util / 100);
+
+ if (smem_total_sink)
+ *smem_total_sink = total_sink / 1000;
+
+ pr_debug("htc_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n",
+ id, percent_utilized, total_sink,
+ smem_total_sink ? "SET" : "");
+
+ spin_unlock_irqrestore(&sink_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(htc_pwrsink_set);
+
+static void compute_audio_current(void)
+{
+ /* unsigned long flags; */
+ unsigned max_percent = 0;
+ int i, active_audio_sinks = 0;
+ pwrsink_audio_id_type last_active_audio_sink = 0;
+
+ /* Make sure this segment will be spinlocked
+ before computing by calling function. */
+ /* spin_lock_irqsave(&audio_sink_lock, flags); */
+ for (i = 0; i <= PWRSINK_AUDIO_LAST; ++i) {
+ max_percent = (audio_sink_array[i].percent > max_percent) ?
+ audio_sink_array[i].percent : max_percent;
+ if (audio_sink_array[i].percent > 0) {
+ active_audio_sinks++;
+ last_active_audio_sink = i;
+ }
+ }
+ if (active_audio_sinks == 0)
+ htc_pwrsink_set(PWRSINK_AUDIO, 0);
+ else if (active_audio_sinks == 1) {
+ pwrsink_audio_id_type laas = last_active_audio_sink;
+ /* TODO: add volume and routing path current. */
+ if (audio_path == 1) /* Speaker */
+ htc_pwrsink_set(PWRSINK_AUDIO,
+ audio_sink_array[laas].percent);
+ else
+ htc_pwrsink_set(PWRSINK_AUDIO,
+ audio_sink_array[laas].percent * 9 / 10);
+ } else if (active_audio_sinks > 1) {
+ /* TODO: add volume and routing path current. */
+ if (audio_path == 1) /* Speaker */
+ htc_pwrsink_set(PWRSINK_AUDIO, max_percent);
+ else
+ htc_pwrsink_set(PWRSINK_AUDIO, max_percent * 9 / 10);
+ }
+ /* spin_unlock_irqrestore(&audio_sink_lock, flags); */
+
+ if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO)
+ pr_info("%s: active_audio_sinks=%d, audio_path=%d\n", __func__,
+ active_audio_sinks, audio_path);
+}
+
+int htc_pwrsink_audio_set(pwrsink_audio_id_type id, unsigned percent_utilized)
+{
+ unsigned long flags;
+
+ if (id < 0 || id > PWRSINK_AUDIO_LAST)
+ return -EINVAL;
+
+ if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO)
+ pr_info("%s: id=%d, percent=%d, percent_old=%d\n", __func__,
+ id, percent_utilized, audio_sink_array[id].percent);
+
+ spin_lock_irqsave(&audio_sink_lock, flags);
+ if (audio_sink_array[id].percent == percent_utilized) {
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ return 0;
+ }
+ audio_sink_array[id].percent = percent_utilized;
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ compute_audio_current();
+ return 0;
+}
+EXPORT_SYMBOL(htc_pwrsink_audio_set);
+
+int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, unsigned volume)
+{
+ unsigned long flags;
+
+ if (id < 0 || id > PWRSINK_AUDIO_LAST)
+ return -EINVAL;
+
+ if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO)
+ pr_info("%s: id=%d, volume=%d, volume_old=%d\n", __func__,
+ id, volume, audio_sink_array[id].volume);
+
+ spin_lock_irqsave(&audio_sink_lock, flags);
+ if (audio_sink_array[id].volume == volume) {
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ return 0;
+ }
+ audio_sink_array[id].volume = volume;
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ compute_audio_current();
+ return 0;
+}
+EXPORT_SYMBOL(htc_pwrsink_audio_volume_set);
+
+int htc_pwrsink_audio_path_set(unsigned path)
+{
+ unsigned long flags;
+
+ if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO)
+ pr_info("%s: path=%d, path_old=%d\n",
+ __func__, path, audio_path);
+
+ spin_lock_irqsave(&audio_sink_lock, flags);
+ if (audio_path == path) {
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ return 0;
+ }
+ audio_path = path;
+ spin_unlock_irqrestore(&audio_sink_lock, flags);
+ compute_audio_current();
+ return 0;
+}
+EXPORT_SYMBOL(htc_pwrsink_audio_path_set);
+
+void htc_pwrsink_suspend_early(struct early_suspend *h)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7);
+}
+
+int htc_pwrsink_suspend_late(struct device *dev)
+{
+ struct pwr_sink_platform_data *pdata = dev_get_platdata(dev);
+
+ if (pdata && pdata->suspend_late)
+ pdata->suspend_late(to_platform_device(dev), PMSG_SUSPEND);
+ else
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1);
+ return 0;
+}
+
+int htc_pwrsink_resume_early(struct device *dev)
+{
+ struct pwr_sink_platform_data *pdata = dev_get_platdata(dev);;
+
+ if (pdata && pdata->resume_early)
+ pdata->resume_early(to_platform_device(dev));
+ else
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7);
+ return 0;
+}
+
+void htc_pwrsink_resume_late(struct early_suspend *h)
+{
+ htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38);
+}
+
+#ifdef CONFIG_WAKELOCK
+struct early_suspend htc_pwrsink_early_suspend = {
+ .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1,
+ .suspend = htc_pwrsink_suspend_early,
+ .resume = htc_pwrsink_resume_late,
+};
+#endif
+
+static int __init htc_pwrsink_probe(struct platform_device *pdev)
+{
+ struct pwr_sink_platform_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ if (!pdata)
+ return -EINVAL;
+
+ total_sink = 0;
+ for (i = 0; i < pdata->num_sinks; i++) {
+ sink_array[pdata->sinks[i].id] = &pdata->sinks[i];
+ total_sink += (pdata->sinks[i].ua_max *
+ pdata->sinks[i].percent_util / 100);
+ }
+
+ initialized = 1;
+
+#ifdef CONFIG_WAKELOCK
+ if (pdata->suspend_early)
+ htc_pwrsink_early_suspend.suspend = pdata->suspend_early;
+ if (pdata->resume_late)
+ htc_pwrsink_early_suspend.resume = pdata->resume_late;
+#endif
+ register_early_suspend(&htc_pwrsink_early_suspend);
+
+ return 0;
+}
+
+static struct dev_pm_ops htc_pwrsink_pm_ops = {
+ .suspend_noirq = htc_pwrsink_suspend_late,
+ .resume_noirq = htc_pwrsink_resume_early,
+};
+
+static struct platform_driver htc_pwrsink_driver = {
+ .probe = htc_pwrsink_probe,
+ .driver = {
+ .name = "htc_pwrsink",
+ .owner = THIS_MODULE,
+ .pm = &htc_pwrsink_pm_ops,
+ },
+};
+
+static int __init htc_pwrsink_init(void)
+{
+ initialized = 0;
+ memset(sink_array, 0, sizeof(sink_array));
+ return platform_driver_register(&htc_pwrsink_driver);
+}
+
+module_init(htc_pwrsink_init);
diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c
new file mode 100644
index 0000000..1753595
--- /dev/null
+++ b/arch/arm/mach-msm/htc_wifi_nvs.c
@@ -0,0 +1,106 @@
+/* arch/arm/mach-msm/htc_wifi_nvs.c
+ *
+ * Code to extract WiFi calibration information from ATAG set up
+ * by the bootloader.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Dmitry Shmidt <dimitrysh@google.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+
+#include <asm/setup.h>
+
+/* configuration tags specific to msm */
+#define ATAG_MSM_WIFI 0x57494649 /* MSM WiFi */
+
+#define NVS_MAX_SIZE 0x800U
+#define NVS_LEN_OFFSET 0x0C
+#define NVS_DATA_OFFSET 0x40
+
+static unsigned char wifi_nvs_ram[NVS_MAX_SIZE];
+static struct proc_dir_entry *wifi_calibration;
+
+unsigned char *get_wifi_nvs_ram( void )
+{
+ return wifi_nvs_ram;
+}
+EXPORT_SYMBOL(get_wifi_nvs_ram);
+
+static int __init parse_tag_msm_wifi(const struct tag *tag)
+{
+ unsigned char *dptr = (unsigned char *)(&tag->u);
+ unsigned size;
+#ifdef ATAG_MSM_WIFI_DEBUG
+ unsigned i;
+#endif
+
+ size = min((tag->hdr.size - 2) * sizeof(__u32), NVS_MAX_SIZE);
+#ifdef ATAG_MSM_WIFI_DEBUG
+ printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag);
+ for(i=0;( i < size );i++) {
+ printk("%02x ", *dptr++);
+ }
+#endif
+ memcpy(wifi_nvs_ram, dptr, size);
+ return 0;
+}
+
+__tagtable(ATAG_MSM_WIFI, parse_tag_msm_wifi);
+
+static unsigned wifi_get_nvs_size( void )
+{
+ unsigned char *ptr;
+ unsigned len;
+
+ ptr = get_wifi_nvs_ram();
+ /* Size in format LE assumed */
+ memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len));
+ len = min(len, (NVS_MAX_SIZE - NVS_DATA_OFFSET));
+ return len;
+}
+
+int wifi_calibration_size_set(void)
+{
+ if (wifi_calibration != NULL)
+ wifi_calibration->size = wifi_get_nvs_size();
+ return 0;
+}
+
+static int wifi_calibration_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned char *ptr;
+ unsigned len;
+
+ ptr = get_wifi_nvs_ram();
+ len = min(wifi_get_nvs_size(), (unsigned)count);
+ memcpy(page, ptr + NVS_DATA_OFFSET, len);
+ return len;
+}
+
+static int __init wifi_nvs_init(void)
+{
+ wifi_calibration = create_proc_entry("calibration", 0444, NULL);
+ if (wifi_calibration != NULL) {
+ wifi_calibration->size = wifi_get_nvs_size();
+ wifi_calibration->read_proc = wifi_calibration_read_proc;
+ wifi_calibration->write_proc = NULL;
+ }
+ return 0;
+}
+
+device_initcall(wifi_nvs_init);
diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c
new file mode 100644
index 0000000..aef7f37
--- /dev/null
+++ b/arch/arm/mach-msm/hw3d.c
@@ -0,0 +1,889 @@
+/* arch/arm/mach-msm/hw3d.c
+ *
+ * Register/Interrupt access for userspace 3D library.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ * Heavily modified: Dima Zavin <dima@android.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 DEBUG */
+/* #define VERBOSE */
+
+#include <linux/clk.h>
+#include <linux/earlysuspend.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/msm_hw3d.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <asm/io.h>
+
+#include <mach/board.h>
+
+#if defined(VERBOSE)
+#define VDBG(x...) pr_debug(x)
+#else
+#define VDBG(x...) do {} while(0)
+#endif
+
+struct mem_region {
+ unsigned long pbase;
+ unsigned long size;
+ void __iomem *vbase;
+};
+
+/* Device minor numbers for master and client */
+#define MINOR_MASTER 0
+#define MINOR_CLIENT 1
+
+struct hw3d_info {
+ dev_t devno;
+ struct cdev master_cdev;
+ struct cdev client_cdev;
+
+ struct clk *grp_clk;
+ struct clk *imem_clk;
+ int irq;
+
+ struct mem_region regions[HW3D_NUM_REGIONS];
+
+ wait_queue_head_t irq_wq;
+ bool irq_pending;
+ bool irq_en;
+ bool suspending;
+ bool revoking;
+ bool enabled;
+
+ struct timer_list revoke_timer;
+ wait_queue_head_t revoke_wq;
+ wait_queue_head_t revoke_done_wq;
+ unsigned int waiter_cnt;
+
+ spinlock_t lock;
+
+ struct file *client_file;
+ struct task_struct *client_task;
+
+ struct early_suspend early_suspend;
+ struct wake_lock wake_lock;
+};
+static struct hw3d_info *hw3d_info;
+
+struct hw3d_data {
+ struct vm_area_struct *vmas[HW3D_NUM_REGIONS];
+ struct mutex mutex;
+ bool closing;
+};
+
+#define REGION_PAGE_ID(addr) \
+ ((((uint32_t)(addr)) >> (28 - PAGE_SHIFT)) & 0xf)
+#define REGION_PAGE_OFFS(addr) \
+ ((((uint32_t)(addr)) & ~(0xf << (28 - PAGE_SHIFT))))
+
+static int hw3d_open(struct inode *, struct file *);
+static int hw3d_release(struct inode *, struct file *);
+static int hw3d_mmap(struct file *, struct vm_area_struct *);
+static int hw3d_flush(struct file *, fl_owner_t);
+static long hw3d_ioctl(struct file *, unsigned int, unsigned long);
+
+static void hw3d_vma_open(struct vm_area_struct *);
+static void hw3d_vma_close(struct vm_area_struct *);
+
+static struct file_operations hw3d_fops = {
+ .open = hw3d_open,
+ .release = hw3d_release,
+ .mmap = hw3d_mmap,
+ .flush = hw3d_flush,
+ .unlocked_ioctl = hw3d_ioctl,
+};
+
+static struct vm_operations_struct hw3d_vm_ops = {
+ .open = hw3d_vma_open,
+ .close = hw3d_vma_close,
+};
+
+static bool is_master(struct hw3d_info *info, struct file *file)
+{
+ int fmin = MINOR(file->f_dentry->d_inode->i_rdev);
+ return fmin == MINOR(info->master_cdev.dev);
+}
+
+static bool is_client(struct hw3d_info *info, struct file *file)
+{
+ int fmin = MINOR(file->f_dentry->d_inode->i_rdev);
+ return fmin == MINOR(info->client_cdev.dev);
+}
+
+inline static void locked_hw3d_irq_disable(struct hw3d_info *info)
+{
+ if (info->irq_en) {
+ disable_irq_nosync(info->irq);
+ info->irq_en = 0;
+ }
+}
+
+inline static void locked_hw3d_irq_enable(struct hw3d_info *info)
+{
+ if (!info->irq_en) {
+ enable_irq(info->irq);
+ info->irq_en = 1;
+ }
+}
+
+static void hw3d_disable_interrupt(struct hw3d_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ locked_hw3d_irq_disable(info);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static irqreturn_t hw3d_irq_handler(int irq, void *data)
+{
+ struct hw3d_info *info = data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ locked_hw3d_irq_disable(info);
+ info->irq_pending = 1;
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ wake_up(&info->irq_wq);
+
+ return IRQ_HANDLED;
+}
+
+static long hw3d_wait_for_interrupt(struct hw3d_info *info, struct file *filp)
+{
+ struct hw3d_data *data = filp->private_data;
+ unsigned long flags;
+ int ret;
+
+ if (is_master(info, filp)) {
+ pr_err("%s: cannot wait for irqs on master node\n", __func__);
+ return -EPERM;
+ }
+
+ for (;;) {
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->irq_pending) {
+ info->irq_pending = 0;
+ spin_unlock_irqrestore(&info->lock, flags);
+ return 0;
+ }
+ locked_hw3d_irq_enable(info);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ ret = wait_event_interruptible(info->irq_wq,
+ info->irq_pending ||
+ info->revoking ||
+ data->closing);
+ /* always make sure the irq gets disabled */
+ if (ret == 0 && !info->irq_pending)
+ ret = -EPIPE;
+ if (ret < 0) {
+ hw3d_disable_interrupt(info);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static long hw3d_wait_for_revoke(struct hw3d_info *info, struct file *filp)
+{
+ struct hw3d_data *data = filp->private_data;
+ int ret;
+
+ if (is_master(info, filp)) {
+ pr_err("%s: cannot revoke on master node\n", __func__);
+ return -EPERM;
+ }
+
+ ret = wait_event_interruptible(info->revoke_wq,
+ info->revoking ||
+ data->closing);
+ if (ret == 0 && data->closing)
+ ret = -EPIPE;
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static void locked_hw3d_client_done(struct hw3d_info *info, int had_timer)
+{
+ if (info->enabled) {
+ pr_debug("hw3d: was enabled\n");
+ info->enabled = 0;
+ clk_disable(info->grp_clk);
+ clk_disable(info->imem_clk);
+ }
+ info->revoking = 0;
+
+ /* double check that the irqs are disabled */
+ locked_hw3d_irq_disable(info);
+
+ if (had_timer)
+ wake_unlock(&info->wake_lock);
+ wake_up(&info->revoke_done_wq);
+}
+
+static void do_force_revoke(struct hw3d_info *info)
+{
+ unsigned long flags;
+
+ /* at this point, the task had a chance to relinquish the gpu, but
+ * it hasn't. So, we kill it */
+ spin_lock_irqsave(&info->lock, flags);
+ pr_debug("hw3d: forcing revoke\n");
+ locked_hw3d_irq_disable(info);
+ if (info->client_task) {
+ pr_info("hw3d: force revoke from pid=%d\n",
+ info->client_task->pid);
+ force_sig(SIGKILL, info->client_task);
+ put_task_struct(info->client_task);
+ info->client_task = NULL;
+ }
+ locked_hw3d_client_done(info, 1);
+ pr_debug("hw3d: done forcing revoke\n");
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+#define REVOKE_TIMEOUT (2 * HZ)
+static void locked_hw3d_revoke(struct hw3d_info *info)
+{
+ /* force us to wait to suspend until the revoke is done. If the
+ * user doesn't release the gpu, the timer will turn off the gpu,
+ * and force kill the process. */
+ wake_lock(&info->wake_lock);
+ info->revoking = 1;
+ wake_up(&info->revoke_wq);
+ mod_timer(&info->revoke_timer, jiffies + REVOKE_TIMEOUT);
+}
+
+bool is_msm_hw3d_file(struct file *file)
+{
+ struct hw3d_info *info = hw3d_info;
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == MAJOR(info->devno) &&
+ (is_master(info, file) || is_client(info, file)))
+ return 1;
+ return 0;
+}
+
+void put_msm_hw3d_file(struct file *file)
+{
+ if (!is_msm_hw3d_file(file))
+ return;
+ fput(file);
+}
+
+int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase,
+ unsigned long *len, struct file **filp)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct file *file;
+ struct hw3d_data *data;
+ uint32_t offset = HW3D_OFFSET_IN_REGION(*offs);
+ int region = HW3D_REGION_ID(*offs);
+ int ret = 0;
+
+ if (unlikely(region >= HW3D_NUM_REGIONS)) {
+ VDBG("hw3d: invalid region %d requested\n", region);
+ return -EINVAL;
+ } else if (unlikely(offset >= info->regions[region].size)) {
+ VDBG("hw3d: offset %08x outside of the requested region %d\n",
+ offset, region);
+ return -EINVAL;
+ }
+
+ file = fget(fd);
+ if (unlikely(file == NULL)) {
+ pr_info("%s: requested data from file descriptor that doesn't "
+ "exist.", __func__);
+ return -EINVAL;
+ } else if (!is_msm_hw3d_file(file)) {
+ ret = -1;
+ goto err;
+ }
+
+ data = file->private_data;
+ if (unlikely(!data)) {
+ VDBG("hw3d: invalid file\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_lock(&data->mutex);
+ if (unlikely(!data->vmas[region])) {
+ mutex_unlock(&data->mutex);
+ VDBG("hw3d: requested hw3d region is not mapped\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ *offs = offset;
+ *pbase = info->regions[region].pbase;
+ *filp = file;
+ *len = data->vmas[region]->vm_end - data->vmas[region]->vm_start;
+ mutex_unlock(&data->mutex);
+ return 0;
+
+err:
+ fput(file);
+ return ret;
+}
+
+static int hw3d_flush(struct file *filp, fl_owner_t id)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct hw3d_data *data = filp->private_data;
+
+ if (!data) {
+ pr_err("%s: no private data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (is_master(info, filp))
+ return 0;
+ pr_debug("hw3d: closing\n");
+ /* releases any blocked ioctls */
+ data->closing = 1;
+ wake_up(&info->revoke_wq);
+ wake_up(&info->irq_wq);
+ return 0;
+}
+
+static int should_wakeup(struct hw3d_info *info, unsigned int cnt)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&info->lock, flags);
+ ret = (cnt != info->waiter_cnt) ||
+ (!info->suspending && !info->client_file);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ return ret;
+}
+
+static int locked_open_wait_for_gpu(struct hw3d_info *info,
+ unsigned long *flags)
+{
+ unsigned int my_cnt;
+ int ret;
+
+ my_cnt = ++info->waiter_cnt;
+ pr_debug("%s: wait_for_open %d\n", __func__, my_cnt);
+
+ /* in case there are other waiters, wake and release them. */
+ wake_up(&info->revoke_done_wq);
+
+ if (info->suspending)
+ pr_info("%s: suspended, waiting (%d %d)\n", __func__,
+ current->group_leader->pid, current->pid);
+ if (info->client_file)
+ pr_info("%s: has client, waiting (%d %d)\n", __func__,
+ current->group_leader->pid, current->pid);
+ spin_unlock_irqrestore(&info->lock, *flags);
+
+ ret = wait_event_interruptible(info->revoke_done_wq,
+ should_wakeup(info, my_cnt));
+
+ spin_lock_irqsave(&info->lock, *flags);
+ pr_debug("%s: woke up (%d %d %p)\n", __func__,
+ info->waiter_cnt, info->suspending, info->client_file);
+
+ if (ret >= 0) {
+ if (my_cnt != info->waiter_cnt) {
+ pr_info("%s: someone else asked for gpu after us %d:%d"
+ "(%d %d)\n", __func__,
+ current->group_leader->pid, current->pid,
+ my_cnt, info->waiter_cnt);
+ ret = -EBUSY;
+ } else if (info->suspending || info->client_file) {
+ pr_err("%s: couldn't get the gpu for %d:%d (%d %p)\n",
+ __func__, current->group_leader->pid,
+ current->pid, info->suspending,
+ info->client_file);
+ ret = -EBUSY;
+ } else
+ ret = 0;
+ }
+ return ret;
+}
+
+static int hw3d_open(struct inode *inode, struct file *file)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct hw3d_data *data;
+ unsigned long flags;
+ int ret = 0;
+
+ pr_info("%s: pid %d tid %d opening %s node\n", __func__,
+ current->group_leader->pid, current->pid,
+ is_master(info, file) ? "master" : "client");
+
+ if (file->private_data != NULL)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct hw3d_data), GFP_KERNEL);
+ if (!data) {
+ pr_err("%s: unable to allocate memory for hw3d_data.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&data->mutex);
+ file->private_data = data;
+
+ /* master always succeeds, so we are done */
+ if (is_master(info, file))
+ return 0;
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->client_file) {
+ pr_debug("hw3d: have client_file, need revoke\n");
+ locked_hw3d_revoke(info);
+ }
+
+ ret = locked_open_wait_for_gpu(info, &flags);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&info->lock, flags);
+ goto err;
+ }
+ pr_info("%s: pid %d tid %d got gpu\n", __func__,
+ current->group_leader->pid, current->pid);
+
+ info->client_file = file;
+ get_task_struct(current->group_leader);
+ info->client_task = current->group_leader;
+
+ /* XXX: we enable these clocks if the client connects..
+ * probably not right? Should only turn the clocks on when the user
+ * tries to map the registers? */
+ clk_enable(info->imem_clk);
+ clk_enable(info->grp_clk);
+ info->enabled = 1;
+
+ spin_unlock_irqrestore(&info->lock, flags);
+ return 0;
+
+err:
+ file->private_data = NULL;
+ kfree(data);
+ return ret;
+
+}
+
+static int hw3d_release(struct inode *inode, struct file *file)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct hw3d_data *data = file->private_data;
+ unsigned long flags;
+
+ BUG_ON(!data);
+
+ file->private_data = NULL;
+
+ if (is_master(info, file))
+ goto done;
+
+ pr_info("%s: in release for pid=%d tid=%d\n", __func__,
+ current->group_leader->pid, current->pid);
+ spin_lock_irqsave(&info->lock, flags);
+
+ if (info->client_task && info->client_task == current->group_leader) {
+ pr_debug("hw3d: releasing %d\n", info->client_task->pid);
+ put_task_struct(info->client_task);
+ info->client_task = NULL;
+ }
+
+ if (info->client_file && info->client_file == file) {
+ int pending;
+ /* this will be true if we are still the "owner" of the gpu */
+ pr_debug("hw3d: had file\n");
+ pending = del_timer(&info->revoke_timer);
+ locked_hw3d_client_done(info, pending);
+ info->client_file = NULL;
+ } else
+ pr_warning("hw3d: release without client_file.\n");
+ spin_unlock_irqrestore(&info->lock, flags);
+
+done:
+ kfree(data);
+ return 0;
+}
+
+static void hw3d_vma_open(struct vm_area_struct *vma)
+{
+ /* XXX: should the master be allowed to fork and keep the mappings? */
+
+ /* TODO: remap garbage page into here.
+ *
+ * For now, just pull the mapping. The user shouldn't be forking
+ * and using it anyway. */
+ zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL);
+}
+
+static void hw3d_vma_close(struct vm_area_struct *vma)
+{
+ struct file *file = vma->vm_file;
+ struct hw3d_data *data = file->private_data;
+ int i;
+
+ pr_debug("hw3d: current %u ppid %u file %p count %ld\n",
+ current->pid, current->parent->pid, file, file_count(file));
+
+ BUG_ON(!data);
+
+ mutex_lock(&data->mutex);
+ for (i = 0; i < HW3D_NUM_REGIONS; ++i) {
+ if (data->vmas[i] == vma) {
+ data->vmas[i] = NULL;
+ goto done;
+ }
+ }
+ pr_warning("%s: vma %p not of ours during vma_close\n", __func__, vma);
+done:
+ mutex_unlock(&data->mutex);
+}
+
+static int hw3d_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct hw3d_data *data = file->private_data;
+ unsigned long vma_size = vma->vm_end - vma->vm_start;
+ int ret = 0;
+ int region = REGION_PAGE_ID(vma->vm_pgoff);
+
+ if (region >= HW3D_NUM_REGIONS) {
+ pr_err("%s: Trying to mmap unknown region %d\n", __func__,
+ region);
+ return -EINVAL;
+ } else if (vma_size > info->regions[region].size) {
+ pr_err("%s: VMA size %ld exceeds region %d size %ld\n",
+ __func__, vma_size, region,
+ info->regions[region].size);
+ return -EINVAL;
+ } else if (REGION_PAGE_OFFS(vma->vm_pgoff) != 0 ||
+ (vma_size & ~PAGE_MASK)) {
+ pr_err("%s: Can't remap part of the region %d\n", __func__,
+ region);
+ return -EINVAL;
+ } else if (!is_master(info, file) &&
+ current->group_leader != info->client_task) {
+ pr_err("%s: current(%d) != client_task(%d)\n", __func__,
+ current->group_leader->pid, info->client_task->pid);
+ return -EPERM;
+ } else if (!is_master(info, file) &&
+ (info->revoking || info->suspending)) {
+ pr_err("%s: cannot mmap while revoking(%d) or suspending(%d)\n",
+ __func__, info->revoking, info->suspending);
+ return -EPERM;
+ }
+
+ mutex_lock(&data->mutex);
+ if (data->vmas[region] != NULL) {
+ pr_err("%s: Region %d already mapped (pid=%d tid=%d)\n",
+ __func__, region, current->group_leader->pid,
+ current->pid);
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* our mappings are always noncached */
+#ifdef pgprot_noncached
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
+
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ info->regions[region].pbase >> PAGE_SHIFT,
+ vma_size, vma->vm_page_prot);
+ if (ret) {
+ pr_err("%s: Cannot remap page range for region %d!\n", __func__,
+ region);
+ ret = -EAGAIN;
+ goto done;
+ }
+
+ /* Prevent a malicious client from stealing another client's data
+ * by forcing a revoke on it and then mmapping the GPU buffers.
+ */
+ if (region != HW3D_REGS)
+ memset(info->regions[region].vbase, 0,
+ info->regions[region].size);
+
+ vma->vm_ops = &hw3d_vm_ops;
+
+ /* mark this region as mapped */
+ data->vmas[region] = vma;
+
+done:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static long hw3d_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct hw3d_info *info = hw3d_info;
+ struct hw3d_region regions[HW3D_NUM_REGIONS];
+ int i;
+
+ if (!file->private_data)
+ return -EINVAL;
+
+ switch (cmd) {
+ case HW3D_WAIT_FOR_REVOKE:
+ return hw3d_wait_for_revoke(info, file);
+
+ case HW3D_WAIT_FOR_INTERRUPT:
+ return hw3d_wait_for_interrupt(info, file);
+
+ case HW3D_GET_REGIONS:
+ for (i = 0; i < HW3D_NUM_REGIONS; ++i) {
+ regions[i].phys = info->regions[i].pbase;
+ regions[i].map_offset = HW3D_REGION_OFFSET(i);
+ regions[i].len = info->regions[i].size;
+ }
+ if (copy_to_user((void __user *)arg, regions, sizeof(regions)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void hw3d_early_suspend(struct early_suspend *h)
+{
+ unsigned long flags;
+ struct hw3d_info *info;
+ info = container_of(h, struct hw3d_info, early_suspend);
+
+ spin_lock_irqsave(&info->lock, flags);
+ info->suspending = 1;
+ if (info->client_file) {
+ pr_info("hw3d: Requesting revoke for suspend\n");
+ locked_hw3d_revoke(info);
+ }
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void hw3d_late_resume(struct early_suspend *h)
+{
+ unsigned long flags;
+ struct hw3d_info *info;
+ info = container_of(h, struct hw3d_info, early_suspend);
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->suspending)
+ pr_info("%s: resuming\n", __func__);
+ info->suspending = 0;
+ wake_up(&info->revoke_done_wq);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static int hw3d_resume(struct platform_device *pdev)
+{
+ struct hw3d_info *info = platform_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->suspending)
+ pr_info("%s: resuming\n", __func__);
+ info->suspending = 0;
+ spin_unlock_irqrestore(&info->lock, flags);
+ return 0;
+}
+
+static int __init hw3d_probe(struct platform_device *pdev)
+{
+ struct hw3d_info *info;
+#define DEV_MASTER MKDEV(MAJOR(info->devno), MINOR_MASTER)
+#define DEV_CLIENT MKDEV(MAJOR(info->devno), MINOR_CLIENT)
+ struct class *hw3d_class;
+ struct device *master_dev;
+ struct device *client_dev;
+ struct resource *res[HW3D_NUM_REGIONS];
+ int i;
+ int irq;
+ int ret = 0;
+
+ res[HW3D_REGS] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "regs");
+ res[HW3D_SMI] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smi");
+ res[HW3D_EBI] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "ebi");
+ irq = platform_get_irq(pdev, 0);
+ if (!res[HW3D_REGS] || !res[HW3D_SMI] || !res[HW3D_EBI] || irq < 0) {
+ pr_err("%s: incomplete resources\n", __func__);
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct hw3d_info), GFP_KERNEL);
+ if (info == NULL) {
+ pr_err("%s: Cannot allocate memory for hw3d_info\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ info->irq = irq;
+ wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, "hw3d_revoke_lock");
+ spin_lock_init(&info->lock);
+ init_waitqueue_head(&info->irq_wq);
+ init_waitqueue_head(&info->revoke_wq);
+ init_waitqueue_head(&info->revoke_done_wq);
+ setup_timer(&info->revoke_timer,
+ (void (*)(unsigned long))do_force_revoke,
+ (unsigned long)info);
+
+ platform_set_drvdata(pdev, info);
+
+ info->grp_clk = clk_get(NULL, "grp_clk");
+ if (IS_ERR(info->grp_clk)) {
+ pr_err("%s: Cannot get grp_clk\n", __func__);
+ ret = PTR_ERR(info->grp_clk);
+ goto err_get_grp_clk;
+ }
+
+ info->imem_clk = clk_get(NULL, "imem_clk");
+ if (IS_ERR(info->imem_clk)) {
+ pr_err("%s: Cannot get imem_clk\n", __func__);
+ ret = PTR_ERR(info->imem_clk);
+ goto err_get_imem_clk;
+ }
+
+ for (i = 0; i < HW3D_NUM_REGIONS; ++i) {
+ info->regions[i].pbase = res[i]->start;
+ info->regions[i].size = res[i]->end - res[i]->start + 1;
+ info->regions[i].vbase = ioremap(info->regions[i].pbase,
+ info->regions[i].size);
+ if (info->regions[i].vbase == 0) {
+ pr_err("%s: Cannot remap region %d\n", __func__, i);
+ goto err_remap_region;
+ }
+ }
+
+ hw3d_class = class_create(THIS_MODULE, "msm_hw3d");
+ if (IS_ERR(hw3d_class))
+ goto err_fail_create_class;
+
+ ret = alloc_chrdev_region(&info->devno, 0, 2, "msm_hw3d");
+ if (ret < 0)
+ goto err_fail_alloc_region;
+
+ /* register the master/client devices */
+ master_dev = device_create(hw3d_class, &pdev->dev,
+ DEV_MASTER, "%s", "msm_hw3dm");
+ if (IS_ERR(master_dev))
+ goto err_dev_master;
+ cdev_init(&info->master_cdev, &hw3d_fops);
+ info->master_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&info->master_cdev, DEV_MASTER, 1);
+ if (ret < 0) {
+ pr_err("%s: Cannot register master device node\n", __func__);
+ goto err_reg_master;
+ }
+
+ client_dev = device_create(hw3d_class, &pdev->dev,
+ DEV_CLIENT, "%s", "msm_hw3dc");
+ if (IS_ERR(client_dev))
+ goto err_dev_client;
+ cdev_init(&info->client_cdev, &hw3d_fops);
+ info->client_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&info->client_cdev, DEV_CLIENT, 1);
+ if (ret < 0) {
+ pr_err("%s: Cannot register client device node\n", __func__);
+ goto err_reg_client;
+ }
+
+ info->early_suspend.suspend = hw3d_early_suspend;
+ info->early_suspend.resume = hw3d_late_resume;
+ info->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&info->early_suspend);
+
+ info->irq_en = 1;
+ ret = request_irq(info->irq, hw3d_irq_handler, IRQF_TRIGGER_HIGH,
+ "hw3d", info);
+ if (ret != 0) {
+ pr_err("%s: Cannot request irq\n", __func__);
+ goto err_req_irq;
+ }
+ hw3d_disable_interrupt(info);
+
+ hw3d_info = info;
+
+ return 0;
+
+err_req_irq:
+ unregister_early_suspend(&info->early_suspend);
+ cdev_del(&info->client_cdev);
+err_reg_client:
+ device_destroy(hw3d_class, DEV_CLIENT);
+err_dev_client:
+ cdev_del(&info->master_cdev);
+err_reg_master:
+ device_destroy(hw3d_class, DEV_MASTER);
+err_dev_master:
+ unregister_chrdev_region(info->devno, 2);
+err_fail_alloc_region:
+ class_unregister(hw3d_class);
+err_fail_create_class:
+err_remap_region:
+ for (i = 0; i < HW3D_NUM_REGIONS; ++i)
+ if (info->regions[i].vbase != 0)
+ iounmap(info->regions[i].vbase);
+ clk_put(info->imem_clk);
+err_get_imem_clk:
+ clk_put(info->grp_clk);
+err_get_grp_clk:
+ wake_lock_destroy(&info->wake_lock);
+ kfree(info);
+ platform_set_drvdata(pdev, NULL);
+err_alloc:
+ hw3d_info = NULL;
+ return ret;
+}
+
+static struct platform_driver msm_hw3d_driver = {
+ .probe = hw3d_probe,
+ .resume = hw3d_resume,
+ .driver = {
+ .name = "msm_hw3d",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init hw3d_init(void)
+{
+ return platform_driver_register(&msm_hw3d_driver);
+}
+
+device_initcall(hw3d_init);
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
new file mode 100644
index 0000000..967db0b
--- /dev/null
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -0,0 +1,199 @@
+/*
+ * Idle processing for ARMv7-based Qualcomm SoCs.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009, Code Aurora Forum. 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/linkage.h>
+#include <asm/assembler.h>
+#include <asm/vfpmacros.h>
+
+ENTRY(msm_arch_idle)
+ wfi
+ bx lr
+
+ENTRY(msm_pm_collapse)
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+ cpsid f
+#endif
+
+ ldr r0, =saved_state
+ stmia r0!, {r4-r14}
+ mrc p15, 0, r1, c1, c0, 0 /* MMU control */
+ mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */
+ mrc p15, 0, r3, c3, c0, 0 /* dacr */
+ mrc p15, 3, r4, c15, c0, 3 /* L2CR1 is the L2 cache control reg 1 */
+ mrc p15, 0, r5, c10, c2, 0 /* PRRR */
+ mrc p15, 0, r6, c10, c2, 1 /* NMRR */
+ mrc p15, 0, r7, c1, c0, 1 /* ACTLR */
+ mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */
+ mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */
+ mrc p15, 0, ip, c13, c0, 1 /* context ID */
+ stmia r0!, {r1-r9, ip}
+
+#ifdef CONFIG_VFP
+ VFPFSTMIA r0, r1 /* Save VFP working registers */
+ fmrx r1, fpexc
+ fmrx r2, fpscr
+ stmia r0!, {r1, r2} /* Save VFP state registers */
+#endif
+ bl v7_flush_dcache_all
+
+ mrc p15, 0, r1, c1, c0, 0 /* read current CR */
+ bic r0, r1, #(1 << 2) /* clear dcache bit */
+ bic r0, r0, #(1 << 12) /* clear icache bit */
+ mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */
+
+ dsb
+ wfi
+
+ mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */
+ isb
+
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+ cpsie f
+#endif
+
+ ldr r0, =saved_state /* restore registers */
+ ldmfd r0, {r4-r14}
+ mov r0, #0 /* return power collapse failed */
+ bx lr
+
+ENTRY(msm_pm_collapse_exit)
+#if 0 /* serial debug */
+ mov r0, #0x80000016
+ mcr p15, 0, r0, c15, c2, 4
+ mov r0, #0xA9000000
+ add r0, r0, #0x00A00000 /* UART1 */
+ /*add r0, r0, #0x00C00000*/ /* UART3 */
+ mov r1, #'A'
+ str r1, [r0, #0x00C]
+#endif
+
+#if 0
+ //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA
+ //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers
+ //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit
+ MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0
+
+
+ //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD]
+ //; Any read to DBGPRSR clear the STICKYPD bit
+ //; ISB guarantees the read completes before attempting to
+ //; execute a CP14 instruction.
+ MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3
+ ISB
+#endif
+
+#if 0 /* allow jtag reconnect */
+1:
+ b 1b
+#endif
+
+ bl __cpu_early_init
+
+ ldr r1, =saved_state_end
+ ldr r2, =msm_pm_collapse_exit
+ adr r3, msm_pm_collapse_exit
+ add r1, r1, r3
+ sub r1, r1, r2
+#ifdef CONFIG_VFP
+ mrc p15, 0, r2, c1, c0, 2 /* Read CP Access Control Register */
+ orr r2, r2, #0x00F00000 /* Enable full access for p10,11 */
+ mcr p15, 0, r2, c1, c0, 2 /* Write CPACR */
+ isb
+ mov r2, #0x40000000 /* Enable VFP */
+ fmxr fpexc, r2
+ isb
+ ldmdb r1!, {r2, r3} /* Read saved VFP state registers */
+ sub r1, r1, #32*8 /* Jump to start of vfp regs area */
+ VFPFLDMIA r1, r4 /* Restore VFP working registers,
+ * r1 incremented to end of vfp
+ * regs area */
+ sub r1, r1, #32*8 /* Jump back to start of vfp regs area */
+ fmxr fpscr, r3 /* Restore FPSCR */
+ fmxr fpexc, r2 /* Restore FPEXC last */
+#endif
+ ldmdb r1!, {r2-r11}
+ mcr p15, 0, r4, c3, c0, 0 /* dacr */
+ mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */
+ mcr p15, 3, r5, c15, c0, 3 /* L2CR1 */
+ mcr p15, 0, r6, c10, c2, 0 /* PRRR */
+ mcr p15, 0, r7, c10, c2, 1 /* NMRR */
+ mcr p15, 0, r8, c1, c0, 1 /* ACTLR */
+ mcr p15, 0, r9, c2, c0, 1 /* TTBR1 */
+ mcr p15, 0, r10, c13, c0, 3 /* TPIDRURO */
+ mcr p15, 0, r11, c13, c0, 1 /* context ID */
+ isb
+ ldmdb r1!, {r4-r14}
+ /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */
+ and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */
+ adr r0, msm_pm_mapped_pa /* get address of the mapped instr */
+ lsr r1, r0, #20 /* get the addr range of addr in MB */
+ lsl r1, r1, #2 /* multiply by 4 to get to the pg index */
+ add r3, r3, r1 /* pgd + pgd_index(addr) */
+ ldr r1, [r3] /* save current entry to r1 */
+ lsr r0, #20 /* align current addr to 1MB boundary */
+ lsl r0, #20
+ /* Create new entry for this 1MB page */
+ orr r0, r0, #0x4 /* PMD_SECT_BUFFERED */
+ orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */
+ orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */
+ str r0, [r3] /* put new entry into the MMU table */
+ mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */
+ dsb
+ isb
+ mcr p15, 0, r2, c1, c0, 0 /* MMU control */
+ isb
+msm_pm_mapped_pa:
+ /* Switch to virtual */
+ adr r2, msm_pm_pa_to_va
+ ldr r0, =msm_pm_pa_to_va
+ mov pc, r0
+msm_pm_pa_to_va:
+ sub r0, r0, r2
+ /* Restore r1 in MMU table */
+ add r3, r3, r0
+ str r1, [r3]
+ mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */
+ dsb
+ isb
+ mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */
+ mcr p15, 0, r3, c7, c5, 6 /* BPIALL */
+ dsb
+ isb
+ stmfd sp!, {lr}
+ bl v7_flush_kern_cache_all
+ ldmfd sp!, {lr}
+ mov r0, #1
+ bx lr
+ nop
+ nop
+ nop
+ nop
+ nop
+1: b 1b
+
+
+ .data
+
+saved_state:
+ .space 4 * 11 /* r4-14 */
+ .space 4 * 10 /* cp15 */
+#ifdef CONFIG_VFP
+ .space 8 * 32 /* VFP working registers */
+ .space 4 * 2 /* VFP state registers */
+#endif
+saved_state_end:
+
diff --git a/arch/arm/mach-msm/idle.S b/arch/arm/mach-msm/idle.S
index 6a94f05..d4c3e81 100644
--- a/arch/arm/mach-msm/idle.S
+++ b/arch/arm/mach-msm/idle.S
@@ -1,9 +1,9 @@
-/* arch/arm/mach-msm/include/mach/idle.S
+/* arch/arm/mach-msm/idle.S
*
- * Idle processing for MSM7K - work around bugs with SWFI.
+ * Idle processing for MSM7X00A - work around bugs with SWFI.
*
* Copyright (c) 2007 QUALCOMM Incorporated.
- * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2007 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
@@ -14,23 +14,93 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- */
-
+ */
+
#include <linux/linkage.h>
#include <asm/assembler.h>
-ENTRY(arch_idle)
-#ifdef CONFIG_MSM7X00A_IDLE
- mrc p15, 0, r1, c1, c0, 0 /* read current CR */
+ENTRY(msm_pm_collapse)
+ ldr r0, =saved_state
+ stmia r0!, {r4-r14}
+ mrc p15, 0, r1, c1, c0, 0 /* MMU control */
+ mrc p15, 0, r2, c2, c0, 0 /* ttb */
+ mrc p15, 0, r3, c3, c0, 0 /* dacr */
+ mrc p15, 0, ip, c13, c0, 1 /* context ID */
+ stmia r0!, {r1-r3, ip}
+#if defined(CONFIG_OPROFILE)
+ mrc p15, 0, r1, c15, c12, 0 /* pmnc */
+ mrc p15, 0, r2, c15, c12, 1 /* ccnt */
+ mrc p15, 0, r3, c15, c12, 2 /* pmn0 */
+ mrc p15, 0, ip, c15, c12, 3 /* pmn1 */
+ stmia r0!, {r1-r3, ip}
+#endif
+ /* fall though */
+ENTRY(msm_arch_idle)
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+ cpsid f
+#endif
+ mrc p15, 0, r1, c1, c0, 0 /* read current CR */
bic r0, r1, #(1 << 2) /* clear dcache bit */
bic r0, r0, #(1 << 12) /* clear icache bit */
mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */
- mov r0, #0 /* prepare wfi value */
+ mov r0, #0 /* prepare wfi value */ /* also used as return value from msm_pm_collapse */
mcr p15, 0, r0, c7, c10, 0 /* flush the cache */
mcr p15, 0, r0, c7, c10, 4 /* memory barrier */
mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */
mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+ cpsie f
#endif
mov pc, lr
+
+ENTRY(msm_pm_collapse_exit)
+#if 0 /* serial debug */
+ mov r0, #0x80000016
+ mcr p15, 0, r0, c15, c2, 4
+ mov r0, #0xA9000000
+ add r0, r0, #0x00A00000 /* UART1 */
+ /*add r0, r0, #0x00C00000*/ /* UART3 */
+ mov r1, #'A'
+ str r1, [r0, #0x00C]
+#endif
+ ldr r1, =saved_state_end
+ ldr r2, =msm_pm_collapse_exit
+ adr r3, msm_pm_collapse_exit
+ add r1, r1, r3
+ sub r1, r1, r2
+#if defined(CONFIG_OPROFILE)
+ ldmdb r1!, {r2-r5}
+ mcr p15, 0, r3, c15, c12, 1 /* ccnt */
+ mcr p15, 0, r4, c15, c12, 2 /* pmn0 */
+ mcr p15, 0, r5, c15, c12, 3 /* pmn1 */
+ mcr p15, 0, r2, c15, c12, 0 /* pmnc */
+#endif
+ ldmdb r1!, {r2-r5}
+ mcr p15, 0, r4, c3, c0, 0 /* dacr */
+ mcr p15, 0, r3, c2, c0, 0 /* ttb */
+ mcr p15, 0, r5, c13, c0, 1 /* context ID */
+ ldmdb r1!, {r4-r14}
+ mov r0, #1
+
+ mcr p15, 0, r2, c1, c0, 0 /* MMU control */
+ mov pc, lr
+ nop
+ nop
+ nop
+ nop
+ nop
+1: b 1b
+
+
+ .data
+
+saved_state:
+ .space 4 * 11 /* r4-14 */
+ .space 4 * 4 /* cp15 - MMU control, ttb, dacr, context ID */
+#if defined(CONFIG_OPROFILE)
+ .space 4 * 4 /* more cp15 - pmnc, ccnt, pmn0, pmn1 */
+#endif
+saved_state_end:
+
diff --git a/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h
new file mode 100644
index 0000000..c224297
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef __ASM_ARCH_BCM_BT_LPM_H
+#define __ASM_ARCH_BCM_BT_LPM_H
+
+#include <linux/serial_core.h>
+
+/* Uart driver must call this every time it beings TX, to ensure
+ * this driver keeps WAKE asserted during TX. Called with uart
+ * spinlock held. */
+extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport);
+
+struct bcm_bt_lpm_platform_data {
+ unsigned int gpio_wake; /* CPU -> BCM wakeup gpio */
+ unsigned int gpio_host_wake; /* BCM -> CPU wakeup gpio */
+
+ /* Callback to request the uart driver to clock off.
+ * Called with uart spinlock held. */
+ void (*request_clock_off_locked)(struct uart_port *uport);
+ /* Callback to request the uart driver to clock on.
+ * Called with uart spinlock held. */
+ void (*request_clock_on_locked)(struct uart_port *uport);
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index e302fbd..6f84d5a 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -26,10 +26,58 @@
uint32_t acpu_switch_time_us;
uint32_t max_speed_delta_khz;
uint32_t vdd_switch_time_us;
+ unsigned long mpll_khz;
unsigned long power_collapse_khz;
unsigned long wait_for_irq_khz;
};
+struct msm_camera_io_ext {
+ uint32_t mdcphy;
+ uint32_t mdcsz;
+ uint32_t appphy;
+ uint32_t appsz;
+ unsigned long camifpadphy;
+ unsigned long camifpadsz;
+};
+
+struct msm_camera_device_platform_data {
+ void (*camera_gpio_on) (void);
+ void (*camera_gpio_off)(void);
+ struct msm_camera_io_ext ioext;
+};
+
+struct msm_camera_legacy_device_platform_data {
+ int sensor_reset;
+ int sensor_pwd;
+ int vcm_pwd;
+ void (*config_gpio_on) (void);
+ void (*config_gpio_off)(void);
+};
+
+struct msm_camera_sensor_info {
+ const char *sensor_name;
+ int sensor_reset;
+ int sensor_pwd;
+ int vcm_pwd;
+ int mclk;
+ int num_flash_levels;
+ int (*camera_flash)(int level);
+ int need_suspend;
+ struct msm_camera_device_platform_data *pdata;
+ struct resource *resource;
+ uint8_t num_resources;
+};
+
+struct snd_endpoint {
+ int id;
+ const char *name;
+};
+
+struct msm_snd_endpoints {
+ struct snd_endpoint *endpoints;
+ unsigned num;
+};
+
struct clk;
/* common init routines for use by arch/arm/mach-msm/board-*.c */
@@ -37,8 +85,13 @@
void __init msm_add_devices(void);
void __init msm_map_common_io(void);
void __init msm_init_irq(void);
-void __init msm_init_gpio(void);
void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks);
void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *);
+#ifdef CONFIG_USB_MSM_72K
+void msm_hsusb_set_vbus_state(int online);
+#else
+static inline void msm_hsusb_set_vbus_state(int online) {}
+#endif
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h
new file mode 100644
index 0000000..994accd
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/board_htc.h
@@ -0,0 +1,68 @@
+/* arch/arm/mach-msm/include/mach/BOARD_HTC.h
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.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 __ASM_ARCH_MSM_BOARD_HTC_H
+#define __ASM_ARCH_MSM_BOARD_HTC_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/setup.h>
+
+struct msm_pmem_setting{
+ resource_size_t pmem_start;
+ resource_size_t pmem_size;
+ resource_size_t pmem_adsp_start;
+ resource_size_t pmem_adsp_size;
+ resource_size_t pmem_gpu0_start;
+ resource_size_t pmem_gpu0_size;
+ resource_size_t pmem_gpu1_start;
+ resource_size_t pmem_gpu1_size;
+ resource_size_t pmem_camera_start;
+ resource_size_t pmem_camera_size;
+ resource_size_t ram_console_start;
+ resource_size_t ram_console_size;
+};
+
+enum {
+ MSM_SERIAL_UART1 = 0,
+ MSM_SERIAL_UART2,
+ MSM_SERIAL_UART3,
+#ifdef CONFIG_SERIAL_MSM_HS
+ MSM_SERIAL_UART1DM,
+ MSM_SERIAL_UART2DM,
+#endif
+ MSM_SERIAL_NUM,
+};
+
+
+/* common init routines for use by arch/arm/mach-msm/board-*.c */
+
+void __init msm_add_usb_devices(void (*phy_reset) (void));
+void __init msm_add_mem_devices(struct msm_pmem_setting *setting);
+void __init msm_init_pmic_vibrator(void);
+
+struct mmc_platform_data;
+int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat);
+int __init msm_add_serial_devices(unsigned uart);
+
+int __init board_mfg_mode(void);
+int __init parse_tag_smi(const struct tag *tags);
+int __init parse_tag_hwid(const struct tag * tags);
+int __init parse_tag_skuid(const struct tag * tags);
+int parse_tag_engineerid(const struct tag * tags);
+
+void notify_usb_connected(int online);
+
+char *board_serialno(void);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
new file mode 100644
index 0000000..1da814f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -0,0 +1,326 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __ASM__ARCH_CAMERA_H
+#define __ASM__ARCH_CAMERA_H
+
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+#include "linux/types.h"
+
+#include <mach/board.h>
+#include <media/msm_camera.h>
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define CDBG(fmt, args...) printk(KERN_INFO "msm_camera: " fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define MSM_CAMERA_MSG 0
+#define MSM_CAMERA_EVT 1
+#define NUM_WB_EXP_NEUTRAL_REGION_LINES 4
+#define NUM_WB_EXP_STAT_OUTPUT_BUFFERS 3
+#define NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS 16
+#define NUM_STAT_OUTPUT_BUFFERS 3
+#define NUM_AF_STAT_OUTPUT_BUFFERS 3
+
+enum msm_queue {
+ MSM_CAM_Q_CTRL, /* control command or control command status */
+ MSM_CAM_Q_VFE_EVT, /* adsp event */
+ MSM_CAM_Q_VFE_MSG, /* adsp message */
+ MSM_CAM_Q_V4L2_REQ, /* v4l2 request */
+};
+
+enum vfe_resp_msg {
+ VFE_EVENT,
+ VFE_MSG_GENERAL,
+ VFE_MSG_SNAPSHOT,
+ VFE_MSG_OUTPUT1,
+ VFE_MSG_OUTPUT2,
+ VFE_MSG_STATS_AF,
+ VFE_MSG_STATS_WE, /* AEC + AWB */
+ VFE_MSG_OUTPUT_P, /* preview (continuous mode ) */
+ VFE_MSG_OUTPUT_T, /* thumbnail (snapshot mode )*/
+ VFE_MSG_OUTPUT_S, /* main image (snapshot mode )*/
+ VFE_MSG_OUTPUT_V, /* video (continuous mode ) */
+ VFE_MSG_STATS_AEC,
+ VFE_MSG_STATS_AWB,
+ VFE_MSG_STATS_RS,
+ VFE_MSG_STATS_CS,
+ VFE_MSG_STATS_IHIST,
+ VFE_MSG_STATS_SKIN,
+};
+
+struct msm_vfe_phy_info {
+ uint32_t sbuf_phy;
+ uint32_t y_phy;
+ uint32_t cbcr_phy;
+ uint8_t output_id; /* OUTPUT_MODE_P/T/S/V */
+};
+
+struct msm_vfe_resp {
+ enum vfe_resp_msg type;
+ struct msm_vfe_evt_msg evt_msg;
+ struct msm_vfe_phy_info phy;
+ void *extdata;
+ int32_t extlen;
+};
+
+struct msm_vfe_callback {
+ void (*vfe_resp)(struct msm_vfe_resp *,
+ enum msm_queue, void *syncdata,
+ gfp_t gfp);
+ void* (*vfe_alloc)(int, void *syncdata, gfp_t gfp);
+ void (*vfe_free)(void *ptr);
+ int (*flash_ctrl)(void *syncdata, int level);
+};
+
+struct msm_camvfe_fn {
+ int (*vfe_init)(struct msm_vfe_callback *, struct platform_device *);
+ int (*vfe_enable)(struct camera_enable_cmd *);
+ int (*vfe_config)(struct msm_vfe_cfg_cmd *, void *);
+ int (*vfe_disable)(struct camera_enable_cmd *,
+ struct platform_device *dev);
+ void (*vfe_release)(struct platform_device *);
+};
+
+struct msm_sensor_ctrl {
+ int (*s_init)(const struct msm_camera_sensor_info *);
+ int (*s_release)(void);
+ int (*s_config)(void __user *);
+};
+
+/* this structure is used in kernel */
+struct msm_queue_cmd {
+ struct list_head list_config;
+ struct list_head list_control;
+ struct list_head list_frame;
+ struct list_head list_pict;
+ enum msm_queue type;
+ void *command;
+ int on_heap;
+};
+
+struct msm_device_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int max;
+ int len;
+ const char *name;
+};
+
+struct msm_sync {
+ /* These two queues are accessed from a process context only. They contain
+ * pmem descriptors for the preview frames and the stats coming from the
+ * camera sensor.
+ */
+ struct hlist_head pmem_frames;
+ struct hlist_head pmem_stats;
+
+ /* The message queue is used by the control thread to send commands
+ * to the config thread, and also by the DSP to send messages to the
+ * config thread. Thus it is the only queue that is accessed from
+ * both interrupt and process context.
+ */
+ struct msm_device_queue event_q;
+
+ /* This queue contains preview frames. It is accessed by the DSP (in
+ * in interrupt context, and by the frame thread.
+ */
+ struct msm_device_queue frame_q;
+ int unblock_poll_frame;
+
+ /* This queue contains snapshot frames. It is accessed by the DSP (in
+ * interrupt context, and by the control thread.
+ */
+ struct msm_device_queue pict_q;
+
+ struct msm_camera_sensor_info *sdata;
+ struct msm_camvfe_fn vfefn;
+ struct msm_sensor_ctrl sctrl;
+ struct wake_lock wake_lock;
+ struct platform_device *pdev;
+ uint8_t opencnt;
+ void *cropinfo;
+ int croplen;
+
+ uint32_t pp_mask;
+ struct msm_queue_cmd *pp_prev;
+ struct msm_queue_cmd *pp_snap;
+
+ /* When this flag is set, we send preview-frame notifications to config
+ * as well as to the frame queue. By default, the flag is cleared.
+ */
+ uint32_t report_preview_to_config;
+
+ const char *apps_id;
+
+ struct mutex lock;
+ struct list_head list;
+ int get_pic_abort;
+};
+
+#define MSM_APPS_ID_V4L2 "msm_v4l2"
+#define MSM_APPS_ID_PROP "msm_qct"
+
+struct msm_device {
+ struct msm_sync *sync; /* most-frequently accessed */
+ struct device *device;
+ struct cdev cdev;
+ /* opened is meaningful only for the config and frame nodes,
+ * which may be opened only once.
+ */
+ atomic_t opened;
+};
+
+struct msm_control_device {
+ struct msm_device *pmsm;
+
+ /* Used for MSM_CAM_IOCTL_CTRL_CMD_DONE responses */
+ uint8_t ctrl_data[50];
+ struct msm_ctrl_cmd ctrl;
+ struct msm_queue_cmd qcmd;
+
+ /* This queue used by the config thread to send responses back to the
+ * control thread. It is accessed only from a process context.
+ */
+ struct msm_device_queue ctrl_q;
+};
+
+struct register_address_value_pair {
+ uint16_t register_address;
+ uint16_t register_value;
+};
+
+struct msm_pmem_region {
+ struct hlist_node list;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ struct file *file;
+ struct msm_pmem_info info;
+};
+
+struct axidata {
+ uint32_t bufnum1;
+ uint32_t bufnum2;
+ struct msm_pmem_region *region;
+ uint32_t bufnum3;
+};
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+/* Below functions are added for V4L2 kernel APIs */
+struct msm_v4l2_driver {
+ struct msm_sync *sync;
+ int (*open)(struct msm_sync *, const char *apps_id);
+ int (*release)(struct msm_sync *);
+ int (*ctrl)(struct msm_sync *, struct msm_ctrl_cmd *);
+ int (*reg_pmem)(struct msm_sync *, struct msm_pmem_info *);
+ int (*get_frame) (struct msm_sync *, struct msm_frame *);
+ int (*put_frame) (struct msm_sync *, struct msm_frame *);
+ int (*get_pict) (struct msm_sync *, struct msm_ctrl_cmd *);
+ unsigned int (*drv_poll) (struct msm_sync *, struct file *,
+ struct poll_table_struct *);
+};
+
+int msm_v4l2_register(struct msm_v4l2_driver *);
+int msm_v4l2_unregister(struct msm_v4l2_driver *);
+#endif
+
+void msm_camvfe_init(void);
+int msm_camvfe_check(void *);
+void msm_camvfe_fn_init(struct msm_camvfe_fn *, void *);
+int msm_camera_drv_start(struct platform_device *dev,
+ int (*sensor_probe)(const struct msm_camera_sensor_info *,
+ struct msm_sensor_ctrl *));
+
+enum msm_camio_clk_type {
+ CAMIO_VFE_MDC_CLK,
+ CAMIO_MDC_CLK,
+ CAMIO_VFE_CLK,
+ CAMIO_VFE_AXI_CLK,
+
+ CAMIO_VFE_CAMIF_CLK,
+ CAMIO_VFE_PBDG_CLK,
+ CAMIO_CAM_MCLK_CLK,
+ CAMIO_CAMIF_PAD_PBDG_CLK,
+ CAMIO_CSI_CLK,
+ CAMIO_CSI_VFE_CLK,
+ CAMIO_CSI_PCLK,
+ CAMIO_MAX_CLK
+};
+
+enum msm_camio_clk_src_type {
+ MSM_CAMIO_CLK_SRC_INTERNAL,
+ MSM_CAMIO_CLK_SRC_EXTERNAL,
+ MSM_CAMIO_CLK_SRC_MAX
+};
+
+enum msm_s_test_mode {
+ S_TEST_OFF,
+ S_TEST_1,
+ S_TEST_2,
+ S_TEST_3
+};
+
+enum msm_s_resolution {
+ S_QTR_SIZE,
+ S_FULL_SIZE,
+ S_INVALID_SIZE
+};
+
+enum msm_s_reg_update {
+ /* Sensor egisters that need to be updated during initialization */
+ S_REG_INIT,
+ /* Sensor egisters that needs periodic I2C writes */
+ S_UPDATE_PERIODIC,
+ /* All the sensor Registers will be updated */
+ S_UPDATE_ALL,
+ /* Not valid update */
+ S_UPDATE_INVALID
+};
+
+enum msm_s_setting {
+ S_RES_PREVIEW,
+ S_RES_CAPTURE
+};
+
+int msm_camio_enable(struct platform_device *dev);
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clk);
+int msm_camio_clk_disable(enum msm_camio_clk_type clk);
+int msm_camio_clk_config(uint32_t freq);
+void msm_camio_clk_rate_set(int rate);
+void msm_camio_clk_axi_rate_set(int rate);
+void msm_disable_io_gpio_clk(struct platform_device *);
+
+void msm_camio_camif_pad_reg_reset(void);
+void msm_camio_camif_pad_reg_reset_2(void);
+
+void msm_camio_vfe_blk_reset(void);
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type);
+void msm_camio_disable(struct platform_device *);
+int msm_camio_probe_on(struct platform_device *);
+int msm_camio_probe_off(struct platform_device *);
+#endif
diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h
index 00f9bbf..9a62569 100644
--- a/arch/arm/mach-msm/include/mach/dma.h
+++ b/arch/arm/mach-msm/include/mach/dma.h
@@ -1,6 +1,8 @@
/* linux/include/asm-arm/arch-msm/dma.h
*
* Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -34,6 +36,7 @@
void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd);
void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful);
+void msm_dmov_flush(unsigned int id);
int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr);
@@ -69,6 +72,7 @@
#define DMOV_FLUSH3(ch) DMOV_SD_AARM(0x140, ch)
#define DMOV_FLUSH4(ch) DMOV_SD_AARM(0x180, ch)
#define DMOV_FLUSH5(ch) DMOV_SD_AARM(0x1C0, ch)
+#define DMOV_FLUSH_TYPE (1 << 31)
#define DMOV_STATUS(ch) DMOV_SD_AARM(0x200, ch)
#define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29))
@@ -100,6 +104,19 @@
#define DMOV_USB_CHAN 11
+#define DMOV_HSUART1_TX_CHAN 4
+#define DMOV_HSUART1_TX_CRCI 8
+
+#define DMOV_HSUART1_RX_CHAN 9
+#define DMOV_HSUART1_RX_CRCI 9
+
+#define DMOV_HSUART2_TX_CHAN 4
+#define DMOV_HSUART2_TX_CRCI 14
+
+#define DMOV_HSUART2_RX_CHAN 11
+#define DMOV_HSUART2_RX_CRCI 15
+
+
/* no client rate control ifc (eg, ram) */
#define DMOV_NONE_CRCI 0
diff --git a/arch/arm/mach-msm/include/mach/fiq.h b/arch/arm/mach-msm/include/mach/fiq.h
new file mode 100644
index 0000000..64a0ea9
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/fiq.h
@@ -0,0 +1,34 @@
+/* linux/include/asm-arm/arch-msm/irqs.h
+ *
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef __ASM_ARCH_MSM_FIQ_H
+#define __ASM_ARCH_MSM_FIQ_H
+
+/* cause an interrupt to be an FIQ instead of a regular IRQ */
+void msm_fiq_select(int number);
+void msm_fiq_unselect(int number);
+
+/* enable/disable an interrupt that is an FIQ (not safe from FIQ context) */
+void msm_fiq_enable(int number);
+void msm_fiq_disable(int number);
+
+/* install an FIQ handler */
+int msm_fiq_set_handler(void (*func)(void *data, void *regs, void *svc_sp),
+ void *data);
+
+/* cause an edge triggered interrupt to fire (safe from FIQ context */
+void msm_trigger_irq(int number);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h
index 262b441..154e536 100644
--- a/arch/arm/mach-msm/include/mach/gpio.h
+++ b/arch/arm/mach-msm/include/mach/gpio.h
@@ -16,6 +16,32 @@
#ifndef __ASM_ARCH_MSM_GPIO_H
#define __ASM_ARCH_MSM_GPIO_H
+#include <linux/interrupt.h>
+#include <asm-generic/gpio.h>
+#include <mach/irqs.h>
+
+#define FIRST_BOARD_GPIO NR_GPIO_IRQS
+
+static inline int gpio_get_value(unsigned gpio)
+{
+ return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+ return __gpio_to_irq(gpio);
+}
+
/**
* struct msm_gpio - GPIO pin description
* @gpio_cfg - configuration bitmap, as per gpio_tlmm_config()
@@ -92,33 +118,33 @@
/* GPIO TLMM: Direction */
enum {
- GPIO_INPUT,
- GPIO_OUTPUT,
+ GPIO_CFG_INPUT,
+ GPIO_CFG_OUTPUT,
};
/* GPIO TLMM: Pullup/Pulldown */
enum {
- GPIO_NO_PULL,
- GPIO_PULL_DOWN,
- GPIO_KEEPER,
- GPIO_PULL_UP,
+ GPIO_CFG_NO_PULL,
+ GPIO_CFG_PULL_DOWN,
+ GPIO_CFG_KEEPER,
+ GPIO_CFG_PULL_UP,
};
/* GPIO TLMM: Drive Strength */
enum {
- GPIO_2MA,
- GPIO_4MA,
- GPIO_6MA,
- GPIO_8MA,
- GPIO_10MA,
- GPIO_12MA,
- GPIO_14MA,
- GPIO_16MA,
+ GPIO_CFG_2MA,
+ GPIO_CFG_4MA,
+ GPIO_CFG_6MA,
+ GPIO_CFG_8MA,
+ GPIO_CFG_10MA,
+ GPIO_CFG_12MA,
+ GPIO_CFG_14MA,
+ GPIO_CFG_16MA,
};
enum {
- GPIO_ENABLE,
- GPIO_DISABLE,
+ GPIO_CFG_ENABLE,
+ GPIO_CFG_DISABLE,
};
#define GPIO_CFG(gpio, func, dir, pull, drvstr) \
diff --git a/arch/arm/mach-msm/include/mach/htc_35mm_jack.h b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h
new file mode 100644
index 0000000..5ce1e2a
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h
@@ -0,0 +1,31 @@
+/* arch/arm/mach-msm/include/mach/htc_35mm_jack.h
+ *
+ * Copyright (C) 2009 HTC, Inc.
+ * Author: Arec Kao <Arec_Kao@htc.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 HTC_35MM_REMOTE_H
+#define HTC_35MM_REMOTE_H
+
+/* Driver interfaces */
+int htc_35mm_jack_plug_event(int insert, int *hpin_stable);
+int htc_35mm_key_event(int key, int *hpin_stable);
+
+/* Platform Specific Callbacks */
+struct h35mm_platform_data {
+ int (*plug_event_enable)(void);
+ int (*headset_has_mic)(void);
+ int (*key_event_enable)(void);
+ int (*key_event_disable)(void);
+};
+#endif
diff --git a/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h
new file mode 100644
index 0000000..2139bf9
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h
@@ -0,0 +1,29 @@
+/* include/asm/mach-msm/htc_acoustic_qsd.h
+ *
+ * Copyright (C) 2009 HTC 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 _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_
+#define _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_
+
+struct qsd_acoustic_ops {
+ void (*enable_mic_bias)(int en);
+};
+
+void acoustic_register_ops(struct qsd_acoustic_ops *ops);
+
+int turn_mic_bias_on(int on);
+int force_headset_speaker_on(int enable);
+int enable_aux_loopback(uint32_t enable);
+
+#endif
+
diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h
new file mode 100644
index 0000000..5bea7a2
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/htc_headset.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 HTC, Inc.
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef __ASM_ARCH_HTC_HEADSET_H
+#define __ASM_ARCH_HTC_HEADSET_H
+
+struct h2w_platform_data {
+ char *power_name;
+ int cable_in1;
+ int cable_in2;
+ int h2w_clk;
+ int h2w_data;
+ int debug_uart;
+ int headset_mic_35mm;
+ void (*config_cpld)(int);
+ void (*init_cpld)(void);
+ void (*set_dat)(int);
+ void (*set_clk)(int);
+ void (*set_dat_dir)(int);
+ void (*set_clk_dir)(int);
+ int (*get_dat)(void);
+ int (*get_clk)(void);
+};
+
+#define BIT_HEADSET (1 << 0)
+#define BIT_HEADSET_NO_MIC (1 << 1)
+#define BIT_TTY (1 << 2)
+#define BIT_FM_HEADSET (1 << 3)
+#define BIT_FM_SPEAKER (1 << 4)
+#define BIT_TTY_VCO (1 << 5)
+#define BIT_TTY_HCO (1 << 6)
+#define BIT_35MM_HEADSET (1 << 7)
+
+enum {
+ H2W_NO_DEVICE = 0,
+ H2W_HTC_HEADSET = 1,
+ NORMAL_HEARPHONE= 2,
+ H2W_DEVICE = 3,
+ H2W_USB_CRADLE = 4,
+ H2W_UART_DEBUG = 5,
+};
+
+enum {
+ H2W_GPIO = 0,
+ H2W_UART1 = 1,
+ H2W_UART3 = 2,
+ H2W_BT = 3
+};
+
+#define RESEND_DELAY (3) /* ms */
+#define MAX_ACK_RESEND_TIMES (6) /* follow spec */
+#define MAX_HOST_RESEND_TIMES (3) /* follow spec */
+#define MAX_HYGEIA_RESEND_TIMES (5)
+
+#define H2W_ASCR_DEVICE_INI (0x01)
+#define H2W_ASCR_ACT_EN (0x02)
+#define H2W_ASCR_PHONE_IN (0x04)
+#define H2W_ASCR_RESET (0x08)
+#define H2W_ASCR_AUDIO_IN (0x10)
+
+#define H2W_LED_OFF (0x0)
+#define H2W_LED_BKL (0x1)
+#define H2W_LED_MTL (0x2)
+
+typedef enum {
+ /* === system group 0x0000~0x00FF === */
+ /* (R) Accessory type register */
+ H2W_SYSTEM = 0x0000,
+ /* (R) Maximum group address */
+ H2W_MAX_GP_ADD = 0x0001,
+ /* (R/W) Accessory system control register0 */
+ H2W_ASCR0 = 0x0002,
+
+ /* === key group 0x0100~0x01FF === */
+ /* (R) Key group maximum sub address */
+ H2W_KEY_MAXADD = 0x0100,
+ /* (R) ASCII key press down flag */
+ H2W_ASCII_DOWN = 0x0101,
+ /* (R) ASCII key release up flag */
+ H2W_ASCII_UP = 0x0102,
+ /* (R) Function key status flag */
+ H2W_FNKEY_UPDOWN = 0x0103,
+ /* (R/W) Key device status */
+ H2W_KD_STATUS = 0x0104,
+
+ /* === led group 0x0200~0x02FF === */
+ /* (R) LED group maximum sub address */
+ H2W_LED_MAXADD = 0x0200,
+ /* (R/W) LED control register0 */
+ H2W_LEDCT0 = 0x0201,
+
+ /* === crdl group 0x0300~0x03FF === */
+ /* (R) Cardle group maximum sub address */
+ H2W_CRDL_MAXADD = 0x0300,
+ /* (R/W) Cardle group function control register0 */
+ H2W_CRDLCT0 = 0x0301,
+
+ /* === car kit group 0x0400~0x04FF === */
+ H2W_CARKIT_MAXADD = 0x0400,
+
+ /* === usb host group 0x0500~0x05FF === */
+ H2W_USBHOST_MAXADD = 0x0500,
+
+ /* === medical group 0x0600~0x06FF === */
+ H2W_MED_MAXADD = 0x0600,
+ H2W_MED_CONTROL = 0x0601,
+ H2W_MED_IN_DATA = 0x0602,
+} H2W_ADDR;
+
+
+typedef struct H2W_INFO {
+ /* system group */
+ unsigned char CLK_SP;
+ int SLEEP_PR;
+ unsigned char HW_REV;
+ int AUDIO_DEVICE;
+ unsigned char ACC_CLASS;
+ unsigned char MAX_GP_ADD;
+
+ /* key group */
+ int KEY_MAXADD;
+ int ASCII_DOWN;
+ int ASCII_UP;
+ int FNKEY_UPDOWN;
+ int KD_STATUS;
+
+ /* led group */
+ int LED_MAXADD;
+ int LEDCT0;
+
+ /* medical group */
+ int MED_MAXADD;
+ unsigned char AP_ID;
+ unsigned char AP_EN;
+ unsigned char DATA_EN;
+} H2W_INFO;
+
+typedef enum {
+ H2W_500KHz = 1,
+ H2W_250KHz = 2,
+ H2W_166KHz = 3,
+ H2W_125KHz = 4,
+ H2W_100KHz = 5,
+ H2W_83KHz = 6,
+ H2W_71KHz = 7,
+ H2W_62KHz = 8,
+ H2W_55KHz = 9,
+ H2W_50KHz = 10,
+} H2W_SPEED;
+
+typedef enum {
+ H2W_KEY_INVALID = -1,
+ H2W_KEY_PLAY = 0,
+ H2W_KEY_FORWARD = 1,
+ H2W_KEY_BACKWARD = 2,
+ H2W_KEY_VOLUP = 3,
+ H2W_KEY_VOLDOWN = 4,
+ H2W_KEY_PICKUP = 5,
+ H2W_KEY_HANGUP = 6,
+ H2W_KEY_MUTE = 7,
+ H2W_KEY_HOLD = 8,
+ H2W_NUM_KEYFUNC = 9,
+} KEYFUNC;
+
+extern int turn_mic_bias_on(int on);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/htc_pwrsink.h b/arch/arm/mach-msm/include/mach/htc_pwrsink.h
new file mode 100644
index 0000000..c7a91f1
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/htc_pwrsink.h
@@ -0,0 +1,87 @@
+/* include/asm/mach-msm/htc_pwrsink.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2008 HTC 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 _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_
+#define _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_
+
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+
+typedef enum {
+ PWRSINK_AUDIO_PCM = 0,
+ PWRSINK_AUDIO_MP3,
+ PWRSINK_AUDIO_AAC,
+
+ PWRSINK_AUDIO_LAST = PWRSINK_AUDIO_AAC,
+ PWRSINK_AUDIO_INVALID
+} pwrsink_audio_id_type;
+
+struct pwr_sink_audio {
+ unsigned volume;
+ unsigned percent;
+};
+
+typedef enum {
+ PWRSINK_SYSTEM_LOAD = 0,
+ PWRSINK_AUDIO,
+ PWRSINK_BACKLIGHT,
+ PWRSINK_LED_BUTTON,
+ PWRSINK_LED_KEYBOARD,
+ PWRSINK_GP_CLK,
+ PWRSINK_BLUETOOTH,
+ PWRSINK_CAMERA,
+ PWRSINK_SDCARD,
+ PWRSINK_VIDEO,
+ PWRSINK_WIFI,
+
+ PWRSINK_LAST = PWRSINK_WIFI,
+ PWRSINK_INVALID
+} pwrsink_id_type;
+
+struct pwr_sink {
+ pwrsink_id_type id;
+ unsigned ua_max;
+ unsigned percent_util;
+};
+
+struct pwr_sink_platform_data {
+ unsigned num_sinks;
+ struct pwr_sink *sinks;
+ int (*suspend_late)(struct platform_device *, pm_message_t state);
+ int (*resume_early)(struct platform_device *);
+ void (*suspend_early)(struct early_suspend *);
+ void (*resume_late)(struct early_suspend *);
+};
+
+#ifndef CONFIG_HTC_PWRSINK
+static inline int htc_pwrsink_set(pwrsink_id_type id, unsigned percent)
+{
+ return 0;
+}
+static inline int htc_pwrsink_audio_set(pwrsink_audio_id_type id,
+ unsigned percent_utilized) { return 0; }
+static inline int htc_pwrsink_audio_volume_set(
+ pwrsink_audio_id_type id, unsigned volume) { return 0; }
+static inline int htc_pwrsink_audio_path_set(unsigned path) { return 0; }
+#else
+extern int htc_pwrsink_set(pwrsink_id_type id, unsigned percent);
+extern int htc_pwrsink_audio_set(pwrsink_audio_id_type id,
+ unsigned percent_utilized);
+extern int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id,
+ unsigned volume);
+extern int htc_pwrsink_audio_path_set(unsigned path);
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-7x00.h b/arch/arm/mach-msm/include/mach/irqs-7x00.h
index f1fe706..65e18d3 100644
--- a/arch/arm/mach-msm/include/mach/irqs-7x00.h
+++ b/arch/arm/mach-msm/include/mach/irqs-7x00.h
@@ -71,5 +71,6 @@
#define NR_MSM_IRQS 64
#define NR_GPIO_IRQS 122
#define NR_BOARD_IRQS 64
+#define NR_SIRC_IRQS 0
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h
index 67c5396..c564576 100644
--- a/arch/arm/mach-msm/include/mach/irqs-7x30.h
+++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h
@@ -155,16 +155,11 @@
#define INT_MDDI_CLIENT INT_MDC
#define INT_NAND_WR_ER_DONE INT_EBI2_WR_ER_DONE
#define INT_NAND_OP_DONE INT_EBI2_OP_DONE
+#define INT_GRAPHICS INT_GRP_3D
#define NR_MSM_IRQS 128
+#define NR_SIRC_IRQS 0
#define NR_GPIO_IRQS 182
-#define PMIC8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS)
-#define NR_PMIC8058_GPIO_IRQS 40
-#define NR_PMIC8058_MPP_IRQS 12
-#define NR_PMIC8058_MISC_IRQS 8
-#define NR_PMIC8058_IRQS (NR_PMIC8058_GPIO_IRQS +\
- NR_PMIC8058_MPP_IRQS +\
- NR_PMIC8058_MISC_IRQS)
-#define NR_BOARD_IRQS NR_PMIC8058_IRQS
+#define NR_BOARD_IRQS 64
#endif /* __ASM_ARCH_MSM_IRQS_7X30_H */
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index 164d355..75d3b98 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -19,6 +19,10 @@
#define MSM_IRQ_BIT(irq) (1 << ((irq) & 31))
+#define FIRST_SIRC_IRQ (NR_MSM_IRQS)
+#define FIRST_GPIO_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS)
+#define FIRST_BOARD_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS)
+
#if defined(CONFIG_ARCH_MSM7X30)
#include "irqs-7x30.h"
#elif defined(CONFIG_ARCH_QSD8X50)
@@ -30,8 +34,8 @@
#error "Unknown architecture specification"
#endif
-#define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS)
-#define MSM_GPIO_TO_INT(n) (NR_MSM_IRQS + (n))
+#define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS)
+#define MSM_GPIO_TO_INT(n) (FIRST_GPIO_IRQ + (n))
#define MSM_INT_TO_REG(base, irq) (base + irq / 32)
#endif
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index 50c7847..4bdc4d3 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -21,10 +21,25 @@
#define PHYS_OFFSET UL(0x00000000)
#elif defined(CONFIG_ARCH_QSD8X50)
#define PHYS_OFFSET UL(0x20000000)
+#define RESET_VECTOR UL(0x00000000)
#elif defined(CONFIG_ARCH_MSM7X30)
#define PHYS_OFFSET UL(0x00200000)
+#define RESET_VECTOR UL(0x00000000)
+#else
+#ifdef CONFIG_MACH_SAPPHIRE
+#define PHYS_OFFSET UL(0x02000000)
#else
#define PHYS_OFFSET UL(0x10000000)
+#define RESET_VECTOR UL(0x00000000)
+#endif
+#endif
+
+#define HAS_ARCH_IO_REMAP_PFN_RANGE
+
+#define CONSISTENT_DMA_SIZE (4*SZ_1M)
+
+#ifdef CONFIG_ARCH_MSM_SCORPION
+#define arch_has_speculative_dfetch() 1
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/mmc.h b/arch/arm/mach-msm/include/mach/mmc.h
index 0ecf254..4292517 100644
--- a/arch/arm/mach-msm/include/mach/mmc.h
+++ b/arch/arm/mach-msm/include/mach/mmc.h
@@ -17,6 +17,7 @@
struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
+ int built_in; /* built-in device flag */
u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *);
struct embedded_sdio_data *embedded_sdio;
diff --git a/arch/arm/mach-msm/include/mach/msm_adsp.h b/arch/arm/mach-msm/include/mach/msm_adsp.h
new file mode 100644
index 0000000..a081683
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_adsp.h
@@ -0,0 +1,112 @@
+/* include/asm-arm/arch-msm/msm_adsp.h
+ *
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef __ASM__ARCH_MSM_ADSP_H
+#define __ASM__ARCH_MSM_ADSP_H
+
+struct msm_adsp_module;
+
+struct msm_adsp_ops {
+ /* event is called from interrupt context when a message
+ * arrives from the DSP. Use the provided function pointer
+ * to copy the message into a local buffer. Do NOT call
+ * it multiple times.
+ */
+ void (*event)(void *driver_data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len));
+};
+
+/* Get, Put, Enable, and Disable are synchronous and must only
+ * be called from thread context. Enable and Disable will block
+ * up to one second in the event of a fatal DSP error but are
+ * much faster otherwise.
+ */
+int msm_adsp_get(const char *name, struct msm_adsp_module **module,
+ struct msm_adsp_ops *ops, void *driver_data);
+void msm_adsp_put(struct msm_adsp_module *module);
+int msm_adsp_enable(struct msm_adsp_module *module);
+int msm_adsp_disable(struct msm_adsp_module *module);
+int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate);
+
+/* Write is safe to call from interrupt context.
+ */
+int msm_adsp_write(struct msm_adsp_module *module,
+ unsigned queue_id,
+ void *data, size_t len);
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+/* Command Queue Indexes */
+#define QDSP_lpmCommandQueue 0
+#define QDSP_mpuAfeQueue 1
+#define QDSP_mpuGraphicsCmdQueue 2
+#define QDSP_mpuModmathCmdQueue 3
+#define QDSP_mpuVDecCmdQueue 4
+#define QDSP_mpuVDecPktQueue 5
+#define QDSP_mpuVEncCmdQueue 6
+#define QDSP_rxMpuDecCmdQueue 7
+#define QDSP_rxMpuDecPktQueue 8
+#define QDSP_txMpuEncQueue 9
+#define QDSP_uPAudPPCmd1Queue 10
+#define QDSP_uPAudPPCmd2Queue 11
+#define QDSP_uPAudPPCmd3Queue 12
+#define QDSP_uPAudPlay0BitStreamCtrlQueue 13
+#define QDSP_uPAudPlay1BitStreamCtrlQueue 14
+#define QDSP_uPAudPlay2BitStreamCtrlQueue 15
+#define QDSP_uPAudPlay3BitStreamCtrlQueue 16
+#define QDSP_uPAudPlay4BitStreamCtrlQueue 17
+#define QDSP_uPAudPreProcCmdQueue 18
+#define QDSP_uPAudRecBitStreamQueue 19
+#define QDSP_uPAudRecCmdQueue 20
+#define QDSP_uPDiagQueue 21
+#define QDSP_uPJpegActionCmdQueue 22
+#define QDSP_uPJpegCfgCmdQueue 23
+#define QDSP_uPVocProcQueue 24
+#define QDSP_vfeCommandQueue 25
+#define QDSP_vfeCommandScaleQueue 26
+#define QDSP_vfeCommandTableQueue 27
+#define QDSP_MAX_NUM_QUEUES 28
+#else
+/* Command Queue Indexes */
+#define QDSP_lpmCommandQueue 0
+#define QDSP_mpuAfeQueue 1
+#define QDSP_mpuGraphicsCmdQueue 2
+#define QDSP_mpuModmathCmdQueue 3
+#define QDSP_mpuVDecCmdQueue 4
+#define QDSP_mpuVDecPktQueue 5
+#define QDSP_mpuVEncCmdQueue 6
+#define QDSP_rxMpuDecCmdQueue 7
+#define QDSP_rxMpuDecPktQueue 8
+#define QDSP_txMpuEncQueue 9
+#define QDSP_uPAudPPCmd1Queue 10
+#define QDSP_uPAudPPCmd2Queue 11
+#define QDSP_uPAudPPCmd3Queue 12
+#define QDSP_uPAudPlay0BitStreamCtrlQueue 13
+#define QDSP_uPAudPlay1BitStreamCtrlQueue 14
+#define QDSP_uPAudPlay2BitStreamCtrlQueue 15
+#define QDSP_uPAudPlay3BitStreamCtrlQueue 16
+#define QDSP_uPAudPlay4BitStreamCtrlQueue 17
+#define QDSP_uPAudPreProcCmdQueue 18
+#define QDSP_uPAudRecBitStreamQueue 19
+#define QDSP_uPAudRecCmdQueue 20
+#define QDSP_uPJpegActionCmdQueue 21
+#define QDSP_uPJpegCfgCmdQueue 22
+#define QDSP_uPVocProcQueue 23
+#define QDSP_vfeCommandQueue 24
+#define QDSP_vfeCommandScaleQueue 25
+#define QDSP_vfeCommandTableQueue 26
+#define QDSP_QUEUE_MAX 26
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_audio_aac.h b/arch/arm/mach-msm/include/mach/msm_audio_aac.h
new file mode 100644
index 0000000..1aea0fa
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_audio_aac.h
@@ -0,0 +1,75 @@
+/* arch/arm/mach-msm/include/mach/msm_audio_aac.h
+ *
+ * Copyright (c) 2009 QUALCOMM USA, 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ *
+ */
+
+#ifndef __MSM_AUDIO_AAC_H
+#define __MSM_AUDIO_AAC_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned)
+#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned)
+
+#define AUDIO_AAC_FORMAT_ADTS -1
+#define AUDIO_AAC_FORMAT_RAW 0x0000
+#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001
+#define AUDIO_AAC_FORMAT_LOAS 0x0002
+
+#define AUDIO_AAC_OBJECT_LC 0x0002
+#define AUDIO_AAC_OBJECT_LTP 0x0004
+#define AUDIO_AAC_OBJECT_ERLC 0x0011
+
+#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001
+#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000
+
+#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001
+#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000
+
+#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001
+#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000
+
+#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001
+#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000
+
+#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001
+#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000
+
+/* Primary channel on both left and right channels */
+#define AUDIO_AAC_DUAL_MONO_PL_PR 0
+/* Secondary channel on both left and right channels */
+#define AUDIO_AAC_DUAL_MONO_SL_SR 1
+/* Primary channel on right channel and 2nd on left channel */
+#define AUDIO_AAC_DUAL_MONO_SL_PR 2
+/* 2nd channel on right channel and primary on left channel */
+#define AUDIO_AAC_DUAL_MONO_PL_SR 3
+
+struct msm_audio_aac_config {
+ signed short format;
+ unsigned short audio_object;
+ unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */
+ unsigned short aac_section_data_resilience_flag;
+ unsigned short aac_scalefactor_data_resilience_flag;
+ unsigned short aac_spectral_data_resilience_flag;
+ unsigned short sbr_on_flag;
+ unsigned short sbr_ps_on_flag;
+ unsigned short dual_mono_mode;
+ unsigned short channel_configuration;
+};
+
+#endif /* __MSM_AUDIO_AAC_H */
diff --git a/arch/arm/mach-msm/include/mach/msm_fast_timer.h b/arch/arm/mach-msm/include/mach/msm_fast_timer.h
new file mode 100644
index 0000000..e1660c1
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_fast_timer.h
@@ -0,0 +1,19 @@
+/* arch/arm/mach-msm/include/mach/msm_fast_timer.h
+ *
+ * Copyright (C) 2009 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.
+ *
+ */
+
+void msm_enable_fast_timer(void);
+void msm_disable_fast_timer(void);
+u32 msm_read_fast_timer(void);
+
diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h
index 1f4fc81..339fa46 100644
--- a/arch/arm/mach-msm/include/mach/msm_fb.h
+++ b/arch/arm/mach-msm/include/mach/msm_fb.h
@@ -21,6 +21,10 @@
struct mddi_info;
+/* output interface format */
+#define MSM_MDP_OUT_IF_FMT_RGB565 0
+#define MSM_MDP_OUT_IF_FMT_RGB666 1
+
struct msm_fb_data {
int xres; /* x resolution in pixels */
int yres; /* y resolution in pixels */
@@ -34,9 +38,12 @@
};
enum {
- MSM_MDDI_PMDH_INTERFACE,
+ MSM_MDDI_PMDH_INTERFACE = 0,
MSM_MDDI_EMDH_INTERFACE,
MSM_EBI2_INTERFACE,
+ MSM_LCDC_INTERFACE,
+
+ MSM_MDP_NUM_INTERFACES = MSM_LCDC_INTERFACE + 1,
};
#define MSMFB_CAP_PARTIAL_UPDATES (1 << 0)
@@ -85,6 +92,8 @@
/* fixup the mfr name, product id */
void (*fixup)(uint16_t *mfr_name, uint16_t *product_id);
+ int vsync_irq;
+
struct resource *fb_resource; /*optional*/
/* number of clients in the list that follows */
int num_clients;
@@ -110,17 +119,50 @@
} client_platform_data[];
};
+struct msm_lcdc_timing {
+ unsigned int clk_rate; /* dclk freq */
+ unsigned int hsync_pulse_width; /* in dclks */
+ unsigned int hsync_back_porch; /* in dclks */
+ unsigned int hsync_front_porch; /* in dclks */
+ unsigned int hsync_skew; /* in dclks */
+ unsigned int vsync_pulse_width; /* in lines */
+ unsigned int vsync_back_porch; /* in lines */
+ unsigned int vsync_front_porch; /* in lines */
+
+ /* control signal polarity */
+ unsigned int vsync_act_low:1;
+ unsigned int hsync_act_low:1;
+ unsigned int den_act_low:1;
+};
+
+struct msm_lcdc_panel_ops {
+ int (*init)(struct msm_lcdc_panel_ops *);
+ int (*uninit)(struct msm_lcdc_panel_ops *);
+ int (*blank)(struct msm_lcdc_panel_ops *);
+ int (*unblank)(struct msm_lcdc_panel_ops *);
+};
+
+struct msm_lcdc_platform_data {
+ struct msm_lcdc_panel_ops *panel_ops;
+ struct msm_lcdc_timing *timing;
+ int fb_id;
+ struct msm_fb_data *fb_data;
+ struct resource *fb_resource;
+};
+
struct mdp_blit_req;
struct fb_info;
struct mdp_device {
struct device dev;
- void (*dma)(struct mdp_device *mpd, uint32_t addr,
+ void (*dma)(struct mdp_device *mdp, uint32_t addr,
uint32_t stride, uint32_t w, uint32_t h, uint32_t x,
uint32_t y, struct msmfb_callback *callback, int interface);
- void (*dma_wait)(struct mdp_device *mdp);
+ void (*dma_wait)(struct mdp_device *mdp, int interface);
int (*blit)(struct mdp_device *mdp, struct fb_info *fb,
struct mdp_blit_req *req);
void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id);
+ int (*check_output_format)(struct mdp_device *mdp, int bpp);
+ int (*set_output_format)(struct mdp_device *mdp, int bpp);
};
struct class_interface;
@@ -140,6 +182,9 @@
int (*unblank)(struct msm_mddi_bridge_platform_data *,
struct msm_mddi_client_data *);
struct msm_fb_data fb_data;
+
+ /* board file will identify what capabilities the panel supports */
+ uint32_t panel_caps;
};
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h
new file mode 100644
index 0000000..93fb9a4
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h
@@ -0,0 +1,38 @@
+/* linux/include/asm-arm/arch-msm/hsusb.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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 __ASM_ARCH_MSM_HSUSB_H
+#define __ASM_ARCH_MSM_HSUSB_H
+
+#include <linux/types.h>
+
+/* platform device data for msm_hsusb driver */
+
+struct msm_hsusb_platform_data {
+ /* hard reset the ULPI PHY */
+ void (*phy_reset)(void);
+
+ /* (de)assert the reset to the usb core */
+ void (*hw_reset)(bool enable);
+
+ /* for notification when USB is connected or disconnected */
+ void (*usb_connected)(int);
+
+ /* val, reg pairs terminated by -1 */
+ int *phy_init_seq;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
new file mode 100644
index 0000000..a2035d9
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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_USB_GADGET_MSM72K_UDC_H__
+#define __LINUX_USB_GADGET_MSM72K_UDC_H__
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(level, fmt, args...) \
+ printk(level "%s: " fmt , driver_name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(fmt, args...) \
+ xprintk(KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(fmt,args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(fmt,args...) \
+ do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(fmt,args...) \
+ xprintk(KERN_ERR , fmt , ## args)
+#define INFO(fmt,args...) \
+ xprintk(KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+
+#define USB_ID (MSM_USB_BASE + 0x0000)
+#define USB_HWGENERAL (MSM_USB_BASE + 0x0004)
+#define USB_HWHOST (MSM_USB_BASE + 0x0008)
+#define USB_HWDEVICE (MSM_USB_BASE + 0x000C)
+#define USB_HWTXBUF (MSM_USB_BASE + 0x0010)
+#define USB_HWRXBUF (MSM_USB_BASE + 0x0014)
+
+#ifdef CONFIG_ARCH_MSM7X00A
+#define USB_SBUSCFG (MSM_USB_BASE + 0x0090)
+#else
+#define USB_AHBBURST (MSM_USB_BASE + 0x0090)
+#define USB_AHBMODE (MSM_USB_BASE + 0x0098)
+#endif
+
+#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */
+#define USB_HCIVERSION (MSM_USB_BASE + 0x0102) /* 16 bit */
+#define USB_HCSPARAMS (MSM_USB_BASE + 0x0104)
+#define USB_HCCPARAMS (MSM_USB_BASE + 0x0108)
+#define USB_DCIVERSION (MSM_USB_BASE + 0x0120) /* 16 bit */
+#define USB_USBCMD (MSM_USB_BASE + 0x0140)
+#define USB_USBSTS (MSM_USB_BASE + 0x0144)
+#define USB_USBINTR (MSM_USB_BASE + 0x0148)
+#define USB_FRINDEX (MSM_USB_BASE + 0x014C)
+#define USB_DEVICEADDR (MSM_USB_BASE + 0x0154)
+#define USB_ENDPOINTLISTADDR (MSM_USB_BASE + 0x0158)
+#define USB_BURSTSIZE (MSM_USB_BASE + 0x0160)
+#define USB_TXFILLTUNING (MSM_USB_BASE + 0x0164)
+#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170)
+#define USB_ENDPTNAK (MSM_USB_BASE + 0x0178)
+#define USB_ENDPTNAKEN (MSM_USB_BASE + 0x017C)
+#define USB_PORTSC (MSM_USB_BASE + 0x0184)
+#define USB_OTGSC (MSM_USB_BASE + 0x01A4)
+#define USB_USBMODE (MSM_USB_BASE + 0x01A8)
+#define USB_ENDPTSETUPSTAT (MSM_USB_BASE + 0x01AC)
+#define USB_ENDPTPRIME (MSM_USB_BASE + 0x01B0)
+#define USB_ENDPTFLUSH (MSM_USB_BASE + 0x01B4)
+#define USB_ENDPTSTAT (MSM_USB_BASE + 0x01B8)
+#define USB_ENDPTCOMPLETE (MSM_USB_BASE + 0x01BC)
+#define USB_ENDPTCTRL(n) (MSM_USB_BASE + 0x01C0 + (4 * (n)))
+
+
+#define USBCMD_RESET 2
+#define USBCMD_ATTACH 1
+#define USBCMD_ATDTW (1 << 14)
+
+#define USBMODE_DEVICE 2
+#define USBMODE_HOST 3
+
+struct ept_queue_head {
+ unsigned config;
+ unsigned active; /* read-only */
+
+ unsigned next;
+ unsigned info;
+ unsigned page0;
+ unsigned page1;
+ unsigned page2;
+ unsigned page3;
+ unsigned page4;
+ unsigned reserved_0;
+
+ unsigned char setup_data[8];
+
+ unsigned reserved_1;
+ unsigned reserved_2;
+ unsigned reserved_3;
+ unsigned reserved_4;
+};
+
+#define CONFIG_MAX_PKT(n) ((n) << 16)
+#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */
+#define CONFIG_IOS (1 << 15) /* IRQ on setup */
+
+struct ept_queue_item {
+ unsigned next;
+ unsigned info;
+ unsigned page0;
+ unsigned page1;
+ unsigned page2;
+ unsigned page3;
+ unsigned page4;
+ unsigned reserved;
+};
+
+#define TERMINATE 1
+
+#define INFO_BYTES(n) ((n) << 16)
+#define INFO_IOC (1 << 15)
+#define INFO_ACTIVE (1 << 7)
+#define INFO_HALTED (1 << 6)
+#define INFO_BUFFER_ERROR (1 << 5)
+#define INFO_TXN_ERROR (1 << 3)
+
+
+#define STS_NAKI (1 << 16) /* */
+#define STS_SLI (1 << 8) /* R/WC - suspend state entered */
+#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */
+#define STS_URI (1 << 6) /* R/WC - RESET recv'd - write to clear */
+#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */
+#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */
+#define STS_UEI (1 << 1) /* R/WC - USB Error */
+#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */
+
+
+/* bits used in all the endpoint status registers */
+#define EPT_TX(n) (1 << ((n) + 16))
+#define EPT_RX(n) (1 << (n))
+
+
+#define CTRL_TXE (1 << 23)
+#define CTRL_TXR (1 << 22)
+#define CTRL_TXI (1 << 21)
+#define CTRL_TXD (1 << 17)
+#define CTRL_TXS (1 << 16)
+#define CTRL_RXE (1 << 7)
+#define CTRL_RXR (1 << 6)
+#define CTRL_RXI (1 << 5)
+#define CTRL_RXD (1 << 1)
+#define CTRL_RXS (1 << 0)
+
+#define CTRL_TXT_MASK (3 << 18)
+#define CTRL_TXT_CTRL (0 << 18)
+#define CTRL_TXT_ISOCH (1 << 18)
+#define CTRL_TXT_BULK (2 << 18)
+#define CTRL_TXT_INT (3 << 18)
+#define CTRL_TXT_EP_TYPE_SHIFT 18
+
+#define CTRL_RXT_MASK (3 << 2)
+#define CTRL_RXT_CTRL (0 << 2)
+#define CTRL_RXT_ISOCH (1 << 2)
+#define CTRL_RXT_BULK (2 << 2)
+#define CTRL_RXT_INT (3 << 2)
+#define CTRL_RXT_EP_TYPE_SHIFT 2
+
+#define ULPI_WAKEUP (1 << 31)
+#define ULPI_RUN (1 << 30)
+#define ULPI_WRITE (1 << 29)
+#define ULPI_READ (0 << 29)
+#define ULPI_STATE_NORMAL (1 << 27)
+#define ULPI_ADDR(n) (((n) & 255) << 16)
+#define ULPI_DATA(n) ((n) & 255)
+#define ULPI_DATA_READ(n) (((n) >> 8) & 255)
+
+#define ULPI_DEBUG_REG (0x15)
+#define ULPI_SCRATCH_REG (0x16)
+
+#define ULPI_FUNC_CTRL_CLR (0x06)
+#define ULPI_FUNC_SUSPENDM (1 << 6)
+
+
+/* USB_PORTSC bits for determining port speed */
+#define PORTSC_PSPD_FS (0 << 26)
+#define PORTSC_PSPD_LS (1 << 26)
+#define PORTSC_PSPD_HS (2 << 26)
+#define PORTSC_PSPD_MASK (3 << 26)
+/* suspend and remote wakeup */
+#define PORTSC_FPR (1 << 6)
+#define PORTSC_SUSP (1 << 7)
+
+/* test mode support */
+#define J_TEST (0x0100)
+#define K_TEST (0x0200)
+#define SE0_NAK_TEST (0x0300)
+#define TST_PKT_TEST (0x0400)
+#define PORTSC_PTC (0xf << 16)
+#define PORTSC_PTC_J_STATE (0x01 << 16)
+#define PORTSC_PTC_K_STATE (0x02 << 16)
+#define PORTSC_PTC_SE0_NAK (0x03 << 16)
+#define PORTSC_PTC_TST_PKT (0x04 << 16)
+
+#define PORTSC_PTS_MASK (3 << 30)
+#define PORTSC_PTS_ULPI (2 << 30)
+#define PORTSC_PTS_SERIAL (3 << 30)
+
+#define PORTSC_CCS (1 << 0) /* current connect status */
+#define PORTSC_FPR (1 << 6) /* R/W - State normal => suspend */
+#define PORTSC_SUSP (1 << 7) /* Read - Port in suspend state */
+#define PORTSC_PORT_RESET (1 << 8)
+#define PORTSC_LS (3 << 10) /* Read - Port's Line status */
+#define PORTSC_PHCD (1 << 23) /* phy suspend mode */
+
+#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
index cfff0e7..571391b 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
@@ -43,35 +43,38 @@
#define IOMEM(x) ((void __force __iomem *)(x))
#endif
-#define MSM_VIC_BASE IOMEM(0xE0000000)
+#define MSM_VIC_BASE IOMEM(0xF8000000)
#define MSM_VIC_PHYS 0xC0000000
#define MSM_VIC_SIZE SZ_4K
-#define MSM_CSR_BASE IOMEM(0xE0001000)
+#define MSM_CSR_BASE IOMEM(0xF8001000)
#define MSM_CSR_PHYS 0xC0100000
#define MSM_CSR_SIZE SZ_4K
-#define MSM_GPT_PHYS MSM_CSR_PHYS
-#define MSM_GPT_BASE MSM_CSR_BASE
-#define MSM_GPT_SIZE SZ_4K
+#define MSM_TMR_PHYS MSM_CSR_PHYS
+#define MSM_TMR_BASE MSM_CSR_BASE
+#define MSM_TMR_SIZE SZ_4K
-#define MSM_DMOV_BASE IOMEM(0xE0002000)
+#define MSM_GPT_BASE MSM_TMR_BASE
+#define MSM_DGT_BASE (MSM_TMR_BASE + 0x10)
+
+#define MSM_DMOV_BASE IOMEM(0xF8002000)
#define MSM_DMOV_PHYS 0xA9700000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
+#define MSM_GPIO1_BASE IOMEM(0xF8003000)
#define MSM_GPIO1_PHYS 0xA9200000
#define MSM_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
+#define MSM_GPIO2_BASE IOMEM(0xF8004000)
#define MSM_GPIO2_PHYS 0xA9300000
#define MSM_GPIO2_SIZE SZ_4K
-#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
+#define MSM_CLK_CTL_BASE IOMEM(0xF8005000)
#define MSM_CLK_CTL_PHYS 0xA8600000
#define MSM_CLK_CTL_SIZE SZ_4K
-#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000)
+#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000)
#define MSM_SHARED_RAM_PHYS 0x01F00000
#define MSM_SHARED_RAM_SIZE SZ_1M
@@ -85,7 +88,7 @@
#define MSM_UART3_SIZE SZ_4K
#ifdef CONFIG_MSM_DEBUG_UART
-#define MSM_DEBUG_UART_BASE 0xE1000000
+#define MSM_DEBUG_UART_BASE 0xF9000000
#if CONFIG_MSM_DEBUG_UART == 1
#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS
#elif CONFIG_MSM_DEBUG_UART == 2
@@ -108,6 +111,9 @@
#define MSM_SDC4_PHYS 0xA0700000
#define MSM_SDC4_SIZE SZ_4K
+#define MSM_NAND_PHYS 0xA0A00000
+#define MSM_NAND_SIZE SZ_4K
+
#define MSM_I2C_PHYS 0xA9900000
#define MSM_I2C_SIZE SZ_4K
@@ -123,12 +129,25 @@
#define MSM_MDP_PHYS 0xAA200000
#define MSM_MDP_SIZE 0x000F0000
+#define MSM_MDC_BASE IOMEM(0xF8200000)
#define MSM_MDC_PHYS 0xAA500000
#define MSM_MDC_SIZE SZ_1M
+#define MSM_AD5_BASE IOMEM(0xF8300000)
#define MSM_AD5_PHYS 0xAC000000
#define MSM_AD5_SIZE (SZ_1M*13)
+#define MSM_VFE_PHYS 0xA0F00000
+#define MSM_VFE_SIZE SZ_1M
+
+#define MSM_UART1DM_PHYS 0xA0200000
+#define MSM_UART2DM_PHYS 0xA0300000
+
+#define MSM_SSBI_PHYS 0xA8100000
+#define MSM_SSBI_SIZE SZ_4K
+
+#define MSM_TSSC_PHYS 0xAA300000
+#define MSM_TSSC_SIZE SZ_4K
#if defined(CONFIG_ARCH_MSM7X30)
#define MSM_GCC_BASE IOMEM(0xF8009000)
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
index 8a00c2d..1c24b39 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
@@ -35,11 +35,11 @@
*
*/
-#define MSM_VIC_BASE IOMEM(0xE0000000)
+#define MSM_VIC_BASE IOMEM(0xF8000000)
#define MSM_VIC_PHYS 0xC0080000
#define MSM_VIC_SIZE SZ_4K
-#define MSM_CSR_BASE IOMEM(0xE0001000)
+#define MSM_CSR_BASE IOMEM(0xF8001000)
#define MSM_CSR_PHYS 0xC0100000
#define MSM_CSR_SIZE SZ_4K
@@ -50,43 +50,43 @@
#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4)
#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24)
-#define MSM_DMOV_BASE IOMEM(0xE0002000)
+#define MSM_DMOV_BASE IOMEM(0xF8002000)
#define MSM_DMOV_PHYS 0xAC400000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
+#define MSM_GPIO1_BASE IOMEM(0xF8003000)
#define MSM_GPIO1_PHYS 0xAC001000
#define MSM_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
+#define MSM_GPIO2_BASE IOMEM(0xF8004000)
#define MSM_GPIO2_PHYS 0xAC101000
#define MSM_GPIO2_SIZE SZ_4K
-#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
+#define MSM_CLK_CTL_BASE IOMEM(0xF8005000)
#define MSM_CLK_CTL_PHYS 0xAB800000
#define MSM_CLK_CTL_SIZE SZ_4K
-#define MSM_CLK_CTL_SH2_BASE IOMEM(0xE0006000)
+#define MSM_CLK_CTL_SH2_BASE IOMEM(0xF8006000)
#define MSM_CLK_CTL_SH2_PHYS 0xABA01000
#define MSM_CLK_CTL_SH2_SIZE SZ_4K
-#define MSM_ACC_BASE IOMEM(0xE0007000)
+#define MSM_ACC_BASE IOMEM(0xF8007000)
#define MSM_ACC_PHYS 0xC0101000
#define MSM_ACC_SIZE SZ_4K
-#define MSM_SAW_BASE IOMEM(0xE0008000)
+#define MSM_SAW_BASE IOMEM(0xF8008000)
#define MSM_SAW_PHYS 0xC0102000
#define MSM_SAW_SIZE SZ_4K
-#define MSM_GCC_BASE IOMEM(0xE0009000)
+#define MSM_GCC_BASE IOMEM(0xF8009000)
#define MSM_GCC_PHYS 0xC0182000
#define MSM_GCC_SIZE SZ_4K
-#define MSM_TCSR_BASE IOMEM(0xE000A000)
+#define MSM_TCSR_BASE IOMEM(0xF800A000)
#define MSM_TCSR_PHYS 0xAB600000
#define MSM_TCSR_SIZE SZ_4K
-#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000)
+#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000)
#define MSM_SHARED_RAM_PHYS 0x00100000
#define MSM_SHARED_RAM_SIZE SZ_1M
@@ -100,7 +100,7 @@
#define MSM_UART3_SIZE SZ_4K
#ifdef CONFIG_MSM_DEBUG_UART
-#define MSM_DEBUG_UART_BASE 0xE1000000
+#define MSM_DEBUG_UART_BASE 0xF9000000
#if CONFIG_MSM_DEBUG_UART == 1
#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS
#elif CONFIG_MSM_DEBUG_UART == 2
@@ -111,12 +111,69 @@
#define MSM_DEBUG_UART_SIZE SZ_4K
#endif
-#define MSM_MDC_BASE IOMEM(0xE0200000)
+#define MSM_MDC_BASE IOMEM(0xF8200000)
#define MSM_MDC_PHYS 0xAA500000
#define MSM_MDC_SIZE SZ_1M
-#define MSM_AD5_BASE IOMEM(0xE0300000)
+#define MSM_AD5_BASE IOMEM(0xF8300000)
#define MSM_AD5_PHYS 0xA7000000
#define MSM_AD5_SIZE (SZ_1M*13)
+#define MSM_VFE_PHYS 0xA0F00000
+#define MSM_VFE_SIZE SZ_1M
+
+#define MSM_I2C_SIZE SZ_4K
+#define MSM_I2C_PHYS 0xACD00000
+
+#define MSM_I2C_2_PHYS 0xACF00000
+#define MSM_I2C_2_SIZE SZ_4K
+
+#define MSM_QUP_PHYS 0xA8301000
+#define MSM_QUP_SIZE SZ_4K
+
+#define MSM_GSBI_QUP_I2C_PHYS 0xA8300000
+#define MSM_GSBI_QUP_I2C_SIZE 4
+
+#define MSM_HSUSB_PHYS 0xA3600000
+#define MSM_HSUSB_SIZE SZ_1K
+
+#define MSM_PMDH_PHYS 0xAD600000
+#define MSM_PMDH_SIZE SZ_4K
+
+#define MSM_EMDH_PHYS 0xAD700000
+#define MSM_EMDH_SIZE SZ_4K
+
+#define MSM_MDP_PHYS 0xA3F00000
+#define MSM_MDP_SIZE 0x000F0000
+
+#define MSM_UART1DM_PHYS 0xA0200000
+#define MSM_UART2DM_PHYS 0xA0300000
+
+#define MSM_TSSC_PHYS 0xAA300000
+#define MSM_TSSC_SIZE SZ_4K
+
+#define MSM_SDC1_PHYS 0xA0400000
+#define MSM_SDC1_SIZE SZ_4K
+
+#define MSM_SDC2_PHYS 0xA0500000
+#define MSM_SDC2_SIZE SZ_4K
+
+#define MSM_SDC3_PHYS 0xA3000000
+#define MSM_SDC3_SIZE SZ_4K
+
+#define MSM_SDC4_PHYS 0xA3100000
+#define MSM_SDC4_SIZE SZ_4K
+
+#define MSM_NAND_PHYS 0xA0200000
+#define MSM_NAND_SIZE SZ_4K
+
+#define MSM_PMIC_SSBI_PHYS 0xAD900000
+#define MSM_PMIC_SSBI_SIZE SZ_4K
+
+#define MSM_GPU_REG_PHYS 0xA3500000
+#define MSM_GPU_REG_SIZE SZ_128K
+
+#define MSM_SPI_PHYS 0xA8000000
+#define MSM_SPI_SIZE SZ_4K
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
index acc819e..eed8606 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
@@ -35,11 +35,11 @@
*
*/
-#define MSM_VIC_BASE IOMEM(0xE0000000)
+#define MSM_VIC_BASE IOMEM(0xF8000000)
#define MSM_VIC_PHYS 0xAC000000
#define MSM_VIC_SIZE SZ_4K
-#define MSM_CSR_BASE IOMEM(0xE0001000)
+#define MSM_CSR_BASE IOMEM(0xF8001000)
#define MSM_CSR_PHYS 0xAC100000
#define MSM_CSR_SIZE SZ_4K
@@ -50,38 +50,42 @@
#define MSM_GPT_BASE MSM_TMR_BASE
#define MSM_DGT_BASE (MSM_TMR_BASE + 0x10)
-#define MSM_DMOV_BASE IOMEM(0xE0002000)
+#define MSM_DMOV_BASE IOMEM(0xF8002000)
#define MSM_DMOV_PHYS 0xA9700000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
+#define MSM_GPIO1_BASE IOMEM(0xF8003000)
#define MSM_GPIO1_PHYS 0xA9000000
#define MSM_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
+#define MSM_GPIO2_BASE IOMEM(0xF8004000)
#define MSM_GPIO2_PHYS 0xA9100000
#define MSM_GPIO2_SIZE SZ_4K
-#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
+#define MSM_CLK_CTL_BASE IOMEM(0xF8005000)
#define MSM_CLK_CTL_PHYS 0xA8600000
#define MSM_CLK_CTL_SIZE SZ_4K
-#define MSM_SIRC_BASE IOMEM(0xE1006000)
+#define MSM_SIRC_BASE IOMEM(0xF8006000)
#define MSM_SIRC_PHYS 0xAC200000
#define MSM_SIRC_SIZE SZ_4K
-#define MSM_SCPLL_BASE IOMEM(0xE1007000)
+#define MSM_SCPLL_BASE IOMEM(0xF8007000)
#define MSM_SCPLL_PHYS 0xA8800000
#define MSM_SCPLL_SIZE SZ_4K
+#define MSM_TCSR_BASE IOMEM(0xF8008000)
+#define MSM_TCSR_PHYS 0xA8700000
+#define MSM_TCSR_SIZE SZ_4K
+
#ifdef CONFIG_MSM_SOC_REV_A
-#define MSM_SMI_BASE 0xE0000000
+#define MSM_8K_SMI_BASE 0xE0000000
#else
-#define MSM_SMI_BASE 0x00000000
+#define MSM_8K_SMI_BASE 0x00000000
#endif
-#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000)
-#define MSM_SHARED_RAM_PHYS (MSM_SMI_BASE + 0x00100000)
+#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000)
+#define MSM_SHARED_RAM_PHYS (MSM_8K_SMI_BASE + 0x00100000)
#define MSM_SHARED_RAM_SIZE SZ_1M
#define MSM_UART1_PHYS 0xA9A00000
@@ -94,7 +98,7 @@
#define MSM_UART3_SIZE SZ_4K
#ifdef CONFIG_MSM_DEBUG_UART
-#define MSM_DEBUG_UART_BASE 0xE1000000
+#define MSM_DEBUG_UART_BASE 0xF9000000
#if CONFIG_MSM_DEBUG_UART == 1
#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS
#elif CONFIG_MSM_DEBUG_UART == 2
@@ -105,14 +109,16 @@
#define MSM_DEBUG_UART_SIZE SZ_4K
#endif
-#define MSM_MDC_BASE IOMEM(0xE0200000)
+#define MSM_MDC_BASE IOMEM(0xF8200000)
#define MSM_MDC_PHYS 0xAA500000
#define MSM_MDC_SIZE SZ_1M
-#define MSM_AD5_BASE IOMEM(0xE0300000)
+#define MSM_AD5_BASE IOMEM(0xF8300000)
#define MSM_AD5_PHYS 0xAC000000
#define MSM_AD5_SIZE (SZ_1M*13)
+#define MSM_VFE_PHYS 0xA0F00000
+#define MSM_VFE_SIZE SZ_1M
#define MSM_I2C_SIZE SZ_4K
#define MSM_I2C_PHYS 0xA9900000
@@ -120,8 +126,17 @@
#define MSM_HSUSB_PHYS 0xA0800000
#define MSM_HSUSB_SIZE SZ_1K
-#define MSM_NAND_PHYS 0xA0A00000
+#define MSM_PMDH_PHYS 0xAA600000
+#define MSM_PMDH_SIZE SZ_4K
+#define MSM_EMDH_PHYS 0xAA700000
+#define MSM_EMDH_SIZE SZ_4K
+
+#define MSM_MDP_PHYS 0xAA200000
+#define MSM_MDP_SIZE 0x000F0000
+
+#define MSM_NAND_PHYS 0xA0A00000
+#define MSM_NAND_SIZE SZ_4K
#define MSM_TSIF_PHYS (0xa0100000)
#define MSM_TSIF_SIZE (0x200)
@@ -131,17 +146,25 @@
#define MSM_UART1DM_PHYS 0xA0200000
#define MSM_UART2DM_PHYS 0xA0900000
+#define MSM_TSSC_PHYS 0xAA300000
+#define MSM_TSSC_SIZE SZ_4K
-#define MSM_SDC1_PHYS 0xA0400000
+#define MSM_SDC1_PHYS 0xA0300000
#define MSM_SDC1_SIZE SZ_4K
-#define MSM_SDC2_PHYS 0xA0500000
+#define MSM_SDC2_PHYS 0xA0400000
#define MSM_SDC2_SIZE SZ_4K
-#define MSM_SDC3_PHYS 0xA0600000
+#define MSM_SDC3_PHYS 0xA0500000
#define MSM_SDC3_SIZE SZ_4K
-#define MSM_SDC4_PHYS 0xA0700000
+#define MSM_SDC4_PHYS 0xA0600000
#define MSM_SDC4_SIZE SZ_4K
+#define MSM_GPU_REG_PHYS 0xA0000000
+#define MSM_GPU_REG_SIZE 0x00020000
+
+#define MSM_SPI_PHYS 0xA1200000
+#define MSM_SPI_SIZE SZ_4K
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h
new file mode 100644
index 0000000..54614a9
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h
@@ -0,0 +1,107 @@
+/* arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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 _MACH_MSM_QDSP6_Q6AUDIO_
+#define _MACH_MSM_QDSP6_Q6AUDIO_
+
+#define AUDIO_FLAG_READ 0
+#define AUDIO_FLAG_WRITE 1
+
+struct audio_buffer {
+ dma_addr_t phys;
+ void *data;
+ uint32_t size;
+ uint32_t used; /* 1 = CPU is waiting for DSP to consume this buf */
+};
+
+struct audio_client {
+ struct audio_buffer buf[2];
+ int cpu_buf; /* next buffer the CPU will touch */
+ int dsp_buf; /* next buffer the DSP will touch */
+ int running;
+ int session;
+
+ wait_queue_head_t wait;
+ struct dal_client *client;
+
+ int cb_status;
+ uint32_t flags;
+};
+
+#define Q6_HW_HANDSET 0
+#define Q6_HW_HEADSET 1
+#define Q6_HW_SPEAKER 2
+#define Q6_HW_TTY 3
+#define Q6_HW_BT_SCO 4
+#define Q6_HW_BT_A2DP 5
+
+#define Q6_HW_COUNT 6
+
+struct q6_hw_info {
+ int min_gain;
+ int max_gain;
+};
+
+/* Obtain a 16bit signed, interleaved audio channel of the specified
+ * rate (Hz) and channels (1 or 2), with two buffers of bufsz bytes.
+ */
+struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t flags,
+ uint32_t acdb_id);
+
+struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id);
+
+struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t acdb_id);
+
+int q6audio_close(struct audio_client *ac);
+int q6voice_close(struct audio_client *ac);
+int q6audio_mp3_close(struct audio_client *ac);
+
+int q6audio_read(struct audio_client *ac, struct audio_buffer *ab);
+int q6audio_write(struct audio_client *ac, struct audio_buffer *ab);
+int q6audio_async(struct audio_client *ac);
+
+int q6audio_do_routing(uint32_t route, uint32_t acdb_id);
+int q6audio_set_tx_mute(int mute);
+int q6audio_reinit_acdb(char* filename);
+int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst);
+int q6audio_set_rx_volume(int level);
+int q6audio_set_stream_volume(struct audio_client *ac, int vol);
+
+struct q6audio_analog_ops {
+ void (*init)(void);
+ void (*speaker_enable)(int en);
+ void (*headset_enable)(int en);
+ void (*receiver_enable)(int en);
+ void (*bt_sco_enable)(int en);
+ void (*int_mic_enable)(int en);
+ void (*ext_mic_enable)(int en);
+ int (*get_rx_vol)(uint8_t hw, int level);
+};
+
+#ifdef CONFIG_MSM_QDSP6
+void q6audio_register_analog_ops(struct q6audio_analog_ops *ops);
+void q6audio_set_acdb_file(char* filename);
+#else
+static inline void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) {}
+static inline void q6audio_set_acdb_file(char* filename) {}
+#endif
+
+/* signal non-recoverable DSP error so we can log and/or panic */
+void q6audio_dsp_not_responding(void);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_qup.h b/arch/arm/mach-msm/include/mach/msm_qup.h
new file mode 100644
index 0000000..e95b5d6
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_qup.h
@@ -0,0 +1,16 @@
+#ifndef _MACH_MSM_QUP_H
+#define _MACH_MSM_QUP_H
+
+struct msm_qup_i2c_platform_data {
+ int clk_freq;
+ uint32_t rmutex;
+ const char *rsl_id;
+ uint32_t pm_lat;
+ int pri_clk;
+ int pri_dat;
+ int aux_clk;
+ int aux_dat;
+ void (*msm_i2c_config_gpio)(int iface, int config_type);
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h
new file mode 100644
index 0000000..b2f8e3e
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h
@@ -0,0 +1,180 @@
+/** include/asm-arm/arch-msm/msm_rpcrouter.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated
+ * Author: San Mehat <san@android.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 __ASM__ARCH_MSM_RPCROUTER_H
+#define __ASM__ARCH_MSM_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+/* RPC API version structure
+ * Version bit 31 : 1->hashkey versioning,
+ * 0->major-minor (backward compatible) versioning
+ * hashkey versioning:
+ * Version bits 31-0 hashkey
+ * major-minor (backward compatible) versioning
+ * Version bits 30-28 reserved (no match)
+ * Version bits 27-16 major (must match)
+ * Version bits 15-0 minor (greater or equal)
+ */
+#define RPC_VERSION_MODE_MASK 0x80000000
+#define RPC_VERSION_MAJOR_MASK 0x0fff0000
+#define RPC_VERSION_MAJOR_OFFSET 16
+#define RPC_VERSION_MINOR_MASK 0x0000ffff
+
+#define MSM_RPC_VERS(major, minor) \
+ ((uint32_t)((((major) << RPC_VERSION_MAJOR_OFFSET) & \
+ RPC_VERSION_MAJOR_MASK) | \
+ ((minor) & RPC_VERSION_MINOR_MASK)))
+#define MSM_RPC_GET_MAJOR(vers) (((vers) & RPC_VERSION_MAJOR_MASK) >> \
+ RPC_VERSION_MAJOR_OFFSET)
+#define MSM_RPC_GET_MINOR(vers) ((vers) & RPC_VERSION_MINOR_MASK)
+#else
+#define MSM_RPC_VERS(major, minor) (major)
+#define MSM_RPC_GET_MAJOR(vers) (vers)
+#define MSM_RPC_GET_MINOR(vers) 0
+#endif
+
+struct msm_rpc_endpoint;
+
+struct rpcsvr_platform_device
+{
+ struct platform_device base;
+ uint32_t prog;
+ uint32_t vers;
+};
+
+#define RPC_DATA_IN 0
+/*
+ * Structures for sending / receiving direct RPC requests
+ * XXX: Any cred/verif lengths > 0 not supported
+ */
+
+struct rpc_request_hdr
+{
+ uint32_t xid;
+ uint32_t type; /* 0 */
+ uint32_t rpc_vers; /* 2 */
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t procedure;
+ uint32_t cred_flavor;
+ uint32_t cred_length;
+ uint32_t verf_flavor;
+ uint32_t verf_length;
+};
+
+typedef struct
+{
+ uint32_t low;
+ uint32_t high;
+} rpc_reply_progmismatch_data;
+
+typedef struct
+{
+} rpc_denied_reply_hdr;
+
+typedef struct
+{
+ uint32_t verf_flavor;
+ uint32_t verf_length;
+ uint32_t accept_stat;
+#define RPC_ACCEPTSTAT_SUCCESS 0
+#define RPC_ACCEPTSTAT_PROG_UNAVAIL 1
+#define RPC_ACCEPTSTAT_PROG_MISMATCH 2
+#define RPC_ACCEPTSTAT_PROC_UNAVAIL 3
+#define RPC_ACCEPTSTAT_GARBAGE_ARGS 4
+#define RPC_ACCEPTSTAT_SYSTEM_ERR 5
+#define RPC_ACCEPTSTAT_PROG_LOCKED 6
+ /*
+ * Following data is dependant on accept_stat
+ * If ACCEPTSTAT == PROG_MISMATCH then there is a
+ * 'rpc_reply_progmismatch_data' structure following the header.
+ * Otherwise the data is procedure specific
+ */
+} rpc_accepted_reply_hdr;
+
+struct rpc_reply_hdr
+{
+ uint32_t xid;
+ uint32_t type;
+ uint32_t reply_stat;
+#define RPCMSG_REPLYSTAT_ACCEPTED 0
+#define RPCMSG_REPLYSTAT_DENIED 1
+ union {
+ rpc_accepted_reply_hdr acc_hdr;
+ rpc_denied_reply_hdr dny_hdr;
+ } data;
+};
+
+/* flags for msm_rpc_connect() */
+#define MSM_RPC_UNINTERRUPTIBLE 0x0001
+#define MSM_RPC_ENABLE_RECEIVE (0x10000)
+
+/* use IS_ERR() to check for failure */
+struct msm_rpc_endpoint *msm_rpc_open(void);
+/* Connect with the specified server version */
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags);
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept);
+/* check if server version can handle client requested version */
+int msm_rpc_is_compatible_version(uint32_t server_version,
+ uint32_t client_version);
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept);
+int msm_rpc_write(struct msm_rpc_endpoint *ept,
+ void *data, int len);
+int msm_rpc_read(struct msm_rpc_endpoint *ept,
+ void **data, unsigned len, long timeout);
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr,
+ uint32_t prog, uint32_t vers, uint32_t proc);
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers);
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers);
+
+/* simple blocking rpc call
+ *
+ * request is mandatory and must have a rpc_request_hdr
+ * at the start. The header will be filled out for you.
+ *
+ * reply provides a buffer for replies of reply_max_size
+ */
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *request, int request_size,
+ void *reply, int reply_max_size,
+ long timeout);
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *request, int request_size,
+ long timeout);
+
+struct msm_rpc_server
+{
+ struct list_head list;
+ uint32_t flags;
+
+ uint32_t prog;
+ uint32_t vers;
+
+ int (*rpc_call)(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len);
+};
+
+int msm_rpc_create_server(struct msm_rpc_server *server);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_debugger.h b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h
new file mode 100644
index 0000000..f490b1b
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#ifndef __ASM_ARCH_MSM_SERIAL_DEBUGGER_H
+#define __ASM_ARCH_MSM_SERIAL_DEBUGGER_H
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER)
+void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq, int wakeup_irq);
+#else
+static inline void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq, int wakeup_irq) {}
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
new file mode 100644
index 0000000..fe54b02
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Nick Pelly <npelly@google.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 __ASM_ARCH_MSM_SERIAL_HS_H
+#define __ASM_ARCH_MSM_SERIAL_HS_H
+
+#include <linux/serial_core.h>
+
+/* API to request the uart clock off or on for low power management
+ * Clients should call request_clock_off() when no uart data is expected,
+ * and must call request_clock_on() before any further uart data can be
+ * received. */
+extern void msm_hs_request_clock_off(struct uart_port *uport);
+extern void msm_hs_request_clock_on(struct uart_port *uport);
+/* uport->lock must be held when calling _locked() */
+extern void msm_hs_request_clock_off_locked(struct uart_port *uport);
+extern void msm_hs_request_clock_on_locked(struct uart_port *uport);
+
+/* Optional platform device data for msm_serial_hs driver.
+ * Used to configure low power rx wakeup */
+struct msm_serial_hs_platform_data {
+ int rx_wakeup_irq; /* wakeup irq */
+ /* bool: inject char into rx tty on wakeup */
+ unsigned char inject_rx_on_wakeup;
+ char rx_to_inject;
+
+ void (*exit_lpm_cb)(struct uart_port *);
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index 029463e..7ed2939 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -106,4 +106,11 @@
SMD_NUM_PORTS,
} smd_port_id_type;
+struct smd_tty_channel_desc {
+ int id;
+ const char *name;
+};
+
+int smd_set_channel_list(const struct smd_tty_channel_desc *, int len);
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h
new file mode 100644
index 0000000..433b2bd
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_spi.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * SPI driver for Qualcomm QSD platforms.
+ */
+
+struct msm_spi_platform_data {
+ u32 max_clock_speed;
+ int (*gpio_config)(void);
+ void (*gpio_release)(void);
+ int (*dma_config)(void);
+};
diff --git a/arch/arm/mach-msm/include/mach/msm_ssbi.h b/arch/arm/mach-msm/include/mach/msm_ssbi.h
new file mode 100644
index 0000000..4d79078
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_ssbi.h
@@ -0,0 +1,35 @@
+/* arch/arm/mach-msm/include/mach/msm_ssbi.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.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 __ASM_ARCH_MSM_SSBI_H
+#define __ASM_ARCH_MSM_SSBI_H
+
+#include <linux/types.h>
+
+struct msm_ssbi_slave_info {
+ const char *name;
+ int irq;
+ void *platform_data;
+};
+
+struct msm_ssbi_platform_data {
+ struct msm_ssbi_slave_info slave;
+ const char *rspinlock_name;
+};
+
+int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len);
+int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_ts.h b/arch/arm/mach-msm/include/mach/msm_ts.h
new file mode 100644
index 0000000..cb41aed
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_ts.h
@@ -0,0 +1,51 @@
+/* arch/arm/mach-msm/include/mach/msm_ts.h
+ *
+ * Internal platform definitions for msm/qsd touchscreen devices
+ *
+ * Copyright (C) 2008 Google Incorporated
+ *
+ * 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_MSM_TS_H
+#define __ASM_ARCH_MSM_TS_H
+
+#include <linux/input.h>
+
+/* The dimensions for the virtual key are for the other axis, i.e. if
+ * virtual keys are in the Y dimension then min/max is the range in the X
+ * dimension where that key would be activated */
+struct ts_virt_key {
+ int key;
+ int min;
+ int max;
+};
+
+struct msm_ts_virtual_keys {
+ struct ts_virt_key *keys;
+ int num_keys;
+};
+
+struct msm_ts_platform_data {
+ uint32_t min_x;
+ uint32_t max_x;
+ uint32_t min_y;
+ uint32_t max_y;
+ uint32_t min_press;
+ uint32_t max_press;
+ struct msm_ts_virtual_keys *vkeys_x;
+ uint32_t virt_x_start;
+ struct msm_ts_virtual_keys *vkeys_y;
+ uint32_t virt_y_start;
+ uint32_t inv_x;
+ uint32_t inv_y;
+};
+
+#endif /* __ASM_ARCH_MSM_TS_H */
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h
new file mode 100644
index 0000000..ece4bc7
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h
@@ -0,0 +1,94 @@
+#ifndef QDSP5AUDPLAYCMDI_H
+#define QDSP5AUDPLAYCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D S P 5 A U D I O P L A Y T A S K C O M M A N D S
+
+GENERAL DESCRIPTION
+ Command Interface for AUDPLAYTASK on QDSP5
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+
+ audplay_cmd_dec_data_avail
+ Send buffer to AUDPLAY task
+
+
+Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaycmdi.h#2 $
+
+===========================================================================*/
+
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \
+ sizeof(audplay_cmd_bitstream_data_avail)
+
+/* Type specification of dec_data_avail message sent to AUDPLAYTASK
+*/
+typedef struct {
+ /*command ID*/
+ unsigned int cmd_id;
+
+ /* Decoder ID for which message is being sent */
+ unsigned int decoder_id;
+
+ /* Start address of data in ARM global memory */
+ unsigned int buf_ptr;
+
+ /* Number of 16-bit words of bit-stream data contiguously available at the
+ * above-mentioned address. */
+ unsigned int buf_size;
+
+ /* Partition number used by audPlayTask to communicate with DSP's RTOS
+ * kernel */
+ unsigned int partition_number;
+} __attribute__((packed)) audplay_cmd_bitstream_data_avail;
+
+#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003
+#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \
+ sizeof(struct audplay_cmd_hpcm_buf_cfg)
+
+struct audplay_cmd_hpcm_buf_cfg {
+ unsigned int cmd_id;
+ unsigned int hostpcm_config;
+ unsigned int feedback_frequency;
+ unsigned int byte_swap;
+ unsigned int max_buffers;
+ unsigned int partition_number;
+} __attribute__((packed));
+
+#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004
+#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \
+ sizeof(struct audplay_cmd_buffer_update)
+
+struct audplay_cmd_buffer_refresh {
+ unsigned int cmd_id;
+ unsigned int num_buffers;
+ unsigned int buf_read_count;
+ unsigned int buf0_address;
+ unsigned int buf0_length;
+ unsigned int buf1_address;
+ unsigned int buf1_length;
+} __attribute__((packed));
+#endif /* QDSP5AUDPLAYCMD_H */
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h
new file mode 100644
index 0000000..c63034b
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h
@@ -0,0 +1,70 @@
+#ifndef QDSP5AUDPLAYMSG_H
+#define QDSP5AUDPLAYMSG_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D S P 5 A U D I O P L A Y T A S K M S G
+
+GENERAL DESCRIPTION
+ Message sent by AUDPLAY task
+
+REFERENCES
+ None
+
+
+Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaymsg.h#3 $
+
+===========================================================================*/
+#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001
+#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \
+ sizeof(audplay_msg_dec_needs_data)
+
+typedef struct{
+ /* reserved*/
+ unsigned int dec_id;
+
+ /* The read pointer offset of external memory until which the
+ * bitstream has been DMAed in. */
+ unsigned int adecDataReadPtrOffset;
+
+ /* The buffer size of external memory. */
+ unsigned int adecDataBufSize;
+
+ unsigned int bitstream_free_len;
+ unsigned int bitstream_write_ptr;
+ unsigned int bitstarem_buf_start;
+ unsigned int bitstream_buf_len;
+} __attribute__((packed)) audplay_msg_dec_needs_data;
+
+#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004
+#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \
+ sizeof(struct audplay_msg_buffer_update)
+
+struct audplay_msg_buffer_update {
+ unsigned int buffer_write_count;
+ unsigned int num_of_buffer;
+ unsigned int buf0_address;
+ unsigned int buf0_length;
+ unsigned int buf1_address;
+ unsigned int buf1_length;
+} __attribute__((packed));
+#endif /* QDSP5AUDPLAYMSG_H */
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h
new file mode 100644
index 0000000..8bee9c6
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h
@@ -0,0 +1,914 @@
+#ifndef QDSP5AUDPPCMDI_H
+#define QDSP5AUDPPCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A U D I O P O S T P R O C E S S I N G I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by AUDPP Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppcmdi.h#2 $
+
+===========================================================================*/
+
+/*
+ * ARM to AUDPPTASK Commands
+ *
+ * ARM uses three command queues to communicate with AUDPPTASK
+ * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands
+ * Location : MEMA
+ * Buffer Size : 6 words
+ * No of buffers in a queue : 20 for gaming audio and 5 for other images
+ * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier
+ * Location : MEMA
+ * Buffer Size : 23
+ * No of buffers in a queue : 2
+ * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands
+ * Location : MEMA
+ * Buffer Size : 145
+ * No of buffers in a queue : 3
+ */
+
+/*
+ * Commands Related to uPAudPPCmd1Queue
+ */
+
+/*
+ * Command Structure to enable or disable the active decoders
+ */
+
+#define AUDPP_CMD_CFG_DEC_TYPE 0x0001
+#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(audpp_cmd_cfg_dec_type)
+
+/* Enable the decoder */
+#define AUDPP_CMD_DEC_TYPE_M 0x000F
+
+#define AUDPP_CMD_ENA_DEC_V 0x4000
+#define AUDPP_CMD_DIS_DEC_V 0x0000
+#define AUDPP_CMD_DEC_STATE_M 0x4000
+
+#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000
+#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000
+
+
+/* Type specification of cmd_cfg_dec */
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short dec0_cfg;
+ unsigned short dec1_cfg;
+ unsigned short dec2_cfg;
+ unsigned short dec3_cfg;
+ unsigned short dec4_cfg;
+} __attribute__((packed)) audpp_cmd_cfg_dec_type;
+
+/*
+ * Command Structure to Pause , Resume and flushes the selected audio decoders
+ */
+
+#define AUDPP_CMD_DEC_CTRL 0x0002
+#define AUDPP_CMD_DEC_CTRL_LEN sizeof(audpp_cmd_dec_ctrl)
+
+/* Decoder control commands for pause, resume and flush */
+#define AUDPP_CMD_FLUSH_V 0x2000
+
+#define AUDPP_CMD_PAUSE_V 0x4000
+#define AUDPP_CMD_RESUME_V 0x0000
+
+#define AUDPP_CMD_UPDATE_V 0x8000
+#define AUDPP_CMD_IGNORE_V 0x0000
+
+
+/* Type Spec for decoder control command*/
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short dec0_ctrl;
+ unsigned short dec1_ctrl;
+ unsigned short dec2_ctrl;
+ unsigned short dec3_ctrl;
+ unsigned short dec4_ctrl;
+} __attribute__((packed)) audpp_cmd_dec_ctrl;
+
+/*
+ * Command Structure to Configure the AVSync FeedBack Mechanism
+ */
+
+#define AUDPP_CMD_AVSYNC 0x0003
+#define AUDPP_CMD_AVSYNC_LEN sizeof(audpp_cmd_avsync)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short object_number;
+ unsigned short interrupt_interval_lsw;
+ unsigned short interrupt_interval_msw;
+} __attribute__((packed)) audpp_cmd_avsync;
+
+/*
+ * Command Structure to enable or disable(sleep) the AUDPPTASK
+ */
+
+#define AUDPP_CMD_CFG 0x0004
+#define AUDPP_CMD_CFG_LEN sizeof(audpp_cmd_cfg)
+
+#define AUDPP_CMD_CFG_SLEEP 0x0000
+#define AUDPP_CMD_CFG_ENABLE 0xFFFF
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short cfg;
+} __attribute__((packed)) audpp_cmd_cfg;
+
+/*
+ * Command Structure to Inject or drop the specified no of samples
+ */
+
+#define AUDPP_CMD_ADJUST_SAMP 0x0005
+#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(audpp_cmd_adjust_samp)
+
+#define AUDPP_CMD_SAMP_DROP -1
+#define AUDPP_CMD_SAMP_INSERT 0x0001
+
+#define AUDPP_CMD_NUM_SAMPLES 0x0001
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short object_no;
+ signed short sample_insert_or_drop;
+ unsigned short num_samples;
+} __attribute__((packed)) audpp_cmd_adjust_samp;
+
+/*
+ * Command Structure to Configure AVSync Feedback Mechanism
+ */
+
+#define AUDPP_CMD_AVSYNC_CMD_2 0x0006
+#define AUDPP_CMD_AVSYNC_CMD_2_LEN sizeof(audpp_cmd_avsync_cmd_2)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short object_number;
+ unsigned short interrupt_interval_lsw;
+ unsigned short interrupt_interval_msw;
+ unsigned short sample_counter_dlsw;
+ unsigned short sample_counter_dmsw;
+ unsigned short sample_counter_msw;
+ unsigned short byte_counter_dlsw;
+ unsigned short byte_counter_dmsw;
+ unsigned short byte_counter_msw;
+} __attribute__((packed)) audpp_cmd_avsync_cmd_2;
+
+/*
+ * Command Structure to Configure AVSync Feedback Mechanism
+ */
+
+#define AUDPP_CMD_AVSYNC_CMD_3 0x0007
+#define AUDPP_CMD_AVSYNC_CMD_3_LEN sizeof(audpp_cmd_avsync_cmd_3)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short object_number;
+ unsigned short interrupt_interval_lsw;
+ unsigned short interrupt_interval_msw;
+ unsigned short sample_counter_dlsw;
+ unsigned short sample_counter_dmsw;
+ unsigned short sample_counter_msw;
+ unsigned short byte_counter_dlsw;
+ unsigned short byte_counter_dmsw;
+ unsigned short byte_counter_msw;
+} __attribute__((packed)) audpp_cmd_avsync_cmd_3;
+
+#define AUDPP_CMD_ROUTING_MODE 0x0008
+#define AUDPP_CMD_ROUTING_MODE_LEN \
+sizeof(struct audpp_cmd_routing_mode)
+
+struct audpp_cmd_routing_mode {
+ unsigned short cmd_id;
+ unsigned short object_number;
+ unsigned short routing_mode;
+} __attribute__((packed));
+
+/*
+ * Commands Related to uPAudPPCmd2Queue
+ */
+
+/*
+ * Command Structure to configure Per decoder Parameters (Common)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000
+#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \
+ sizeof(audpp_cmd_cfg_adec_params_common)
+
+#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000
+#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000
+
+#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000
+#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000
+
+/* Sampling frequency*/
+#define AUDPP_CMD_SAMP_RATE_96000 0x0000
+#define AUDPP_CMD_SAMP_RATE_88200 0x0001
+#define AUDPP_CMD_SAMP_RATE_64000 0x0002
+#define AUDPP_CMD_SAMP_RATE_48000 0x0003
+#define AUDPP_CMD_SAMP_RATE_44100 0x0004
+#define AUDPP_CMD_SAMP_RATE_32000 0x0005
+#define AUDPP_CMD_SAMP_RATE_24000 0x0006
+#define AUDPP_CMD_SAMP_RATE_22050 0x0007
+#define AUDPP_CMD_SAMP_RATE_16000 0x0008
+#define AUDPP_CMD_SAMP_RATE_12000 0x0009
+#define AUDPP_CMD_SAMP_RATE_11025 0x000A
+#define AUDPP_CMD_SAMP_RATE_8000 0x000B
+
+
+/*
+ * Type specification of cmd_adec_cfg sent to all decoder
+ */
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short length;
+ unsigned short dec_id;
+ unsigned short status_msg_flag;
+ unsigned short decoder_frame_counter_msg_period;
+ unsigned short input_sampling_frequency;
+} __attribute__((packed)) audpp_cmd_cfg_adec_params_common;
+
+/*
+ * Command Structure to configure Per decoder Parameters (Wav)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \
+ sizeof(audpp_cmd_cfg_adec_params_wav)
+
+
+#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002
+
+#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000
+#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001
+#define AUDPP_CMD_WAV_PCM_WIDTH_32 0x0002
+
+typedef struct {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+ unsigned short pcm_width;
+ unsigned short sign;
+} __attribute__((packed)) audpp_cmd_cfg_adec_params_wav;
+
+/*
+ * Command Structure to configure Per decoder Parameters (ADPCM)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \
+ sizeof(audpp_cmd_cfg_adec_params_adpcm)
+
+
+#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002
+
+typedef struct {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+ unsigned short block_size;
+} __attribute__((packed)) audpp_cmd_cfg_adec_params_adpcm;
+
+/*
+ * Command Structure to configure Per decoder Parameters (MP3)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \
+ sizeof(audpp_cmd_cfg_adec_params_mp3)
+
+typedef struct {
+ audpp_cmd_cfg_adec_params_common common;
+} __attribute__((packed)) audpp_cmd_cfg_adec_params_mp3;
+
+
+/*
+ * Command Structure to configure Per decoder Parameters (AAC)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \
+ sizeof(audpp_cmd_cfg_adec_params_aac)
+
+
+#define AUDPP_CMD_AAC_FORMAT_ADTS -1
+#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000
+#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001
+#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002
+
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011
+
+#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001
+#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000
+
+#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001
+#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000
+
+typedef struct {
+ audpp_cmd_cfg_adec_params_common common;
+ signed short format;
+ unsigned short audio_object;
+ unsigned short ep_config;
+ unsigned short aac_section_data_resilience_flag;
+ unsigned short aac_scalefactor_data_resilience_flag;
+ unsigned short aac_spectral_data_resilience_flag;
+ unsigned short sbr_on_flag;
+ unsigned short sbr_ps_on_flag;
+ unsigned short dual_mono_mode;
+ unsigned short channel_configuration;
+} __attribute__((packed)) audpp_cmd_cfg_adec_params_aac;
+
+/*
+ * Command Structure to configure Per decoder Parameters (V13K)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_v13k)
+
+
+#define AUDPP_CMD_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002
+
+struct audpp_cmd_cfg_adec_params_v13k {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed));
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_evrc)
+
+struct audpp_cmd_cfg_adec_params_evrc {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__ ((packed));
+
+/*
+ * Command Structure to configure the HOST PCM interface
+ */
+
+#define AUDPP_CMD_PCM_INTF 0x0001
+#define AUDPP_CMD_PCM_INTF_2 0x0002
+#define AUDPP_CMD_PCM_INTF_LEN sizeof(audpp_cmd_pcm_intf)
+
+#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001
+#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002
+
+/* These two values differentiate the two types of commands that could be issued
+ * Interface configuration command and Buffer update command */
+
+#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000
+#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1
+
+#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F
+#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008
+#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004
+
+/* These flags control the enabling and disabling of the interface together
+ * with host interface bit mask. */
+
+#define AUDPP_CMD_PCM_INTF_ENA_V -1
+#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000
+
+
+#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0
+#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1
+
+
+#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5
+#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6
+
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short object_num;
+ signed short config;
+ unsigned short intf_type;
+
+ /* DSP -> ARM Configuration */
+ unsigned short read_buf1LSW;
+ unsigned short read_buf1MSW;
+ unsigned short read_buf1_len;
+
+ unsigned short read_buf2LSW;
+ unsigned short read_buf2MSW;
+ unsigned short read_buf2_len;
+ /* 0:HOST_PCM_INTF disable
+ ** 0xFFFF: HOST_PCM_INTF enable
+ */
+ signed short dsp_to_arm_flag;
+ unsigned short partition_number;
+
+ /* ARM -> DSP Configuration */
+ unsigned short write_buf1LSW;
+ unsigned short write_buf1MSW;
+ unsigned short write_buf1_len;
+
+ unsigned short write_buf2LSW;
+ unsigned short write_buf2MSW;
+ unsigned short write_buf2_len;
+
+ /* 0:HOST_PCM_INTF disable
+ ** 0xFFFF: HOST_PCM_INTF enable
+ */
+ signed short arm_to_rx_flag;
+ unsigned short weight_decoder_to_rx;
+ unsigned short weight_arm_to_rx;
+
+ unsigned short partition_number_arm_to_dsp;
+ unsigned short sample_rate;
+ unsigned short channel_mode;
+} __attribute__((packed)) audpp_cmd_pcm_intf;
+
+/*
+ ** BUFFER UPDATE COMMAND
+ */
+#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \
+ sizeof(audpp_cmd_pcm_intf_send_buffer)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short host_pcm_object;
+ /* set config = 0xFFFF for configuration*/
+ signed short config;
+ unsigned short intf_type;
+ unsigned short dsp_to_arm_buf_id;
+ unsigned short arm_to_dsp_buf_id;
+ unsigned short arm_to_dsp_buf_len;
+} __attribute__((packed)) audpp_cmd_pcm_intf_send_buffer;
+
+
+/*
+ * Commands Related to uPAudPPCmd3Queue
+ */
+
+/*
+ * Command Structure to configure post processing params (Commmon)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \
+ sizeof(audpp_cmd_cfg_object_params_common)
+
+#define AUDPP_CMD_OBJ0_UPDATE 0x8000
+#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ1_UPDATE 0x8000
+#define AUDPP_CMD_OBJ1_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ2_UPDATE 0x8000
+#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ3_UPDATE 0x8000
+#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ4_UPDATE 0x8000
+#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_HPCM_UPDATE 0x8000
+#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000
+#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short obj0_cfg;
+ unsigned short obj1_cfg;
+ unsigned short obj2_cfg;
+ unsigned short obj3_cfg;
+ unsigned short obj4_cfg;
+ unsigned short host_pcm_obj_cfg;
+ unsigned short comman_cfg;
+ unsigned short command_type;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_common;
+
+/*
+ * Command Structure to configure post processing params (Volume)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \
+ sizeof(audpp_cmd_cfg_object_params_volume)
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ unsigned short volume;
+ unsigned short pan;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_volume;
+
+/*
+ * Command Structure to configure post processing params (PCM Filter) --DOUBT
+ */
+
+typedef struct {
+ unsigned short numerator_b0_filter_lsw;
+ unsigned short numerator_b0_filter_msw;
+ unsigned short numerator_b1_filter_lsw;
+ unsigned short numerator_b1_filter_msw;
+ unsigned short numerator_b2_filter_lsw;
+ unsigned short numerator_b2_filter_msw;
+} __attribute__((packed)) numerator;
+
+typedef struct {
+ unsigned short denominator_a0_filter_lsw;
+ unsigned short denominator_a0_filter_msw;
+ unsigned short denominator_a1_filter_lsw;
+ unsigned short denominator_a1_filter_msw;
+} __attribute__((packed)) denominator;
+
+typedef struct {
+ unsigned short shift_factor_0;
+} __attribute__((packed)) shift_factor;
+
+typedef struct {
+ unsigned short pan_filter_0;
+} __attribute__((packed)) pan;
+
+typedef struct {
+ numerator numerator_filter;
+ denominator denominator_filter;
+ shift_factor shift_factor_filter;
+ pan pan_filter;
+} __attribute__((packed)) filter_1;
+
+typedef struct {
+ numerator numerator_filter[2];
+ denominator denominator_filter[2];
+ shift_factor shift_factor_filter[2];
+ pan pan_filter[2];
+} __attribute__((packed)) filter_2;
+
+typedef struct {
+ numerator numerator_filter[3];
+ denominator denominator_filter[3];
+ shift_factor shift_factor_filter[3];
+ pan pan_filter[3];
+} __attribute__((packed)) filter_3;
+
+typedef struct {
+ numerator numerator_filter[4];
+ denominator denominator_filter[4];
+ shift_factor shift_factor_filter[4];
+ pan pan_filter[4];
+} __attribute__((packed)) filter_4;
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \
+ sizeof(audpp_cmd_cfg_object_params_pcm)
+
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ unsigned short active_flag;
+ unsigned short num_bands;
+ union {
+ filter_1 filter_1_params;
+ filter_2 filter_2_params;
+ filter_3 filter_3_params;
+ filter_4 filter_4_params;
+ } __attribute__((packed)) params_filter;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_pcm;
+
+
+/*
+ * Command Structure to configure post processing parameters (equalizer)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \
+ sizeof(audpp_cmd_cfg_object_params_eqalizer)
+
+typedef struct {
+ unsigned short numerator_coeff_0_lsw;
+ unsigned short numerator_coeff_0_msw;
+ unsigned short numerator_coeff_1_lsw;
+ unsigned short numerator_coeff_1_msw;
+ unsigned short numerator_coeff_2_lsw;
+ unsigned short numerator_coeff_2_msw;
+} __attribute__((packed)) eq_numerator;
+
+typedef struct {
+ unsigned short denominator_coeff_0_lsw;
+ unsigned short denominator_coeff_0_msw;
+ unsigned short denominator_coeff_1_lsw;
+ unsigned short denominator_coeff_1_msw;
+} __attribute__((packed)) eq_denominator;
+
+typedef struct {
+ unsigned short shift_factor;
+} __attribute__((packed)) eq_shiftfactor;
+
+typedef struct {
+ eq_numerator numerator;
+ eq_denominator denominator;
+ eq_shiftfactor shiftfactor;
+} __attribute__((packed)) eq_coeff_1;
+
+typedef struct {
+ eq_numerator numerator[2];
+ eq_denominator denominator[2];
+ eq_shiftfactor shiftfactor[2];
+} __attribute__((packed)) eq_coeff_2;
+
+typedef struct {
+ eq_numerator numerator[3];
+ eq_denominator denominator[3];
+ eq_shiftfactor shiftfactor[3];
+} __attribute__((packed)) eq_coeff_3;
+
+typedef struct {
+ eq_numerator numerator[4];
+ eq_denominator denominator[4];
+ eq_shiftfactor shiftfactor[4];
+} __attribute__((packed)) eq_coeff_4;
+
+typedef struct {
+ eq_numerator numerator[5];
+ eq_denominator denominator[5];
+ eq_shiftfactor shiftfactor[5];
+} __attribute__((packed)) eq_coeff_5;
+
+typedef struct {
+ eq_numerator numerator[6];
+ eq_denominator denominator[6];
+ eq_shiftfactor shiftfactor[6];
+} __attribute__((packed)) eq_coeff_6;
+
+typedef struct {
+ eq_numerator numerator[7];
+ eq_denominator denominator[7];
+ eq_shiftfactor shiftfactor[7];
+} __attribute__((packed)) eq_coeff_7;
+
+typedef struct {
+ eq_numerator numerator[8];
+ eq_denominator denominator[8];
+ eq_shiftfactor shiftfactor[8];
+} __attribute__((packed)) eq_coeff_8;
+
+typedef struct {
+ eq_numerator numerator[9];
+ eq_denominator denominator[9];
+ eq_shiftfactor shiftfactor[9];
+} __attribute__((packed)) eq_coeff_9;
+
+typedef struct {
+ eq_numerator numerator[10];
+ eq_denominator denominator[10];
+ eq_shiftfactor shiftfactor[10];
+} __attribute__((packed)) eq_coeff_10;
+
+typedef struct {
+ eq_numerator numerator[11];
+ eq_denominator denominator[11];
+ eq_shiftfactor shiftfactor[11];
+} __attribute__((packed)) eq_coeff_11;
+
+typedef struct {
+ eq_numerator numerator[12];
+ eq_denominator denominator[12];
+ eq_shiftfactor shiftfactor[12];
+} __attribute__((packed)) eq_coeff_12;
+
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ unsigned short eq_flag;
+ unsigned short num_bands;
+ union {
+ eq_coeff_1 eq_coeffs_1;
+ eq_coeff_2 eq_coeffs_2;
+ eq_coeff_3 eq_coeffs_3;
+ eq_coeff_4 eq_coeffs_4;
+ eq_coeff_5 eq_coeffs_5;
+ eq_coeff_6 eq_coeffs_6;
+ eq_coeff_7 eq_coeffs_7;
+ eq_coeff_8 eq_coeffs_8;
+ eq_coeff_9 eq_coeffs_9;
+ eq_coeff_10 eq_coeffs_10;
+ eq_coeff_11 eq_coeffs_11;
+ eq_coeff_12 eq_coeffs_12;
+ } __attribute__((packed)) eq_coeff;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_eqalizer;
+
+
+/*
+ * Command Structure to configure post processing parameters (ADRC)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \
+ sizeof(audpp_cmd_cfg_object_params_adrc)
+
+
+#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000
+#define AUDPP_CMD_ADRC_FLAG_ENA -1
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ signed short adrc_flag;
+ unsigned short compression_th;
+ unsigned short compression_slope;
+ unsigned short rms_time;
+ unsigned short attack_const_lsw;
+ unsigned short attack_const_msw;
+ unsigned short release_const_lsw;
+ unsigned short release_const_msw;
+ unsigned short adrc_system_delay;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_adrc;
+
+/*
+ * Command Structure to configure post processing parameters(Spectrum Analizer)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \
+ sizeof(audpp_cmd_cfg_object_params_spectram)
+
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ unsigned short sample_interval;
+ unsigned short num_coeff;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_spectram;
+
+/*
+ * Command Structure to configure post processing parameters (QConcert)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \
+ sizeof(audpp_cmd_cfg_object_params_qconcert)
+
+
+#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1
+#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000
+
+#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002
+
+#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF
+#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027
+
+
+#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF
+
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ signed short enable_flag;
+ signed short output_mode;
+ signed short gain;
+ signed short expansion;
+ signed short delay;
+ unsigned short stages_per_mode;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_qconcert;
+
+/*
+ * Command Structure to configure post processing parameters (Side Chain)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \
+ sizeof(audpp_cmd_cfg_object_params_sidechain)
+
+
+#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000
+#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ signed short active_flag;
+ unsigned short num_bands;
+ union {
+ filter_1 filter_1_params;
+ filter_2 filter_2_params;
+ filter_3 filter_3_params;
+ filter_4 filter_4_params;
+ } __attribute__((packed)) params_filter;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_sidechain;
+
+
+/*
+ * Command Structure to configure post processing parameters (QAFX)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \
+ sizeof(audpp_cmd_cfg_object_params_qafx)
+
+#define AUDPP_CMD_QAFX_ENA_DISA 0x0000
+#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1
+#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001
+
+#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100
+#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010
+#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000
+
+#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103
+#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107
+
+#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010
+#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011
+#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012
+#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013
+#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014
+
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ signed short enable;
+ unsigned short command_type;
+ unsigned short num_commands;
+ unsigned short commands;
+} __attribute__((packed)) audpp_cmd_cfg_object_params_qafx;
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (Common)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG 0x0001
+#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \
+ sizeof(audpp_cmd_reverb_config_common)
+
+#define AUDPP_CMD_ENA_ENA 0xFFFF
+#define AUDPP_CMD_ENA_DIS 0x0000
+#define AUDPP_CMD_ENA_CFG 0x0001
+
+#define AUDPP_CMD_CMD_TYPE_ENV 0x0104
+#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015
+#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000
+
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short enable;
+ unsigned short cmd_type;
+} __attribute__((packed)) audpp_cmd_reverb_config_common;
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (ENV-0x0104)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \
+ sizeof(audpp_cmd_reverb_config_env_104)
+
+typedef struct {
+ audpp_cmd_reverb_config_common common;
+ unsigned short env_gain;
+ unsigned short decay_msw;
+ unsigned short decay_lsw;
+ unsigned short decay_timeratio_msw;
+ unsigned short decay_timeratio_lsw;
+ unsigned short delay_time;
+ unsigned short reverb_gain;
+ unsigned short reverb_delay;
+} __attribute__((packed)) audpp_cmd_reverb_config_env_104;
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (ENV-0x0015)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \
+ sizeof(audpp_cmd_reverb_config_env_15)
+
+typedef struct {
+ audpp_cmd_reverb_config_common common;
+ unsigned short object_num;
+ unsigned short absolute_gain;
+} __attribute__((packed)) audpp_cmd_reverb_config_env_15;
+
+
+#endif /* QDSP5AUDPPCMDI_H */
+
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h
new file mode 100644
index 0000000..e229df3
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h
@@ -0,0 +1,318 @@
+#ifndef QDSP5AUDPPMSG_H
+#define QDSP5AUDPPMSG_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D S P 5 A U D I O P O S T P R O C E S S I N G M S G
+
+GENERAL DESCRIPTION
+ Messages sent by AUDPPTASK to ARM
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+ $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppmsg.h#4 $
+
+===========================================================================*/
+
+/*
+ * AUDPPTASK uses audPPuPRlist to send messages to the ARM
+ * Location : MEMA
+ * Buffer Size : 45
+ * No of Buffers in a queue : 5 for gaming audio and 1 for other images
+ */
+
+/*
+ * MSG to Informs the ARM os Success/Failure of bringing up the decoder
+ */
+
+#define AUDPP_MSG_STATUS_MSG 0x0001
+#define AUDPP_MSG_STATUS_MSG_LEN \
+ sizeof(audpp_msg_status_msg)
+
+#define AUDPP_MSG_STATUS_SLEEP 0x0000
+#define AUDPP_MSG__STATUS_INIT 0x0001
+#define AUDPP_MSG_MSG_STATUS_CFG 0x0002
+#define AUDPP_MSG_STATUS_PLAY 0x0003
+
+#define AUDPP_MSG_REASON_MIPS 0x0000
+#define AUDPP_MSG_REASON_MEM 0x0001
+
+typedef struct{
+ unsigned short dec_id;
+ unsigned short status;
+ unsigned short reason;
+} __attribute__((packed)) audpp_msg_status_msg;
+
+/*
+ * MSG to communicate the spectrum analyzer output bands to the ARM
+ */
+#define AUDPP_MSG_SPA_BANDS 0x0002
+#define AUDPP_MSG_SPA_BANDS_LEN \
+ sizeof(audpp_msg_spa_bands)
+
+typedef struct {
+ unsigned short current_object;
+ unsigned short spa_band_1;
+ unsigned short spa_band_2;
+ unsigned short spa_band_3;
+ unsigned short spa_band_4;
+ unsigned short spa_band_5;
+ unsigned short spa_band_6;
+ unsigned short spa_band_7;
+ unsigned short spa_band_8;
+ unsigned short spa_band_9;
+ unsigned short spa_band_10;
+ unsigned short spa_band_11;
+ unsigned short spa_band_12;
+ unsigned short spa_band_13;
+ unsigned short spa_band_14;
+ unsigned short spa_band_15;
+ unsigned short spa_band_16;
+ unsigned short spa_band_17;
+ unsigned short spa_band_18;
+ unsigned short spa_band_19;
+ unsigned short spa_band_20;
+ unsigned short spa_band_21;
+ unsigned short spa_band_22;
+ unsigned short spa_band_23;
+ unsigned short spa_band_24;
+ unsigned short spa_band_25;
+ unsigned short spa_band_26;
+ unsigned short spa_band_27;
+ unsigned short spa_band_28;
+ unsigned short spa_band_29;
+ unsigned short spa_band_30;
+ unsigned short spa_band_31;
+ unsigned short spa_band_32;
+} __attribute__((packed)) audpp_msg_spa_bands;
+
+/*
+ * MSG to communicate the PCM I/O buffer status to ARM
+ */
+#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003
+#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \
+ sizeof(audpp_msg_host_pcm_intf_msg)
+
+#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000
+#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001
+#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002
+#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003
+
+#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000
+#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001
+#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002
+#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003
+#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004
+#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005
+#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006
+#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007
+#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008
+#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009
+#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A
+#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B
+
+#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001
+#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002
+
+typedef struct{
+ unsigned short obj_num;
+ unsigned short numbers_of_samples;
+ unsigned short host_pcm_id;
+ unsigned short buf_indx;
+ unsigned short samp_freq_indx;
+ unsigned short channel_mode;
+} __attribute__((packed)) audpp_msg_host_pcm_intf_msg;
+
+
+/*
+ * MSG to communicate 3D position of the source and listener , source volume
+ * source rolloff, source orientation
+ */
+
+#define AUDPP_MSG_QAFX_POS 0x0004
+#define AUDPP_MSG_QAFX_POS_LEN \
+ sizeof(audpp_msg_qafx_pos)
+
+typedef struct {
+ unsigned short current_object;
+ unsigned short x_pos_lis_msw;
+ unsigned short x_pos_lis_lsw;
+ unsigned short y_pos_lis_msw;
+ unsigned short y_pos_lis_lsw;
+ unsigned short z_pos_lis_msw;
+ unsigned short z_pos_lis_lsw;
+ unsigned short x_fwd_msw;
+ unsigned short x_fwd_lsw;
+ unsigned short y_fwd_msw;
+ unsigned short y_fwd_lsw;
+ unsigned short z_fwd_msw;
+ unsigned short z_fwd_lsw;
+ unsigned short x_up_msw;
+ unsigned short x_up_lsw;
+ unsigned short y_up_msw;
+ unsigned short y_up_lsw;
+ unsigned short z_up_msw;
+ unsigned short z_up_lsw;
+ unsigned short x_vel_lis_msw;
+ unsigned short x_vel_lis_lsw;
+ unsigned short y_vel_lis_msw;
+ unsigned short y_vel_lis_lsw;
+ unsigned short z_vel_lis_msw;
+ unsigned short z_vel_lis_lsw;
+ unsigned short threed_enable_flag;
+ unsigned short volume;
+ unsigned short x_pos_source_msw;
+ unsigned short x_pos_source_lsw;
+ unsigned short y_pos_source_msw;
+ unsigned short y_pos_source_lsw;
+ unsigned short z_pos_source_msw;
+ unsigned short z_pos_source_lsw;
+ unsigned short max_dist_0_msw;
+ unsigned short max_dist_0_lsw;
+ unsigned short min_dist_0_msw;
+ unsigned short min_dist_0_lsw;
+ unsigned short roll_off_factor;
+ unsigned short mute_after_max_flag;
+ unsigned short x_vel_source_msw;
+ unsigned short x_vel_source_lsw;
+ unsigned short y_vel_source_msw;
+ unsigned short y_vel_source_lsw;
+ unsigned short z_vel_source_msw;
+ unsigned short z_vel_source_lsw;
+} __attribute__((packed)) audpp_msg_qafx_pos;
+
+/*
+ * MSG to provide AVSYNC feedback from DSP to ARM
+ */
+
+#define AUDPP_MSG_AVSYNC_MSG 0x0005
+#define AUDPP_MSG_AVSYNC_MSG_LEN \
+ sizeof(audpp_msg_avsync_msg)
+
+typedef struct {
+ unsigned short active_flag;
+ unsigned short num_samples_counter0_HSW;
+ unsigned short num_samples_counter0_MSW;
+ unsigned short num_samples_counter0_LSW;
+ unsigned short num_bytes_counter0_HSW;
+ unsigned short num_bytes_counter0_MSW;
+ unsigned short num_bytes_counter0_LSW;
+ unsigned short samp_freq_obj_0;
+ unsigned short samp_freq_obj_1;
+ unsigned short samp_freq_obj_2;
+ unsigned short samp_freq_obj_3;
+ unsigned short samp_freq_obj_4;
+ unsigned short samp_freq_obj_5;
+ unsigned short samp_freq_obj_6;
+ unsigned short samp_freq_obj_7;
+ unsigned short samp_freq_obj_8;
+ unsigned short samp_freq_obj_9;
+ unsigned short samp_freq_obj_10;
+ unsigned short samp_freq_obj_11;
+ unsigned short samp_freq_obj_12;
+ unsigned short samp_freq_obj_13;
+ unsigned short samp_freq_obj_14;
+ unsigned short samp_freq_obj_15;
+ unsigned short num_samples_counter4_HSW;
+ unsigned short num_samples_counter4_MSW;
+ unsigned short num_samples_counter4_LSW;
+ unsigned short num_bytes_counter4_HSW;
+ unsigned short num_bytes_counter4_MSW;
+ unsigned short num_bytes_counter4_LSW;
+} __attribute__((packed)) audpp_msg_avsync_msg;
+
+/*
+ * MSG to provide PCM DMA Missed feedback from the DSP to ARM
+ */
+
+#define AUDPP_MSG_PCMDMAMISSED 0x0006
+#define AUDPP_MSG_PCMDMAMISSED_LEN \
+ sizeof(audpp_msg_pcmdmamissed);
+
+typedef struct{
+ /*
+ ** Bit 0 0 = PCM DMA not missed for object 0
+ ** 1 = PCM DMA missed for object0
+ ** Bit 1 0 = PCM DMA not missed for object 1
+ ** 1 = PCM DMA missed for object1
+ ** Bit 2 0 = PCM DMA not missed for object 2
+ ** 1 = PCM DMA missed for object2
+ ** Bit 3 0 = PCM DMA not missed for object 3
+ ** 1 = PCM DMA missed for object3
+ ** Bit 4 0 = PCM DMA not missed for object 4
+ ** 1 = PCM DMA missed for object4
+ */
+ unsigned short pcmdmamissed;
+} __attribute__((packed)) audpp_msg_pcmdmamissed;
+
+/*
+ * MSG to AUDPP enable or disable feedback form DSP to ARM
+ */
+
+#define AUDPP_MSG_CFG_MSG 0x0007
+#define AUDPP_MSG_CFG_MSG_LEN \
+ sizeof(audpp_msg_cfg_msg)
+
+#define AUDPP_MSG_ENA_ENA 0xFFFF
+#define AUDPP_MSG_ENA_DIS 0x0000
+
+typedef struct{
+ /* Enabled - 0xffff
+ ** Disabled - 0
+ */
+ unsigned short enabled;
+} __attribute__((packed)) audpp_msg_cfg_msg;
+
+/*
+ * MSG to communicate the reverb per object volume
+ */
+
+#define AUDPP_MSG_QREVERB_VOLUME 0x0008
+#define AUDPP_MSG_QREVERB_VOLUME_LEN \
+ sizeof(audpp_msg_qreverb_volume)
+
+
+typedef struct {
+ unsigned short obj_0_gain;
+ unsigned short obj_1_gain;
+ unsigned short obj_2_gain;
+ unsigned short obj_3_gain;
+ unsigned short obj_4_gain;
+ unsigned short hpcm_obj_volume;
+} __attribute__((packed)) audpp_msg_qreverb_volume;
+
+#define AUDPP_MSG_ROUTING_ACK 0x0009
+#define AUDPP_MSG_ROUTING_ACK_LEN \
+ sizeof(struct audpp_msg_routing_ack)
+
+struct audpp_msg_routing_ack {
+ unsigned short dec_id;
+ unsigned short routing_mode;
+} __attribute__((packed));
+
+#define AUDPP_MSG_FLUSH_ACK 0x000A
+
+#endif /* QDSP5AUDPPMSG_H */
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h
new file mode 100644
index 0000000..cd9d5906
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h
@@ -0,0 +1,256 @@
+#ifndef QDSP5AUDPREPROCCMDI_H
+#define QDSP5AUDPREPROCCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A U D I O P R E P R O C E S S I N G I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by AUDPREPROC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreproccmdi.h#2 $
+
+===========================================================================*/
+
+/*
+ * AUDIOPREPROC COMMANDS:
+ * ARM uses uPAudPreProcCmdQueue to communicate with AUDPREPROCTASK
+ * Location : MEMB
+ * Buffer size : 51
+ * Number of buffers in a queue : 3
+ */
+
+/*
+ * Command to configure the parameters of AGC
+ */
+
+#define AUDPREPROC_CMD_CFG_AGC_PARAMS 0x0000
+#define AUDPREPROC_CMD_CFG_AGC_PARAMS_LEN \
+ sizeof(audpreproc_cmd_cfg_agc_params)
+
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE 0x0009
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH 0x000A
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE 0x000B
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH 0x000C
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG 0x000D
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN 0x000E
+#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG 0x000F
+
+#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA -1
+#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS 0x0000
+
+#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_ADP_GAIN -1
+#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_STATIC_GAIN 0x0000
+
+#define AUDPREPROC_CMD_PARAM_MASK_RMS_TAY 0x0004
+#define AUDPREPROC_CMD_PARAM_MASK_RELEASEK 0x0005
+#define AUDPREPROC_CMD_PARAM_MASK_DELAY 0x0006
+#define AUDPREPROC_CMD_PARAM_MASK_ATTACKK 0x0007
+#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW 0x0008
+#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST 0x0009
+#define AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK 0x000A
+#define AUDPREPROC_CMD_PARAM_MASK_AIG_MIN 0x000B
+#define AUDPREPROC_CMD_PARAM_MASK_AIG_MAX 0x000C
+#define AUDPREPROC_CMD_PARAM_MASK_LEAK_UP 0x000D
+#define AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN 0x000E
+#define AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK 0x000F
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short tx_agc_param_mask;
+ unsigned short tx_agc_enable_flag;
+ unsigned short static_gain;
+ signed short adaptive_gain_flag;
+ unsigned short expander_th;
+ unsigned short expander_slope;
+ unsigned short compressor_th;
+ unsigned short compressor_slope;
+ unsigned short param_mask;
+ unsigned short aig_attackk;
+ unsigned short aig_leak_down;
+ unsigned short aig_leak_up;
+ unsigned short aig_max;
+ unsigned short aig_min;
+ unsigned short aig_releasek;
+ unsigned short aig_leakrate_fast;
+ unsigned short aig_leakrate_slow;
+ unsigned short attackk_msw;
+ unsigned short attackk_lsw;
+ unsigned short delay;
+ unsigned short releasek_msw;
+ unsigned short releasek_lsw;
+ unsigned short rms_tav;
+} __attribute__((packed)) audpreproc_cmd_cfg_agc_params;
+
+
+/*
+ * Command to configure the params of Advanved AGC
+ */
+
+#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2 0x0001
+#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2_LEN \
+ sizeof(audpreproc_cmd_cfg_agc_params_2)
+
+#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_ENA -1;
+#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_DIS 0x0000;
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short agc_param_mask;
+ signed short tx_agc_enable_flag;
+ unsigned short comp_static_gain;
+ unsigned short exp_th;
+ unsigned short exp_slope;
+ unsigned short comp_th;
+ unsigned short comp_slope;
+ unsigned short comp_rms_tav;
+ unsigned short comp_samp_mask;
+ unsigned short comp_attackk_msw;
+ unsigned short comp_attackk_lsw;
+ unsigned short comp_releasek_msw;
+ unsigned short comp_releasek_lsw;
+ unsigned short comp_delay;
+ unsigned short comp_makeup_gain;
+} __attribute__((packed)) audpreproc_cmd_cfg_agc_params_2;
+
+/*
+ * Command to configure params for ns
+ */
+
+#define AUDPREPROC_CMD_CFG_NS_PARAMS 0x0002
+#define AUDPREPROC_CMD_CFG_NS_PARAMS_LEN \
+ sizeof(audpreproc_cmd_cfg_ns_params)
+
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_ENA 0x0001
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_DES_ENA 0x0002
+#define AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA 0x0004
+#define AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_ENA 0x0008
+#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS 0x0000
+
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_ENA 0x0010
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA 0x0020
+#define AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA 0x0040
+#define AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_ENA 0x0080
+#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_ENA 0x0100
+#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_ENA 0x0200
+#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_ENA 0x0400
+#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_ENA 0x0800
+#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS 0x0000
+#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_ENA 0x1000
+#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS 0x0000
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short ec_mode_new;
+ unsigned short dens_gamma_n;
+ unsigned short dens_nfe_block_size;
+ unsigned short dens_limit_ns;
+ unsigned short dens_limit_ns_d;
+ unsigned short wb_gamma_e;
+ unsigned short wb_gamma_n;
+} __attribute__((packed)) audpreproc_cmd_cfg_ns_params;
+
+/*
+ * Command to configure parameters for IIR tuning filter
+ */
+
+#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS 0x0003
+#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS_LEN \
+ sizeof(audpreproc_cmd_cfg_iir_tuning_filter_params)
+
+#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS 0x0000
+#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA 0x0001
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short active_flag;
+ unsigned short num_bands;
+ unsigned short numerator_coeff_b0_filter0_lsw;
+ unsigned short numerator_coeff_b0_filter0_msw;
+ unsigned short numerator_coeff_b1_filter0_lsw;
+ unsigned short numerator_coeff_b1_filter0_msw;
+ unsigned short numerator_coeff_b2_filter0_lsw;
+ unsigned short numerator_coeff_b2_filter0_msw;
+ unsigned short numerator_coeff_b0_filter1_lsw;
+ unsigned short numerator_coeff_b0_filter1_msw;
+ unsigned short numerator_coeff_b1_filter1_lsw;
+ unsigned short numerator_coeff_b1_filter1_msw;
+ unsigned short numerator_coeff_b2_filter1_lsw;
+ unsigned short numerator_coeff_b2_filter1_msw;
+ unsigned short numerator_coeff_b0_filter2_lsw;
+ unsigned short numerator_coeff_b0_filter2_msw;
+ unsigned short numerator_coeff_b1_filter2_lsw;
+ unsigned short numerator_coeff_b1_filter2_msw;
+ unsigned short numerator_coeff_b2_filter2_lsw;
+ unsigned short numerator_coeff_b2_filter2_msw;
+ unsigned short numerator_coeff_b0_filter3_lsw;
+ unsigned short numerator_coeff_b0_filter3_msw;
+ unsigned short numerator_coeff_b1_filter3_lsw;
+ unsigned short numerator_coeff_b1_filter3_msw;
+ unsigned short numerator_coeff_b2_filter3_lsw;
+ unsigned short numerator_coeff_b2_filter3_msw;
+ unsigned short denominator_coeff_a0_filter0_lsw;
+ unsigned short denominator_coeff_a0_filter0_msw;
+ unsigned short denominator_coeff_a1_filter0_lsw;
+ unsigned short denominator_coeff_a1_filter0_msw;
+ unsigned short denominator_coeff_a0_filter1_lsw;
+ unsigned short denominator_coeff_a0_filter1_msw;
+ unsigned short denominator_coeff_a1_filter1_lsw;
+ unsigned short denominator_coeff_a1_filter1_msw;
+ unsigned short denominator_coeff_a0_filter2_lsw;
+ unsigned short denominator_coeff_a0_filter2_msw;
+ unsigned short denominator_coeff_a1_filter2_lsw;
+ unsigned short denominator_coeff_a1_filter2_msw;
+ unsigned short denominator_coeff_a0_filter3_lsw;
+ unsigned short denominator_coeff_a0_filter3_msw;
+ unsigned short denominator_coeff_a1_filter3_lsw;
+ unsigned short denominator_coeff_a1_filter3_msw;
+
+ unsigned short shift_factor_filter0;
+ unsigned short shift_factor_filter1;
+ unsigned short shift_factor_filter2;
+ unsigned short shift_factor_filter3;
+
+ unsigned short channel_selected0;
+ unsigned short channel_selected1;
+ unsigned short channel_selected2;
+ unsigned short channel_selected3;
+} __attribute__((packed))audpreproc_cmd_cfg_iir_tuning_filter_params;
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h
new file mode 100644
index 0000000..9187f45
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h
@@ -0,0 +1,85 @@
+#ifndef QDSP5AUDPREPROCMSG_H
+#define QDSP5AUDPREPROCMSG_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A U D I O P R E P R O C E S S I N G M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of messages
+ that are rcvd by AUDPREPROC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+ $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreprocmsg.h#3 $
+
+===========================================================================*/
+
+/*
+ * ADSPREPROCTASK Messages
+ * AUDPREPROCTASK uses audPreProcUpRlist to communicate with ARM
+ * Location : MEMA
+ * Message Length : 2
+ */
+
+/*
+ * Message to indicate particular feature has been enabled or disabled
+ */
+
+
+#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG 0x0000
+#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG_LEN \
+ sizeof(audpreproc_msg_cmd_cfg_done_msg)
+
+#define AUDPREPROC_MSG_TYPE_AGC 0x0000
+#define AUDPREPROC_MSG_TYPE_NOISE_REDUCTION 0x0001
+#define AUDPREPROC_MSG_TYPE_IIR_FILTER 0x0002
+
+
+#define AUDPREPROC_MSG_STATUS_FLAG_ENA -1
+#define AUDPREPROC_MSG_STATUS_FLAG_DIS 0x0000
+
+typedef struct {
+ unsigned short type;
+ signed short status_flag;
+} __attribute__((packed)) audpreproc_msg_cmd_cfg_done_msg;
+
+
+/*
+ * Message to indicate particular feature has selected for wrong samp freq
+ */
+
+#define AUDPREPROC_MSG_ERROR_MSG_ID 0x0001
+#define AUDPREPROC_MSG_ERROR_MSG_ID_LEN \
+ sizeof(audpreproc_msg_error_msg_id)
+
+#define AUDPREPROC_MSG_ERR_INDEX_NS 0x0000
+
+typedef struct {
+ unsigned short err_index;
+} __attribute__((packed)) audpreproc_msg_error_msg_id;
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h
new file mode 100644
index 0000000..e88bd5d
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h
@@ -0,0 +1,176 @@
+#ifndef QDSP5AUDRECCMDI_H
+#define QDSP5AUDRECCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A U D I O R E C O R D I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by AUDREC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+ $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audreccmdi.h#3 $
+
+============================================================================*/
+
+/*
+ * AUDRECTASK COMMANDS
+ * ARM uses 2 queues to communicate with the AUDRECTASK
+ * 1.uPAudRecCmdQueue
+ * Location :MEMC
+ * Buffer Size : 8
+ * No of Buffers in a queue : 3
+ * 2.audRecUpBitStreamQueue
+ * Location : MEMC
+ * Buffer Size : 4
+ * No of buffers in a queue : 2
+ */
+
+/*
+ * Commands on uPAudRecCmdQueue
+ */
+
+/*
+ * Command to initiate and terminate the audio recording section
+ */
+
+#define AUDREC_CMD_CFG 0x0000
+#define AUDREC_CMD_CFG_LEN sizeof(audrec_cmd_cfg)
+
+#define AUDREC_CMD_TYPE_0_INDEX_WAV 0x0000
+#define AUDREC_CMD_TYPE_0_INDEX_AAC 0x0001
+
+#define AUDREC_CMD_TYPE_0_ENA 0x4000
+#define AUDREC_CMD_TYPE_0_DIS 0x0000
+
+#define AUDREC_CMD_TYPE_0_NOUPDATE 0x0000
+#define AUDREC_CMD_TYPE_0_UPDATE 0x8000
+
+#define AUDREC_CMD_TYPE_1_INDEX_SBC 0x0002
+
+#define AUDREC_CMD_TYPE_1_ENA 0x4000
+#define AUDREC_CMD_TYPE_1_DIS 0x0000
+
+#define AUDREC_CMD_TYPE_1_NOUPDATE 0x0000
+#define AUDREC_CMD_TYPE_1_UPDATE 0x8000
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short type_0;
+ unsigned short type_1;
+} __attribute__((packed)) audrec_cmd_cfg;
+
+
+/*
+ * Command to configure the recording parameters for RecType0(AAC/WAV) encoder
+ */
+
+#define AUDREC_CMD_AREC0PARAM_CFG 0x0001
+#define AUDREC_CMD_AREC0PARAM_CFG_LEN \
+ sizeof(audrec_cmd_arec0param_cfg)
+
+#define AUDREC_CMD_SAMP_RATE_INDX_8000 0x000B
+#define AUDREC_CMD_SAMP_RATE_INDX_11025 0x000A
+#define AUDREC_CMD_SAMP_RATE_INDX_12000 0x0009
+#define AUDREC_CMD_SAMP_RATE_INDX_16000 0x0008
+#define AUDREC_CMD_SAMP_RATE_INDX_22050 0x0007
+#define AUDREC_CMD_SAMP_RATE_INDX_24000 0x0006
+#define AUDREC_CMD_SAMP_RATE_INDX_32000 0x0005
+#define AUDREC_CMD_SAMP_RATE_INDX_44100 0x0004
+#define AUDREC_CMD_SAMP_RATE_INDX_48000 0x0003
+
+#define AUDREC_CMD_STEREO_MODE_MONO 0x0000
+#define AUDREC_CMD_STEREO_MODE_STEREO 0x0001
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short ptr_to_extpkt_buffer_msw;
+ unsigned short ptr_to_extpkt_buffer_lsw;
+ unsigned short buf_len;
+ unsigned short samp_rate_index;
+ unsigned short stereo_mode;
+ unsigned short rec_quality;
+} __attribute__((packed)) audrec_cmd_arec0param_cfg;
+
+/*
+ * Command to configure the recording parameters for RecType1(SBC) encoder
+ */
+
+#define AUDREC_CMD_AREC1PARAM_CFG 0x0002
+#define AUDREC_CMD_AREC1PARAM_CFG_LEN \
+ sizeof(audrec_cmd_arec1param_cfg)
+
+#define AUDREC_CMD_PARAM_BUF_BLOCKS_4 0x0000
+#define AUDREC_CMD_PARAM_BUF_BLOCKS_8 0x0001
+#define AUDREC_CMD_PARAM_BUF_BLOCKS_12 0x0002
+#define AUDREC_CMD_PARAM_BUF_BLOCKS_16 0x0003
+
+#define AUDREC_CMD_PARAM_BUF_SUB_BANDS_8 0x0010
+#define AUDREC_CMD_PARAM_BUF_MODE_MONO 0x0000
+#define AUDREC_CMD_PARAM_BUF_MODE_DUAL 0x0040
+#define AUDREC_CMD_PARAM_BUF_MODE_STEREO 0x0050
+#define AUDREC_CMD_PARAM_BUF_MODE_JSTEREO 0x0060
+#define AUDREC_CMD_PARAM_BUF_LOUDNESS 0x0000
+#define AUDREC_CMD_PARAM_BUF_SNR 0x0100
+#define AUDREC_CMD_PARAM_BUF_BASIC_VER 0x0000
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short ptr_to_extpkt_buffer_msw;
+ unsigned short ptr_to_extpkt_buffer_lsw;
+ unsigned short buf_len;
+ unsigned short param_buf;
+ unsigned short bit_rate_0;
+ unsigned short bit_rate_1;
+} __attribute__((packed)) audrec_cmd_arec1param_cfg;
+
+
+/*
+ * Commands on audRecUpBitStreamQueue
+ */
+
+/*
+ * Command to indicate the current packet read count
+ */
+
+#define AUDREC_CMD_PACKET_EXT_PTR 0x0000
+#define AUDREC_CMD_PACKET_EXT_PTR_LEN \
+ sizeof(audrec_cmd_packet_ext_ptr)
+
+#define AUDREC_CMD_TYPE_0 0x0000
+#define AUDREC_CMD_TYPE_1 0x0001
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short type;
+ unsigned short curr_rec_count_msw;
+ unsigned short curr_rec_count_lsw;
+} __attribute__((packed)) audrec_cmd_packet_ext_ptr;
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h
new file mode 100644
index 0000000..bb6eb50
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h
@@ -0,0 +1,127 @@
+#ifndef QDSP5AUDRECMSGI_H
+#define QDSP5AUDRECMSGI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A U D I O R E C O R D M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of messages
+ that are sent by AUDREC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+ $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audrecmsg.h#3 $
+
+============================================================================*/
+
+/*
+ * AUDRECTASK MESSAGES
+ * AUDRECTASK uses audRecUpRlist to communicate with ARM
+ * Location : MEMC
+ * Buffer size : 4
+ * No of buffers in a queue : 2
+ */
+
+/*
+ * Message to notify that config command is done
+ */
+
+#define AUDREC_MSG_CMD_CFG_DONE_MSG 0x0002
+#define AUDREC_MSG_CMD_CFG_DONE_MSG_LEN \
+ sizeof(audrec_msg_cmd_cfg_done_msg)
+
+
+#define AUDREC_MSG_CFG_DONE_TYPE_0_ENA 0x4000
+#define AUDREC_MSG_CFG_DONE_TYPE_0_DIS 0x0000
+
+#define AUDREC_MSG_CFG_DONE_TYPE_0_NO_UPDATE 0x0000
+#define AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE 0x8000
+
+#define AUDREC_MSG_CFG_DONE_TYPE_1_ENA 0x4000
+#define AUDREC_MSG_CFG_DONE_TYPE_1_DIS 0x0000
+
+#define AUDREC_MSG_CFG_DONE_TYPE_1_NO_UPDATE 0x0000
+#define AUDREC_MSG_CFG_DONE_TYPE_1_UPDATE 0x8000
+
+typedef struct {
+ unsigned short type_0;
+ unsigned short type_1;
+} __attribute__((packed))audrec_msg_cmd_cfg_done_msg;
+
+
+/*
+ * Message to notify arec0/1 cfg done and recording params revd by task
+ */
+
+#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG 0x0003
+#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG_LEN \
+ sizeof(audrec_msg_cmd_arec_param_cfg_done_msg)
+
+#define AUDREC_MSG_AREC_PARAM_TYPE_0 0x0000
+#define AUDREC_MSG_AREC_PARAM_TYPE_1 0x0001
+
+typedef struct {
+ unsigned short type;
+} __attribute__((packed))audrec_msg_cmd_arec_param_cfg_done_msg;
+
+
+/*
+ * Message to notify no more buffers are available in ext mem to DME
+ */
+
+#define AUDREC_MSG_FATAL_ERR_MSG 0x0004
+#define AUDREC_MSG_FATAL_ERR_MSG_LEN \
+ sizeof(audrec_msg_fatal_err_msg)
+
+#define AUDREC_MSG_FATAL_ERR_TYPE_0 0x0000
+#define AUDREC_MSG_FATAL_ERR_TYPE_1 0x0001
+
+typedef struct {
+ unsigned short type;
+} __attribute__((packed))audrec_msg_fatal_err_msg;
+
+/*
+ * Message to notify DME deliverd the encoded pkt to ext pkt buffer
+ */
+
+#define AUDREC_MSG_PACKET_READY_MSG 0x0005
+#define AUDREC_MSG_PACKET_READY_MSG_LEN \
+ sizeof(audrec_msg_packet_ready_msg)
+
+#define AUDREC_MSG_PACKET_READY_TYPE_0 0x0000
+#define AUDREC_MSG_PACKET_READY_TYPE_1 0x0001
+
+typedef struct {
+ unsigned short type;
+ unsigned short pkt_counter_msw;
+ unsigned short pkt_counter_lsw;
+ unsigned short pkt_read_cnt_msw;
+ unsigned short pkt_read_cnt_lsw;
+} __attribute__((packed))audrec_msg_packet_ready_msg;
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h
new file mode 100644
index 0000000..d8170f0
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h
@@ -0,0 +1,376 @@
+#ifndef QDSP5VIDJPEGCMDI_H
+#define QDSP5VIDJPEGCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ J P E G I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by JPEG Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+when who what, where, why
+-------- --- ----------------------------------------------------------
+06/09/08 sv initial version
+===========================================================================*/
+
+/*
+ * ARM to JPEG configuration commands are passed through the
+ * uPJpegCfgCmdQueue
+ */
+
+/*
+ * Command to configure JPEG Encoder
+ */
+
+#define JPEG_CMD_ENC_CFG 0x0000
+#define JPEG_CMD_ENC_CFG_LEN sizeof(jpeg_cmd_enc_cfg)
+
+#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_0 0x0000
+#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_90 0x0100
+#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_180 0x0200
+#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_270 0x0300
+#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M 0x0003
+#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2 0x0000
+#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V1 0x0001
+#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H1V2 0x0002
+
+#define JPEG_CMD_IP_SIZE_CFG_LUMA_HEIGHT_M 0x0000FFFF
+#define JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M 0xFFFF0000
+#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_ENA 0x0001
+#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_DIS 0x0000
+
+#define JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M 0xFFFF
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int process_cfg;
+ unsigned int ip_size_cfg;
+ unsigned int op_size_cfg;
+ unsigned int frag_cfg;
+ unsigned int frag_cfg_part[16];
+
+ unsigned int part_num;
+
+ unsigned int op_buf_0_cfg_part1;
+ unsigned int op_buf_0_cfg_part2;
+ unsigned int op_buf_1_cfg_part1;
+ unsigned int op_buf_1_cfg_part2;
+
+ unsigned int luma_qunt_table[32];
+ unsigned int chroma_qunt_table[32];
+
+ unsigned int upsamp_ip_size_cfg;
+ unsigned int upsamp_ip_frame_off;
+ unsigned int upsamp_pp_filter_coeff[64];
+} __attribute__((packed)) jpeg_cmd_enc_cfg;
+
+/*
+ * Command to configure JPEG Decoder
+ */
+
+#define JPEG_CMD_DEC_CFG 0x0001
+#define JPEG_CMD_DEC_CFG_LEN sizeof(jpeg_cmd_dec_cfg)
+
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_M 0x0001
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2 0x0000
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V1 0x0001
+
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_8 0x000000
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_4 0x010000
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_2 0x020000
+#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_1 0x030000
+
+#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_NOT_FINAL 0x0000
+#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_FINAL 0x0001
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int img_dimension_cfg;
+ unsigned int op_data_format;
+ unsigned int restart_interval;
+ unsigned int ip_buf_partition_num;
+ unsigned int ip_stream_buf_cfg_part1;
+ unsigned int ip_stream_buf_cfg_part2;
+ unsigned int ip_stream_buf_cfg_part3;
+ unsigned int op_stream_buf_0_cfg_part1;
+ unsigned int op_stream_buf_0_cfg_part2;
+ unsigned int op_stream_buf_0_cfg_part3;
+ unsigned int op_stream_buf_1_cfg_part1;
+ unsigned int op_stream_buf_1_cfg_part2;
+ unsigned int op_stream_buf_1_cfg_part3;
+ unsigned int luma_qunt_table_0_3;
+ unsigned int luma_qunt_table_4_7;
+ unsigned int luma_qunt_table_8_11;
+ unsigned int luma_qunt_table_12_15;
+ unsigned int luma_qunt_table_16_19;
+ unsigned int luma_qunt_table_20_23;
+ unsigned int luma_qunt_table_24_27;
+ unsigned int luma_qunt_table_28_31;
+ unsigned int luma_qunt_table_32_35;
+ unsigned int luma_qunt_table_36_39;
+ unsigned int luma_qunt_table_40_43;
+ unsigned int luma_qunt_table_44_47;
+ unsigned int luma_qunt_table_48_51;
+ unsigned int luma_qunt_table_52_55;
+ unsigned int luma_qunt_table_56_59;
+ unsigned int luma_qunt_table_60_63;
+ unsigned int chroma_qunt_table_0_3;
+ unsigned int chroma_qunt_table_4_7;
+ unsigned int chroma_qunt_table_8_11;
+ unsigned int chroma_qunt_table_12_15;
+ unsigned int chroma_qunt_table_16_19;
+ unsigned int chroma_qunt_table_20_23;
+ unsigned int chroma_qunt_table_24_27;
+ unsigned int chroma_qunt_table_28_31;
+ unsigned int chroma_qunt_table_32_35;
+ unsigned int chroma_qunt_table_36_39;
+ unsigned int chroma_qunt_table_40_43;
+ unsigned int chroma_qunt_table_44_47;
+ unsigned int chroma_qunt_table_48_51;
+ unsigned int chroma_qunt_table_52_55;
+ unsigned int chroma_qunt_table_56_59;
+ unsigned int chroma_qunt_table_60_63;
+ unsigned int luma_dc_hm_code_cnt_table_0_3;
+ unsigned int luma_dc_hm_code_cnt_table_4_7;
+ unsigned int luma_dc_hm_code_cnt_table_8_11;
+ unsigned int luma_dc_hm_code_cnt_table_12_15;
+ unsigned int luma_dc_hm_code_val_table_0_3;
+ unsigned int luma_dc_hm_code_val_table_4_7;
+ unsigned int luma_dc_hm_code_val_table_8_11;
+ unsigned int chroma_dc_hm_code_cnt_table_0_3;
+ unsigned int chroma_dc_hm_code_cnt_table_4_7;
+ unsigned int chroma_dc_hm_code_cnt_table_8_11;
+ unsigned int chroma_dc_hm_code_cnt_table_12_15;
+ unsigned int chroma_dc_hm_code_val_table_0_3;
+ unsigned int chroma_dc_hm_code_val_table_4_7;
+ unsigned int chroma_dc_hm_code_val_table_8_11;
+ unsigned int luma_ac_hm_code_cnt_table_0_3;
+ unsigned int luma_ac_hm_code_cnt_table_4_7;
+ unsigned int luma_ac_hm_code_cnt_table_8_11;
+ unsigned int luma_ac_hm_code_cnt_table_12_15;
+ unsigned int luma_ac_hm_code_val_table_0_3;
+ unsigned int luma_ac_hm_code_val_table_4_7;
+ unsigned int luma_ac_hm_code_val_table_8_11;
+ unsigned int luma_ac_hm_code_val_table_12_15;
+ unsigned int luma_ac_hm_code_val_table_16_19;
+ unsigned int luma_ac_hm_code_val_table_20_23;
+ unsigned int luma_ac_hm_code_val_table_24_27;
+ unsigned int luma_ac_hm_code_val_table_28_31;
+ unsigned int luma_ac_hm_code_val_table_32_35;
+ unsigned int luma_ac_hm_code_val_table_36_39;
+ unsigned int luma_ac_hm_code_val_table_40_43;
+ unsigned int luma_ac_hm_code_val_table_44_47;
+ unsigned int luma_ac_hm_code_val_table_48_51;
+ unsigned int luma_ac_hm_code_val_table_52_55;
+ unsigned int luma_ac_hm_code_val_table_56_59;
+ unsigned int luma_ac_hm_code_val_table_60_63;
+ unsigned int luma_ac_hm_code_val_table_64_67;
+ unsigned int luma_ac_hm_code_val_table_68_71;
+ unsigned int luma_ac_hm_code_val_table_72_75;
+ unsigned int luma_ac_hm_code_val_table_76_79;
+ unsigned int luma_ac_hm_code_val_table_80_83;
+ unsigned int luma_ac_hm_code_val_table_84_87;
+ unsigned int luma_ac_hm_code_val_table_88_91;
+ unsigned int luma_ac_hm_code_val_table_92_95;
+ unsigned int luma_ac_hm_code_val_table_96_99;
+ unsigned int luma_ac_hm_code_val_table_100_103;
+ unsigned int luma_ac_hm_code_val_table_104_107;
+ unsigned int luma_ac_hm_code_val_table_108_111;
+ unsigned int luma_ac_hm_code_val_table_112_115;
+ unsigned int luma_ac_hm_code_val_table_116_119;
+ unsigned int luma_ac_hm_code_val_table_120_123;
+ unsigned int luma_ac_hm_code_val_table_124_127;
+ unsigned int luma_ac_hm_code_val_table_128_131;
+ unsigned int luma_ac_hm_code_val_table_132_135;
+ unsigned int luma_ac_hm_code_val_table_136_139;
+ unsigned int luma_ac_hm_code_val_table_140_143;
+ unsigned int luma_ac_hm_code_val_table_144_147;
+ unsigned int luma_ac_hm_code_val_table_148_151;
+ unsigned int luma_ac_hm_code_val_table_152_155;
+ unsigned int luma_ac_hm_code_val_table_156_159;
+ unsigned int luma_ac_hm_code_val_table_160_161;
+ unsigned int chroma_ac_hm_code_cnt_table_0_3;
+ unsigned int chroma_ac_hm_code_cnt_table_4_7;
+ unsigned int chroma_ac_hm_code_cnt_table_8_11;
+ unsigned int chroma_ac_hm_code_cnt_table_12_15;
+ unsigned int chroma_ac_hm_code_val_table_0_3;
+ unsigned int chroma_ac_hm_code_val_table_4_7;
+ unsigned int chroma_ac_hm_code_val_table_8_11;
+ unsigned int chroma_ac_hm_code_val_table_12_15;
+ unsigned int chroma_ac_hm_code_val_table_16_19;
+ unsigned int chroma_ac_hm_code_val_table_20_23;
+ unsigned int chroma_ac_hm_code_val_table_24_27;
+ unsigned int chroma_ac_hm_code_val_table_28_31;
+ unsigned int chroma_ac_hm_code_val_table_32_35;
+ unsigned int chroma_ac_hm_code_val_table_36_39;
+ unsigned int chroma_ac_hm_code_val_table_40_43;
+ unsigned int chroma_ac_hm_code_val_table_44_47;
+ unsigned int chroma_ac_hm_code_val_table_48_51;
+ unsigned int chroma_ac_hm_code_val_table_52_55;
+ unsigned int chroma_ac_hm_code_val_table_56_59;
+ unsigned int chroma_ac_hm_code_val_table_60_63;
+ unsigned int chroma_ac_hm_code_val_table_64_67;
+ unsigned int chroma_ac_hm_code_val_table_68_71;
+ unsigned int chroma_ac_hm_code_val_table_72_75;
+ unsigned int chroma_ac_hm_code_val_table_76_79;
+ unsigned int chroma_ac_hm_code_val_table_80_83;
+ unsigned int chroma_ac_hm_code_val_table_84_87;
+ unsigned int chroma_ac_hm_code_val_table_88_91;
+ unsigned int chroma_ac_hm_code_val_table_92_95;
+ unsigned int chroma_ac_hm_code_val_table_96_99;
+ unsigned int chroma_ac_hm_code_val_table_100_103;
+ unsigned int chroma_ac_hm_code_val_table_104_107;
+ unsigned int chroma_ac_hm_code_val_table_108_111;
+ unsigned int chroma_ac_hm_code_val_table_112_115;
+ unsigned int chroma_ac_hm_code_val_table_116_119;
+ unsigned int chroma_ac_hm_code_val_table_120_123;
+ unsigned int chroma_ac_hm_code_val_table_124_127;
+ unsigned int chroma_ac_hm_code_val_table_128_131;
+ unsigned int chroma_ac_hm_code_val_table_132_135;
+ unsigned int chroma_ac_hm_code_val_table_136_139;
+ unsigned int chroma_ac_hm_code_val_table_140_143;
+ unsigned int chroma_ac_hm_code_val_table_144_147;
+ unsigned int chroma_ac_hm_code_val_table_148_151;
+ unsigned int chroma_ac_hm_code_val_table_152_155;
+ unsigned int chroma_ac_hm_code_val_table_156_159;
+ unsigned int chroma_ac_hm_code_val_table_160_161;
+} __attribute__((packed)) jpeg_cmd_dec_cfg;
+
+
+/*
+ * ARM to JPEG configuration commands are passed through the
+ * uPJpegActionCmdQueue
+ */
+
+/*
+ * Command to start the encode process
+ */
+
+#define JPEG_CMD_ENC_ENCODE 0x0000
+#define JPEG_CMD_ENC_ENCODE_LEN sizeof(jpeg_cmd_enc_encode)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) jpeg_cmd_enc_encode;
+
+
+/*
+ * Command to transition from current state of encoder to IDLE state
+ */
+
+#define JPEG_CMD_ENC_IDLE 0x0001
+#define JPEG_CMD_ENC_IDLE_LEN sizeof(jpeg_cmd_enc_idle)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) jpeg_cmd_enc_idle;
+
+
+/*
+ * Command to inform the encoder that another buffer is ready
+ */
+
+#define JPEG_CMD_ENC_OP_CONSUMED 0x0002
+#define JPEG_CMD_ENC_OP_CONSUMED_LEN sizeof(jpeg_cmd_enc_op_consumed)
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int op_buf_addr;
+ unsigned int op_buf_size;
+} __attribute__((packed)) jpeg_cmd_enc_op_consumed;
+
+
+/*
+ * Command to start the decoding process
+ */
+
+#define JPEG_CMD_DEC_DECODE 0x0003
+#define JPEG_CMD_DEC_DECODE_LEN sizeof(jpeg_cmd_dec_decode)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) jpeg_cmd_dec_decode;
+
+
+/*
+ * Command to transition from the current state of decoder to IDLE
+ */
+
+#define JPEG_CMD_DEC_IDLE 0x0004
+#define JPEG_CMD_DEC_IDLE_LEN sizeof(jpeg_cmd_dec_idle)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) jpeg_cmd_dec_idle;
+
+
+/*
+ * Command to inform that an op buffer is ready for use
+ */
+
+#define JPEG_CMD_DEC_OP_CONSUMED 0x0005
+#define JPEG_CMD_DEC_OP_CONSUMED_LEN sizeof(jpeg_cmd_dec_op_consumed)
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int luma_op_buf_addr;
+ unsigned int luma_op_buf_size;
+ unsigned int chroma_op_buf_addr;
+} __attribute__((packed)) jpeg_cmd_dec_op_consumed;
+
+
+/*
+ * Command to pass a new ip buffer to the jpeg decoder
+ */
+
+#define JPEG_CMD_DEC_IP 0x0006
+#define JPEG_CMD_DEC_IP_LEN sizeof(jpeg_cmd_dec_ip_len)
+
+#define JPEG_CMD_EOI_INDICATOR_NOT_END 0x0000
+#define JPEG_CMD_EOI_INDICATOR_END 0x0001
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int ip_buf_addr;
+ unsigned int ip_buf_size;
+ unsigned int eoi_indicator;
+} __attribute__((packed)) jpeg_cmd_dec_ip;
+
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h
new file mode 100644
index 0000000..d11aa3f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h
@@ -0,0 +1,177 @@
+#ifndef QDSP5VIDJPEGMSGI_H
+#define QDSP5VIDJPEGMSGI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ J P E G I N T E R N A L M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of messages
+ that are sent by JPEG Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+05/10/08 sv initial version
+===========================================================================*/
+
+/*
+ * Messages from JPEG task to ARM through jpeguPMsgQueue
+ */
+
+/*
+ * Message is ACK for CMD_JPEGE_ENCODE cmd
+ */
+
+#define JPEG_MSG_ENC_ENCODE_ACK 0x0000
+#define JPEG_MSG_ENC_ENCODE_ACK_LEN \
+ sizeof(jpeg_msg_enc_encode_ack)
+
+typedef struct {
+} __attribute__((packed)) jpeg_msg_enc_encode_ack;
+
+
+/*
+ * Message informs the up when op buffer is ready for consumption and
+ * when encoding is complete or errors
+ */
+
+#define JPEG_MSG_ENC_OP_PRODUCED 0x0001
+#define JPEG_MSG_ENC_OP_PRODUCED_LEN \
+ sizeof(jpeg_msg_enc_op_produced)
+
+#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_PROGRESS 0x0000
+#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_COMPLETE 0x0001
+#define JPEG_MSGOP_OP_BUF_STATUS_ENC_ERR 0x10000
+
+typedef struct {
+ unsigned int op_buf_addr;
+ unsigned int op_buf_size;
+ unsigned int op_buf_status;
+} __attribute__((packed)) jpeg_msg_enc_op_produced;
+
+
+/*
+ * Message to ack CMD_JPEGE_IDLE
+ */
+
+#define JPEG_MSG_ENC_IDLE_ACK 0x0002
+#define JPEG_MSG_ENC_IDLE_ACK_LEN sizeof(jpeg_msg_enc_idle_ack)
+
+
+typedef struct {
+} __attribute__ ((packed)) jpeg_msg_enc_idle_ack;
+
+
+/*
+ * Message to indicate the illegal command
+ */
+
+#define JPEG_MSG_ENC_ILLEGAL_COMMAND 0x0003
+#define JPEG_MSG_ENC_ILLEGAL_COMMAND_LEN \
+ sizeof(jpeg_msg_enc_illegal_command)
+
+typedef struct {
+ unsigned int status;
+} __attribute__((packed)) jpeg_msg_enc_illegal_command;
+
+
+/*
+ * Message to ACK CMD_JPEGD_DECODE
+ */
+
+#define JPEG_MSG_DEC_DECODE_ACK 0x0004
+#define JPEG_MSG_DEC_DECODE_ACK_LEN \
+ sizeof(jpeg_msg_dec_decode_ack)
+
+
+typedef struct {
+} __attribute__((packed)) jpeg_msg_dec_decode_ack;
+
+
+/*
+ * Message to inform up that an op buffer is ready for consumption and when
+ * decoding is complete or an error occurs
+ */
+
+#define JPEG_MSG_DEC_OP_PRODUCED 0x0005
+#define JPEG_MSG_DEC_OP_PRODUCED_LEN \
+ sizeof(jpeg_msg_dec_op_produced)
+
+#define JPEG_MSG_DEC_OP_BUF_STATUS_PROGRESS 0x0000
+#define JPEG_MSG_DEC_OP_BUF_STATUS_DONE 0x0001
+
+typedef struct {
+ unsigned int luma_op_buf_addr;
+ unsigned int chroma_op_buf_addr;
+ unsigned int num_mcus;
+ unsigned int op_buf_status;
+} __attribute__((packed)) jpeg_msg_dec_op_produced;
+
+/*
+ * Message to ack CMD_JPEGD_IDLE cmd
+ */
+
+#define JPEG_MSG_DEC_IDLE_ACK 0x0006
+#define JPEG_MSG_DEC_IDLE_ACK_LEN sizeof(jpeg_msg_dec_idle_ack)
+
+
+typedef struct {
+} __attribute__((packed)) jpeg_msg_dec_idle_ack;
+
+
+/*
+ * Message to indicate illegal cmd was received
+ */
+
+#define JPEG_MSG_DEC_ILLEGAL_COMMAND 0x0007
+#define JPEG_MSG_DEC_ILLEGAL_COMMAND_LEN \
+ sizeof(jpeg_msg_dec_illegal_command)
+
+
+typedef struct {
+ unsigned int status;
+} __attribute__((packed)) jpeg_msg_dec_illegal_command;
+
+/*
+ * Message to request up for the next segment of ip bit stream
+ */
+
+#define JPEG_MSG_DEC_IP_REQUEST 0x0008
+#define JPEG_MSG_DEC_IP_REQUEST_LEN \
+ sizeof(jpeg_msg_dec_ip_request)
+
+
+typedef struct {
+} __attribute__((packed)) jpeg_msg_dec_ip_request;
+
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h
new file mode 100644
index 0000000..6c76e2c
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h
@@ -0,0 +1,82 @@
+#ifndef QDSP5LPMCMDI_H
+#define QDSP5LPMCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ L P M I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by LPM Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+06/12/08 sv initial version
+===========================================================================*/
+
+
+/*
+ * Command to start LPM processing based on the config params
+ */
+
+#define LPM_CMD_START 0x0000
+#define LPM_CMD_START_LEN sizeof(lpm_cmd_start)
+
+#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_0 0x00000000
+#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_1 0x00010000
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int ip_data_cfg_part1;
+ unsigned int ip_data_cfg_part2;
+ unsigned int ip_data_cfg_part3;
+ unsigned int ip_data_cfg_part4;
+ unsigned int op_data_cfg_part1;
+ unsigned int op_data_cfg_part2;
+ unsigned int op_data_cfg_part3;
+ unsigned int spatial_filter_part[32];
+} __attribute__((packed)) lpm_cmd_start;
+
+
+
+/*
+ * Command to stop LPM processing
+ */
+
+#define LPM_CMD_IDLE 0x0001
+#define LPM_CMD_IDLE_LEN sizeof(lpm_cmd_idle)
+
+typedef struct {
+ unsigned int cmd_id;
+} __attribute__((packed)) lpm_cmd_idle;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h
new file mode 100644
index 0000000..3d1039d
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h
@@ -0,0 +1,80 @@
+#ifndef QDSP5LPMMSGI_H
+#define QDSP5LPMMSGI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ L P M I N T E R N A L M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by LPM Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+06/12/08 sv initial version
+===========================================================================*/
+
+/*
+ * Message to acknowledge CMD_LPM_IDLE command
+ */
+
+#define LPM_MSG_IDLE_ACK 0x0000
+#define LPM_MSG_IDLE_ACK_LEN sizeof(lpm_msg_idle_ack)
+
+typedef struct {
+} __attribute__((packed)) lpm_msg_idle_ack;
+
+
+/*
+ * Message to acknowledge CMD_LPM_START command
+ */
+
+
+#define LPM_MSG_START_ACK 0x0001
+#define LPM_MSG_START_ACK_LEN sizeof(lpm_msg_start_ack)
+
+
+typedef struct {
+} __attribute__((packed)) lpm_msg_start_ack;
+
+
+/*
+ * Message to notify the ARM that LPM processing is complete
+ */
+
+#define LPM_MSG_DONE 0x0002
+#define LPM_MSG_DONE_LEN sizeof(lpm_msg_done)
+
+typedef struct {
+} __attribute__((packed)) lpm_msg_done;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h
new file mode 100644
index 0000000..3a32ee9
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h
@@ -0,0 +1,235 @@
+#ifndef QDSP5VIDDECCMDI_H
+#define QDSP5VIDDECCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ V I D E O D E C O D E R I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by VIDDEC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdeccmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+05/10/08 ac initial version
+===========================================================================*/
+
+
+/*
+ * Command to inform VIDDEC that new subframe packet is ready
+ */
+
+#define VIDDEC_CMD_SUBFRAME_PKT 0x0000
+#define VIDDEC_CMD_SUBFRAME_PKT_LEN \
+ sizeof(viddec_cmd_subframe_pkt)
+
+#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DM 0x0000
+#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DMA 0x0001
+
+#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_CONTI 0x0000
+#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST 0x0001
+#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_LAST 0x0002
+#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST_AND_LAST 0x0003
+
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_MPEG_4 0x0000
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_P0 0x0001
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_264 0x0002
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_p3 0x0003
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_RV9 0x0004
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_WMV9 0x0005
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_SMCDB 0x0006
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_QFRE 0x0007
+#define VIDDEC_CMD_CODEC_SELECTION_WORD_VLD 0x0008
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short packet_seq_number;
+ unsigned short codec_instance_id;
+ unsigned short subframe_packet_size_high;
+ unsigned short subframe_packet_size_low;
+ unsigned short subframe_packet_high;
+ unsigned short subframe_packet_low;
+ unsigned short subframe_packet_partition;
+ unsigned short statistics_packet_size_high;
+ unsigned short statistics_packet_size_low;
+ unsigned short statistics_packet_high;
+ unsigned short statistics_packet_low;
+ unsigned short statistics_partition;
+ unsigned short subframe_info_1;
+ unsigned short subframe_info_0;
+ unsigned short codec_selection_word;
+ unsigned short num_mbs;
+} __attribute__((packed)) viddec_cmd_subframe_pkt;
+
+
+/*
+ * Command to inform VIDDEC task that post processing is required for the frame
+ */
+
+#define VIDDEC_CMD_PP_ENABLE 0x0001
+#define VIDDEC_CMD_PP_ENABLE_LEN \
+ sizeof(viddec_cmd_pp_enable)
+
+#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DM 0x0000
+#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DMA 0x0001
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short packet_seq_num;
+ unsigned short codec_instance_id;
+ unsigned short postproc_info_0;
+ unsigned short codec_selection_word;
+ unsigned short pp_output_addr_high;
+ unsigned short pp_output_addr_low;
+ unsigned short postproc_info_1;
+ unsigned short load_sharing_packet_size_high;
+ unsigned short load_sharing_packet_size_low;
+ unsigned short load_sharing_packet_high;
+ unsigned short load_sharing_packet_low;
+ unsigned short load_sharing_partition;
+ unsigned short pp_param_0;
+ unsigned short pp_param_1;
+ unsigned short pp_param_2;
+ unsigned short pp_param_3;
+} __attribute__((packed)) viddec_cmd_pp_enable;
+
+
+/*
+ * FRAME Header Packet : It is at the start of new frame
+ */
+
+#define VIDDEC_CMD_FRAME_HEADER_PACKET 0x0002
+#define VIDDEC_CMD_FRAME_HEADER_PACKET_LEN \
+ sizeof(viddec_cmd_frame_header_packet)
+
+#define VIDDEC_CMD_FRAME_INFO_0_ERROR_SKIP 0x0000
+#define VIDDEC_CMD_FRAME_INFO_0_ERROR_BLACK 0x0800
+
+typedef struct {
+ unsigned short packet_id;
+ unsigned short x_dimension;
+ unsigned short y_dimension;
+ unsigned short line_width;
+ unsigned short frame_info_0;
+ unsigned short frame_buffer_0_high;
+ unsigned short frame_buffer_0_low;
+ unsigned short frame_buffer_1_high;
+ unsigned short frame_buffer_1_low;
+ unsigned short frame_buffer_2_high;
+ unsigned short frame_buffer_2_low;
+ unsigned short frame_buffer_3_high;
+ unsigned short frame_buffer_3_low;
+ unsigned short frame_buffer_4_high;
+ unsigned short frame_buffer_4_low;
+ unsigned short frame_buffer_5_high;
+ unsigned short frame_buffer_5_low;
+ unsigned short frame_buffer_6_high;
+ unsigned short frame_buffer_6_low;
+ unsigned short frame_buffer_7_high;
+ unsigned short frame_buffer_7_low;
+ unsigned short frame_buffer_8_high;
+ unsigned short frame_buffer_8_low;
+ unsigned short frame_buffer_9_high;
+ unsigned short frame_buffer_9_low;
+ unsigned short frame_buffer_10_high;
+ unsigned short frame_buffer_10_low;
+ unsigned short frame_buffer_11_high;
+ unsigned short frame_buffer_11_low;
+ unsigned short frame_buffer_12_high;
+ unsigned short frame_buffer_12_low;
+ unsigned short frame_buffer_13_high;
+ unsigned short frame_buffer_13_low;
+ unsigned short frame_buffer_14_high;
+ unsigned short frame_buffer_14_low;
+ unsigned short frame_buffer_15_high;
+ unsigned short frame_buffer_15_low;
+ unsigned short output_frame_buffer_high;
+ unsigned short output_frame_buffer_low;
+ unsigned short end_of_packet_marker;
+} __attribute__((packed)) viddec_cmd_frame_header_packet;
+
+
+/*
+ * SLICE HEADER PACKET
+ * I-Slice and P-Slice
+ */
+
+#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE 0x0003
+#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE_LEN \
+ sizeof(viddec_cmd_slice_header_pkt_islice)
+
+#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_PSLICE 0x0000
+#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_BSLICE 0x0100
+#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_ISLICE 0x0200
+#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SPSLICE 0x0300
+#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SISLICE 0x0400
+#define VIDDEC_CMD_ISLICE_INFO_1_NOPADDING 0x0000
+#define VIDDEC_CMD_ISLICE_INFO_1_PADDING 0x0800
+
+#define VIDDEC_CMD_ISLICE_EOP_MARKER 0x7FFF
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short packet_id;
+ unsigned short slice_info_0;
+ unsigned short slice_info_1;
+ unsigned short slice_info_2;
+ unsigned short num_bytes_in_rbsp_high;
+ unsigned short num_bytes_in_rbsp_low;
+ unsigned short num_bytes_in_rbsp_consumed;
+ unsigned short end_of_packet_marker;
+} __attribute__((packed)) viddec_cmd_slice_header_pkt_islice;
+
+
+#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE 0x0003
+#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE_LEN \
+ sizeof(viddec_cmd_slice_header_pkt_pslice)
+
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short packet_id;
+ unsigned short slice_info_0;
+ unsigned short slice_info_1;
+ unsigned short slice_info_2;
+ unsigned short slice_info_3;
+ unsigned short refidx_l0_map_tab_info_0;
+ unsigned short refidx_l0_map_tab_info_1;
+ unsigned short refidx_l0_map_tab_info_2;
+ unsigned short refidx_l0_map_tab_info_3;
+ unsigned short num_bytes_in_rbsp_high;
+ unsigned short num_bytes_in_rbsp_low;
+ unsigned short num_bytes_in_rbsp_consumed;
+ unsigned short end_of_packet_marker;
+} __attribute__((packed)) viddec_cmd_slice_header_pkt_pslice;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h
new file mode 100644
index 0000000..c1744c1
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h
@@ -0,0 +1,107 @@
+#ifndef QDSP5VIDDECMSGI_H
+#define QDSP5VIDDECMSGI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ V I D E O D E C O D E R I N T E R N A L M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of messages
+ that are sent by VIDDEC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdecmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+05/10/08 ac initial version
+===========================================================================*/
+
+/*
+ * Message to inform ARM which VDEC_SUBFRAME_PKT_CMD processed by VIDDEC TASK
+ */
+
+#define VIDDEC_MSG_SUBF_DONE 0x0000
+#define VIDDEC_MSG_SUBF_DONE_LEN \
+ sizeof(viddec_msg_subf_done)
+
+typedef struct {
+ unsigned short packet_seq_number;
+ unsigned short codec_instance_id;
+} __attribute__((packed)) viddec_msg_subf_done;
+
+
+/*
+ * Message to inform ARM one frame has been decoded
+ */
+
+#define VIDDEC_MSG_FRAME_DONE 0x0001
+#define VIDDEC_MSG_FRAME_DONE_LEN \
+ sizeof(viddec_msg_frame_done)
+
+typedef struct {
+ unsigned short packet_seq_number;
+ unsigned short codec_instance_id;
+} __attribute__((packed)) viddec_msg_frame_done;
+
+
+/*
+ * Message to inform ARM that post processing frame has been decoded
+ */
+
+#define VIDDEC_MSG_PP_ENABLE_CMD_DONE 0x0002
+#define VIDDEC_MSG_PP_ENABLE_CMD_DONE_LEN \
+ sizeof(viddec_msg_pp_enable_cmd_done)
+
+typedef struct {
+ unsigned short packet_seq_number;
+ unsigned short codec_instance_id;
+} __attribute__((packed)) viddec_msg_pp_enable_cmd_done;
+
+
+/*
+ * Message to inform ARM that one post processing frame has been decoded
+ */
+
+
+#define VIDDEC_MSG_PP_FRAME_DONE 0x0003
+#define VIDDEC_MSG_PP_FRAME_DONE_LEN \
+ sizeof(viddec_msg_pp_frame_done)
+
+#define VIDDEC_MSG_DISP_WORTHY_DISP 0x0000
+#define VIDDEC_MSG_DISP_WORTHY_DISP_NONE 0xFFFF
+
+
+typedef struct {
+ unsigned short packet_seq_number;
+ unsigned short codec_instance_id;
+ unsigned short display_worthy;
+} __attribute__((packed)) viddec_msg_pp_frame_done;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h
new file mode 100755
index 0000000..819544d
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h
@@ -0,0 +1,212 @@
+#ifndef QDSP5VIDENCCMDI_H
+#define QDSP5VIDENCCMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ V I D E O E N C O D E R I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by VIDENC Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 2008 by QUALCOMM, Incorporated.
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+09/25/08 umeshp initial version
+===========================================================================*/
+
+ #define VIDENC_CMD_CFG 0x0000
+ #define VIDENC_CMD_ACTIVE 0x0001
+ #define VIDENC_CMD_IDLE 0x0002
+ #define VIDENC_CMD_FRAME_START 0x0003
+ #define VIDENC_CMD_STATUS_QUERY 0x0004
+ #define VIDENC_CMD_RC_CFG 0x0005
+ #define VIDENC_CMD_DIS_CFG 0x0006
+ #define VIDENC_CMD_DIS 0x0007
+ #define VIDENC_CMD_INTRA_REFRESH 0x0008
+ #define VIDENC_CMD_DIGITAL_ZOOM 0x0009
+
+
+/*
+ * Command to pass the frame message information to VIDENC
+ */
+
+
+#define VIDENC_CMD_FRAME_START_LEN \
+ sizeof(videnc_cmd_frame_start)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short frame_info;
+ unsigned short frame_rho_budget_word_high;
+ unsigned short frame_rho_budget_word_low;
+ unsigned short input_luma_addr_high;
+ unsigned short input_luma_addr_low;
+ unsigned short input_chroma_addr_high;
+ unsigned short input_chroma_addr_low;
+ unsigned short ref_vop_buf_ptr_high;
+ unsigned short ref_vop_buf_ptr_low;
+ unsigned short enc_pkt_buf_ptr_high;
+ unsigned short enc_pkt_buf_ptr_low;
+ unsigned short enc_pkt_buf_size_high;
+ unsigned short enc_pkt_buf_size_low;
+ unsigned short unfilt_recon_vop_buf_ptr_high;
+ unsigned short unfilt_recon_vop_buf_ptr_low;
+ unsigned short filt_recon_vop_buf_ptr_high;
+ unsigned short filt_recon_vop_buf_ptr_low;
+} __attribute__((packed)) videnc_cmd_frame_start;
+
+/*
+ * Command to pass the frame-level digital stabilization parameters to VIDENC
+ */
+
+
+#define VIDENC_CMD_DIS_LEN \
+ sizeof(videnc_cmd_dis)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short vfe_out_prev_luma_addr_high;
+ unsigned short vfe_out_prev_luma_addr_low;
+ unsigned short stabilization_info;
+} __attribute__((packed)) videnc_cmd_dis;
+
+/*
+ * Command to pass the codec related parameters to VIDENC
+ */
+
+
+#define VIDENC_CMD_CFG_LEN \
+ sizeof(videnc_cmd_cfg)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short cfg_info_0;
+ unsigned short cfg_info_1;
+ unsigned short four_mv_threshold;
+ unsigned short ise_fse_mv_cost_fac;
+ unsigned short venc_frame_dim;
+ unsigned short venc_DM_partition;
+} __attribute__((packed)) videnc_cmd_cfg;
+
+/*
+ * Command to start the video encoding
+ */
+
+
+#define VIDENC_CMD_ACTIVE_LEN \
+ sizeof(videnc_cmd_active)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) videnc_cmd_active;
+
+/*
+ * Command to stop the video encoding
+ */
+
+
+#define VIDENC_CMD_IDLE_LEN \
+ sizeof(videnc_cmd_idle)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) videnc_cmd_idle;
+
+/*
+ * Command to query staus of VIDENC
+ */
+
+
+#define VIDENC_CMD_STATUS_QUERY_LEN \
+ sizeof(videnc_cmd_status_query)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) videnc_cmd_status_query;
+
+/*
+ * Command to set rate control for a frame
+ */
+
+
+#define VIDENC_CMD_RC_CFG_LEN \
+ sizeof(videnc_cmd_rc_cfg)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short max_frame_qp_delta;
+ unsigned short max_min_frame_qp;
+} __attribute__((packed)) videnc_cmd_rc_cfg;
+
+/*
+ * Command to set intra-refreshing
+ */
+
+
+#define VIDENC_CMD_INTRA_REFRESH_LEN \
+ sizeof(videnc_cmd_intra_refresh)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short num_mb_refresh;
+ unsigned short mb_index[15];
+} __attribute__((packed)) videnc_cmd_intra_refresh;
+
+/*
+ * Command to pass digital zoom information to the VIDENC
+ */
+#define VIDENC_CMD_DIGITAL_ZOOM_LEN \
+ sizeof(videnc_cmd_digital_zoom)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short digital_zoom_en;
+ unsigned short luma_frame_shift_X;
+ unsigned short luma_frame_shift_Y;
+ unsigned short up_ip_luma_rows;
+ unsigned short up_ip_luma_cols;
+ unsigned short up_ip_chroma_rows;
+ unsigned short up_ip_chroma_cols;
+ unsigned short luma_ph_incr_V_low;
+ unsigned short luma_ph_incr_V_high;
+ unsigned short luma_ph_incr_H_low;
+ unsigned short luma_ph_incr_H_high;
+ unsigned short chroma_ph_incr_V_low;
+ unsigned short chroma_ph_incr_V_high;
+ unsigned short chroma_ph_incr_H_low;
+ unsigned short chroma_ph_incr_H_high;
+} __attribute__((packed)) videnc_cmd_digital_zoom;
+
+/*
+ * Command to configure digital stabilization parameters
+ */
+
+#define VIDENC_CMD_DIS_CFG_LEN \
+ sizeof(videnc_cmd_dis_cfg)
+
+typedef struct {
+ unsigned short cmd_id;
+ unsigned short image_stab_subf_start_row_col;
+ unsigned short image_stab_subf_dim;
+ unsigned short image_stab_info_0;
+} __attribute__((packed)) videnc_cmd_dis_cfg;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h
new file mode 100644
index 0000000..f76d4e4
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h
@@ -0,0 +1,910 @@
+#ifndef QDSP5VFECMDI_H
+#define QDSP5VFECMDI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ V F E I N T E R N A L C O M M A N D S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are accepted by VFE Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfecmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+06/12/08 sv initial version
+===========================================================================*/
+
+/******************************************************************************
+ * Commands through vfeCommandScaleQueue
+ *****************************************************************************/
+
+/*
+ * Command to program scaler for op1 . max op of scaler is VGA
+ */
+
+
+#define VFE_CMD_SCALE_OP1_CFG 0x0000
+#define VFE_CMD_SCALE_OP1_CFG_LEN \
+ sizeof(vfe_cmd_scale_op1_cfg)
+
+#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_STANDARD 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_CASCADED 0x0001
+#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_ENA 0x0002
+#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_ENA 0x0004
+#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_ENA 0x0008
+#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_ENA 0x0010
+#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_STANDARD 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_CASCADED 0x0020
+#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_ENA 0x0040
+#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_ENA 0x0080
+
+#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000
+#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int scale_op1_sel;
+ unsigned int y_scaler_cfg_part1;
+ unsigned int y_scaler_cfg_part2;
+ unsigned int cbcr_scaler_cfg_part1;
+ unsigned int cbcr_scaler_cfg_part2;
+ unsigned int cbcr_scaler_cfg_part3;
+ unsigned int pp_y_scaler_cfg_part1;
+ unsigned int pp_y_scaler_cfg_part2;
+ unsigned int y_scaler_v_coeff_bank_part1[16];
+ unsigned int y_scaler_v_coeff_bank_part2[16];
+ unsigned int y_scaler_h_coeff_bank_part1[16];
+ unsigned int y_scaler_h_coeff_bank_part2[16];
+} __attribute__((packed)) vfe_cmd_scale_op1_cfg;
+
+
+/*
+ * Command to program scaler for op2
+ */
+
+#define VFE_CMD_SCALE_OP2_CFG 0x0001
+#define VFE_CMD_SCALE_OP2_CFG_LEN \
+ sizeof(vfe_cmd_scale_op2_cfg)
+
+#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_STANDARD 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_CASCADED 0x0001
+#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_ENA 0x0002
+#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_ENA 0x0004
+#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_ENA 0x0008
+#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_ENA 0x0010
+#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_STANDARD 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_CASCADED 0x0020
+#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_ENA 0x0040
+#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_DIS 0x0000
+#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_ENA 0x0080
+
+#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000
+#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int scale_op2_sel;
+ unsigned int y_scaler_cfg_part1;
+ unsigned int y_scaler_cfg_part2;
+ unsigned int cbcr_scaler_cfg_part1;
+ unsigned int cbcr_scaler_cfg_part2;
+ unsigned int cbcr_scaler_cfg_part3;
+ unsigned int pp_y_scaler_cfg_part1;
+ unsigned int pp_y_scaler_cfg_part2;
+ unsigned int y_scaler_v_coeff_bank_part1[16];
+ unsigned int y_scaler_v_coeff_bank_part2[16];
+ unsigned int y_scaler_h_coeff_bank_part1[16];
+ unsigned int y_scaler_h_coeff_bank_part2[16];
+} __attribute__((packed)) vfe_cmd_scale_op2_cfg;
+
+
+/******************************************************************************
+ * Commands through vfeCommandTableQueue
+ *****************************************************************************/
+
+/*
+ * Command to program the AXI ip paths
+ */
+
+#define VFE_CMD_AXI_IP_CFG 0x0000
+#define VFE_CMD_AXI_IP_CFG_LEN sizeof(vfe_cmd_axi_ip_cfg)
+
+#define VFE_CMD_IP_SEL_IP_FORMAT_8 0x0000
+#define VFE_CMD_IP_SEL_IP_FORMAT_10 0x0001
+#define VFE_CMD_IP_SEL_IP_FORMAT_12 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int ip_sel;
+ unsigned int ip_cfg_part1;
+ unsigned int ip_cfg_part2;
+ unsigned int ip_unpack_cfg_part[6];
+ unsigned int ip_buf_addr[8];
+} __attribute__ ((packed)) vfe_cmd_axi_ip_cfg;
+
+
+/*
+ * Command to program axi op paths
+ */
+
+#define VFE_CMD_AXI_OP_CFG 0x0001
+#define VFE_CMD_AXI_OP_CFG_LEN sizeof(vfe_cmd_axi_op_cfg)
+
+#define VFE_CMD_OP_SEL_OP1 0x0000
+#define VFE_CMD_OP_SEL_OP2 0x0001
+#define VFE_CMD_OP_SEL_OP1_OP2 0x0002
+#define VFE_CMD_OP_SEL_CTOA 0x0003
+#define VFE_CMD_OP_SEL_CTOA_OP1 0x0004
+#define VFE_CMD_OP_SEL_CTOA_OP2 0x0005
+#define VFE_CMD_OP_SEL_OP_FORMAT_8 0x0000
+#define VFE_CMD_OP_SEL_OP_FORMAT_10 0x0008
+#define VFE_CMD_OP_SEL_OP_FORMAT_12 0x0010
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int op_sel;
+ unsigned int op1_y_cfg_part1;
+ unsigned int op1_y_cfg_part2;
+ unsigned int op1_cbcr_cfg_part1;
+ unsigned int op1_cbcr_cfg_part2;
+ unsigned int op2_y_cfg_part1;
+ unsigned int op2_y_cfg_part2;
+ unsigned int op2_cbcr_cfg_part1;
+ unsigned int op2_cbcr_cfg_part2;
+ unsigned int op1_buf1_addr[16];
+ unsigned int op2_buf1_addr[16];
+} __attribute__((packed)) vfe_cmd_axi_op_cfg;
+
+
+
+
+/*
+ * Command to program the roll off correction module
+ */
+
+#define VFE_CMD_ROLLOFF_CFG 0x0002
+#define VFE_CMD_ROLLOFF_CFG_LEN \
+ sizeof(vfe_cmd_rolloff_cfg)
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int correction_opt_center_pos;
+ unsigned int radius_square_entry[32];
+ unsigned int red_table_entry[32];
+ unsigned int green_table_entry[32];
+ unsigned int blue_table_entry[32];
+} __attribute__((packed)) vfe_cmd_rolloff_cfg;
+
+/*
+ * Command to program RGB gamma table
+ */
+
+#define VFE_CMD_RGB_GAMMA_CFG 0x0003
+#define VFE_CMD_RGB_GAMMA_CFG_LEN \
+ sizeof(vfe_cmd_rgb_gamma_cfg)
+
+#define VFE_CMD_RGB_GAMMA_SEL_LINEAR 0x0000
+#define VFE_CMD_RGB_GAMMA_SEL_PW_LINEAR 0x0001
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int rgb_gamma_sel;
+ unsigned int rgb_gamma_entry[256];
+} __attribute__((packed)) vfe_cmd_rgb_gamma_cfg;
+
+
+/*
+ * Command to program luma gamma table for the noise reduction path
+ */
+
+#define VFE_CMD_Y_GAMMA_CFG 0x0004
+#define VFE_CMD_Y_GAMMA_CFG_LEN \
+ sizeof(vfe_cmd_y_gamma_cfg)
+
+#define VFE_CMD_Y_GAMMA_SEL_LINEAR 0x0000
+#define VFE_CMD_Y_GAMMA_SEL_PW_LINEAR 0x0001
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int y_gamma_sel;
+ unsigned int y_gamma_entry[256];
+} __attribute__((packed)) vfe_cmd_y_gamma_cfg;
+
+
+
+/******************************************************************************
+ * Commands through vfeCommandQueue
+ *****************************************************************************/
+
+/*
+ * Command to reset the VFE to a known good state.All previously programmed
+ * Params will be lost
+ */
+
+
+#define VFE_CMD_RESET 0x0000
+#define VFE_CMD_RESET_LEN sizeof(vfe_cmd_reset)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) vfe_cmd_reset;
+
+
+/*
+ * Command to start VFE processing based on the config params
+ */
+
+
+#define VFE_CMD_START 0x0001
+#define VFE_CMD_START_LEN sizeof(vfe_cmd_start)
+
+#define VFE_CMD_STARTUP_PARAMS_SRC_CAMIF 0x0000
+#define VFE_CMD_STARTUP_PARAMS_SRC_AXI 0x0001
+#define VFE_CMD_STARTUP_PARAMS_MODE_CONTINUOUS 0x0000
+#define VFE_CMD_STARTUP_PARAMS_MODE_SNAPSHOT 0x0002
+
+#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_ENA 0x0001
+#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_ENA 0x0002
+#define VFE_CMD_IMAGE_PL_WHITE_BAL_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_WHITE_BAL_ENA 0x0004
+#define VFE_CMD_IMAGE_PL_RGB_GAMMA_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_RGB_GAMMA_ENA 0x0008
+#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_ENA 0x0010
+#define VFE_CMD_IMAGE_PL_ADP_FILTER_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_ADP_FILTER_ENA 0x0020
+#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_DIS 0x0000
+#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_ENA 0x0040
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int startup_params;
+ unsigned int image_pipeline;
+ unsigned int frame_dimension;
+} __attribute__((packed)) vfe_cmd_start;
+
+
+/*
+ * Command to halt all processing
+ */
+
+#define VFE_CMD_STOP 0x0002
+#define VFE_CMD_STOP_LEN sizeof(vfe_cmd_stop)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) vfe_cmd_stop;
+
+
+/*
+ * Command to commit the params that have been programmed to take
+ * effect on the next frame
+ */
+
+#define VFE_CMD_UPDATE 0x0003
+#define VFE_CMD_UPDATE_LEN sizeof(vfe_cmd_update)
+
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) vfe_cmd_update;
+
+
+/*
+ * Command to program CAMIF module
+ */
+
+#define VFE_CMD_CAMIF_CFG 0x0004
+#define VFE_CMD_CAMIF_CFG_LEN sizeof(vfe_cmd_camif_cfg)
+
+#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_HIGH 0x0000
+#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_LOW 0x0002
+#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_HIGH 0x0000
+#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_LOW 0x0004
+#define VFE_CMD_CFG_SYNC_MODE_APS 0x0000
+#define VFE_CMD_CFG_SYNC_MODE_EFS 0X0008
+#define VFE_CMD_CFG_SYNC_MODE_ELS 0x0010
+#define VFE_CMD_CFG_SYNC_MODE_RVD 0x0018
+#define VFE_CMD_CFG_VFE_SUBSAMP_EN_DIS 0x0000
+#define VFE_CMD_CFG_VFE_SUBSAMP_EN_ENA 0x0020
+#define VFE_CMD_CFG_BUS_SUBSAMP_EN_DIS 0x0000
+#define VFE_CMD_CFG_BUS_SUBSAMP_EN_ENA 0x0080
+#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_DIS 0x0000
+#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_ENA 0x0800
+
+#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_16 0x0000
+#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_12 0x0010
+
+#define VFE_CMD_EPOCH_IRQ_1_DIS 0x0000
+#define VFE_CMD_EPOCH_IRQ_1_ENA 0x4000
+#define VFE_CMD_EPOCH_IRQ_2_DIS 0x0000
+#define VFE_CMD_EPOCH_IRQ_2_ENA 0x8000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int cfg;
+ unsigned int efs_cfg;
+ unsigned int frame_cfg;
+ unsigned int window_width_cfg;
+ unsigned int window_height_cfg;
+ unsigned int subsamp1_cfg;
+ unsigned int subsamp2_cfg;
+ unsigned int epoch_irq;
+} __attribute__((packed)) vfe_cmd_camif_cfg;
+
+
+
+/*
+ * Command to program the black level module
+ */
+
+#define VFE_CMD_BLACK_LVL_CFG 0x0005
+#define VFE_CMD_BLACK_LVL_CFG_LEN sizeof(vfe_cmd_black_lvl_cfg)
+
+#define VFE_CMD_BL_SEL_MANUAL 0x0000
+#define VFE_CMD_BL_SEL_AUTO 0x0001
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int black_lvl_sel;
+ unsigned int cfg_part[3];
+} __attribute__((packed)) vfe_cmd_black_lvl_cfg;
+
+
+/*
+ * Command to program the active region by cropping the region of interest
+ */
+
+#define VFE_CMD_ACTIVE_REGION_CFG 0x0006
+#define VFE_CMD_ACTIVE_REGION_CFG_LEN \
+ sizeof(vfe_cmd_active_region_cfg)
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int cfg_part1;
+ unsigned int cfg_part2;
+} __attribute__((packed)) vfe_cmd_active_region_cfg;
+
+
+
+/*
+ * Command to program the defective pixel correction(DPC) ,
+ * adaptive bayer filter (ABF) and demosaic modules
+ */
+
+#define VFE_CMD_DEMOSAIC_CFG 0x0007
+#define VFE_CMD_DEMOSAIC_CFG_LEN sizeof(vfe_cmd_demosaic_cfg)
+
+#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_DIS 0x0000
+#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_ENA 0x0001
+#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_DIS 0x0000
+#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_ENA 0x0002
+#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_OFF 0x0000
+#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_ON 0x0004
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1 0x00000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_2 0x10000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_4 0x20000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_8 0x30000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_2 0x50000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_4 0x60000000
+#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_8 0x70000000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int demosaic_part1;
+ unsigned int demosaic_part2;
+ unsigned int demosaic_part3;
+ unsigned int demosaic_part4;
+ unsigned int demosaic_part5;
+} __attribute__((packed)) vfe_cmd_demosaic_cfg;
+
+
+/*
+ * Command to program the ip format
+ */
+
+#define VFE_CMD_IP_FORMAT_CFG 0x0008
+#define VFE_CMD_IP_FORMAT_CFG_LEN \
+ sizeof(vfe_cmd_ip_format_cfg)
+
+#define VFE_CMD_IP_FORMAT_SEL_RGRG 0x0000
+#define VFE_CMD_IP_FORMAT_SEL_GRGR 0x0001
+#define VFE_CMD_IP_FORMAT_SEL_BGBG 0x0002
+#define VFE_CMD_IP_FORMAT_SEL_GBGB 0x0003
+#define VFE_CMD_IP_FORMAT_SEL_YCBYCR 0x0004
+#define VFE_CMD_IP_FORMAT_SEL_YCRYCB 0x0005
+#define VFE_CMD_IP_FORMAT_SEL_CBYCRY 0x0006
+#define VFE_CMD_IP_FORMAT_SEL_CRYCBY 0x0007
+#define VFE_CMD_IP_FORMAT_SEL_NO_CHROMA 0x0000
+#define VFE_CMD_IP_FORMAT_SEL_CHROMA 0x0008
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int ip_format_sel;
+ unsigned int balance_gains_part1;
+ unsigned int balance_gains_part2;
+} __attribute__((packed)) vfe_cmd_ip_format_cfg;
+
+
+
+/*
+ * Command to program max and min allowed op values
+ */
+
+#define VFE_CMD_OP_CLAMP_CFG 0x0009
+#define VFE_CMD_OP_CLAMP_CFG_LEN \
+ sizeof(vfe_cmd_op_clamp_cfg)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int op_clamp_max;
+ unsigned int op_clamp_min;
+} __attribute__((packed)) vfe_cmd_op_clamp_cfg;
+
+
+/*
+ * Command to program chroma sub sample module
+ */
+
+#define VFE_CMD_CHROMA_SUBSAMPLE_CFG 0x000A
+#define VFE_CMD_CHROMA_SUBSAMPLE_CFG_LEN \
+ sizeof(vfe_cmd_chroma_subsample_cfg)
+
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_INTERESTIAL_SAMPS 0x0000
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_COSITED_SAMPS 0x0001
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_INTERESTIAL_SAMPS 0x0000
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_COSITED_SAMPS 0x0002
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_DIS 0x0000
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_ENA 0x0004
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_DIS 0x0000
+#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_ENA 0x0008
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int chroma_subsamp_sel;
+} __attribute__((packed)) vfe_cmd_chroma_subsample_cfg;
+
+
+/*
+ * Command to program the white balance module
+ */
+
+#define VFE_CMD_WHITE_BALANCE_CFG 0x000B
+#define VFE_CMD_WHITE_BALANCE_CFG_LEN \
+ sizeof(vfe_cmd_white_balance_cfg)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int white_balance_gains;
+} __attribute__((packed)) vfe_cmd_white_balance_cfg;
+
+
+/*
+ * Command to program the color processing module
+ */
+
+#define VFE_CMD_COLOR_PROCESS_CFG 0x000C
+#define VFE_CMD_COLOR_PROCESS_CFG_LEN \
+ sizeof(vfe_cmd_color_process_cfg)
+
+#define VFE_CMD_COLOR_CORRE_PART7_Q7_FACTORS 0x0000
+#define VFE_CMD_COLOR_CORRE_PART7_Q8_FACTORS 0x0001
+#define VFE_CMD_COLOR_CORRE_PART7_Q9_FACTORS 0x0002
+#define VFE_CMD_COLOR_CORRE_PART7_Q10_FACTORS 0x0003
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int color_correction_part1;
+ unsigned int color_correction_part2;
+ unsigned int color_correction_part3;
+ unsigned int color_correction_part4;
+ unsigned int color_correction_part5;
+ unsigned int color_correction_part6;
+ unsigned int color_correction_part7;
+ unsigned int chroma_enhance_part1;
+ unsigned int chroma_enhance_part2;
+ unsigned int chroma_enhance_part3;
+ unsigned int chroma_enhance_part4;
+ unsigned int chroma_enhance_part5;
+ unsigned int luma_calc_part1;
+ unsigned int luma_calc_part2;
+} __attribute__((packed)) vfe_cmd_color_process_cfg;
+
+
+/*
+ * Command to program adaptive filter module
+ */
+
+#define VFE_CMD_ADP_FILTER_CFG 0x000D
+#define VFE_CMD_ADP_FILTER_CFG_LEN \
+ sizeof(vfe_cmd_adp_filter_cfg)
+
+#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_DIS 0x0000
+#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_ENA 0x0001
+#define VFE_CMD_ASF_CFG_PART_NO_SHARP_MODE 0x0000
+#define VFE_CMD_ASF_CFG_PART_SINGLE_FILTER 0x0002
+#define VFE_CMD_ASF_CFG_PART_DUAL_FILTER 0x0004
+#define VFE_CMD_ASF_CFG_PART_SHARP_MODE 0x0007
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int asf_cfg_part[7];
+} __attribute__((packed)) vfe_cmd_adp_filter_cfg;
+
+
+/*
+ * Command to program for frame skip pattern for op1 and op2
+ */
+
+#define VFE_CMD_FRAME_SKIP_CFG 0x000E
+#define VFE_CMD_FRAME_SKIP_CFG_LEN \
+ sizeof(vfe_cmd_frame_skip_cfg)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int frame_skip_pattern_op1;
+ unsigned int frame_skip_pattern_op2;
+} __attribute__((packed)) vfe_cmd_frame_skip_cfg;
+
+
+/*
+ * Command to program field-of-view crop for digital zoom
+ */
+
+#define VFE_CMD_FOV_CROP 0x000F
+#define VFE_CMD_FOV_CROP_LEN sizeof(vfe_cmd_fov_crop)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int fov_crop_part1;
+ unsigned int fov_crop_part2;
+} __attribute__((packed)) vfe_cmd_fov_crop;
+
+
+
+/*
+ * Command to program auto focus(AF) statistics module
+ */
+
+#define VFE_CMD_STATS_AUTOFOCUS_CFG 0x0010
+#define VFE_CMD_STATS_AUTOFOCUS_CFG_LEN \
+ sizeof(vfe_cmd_stats_autofocus_cfg)
+
+#define VFE_CMD_AF_STATS_SEL_STATS_DIS 0x0000
+#define VFE_CMD_AF_STATS_SEL_STATS_ENA 0x0001
+#define VFE_CMD_AF_STATS_SEL_PRI_FIXED 0x0000
+#define VFE_CMD_AF_STATS_SEL_PRI_VAR 0x0002
+#define VFE_CMD_AF_STATS_CFG_PART_METRIC_SUM 0x00000000
+#define VFE_CMD_AF_STATS_CFG_PART_METRIC_MAX 0x00200000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int af_stats_sel;
+ unsigned int af_stats_cfg_part[8];
+ unsigned int af_stats_op_buf_hdr;
+ unsigned int af_stats_op_buf[3];
+} __attribute__((packed)) vfe_cmd_stats_autofocus_cfg;
+
+
+/*
+ * Command to program White balance(wb) and exposure (exp)
+ * statistics module
+ */
+
+#define VFE_CMD_STATS_WB_EXP_CFG 0x0011
+#define VFE_CMD_STATS_WB_EXP_CFG_LEN \
+ sizeof(vfe_cmd_stats_wb_exp_cfg)
+
+#define VFE_CMD_WB_EXP_STATS_SEL_STATS_DIS 0x0000
+#define VFE_CMD_WB_EXP_STATS_SEL_STATS_ENA 0x0001
+#define VFE_CMD_WB_EXP_STATS_SEL_PRI_FIXED 0x0000
+#define VFE_CMD_WB_EXP_STATS_SEL_PRI_VAR 0x0002
+
+#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_8_8 0x0000
+#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_16_16 0x0001
+#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_8_8 0x0000
+#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_4_4 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int wb_exp_stats_sel;
+ unsigned int wb_exp_stats_cfg_part1;
+ unsigned int wb_exp_stats_cfg_part2;
+ unsigned int wb_exp_stats_cfg_part3;
+ unsigned int wb_exp_stats_cfg_part4;
+ unsigned int wb_exp_stats_op_buf_hdr;
+ unsigned int wb_exp_stats_op_buf[3];
+} __attribute__((packed)) vfe_cmd_stats_wb_exp_cfg;
+
+
+/*
+ * Command to program histogram(hg) stats module
+ */
+
+#define VFE_CMD_STATS_HG_CFG 0x0012
+#define VFE_CMD_STATS_HG_CFG_LEN \
+ sizeof(vfe_cmd_stats_hg_cfg)
+
+#define VFE_CMD_HG_STATS_SEL_PRI_FIXED 0x0000
+#define VFE_CMD_HG_STATS_SEL_PRI_VAR 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int hg_stats_sel;
+ unsigned int hg_stats_cfg_part1;
+ unsigned int hg_stats_cfg_part2;
+ unsigned int hg_stats_op_buf_hdr;
+ unsigned int hg_stats_op_buf;
+} __attribute__((packed)) vfe_cmd_stats_hg_cfg;
+
+
+/*
+ * Command to acknowledge last MSG_VFE_OP1 message
+ */
+
+#define VFE_CMD_OP1_ACK 0x0013
+#define VFE_CMD_OP1_ACK_LEN sizeof(vfe_cmd_op1_ack)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int op1_buf_y_addr;
+ unsigned int op1_buf_cbcr_addr;
+} __attribute__((packed)) vfe_cmd_op1_ack;
+
+
+
+/*
+ * Command to acknowledge last MSG_VFE_OP2 message
+ */
+
+#define VFE_CMD_OP2_ACK 0x0014
+#define VFE_CMD_OP2_ACK_LEN sizeof(vfe_cmd_op2_ack)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int op2_buf_y_addr;
+ unsigned int op2_buf_cbcr_addr;
+} __attribute__((packed)) vfe_cmd_op2_ack;
+
+
+
+/*
+ * Command to acknowledge MSG_VFE_STATS_AUTOFOCUS msg
+ */
+
+#define VFE_CMD_STATS_AF_ACK 0x0015
+#define VFE_CMD_STATS_AF_ACK_LEN sizeof(vfe_cmd_stats_af_ack)
+
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int af_stats_op_buf;
+} __attribute__((packed)) vfe_cmd_stats_af_ack;
+
+
+/*
+ * Command to acknowledge MSG_VFE_STATS_WB_EXP msg
+ */
+
+#define VFE_CMD_STATS_WB_EXP_ACK 0x0016
+#define VFE_CMD_STATS_WB_EXP_ACK_LEN sizeof(vfe_cmd_stats_wb_exp_ack)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int wb_exp_stats_op_buf;
+} __attribute__((packed)) vfe_cmd_stats_wb_exp_ack;
+
+
+/*
+ * Command to acknowledge MSG_VFE_EPOCH1 message
+ */
+
+#define VFE_CMD_EPOCH1_ACK 0x0017
+#define VFE_CMD_EPOCH1_ACK_LEN sizeof(vfe_cmd_epoch1_ack)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) vfe_cmd_epoch1_ack;
+
+
+/*
+ * Command to acknowledge MSG_VFE_EPOCH2 message
+ */
+
+#define VFE_CMD_EPOCH2_ACK 0x0018
+#define VFE_CMD_EPOCH2_ACK_LEN sizeof(vfe_cmd_epoch2_ack)
+
+typedef struct {
+ unsigned short cmd_id;
+} __attribute__((packed)) vfe_cmd_epoch2_ack;
+
+
+
+/*
+ * Command to configure, enable or disable synchronous timer1
+ */
+
+#define VFE_CMD_SYNC_TIMER1_CFG 0x0019
+#define VFE_CMD_SYNC_TIMER1_CFG_LEN \
+ sizeof(vfe_cmd_sync_timer1_cfg)
+
+#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_DIS 0x0000
+#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_ENA 0x0001
+#define VFE_CMD_SYNC_T1_CFG_PART1_POL_HIGH 0x0000
+#define VFE_CMD_SYNC_T1_CFG_PART1_POL_LOW 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int sync_t1_cfg_part1;
+ unsigned int sync_t1_h_sync_countdown;
+ unsigned int sync_t1_pclk_countdown;
+ unsigned int sync_t1_duration;
+} __attribute__((packed)) vfe_cmd_sync_timer1_cfg;
+
+
+/*
+ * Command to configure, enable or disable synchronous timer1
+ */
+
+#define VFE_CMD_SYNC_TIMER2_CFG 0x001A
+#define VFE_CMD_SYNC_TIMER2_CFG_LEN \
+ sizeof(vfe_cmd_sync_timer2_cfg)
+
+#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_DIS 0x0000
+#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_ENA 0x0001
+#define VFE_CMD_SYNC_T2_CFG_PART1_POL_HIGH 0x0000
+#define VFE_CMD_SYNC_T2_CFG_PART1_POL_LOW 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int sync_t2_cfg_part1;
+ unsigned int sync_t2_h_sync_countdown;
+ unsigned int sync_t2_pclk_countdown;
+ unsigned int sync_t2_duration;
+} __attribute__((packed)) vfe_cmd_sync_timer2_cfg;
+
+
+/*
+ * Command to configure and start asynchronous timer1
+ */
+
+#define VFE_CMD_ASYNC_TIMER1_START 0x001B
+#define VFE_CMD_ASYNC_TIMER1_START_LEN \
+ sizeof(vfe_cmd_async_timer1_start)
+
+#define VFE_CMD_ASYNC_T1_POLARITY_A_HIGH 0x0000
+#define VFE_CMD_ASYNC_T1_POLARITY_A_LOW 0x0001
+#define VFE_CMD_ASYNC_T1_POLARITY_B_HIGH 0x0000
+#define VFE_CMD_ASYNC_T1_POLARITY_B_LOW 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int async_t1a_cfg;
+ unsigned int async_t1b_cfg;
+ unsigned int async_t1_polarity;
+} __attribute__((packed)) vfe_cmd_async_timer1_start;
+
+
+/*
+ * Command to configure and start asynchronous timer2
+ */
+
+#define VFE_CMD_ASYNC_TIMER2_START 0x001C
+#define VFE_CMD_ASYNC_TIMER2_START_LEN \
+ sizeof(vfe_cmd_async_timer2_start)
+
+#define VFE_CMD_ASYNC_T2_POLARITY_A_HIGH 0x0000
+#define VFE_CMD_ASYNC_T2_POLARITY_A_LOW 0x0001
+#define VFE_CMD_ASYNC_T2_POLARITY_B_HIGH 0x0000
+#define VFE_CMD_ASYNC_T2_POLARITY_B_LOW 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int async_t2a_cfg;
+ unsigned int async_t2b_cfg;
+ unsigned int async_t2_polarity;
+} __attribute__((packed)) vfe_cmd_async_timer2_start;
+
+
+/*
+ * Command to program partial configurations of auto focus(af)
+ */
+
+#define VFE_CMD_STATS_AF_UPDATE 0x001D
+#define VFE_CMD_STATS_AF_UPDATE_LEN \
+ sizeof(vfe_cmd_stats_af_update)
+
+#define VFE_CMD_AF_UPDATE_PART1_WINDOW_ONE 0x00000000
+#define VFE_CMD_AF_UPDATE_PART1_WINDOW_MULTI 0x80000000
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int af_update_part1;
+ unsigned int af_update_part2;
+} __attribute__((packed)) vfe_cmd_stats_af_update;
+
+
+/*
+ * Command to program partial cfg of wb and exp
+ */
+
+#define VFE_CMD_STATS_WB_EXP_UPDATE 0x001E
+#define VFE_CMD_STATS_WB_EXP_UPDATE_LEN \
+ sizeof(vfe_cmd_stats_wb_exp_update)
+
+#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_8_8 0x0000
+#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_16_16 0x0001
+#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_8_8 0x0000
+#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_4_4 0x0002
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int wb_exp_update_part1;
+ unsigned int wb_exp_update_part2;
+ unsigned int wb_exp_update_part3;
+ unsigned int wb_exp_update_part4;
+} __attribute__((packed)) vfe_cmd_stats_wb_exp_update;
+
+
+
+/*
+ * Command to re program the CAMIF FRAME CONFIG settings
+ */
+
+#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG 0x001F
+#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG_LEN \
+ sizeof(vfe_cmd_update_camif_frame_cfg)
+
+typedef struct {
+ unsigned int cmd_id;
+ unsigned int camif_frame_cfg;
+} __attribute__((packed)) vfe_cmd_update_camif_frame_cfg;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h
new file mode 100644
index 0000000..0053cfb
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h
@@ -0,0 +1,290 @@
+#ifndef QDSP5VFEMSGI_H
+#define QDSP5VFEMSGI_H
+
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ V F E I N T E R N A L M E S S A G E S
+
+GENERAL DESCRIPTION
+ This file contains defintions of format blocks of commands
+ that are sent by VFE Task
+
+REFERENCES
+ None
+
+EXTERNALIZED FUNCTIONS
+ None
+
+Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated.
+
+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.
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+/*===========================================================================
+
+ EDIT HISTORY FOR FILE
+
+This section contains comments describing changes made to this file.
+Notice that changes are listed in reverse chronological order.
+
+$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfemsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $
+Revision History:
+
+when who what, where, why
+-------- --- ----------------------------------------------------------
+06/12/08 sv initial version
+===========================================================================*/
+
+
+/*
+ * Message to acknowledge CMD_VFE_REST command
+ */
+
+#define VFE_MSG_RESET_ACK 0x0000
+#define VFE_MSG_RESET_ACK_LEN sizeof(vfe_msg_reset_ack)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_reset_ack;
+
+
+/*
+ * Message to acknowledge CMD_VFE_START command
+ */
+
+#define VFE_MSG_START_ACK 0x0001
+#define VFE_MSG_START_ACK_LEN sizeof(vfe_msg_start_ack)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_start_ack;
+
+/*
+ * Message to acknowledge CMD_VFE_STOP command
+ */
+
+#define VFE_MSG_STOP_ACK 0x0002
+#define VFE_MSG_STOP_ACK_LEN sizeof(vfe_msg_stop_ack)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_stop_ack;
+
+
+/*
+ * Message to acknowledge CMD_VFE_UPDATE command
+ */
+
+#define VFE_MSG_UPDATE_ACK 0x0003
+#define VFE_MSG_UPDATE_ACK_LEN sizeof(vfe_msg_update_ack)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_update_ack;
+
+
+/*
+ * Message to notify the ARM that snapshot processing is complete
+ * and that the VFE is now STATE_VFE_IDLE
+ */
+
+#define VFE_MSG_SNAPSHOT_DONE 0x0004
+#define VFE_MSG_SNAPSHOT_DONE_LEN \
+ sizeof(vfe_msg_snapshot_done)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_snapshot_done;
+
+
+
+/*
+ * Message to notify ARM that illegal cmd was received and
+ * system is in the IDLE state
+ */
+
+#define VFE_MSG_ILLEGAL_CMD 0x0005
+#define VFE_MSG_ILLEGAL_CMD_LEN \
+ sizeof(vfe_msg_illegal_cmd)
+
+typedef struct {
+ unsigned int status;
+} __attribute__((packed)) vfe_msg_illegal_cmd;
+
+
+/*
+ * Message to notify ARM that op1 buf is full and ready
+ */
+
+#define VFE_MSG_OP1 0x0006
+#define VFE_MSG_OP1_LEN sizeof(vfe_msg_op1)
+
+typedef struct {
+ unsigned int op1_buf_y_addr;
+ unsigned int op1_buf_cbcr_addr;
+ unsigned int black_level_even_col;
+ unsigned int black_level_odd_col;
+ unsigned int defect_pixels_detected;
+ unsigned int asf_max_edge;
+} __attribute__((packed)) vfe_msg_op1;
+
+
+/*
+ * Message to notify ARM that op2 buf is full and ready
+ */
+
+#define VFE_MSG_OP2 0x0007
+#define VFE_MSG_OP2_LEN sizeof(vfe_msg_op2)
+
+typedef struct {
+ unsigned int op2_buf_y_addr;
+ unsigned int op2_buf_cbcr_addr;
+ unsigned int black_level_even_col;
+ unsigned int black_level_odd_col;
+ unsigned int defect_pixels_detected;
+ unsigned int asf_max_edge;
+} __attribute__((packed)) vfe_msg_op2;
+
+
+/*
+ * Message to notify ARM that autofocus(af) stats are ready
+ */
+
+#define VFE_MSG_STATS_AF 0x0008
+#define VFE_MSG_STATS_AF_LEN sizeof(vfe_msg_stats_af)
+
+typedef struct {
+ unsigned int af_stats_op_buffer;
+} __attribute__((packed)) vfe_msg_stats_af;
+
+
+/*
+ * Message to notify ARM that white balance(wb) and exposure (exp)
+ * stats are ready
+ */
+
+#define VFE_MSG_STATS_WB_EXP 0x0009
+#define VFE_MSG_STATS_WB_EXP_LEN \
+ sizeof(vfe_msg_stats_wb_exp)
+
+typedef struct {
+ unsigned int wb_exp_stats_op_buf;
+} __attribute__((packed)) vfe_msg_stats_wb_exp;
+
+
+/*
+ * Message to notify the ARM that histogram(hg) stats are ready
+ */
+
+#define VFE_MSG_STATS_HG 0x000A
+#define VFE_MSG_STATS_HG_LEN sizeof(vfe_msg_stats_hg)
+
+typedef struct {
+ unsigned int hg_stats_op_buf;
+} __attribute__((packed)) vfe_msg_stats_hg;
+
+
+/*
+ * Message to notify the ARM that epoch1 event occurred in the CAMIF
+ */
+
+#define VFE_MSG_EPOCH1 0x000B
+#define VFE_MSG_EPOCH1_LEN sizeof(vfe_msg_epoch1)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_epoch1;
+
+
+/*
+ * Message to notify the ARM that epoch2 event occurred in the CAMIF
+ */
+
+#define VFE_MSG_EPOCH2 0x000C
+#define VFE_MSG_EPOCH2_LEN sizeof(vfe_msg_epoch2)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_epoch2;
+
+
+/*
+ * Message to notify the ARM that sync timer1 op is completed
+ */
+
+#define VFE_MSG_SYNC_T1_DONE 0x000D
+#define VFE_MSG_SYNC_T1_DONE_LEN sizeof(vfe_msg_sync_t1_done)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_sync_t1_done;
+
+
+/*
+ * Message to notify the ARM that sync timer2 op is completed
+ */
+
+#define VFE_MSG_SYNC_T2_DONE 0x000E
+#define VFE_MSG_SYNC_T2_DONE_LEN sizeof(vfe_msg_sync_t2_done)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_sync_t2_done;
+
+
+/*
+ * Message to notify the ARM that async t1 operation completed
+ */
+
+#define VFE_MSG_ASYNC_T1_DONE 0x000F
+#define VFE_MSG_ASYNC_T1_DONE_LEN sizeof(vfe_msg_async_t1_done)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_async_t1_done;
+
+
+
+/*
+ * Message to notify the ARM that async t2 operation completed
+ */
+
+#define VFE_MSG_ASYNC_T2_DONE 0x0010
+#define VFE_MSG_ASYNC_T2_DONE_LEN sizeof(vfe_msg_async_t2_done)
+
+typedef struct {
+} __attribute__((packed)) vfe_msg_async_t2_done;
+
+
+
+/*
+ * Message to notify the ARM that an error has occurred
+ */
+
+#define VFE_MSG_ERROR 0x0011
+#define VFE_MSG_ERROR_LEN sizeof(vfe_msg_error)
+
+#define VFE_MSG_ERR_COND_NO_CAMIF_ERR 0x0000
+#define VFE_MSG_ERR_COND_CAMIF_ERR 0x0001
+#define VFE_MSG_ERR_COND_OP1_Y_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_OP1_Y_BUS_OF 0x0002
+#define VFE_MSG_ERR_COND_OP1_CBCR_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_OP1_CBCR_BUS_OF 0x0004
+#define VFE_MSG_ERR_COND_OP2_Y_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_OP2_Y_BUS_OF 0x0008
+#define VFE_MSG_ERR_COND_OP2_CBCR_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_OP2_CBCR_BUS_OF 0x0010
+#define VFE_MSG_ERR_COND_AF_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_AF_BUS_OF 0x0020
+#define VFE_MSG_ERR_COND_WB_EXP_NO_BUS_OF 0x0000
+#define VFE_MSG_ERR_COND_WB_EXP_BUS_OF 0x0040
+#define VFE_MSG_ERR_COND_NO_AXI_ERR 0x0000
+#define VFE_MSG_ERR_COND_AXI_ERR 0x0080
+
+#define VFE_MSG_CAMIF_STS_IDLE 0x0000
+#define VFE_MSG_CAMIF_STS_CAPTURE_DATA 0x0001
+
+typedef struct {
+ unsigned int err_cond;
+ unsigned int camif_sts;
+} __attribute__((packed)) vfe_msg_error;
+
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h
new file mode 100644
index 0000000..6b25ec2
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASM__ARCH_MSM_REMOTE_SPINLOCK_H
+#define __ASM__ARCH_MSM_REMOTE_SPINLOCK_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/* Remote spinlock type definitions. */
+struct raw_remote_spinlock;
+typedef struct {
+ spinlock_t local;
+#if defined(CONFIG_MSM_REMOTE_SPINLOCK)
+ struct raw_remote_spinlock *remote;
+#endif
+} remote_spinlock_t;
+
+#if defined(CONFIG_MSM_REMOTE_SPINLOCK)
+int _remote_spin_lock_init(remote_spinlock_t *lock, const char *name);
+void _remote_spin_lock(remote_spinlock_t *lock);
+void _remote_spin_unlock(remote_spinlock_t *lock);
+#else
+static inline int _remote_spin_lock_init(remote_spinlock_t *lock,
+ const char *name) { return 0; }
+static inline void _remote_spin_lock(remote_spinlock_t *lock) { }
+static inline void _remote_spin_unlock(remote_spinlock_t *lock) { }
+#endif
+
+/* Note: only the below functions constitute the supported interface */
+static inline int remote_spin_lock_init(remote_spinlock_t *lock,
+ const char *name)
+{
+ spin_lock_init(&lock->local);
+ return _remote_spin_lock_init(lock, name);
+}
+
+#define remote_spin_lock(lock) \
+ do { \
+ typecheck(remote_spinlock_t *, lock); \
+ spin_lock(&((lock)->local)); \
+ _remote_spin_lock(lock); \
+ } while (0)
+
+#define remote_spin_unlock(lock) \
+ do { \
+ typecheck(remote_spinlock_t *, lock); \
+ _remote_spin_unlock(lock); \
+ spin_unlock(&((lock)->local)); \
+ } while (0)
+
+
+#define remote_spin_lock_irqsave(lock,flags) \
+ do { \
+ typecheck(remote_spinlock_t *, lock); \
+ spin_lock_irqsave(&((lock)->local), (flags)); \
+ _remote_spin_lock(lock); \
+ } while (0)
+
+#define remote_spin_unlock_irqrestore(lock,flags) \
+ do { \
+ typecheck(remote_spinlock_t *, lock); \
+ _remote_spin_unlock(lock); \
+ spin_unlock_irqrestore(&((lock)->local), (flags)); \
+ } while (0)
+
+#endif /* __ASM__ARCH_MSM_REMOTE_SPINLOCK_H */
diff --git a/arch/arm/mach-msm/include/mach/sirc.h b/arch/arm/mach-msm/include/mach/sirc.h
index 7281337..c23d429 100644
--- a/arch/arm/mach-msm/include/mach/sirc.h
+++ b/arch/arm/mach-msm/include/mach/sirc.h
@@ -42,6 +42,7 @@
struct sirc_cascade_regs {
void *int_status;
unsigned int cascade_irq;
+ unsigned int cascade_fiq;
};
void msm_init_sirc(void);
@@ -56,8 +57,6 @@
* Secondary interrupt controller interrupts
*/
-#define FIRST_SIRC_IRQ (NR_MSM_IRQS + NR_GPIO_IRQS)
-
#define INT_UART1 (FIRST_SIRC_IRQ + 0)
#define INT_UART2 (FIRST_SIRC_IRQ + 1)
#define INT_UART3 (FIRST_SIRC_IRQ + 2)
diff --git a/arch/arm/mach-msm/include/mach/system.h b/arch/arm/mach-msm/include/mach/system.h
index d2e83f4..cdb049d 100644
--- a/arch/arm/mach-msm/include/mach/system.h
+++ b/arch/arm/mach-msm/include/mach/system.h
@@ -26,3 +26,6 @@
* PSHOLD line on the PMIC to hard reset the system
*/
extern void (*msm_hw_reset_hook)(void);
+
+void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat);
+
diff --git a/arch/arm/mach-msm/include/mach/vmalloc.h b/arch/arm/mach-msm/include/mach/vmalloc.h
index 05f81fd..c915172 100644
--- a/arch/arm/mach-msm/include/mach/vmalloc.h
+++ b/arch/arm/mach-msm/include/mach/vmalloc.h
@@ -16,7 +16,9 @@
#ifndef __ASM_ARCH_MSM_VMALLOC_H
#define __ASM_ARCH_MSM_VMALLOC_H
-#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
+/* IO devices are mapped at 0xF8000000 and above */
+
+#define VMALLOC_END 0xF8000000
#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 1c05060..cc474e8 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <mach/hardware.h>
#include <asm/page.h>
@@ -40,11 +41,13 @@
static struct map_desc msm_io_desc[] __initdata = {
MSM_DEVICE(VIC),
MSM_DEVICE(CSR),
- MSM_DEVICE(GPT),
+ MSM_DEVICE(TMR),
MSM_DEVICE(DMOV),
MSM_DEVICE(GPIO1),
MSM_DEVICE(GPIO2),
MSM_DEVICE(CLK_CTL),
+ MSM_DEVICE(AD5),
+ MSM_DEVICE(MDC),
#ifdef CONFIG_MSM_DEBUG_UART
MSM_DEVICE(DEBUG_UART),
#endif
@@ -83,6 +86,7 @@
MSM_DEVICE(SCPLL),
MSM_DEVICE(AD5),
MSM_DEVICE(MDC),
+ MSM_DEVICE(TCSR),
#ifdef CONFIG_MSM_DEBUG_UART
MSM_DEVICE(DEBUG_UART),
#endif
@@ -96,6 +100,19 @@
void __init msm_map_qsd8x50_io(void)
{
+ unsigned int unused;
+
+ /* The bootloader may not have done it, so disable predecode repair
+ * cache for thumb2 (DPRC, set bit 4 in PVR0F2) due to a bug.
+ */
+ asm volatile ("mrc p15, 0, %0, c15, c15, 2\n\t"
+ "orr %0, %0, #0x10\n\t"
+ "mcr p15, 0, %0, c15, c15, 2"
+ : "=&r" (unused));
+ /* clear out EFSR and ADFSR on boot */
+ asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t"
+ "mcr p15, 0, %0, c5, c1, 0"
+ : : "r" (0));
iotable_init(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc));
}
#endif /* CONFIG_ARCH_QSD8X50 */
@@ -129,6 +146,10 @@
void __init msm_map_msm7x30_io(void)
{
+ /* clear out EFSR and ADFSR on boot */
+ asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t"
+ "mcr p15, 0, %0, c5, c1, 0"
+ : : "r" (0));
iotable_init(msm7x30_io_desc, ARRAY_SIZE(msm7x30_io_desc));
}
#endif /* CONFIG_ARCH_MSM7X30 */
@@ -136,6 +157,7 @@
void __iomem *
__msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
+#ifdef CONFIG_ARCH_MSM_ARM11
if (mtype == MT_DEVICE) {
/* The peripherals in the 88000000 - D0000000 range
* are only accessable by type MT_DEVICE_NONSHARED.
@@ -144,7 +166,9 @@
if ((phys_addr >= 0x88000000) && (phys_addr < 0xD0000000))
mtype = MT_DEVICE_NONSHARED;
}
-
+#endif
return __arm_ioremap_caller(phys_addr, size, mtype,
__builtin_return_address(0));
}
+
+EXPORT_SYMBOL(__msm_ioremap);
diff --git a/arch/arm/mach-msm/irq-vic.c b/arch/arm/mach-msm/irq-vic.c
deleted file mode 100644
index 99f2c34..0000000
--- a/arch/arm/mach-msm/irq-vic.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009, Code Aurora Forum. 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/init.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/timer.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-
-#include <asm/cacheflush.h>
-
-#include <mach/hardware.h>
-
-#include <mach/msm_iomap.h>
-
-#include "smd_private.h"
-
-enum {
- IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0,
- IRQ_DEBUG_SLEEP_INT = 1U << 1,
- IRQ_DEBUG_SLEEP_ABORT = 1U << 2,
- IRQ_DEBUG_SLEEP = 1U << 3,
- IRQ_DEBUG_SLEEP_REQUEST = 1U << 4,
-};
-static int msm_irq_debug_mask;
-module_param_named(debug_mask, msm_irq_debug_mask, int,
- S_IRUGO | S_IWUSR | S_IWGRP);
-
-#define VIC_REG(off) (MSM_VIC_BASE + (off))
-#define VIC_INT_TO_REG_ADDR(base, irq) (base + (irq / 32) * 4)
-#define VIC_INT_TO_REG_INDEX(irq) ((irq >> 5) & 3)
-
-#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_SELECT2 VIC_REG(0x0008) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_SELECT3 VIC_REG(0x000C) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_EN0 VIC_REG(0x0010)
-#define VIC_INT_EN1 VIC_REG(0x0014)
-#define VIC_INT_EN2 VIC_REG(0x0018)
-#define VIC_INT_EN3 VIC_REG(0x001C)
-#define VIC_INT_ENCLEAR0 VIC_REG(0x0020)
-#define VIC_INT_ENCLEAR1 VIC_REG(0x0024)
-#define VIC_INT_ENCLEAR2 VIC_REG(0x0028)
-#define VIC_INT_ENCLEAR3 VIC_REG(0x002C)
-#define VIC_INT_ENSET0 VIC_REG(0x0030)
-#define VIC_INT_ENSET1 VIC_REG(0x0034)
-#define VIC_INT_ENSET2 VIC_REG(0x0038)
-#define VIC_INT_ENSET3 VIC_REG(0x003C)
-#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_TYPE2 VIC_REG(0x0048) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_TYPE3 VIC_REG(0x004C) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */
-#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */
-#define VIC_INT_POLARITY2 VIC_REG(0x0058) /* 1: NEG, 0: POS */
-#define VIC_INT_POLARITY3 VIC_REG(0x005C) /* 1: NEG, 0: POS */
-#define VIC_NO_PEND_VAL VIC_REG(0x0060)
-
-#if defined(CONFIG_ARCH_MSM_SCORPION)
-#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064)
-#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */
-#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */
-#else
-#define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */
-#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */
-#define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */
-#endif
-
-#define VIC_IRQ_STATUS0 VIC_REG(0x0080)
-#define VIC_IRQ_STATUS1 VIC_REG(0x0084)
-#define VIC_IRQ_STATUS2 VIC_REG(0x0088)
-#define VIC_IRQ_STATUS3 VIC_REG(0x008C)
-#define VIC_FIQ_STATUS0 VIC_REG(0x0090)
-#define VIC_FIQ_STATUS1 VIC_REG(0x0094)
-#define VIC_FIQ_STATUS2 VIC_REG(0x0098)
-#define VIC_FIQ_STATUS3 VIC_REG(0x009C)
-#define VIC_RAW_STATUS0 VIC_REG(0x00A0)
-#define VIC_RAW_STATUS1 VIC_REG(0x00A4)
-#define VIC_RAW_STATUS2 VIC_REG(0x00A8)
-#define VIC_RAW_STATUS3 VIC_REG(0x00AC)
-#define VIC_INT_CLEAR0 VIC_REG(0x00B0)
-#define VIC_INT_CLEAR1 VIC_REG(0x00B4)
-#define VIC_INT_CLEAR2 VIC_REG(0x00B8)
-#define VIC_INT_CLEAR3 VIC_REG(0x00BC)
-#define VIC_SOFTINT0 VIC_REG(0x00C0)
-#define VIC_SOFTINT1 VIC_REG(0x00C4)
-#define VIC_SOFTINT2 VIC_REG(0x00C8)
-#define VIC_SOFTINT3 VIC_REG(0x00CC)
-#define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */
-#define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */
-#define VIC_IRQ_VEC_WR VIC_REG(0x00D8)
-
-#if defined(CONFIG_ARCH_MSM_SCORPION)
-#define VIC_FIQ_VEC_RD VIC_REG(0x00DC)
-#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0)
-#define VIC_FIQ_VEC_WR VIC_REG(0x00E4)
-#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8)
-#define VIC_IRQ_IN_STACK VIC_REG(0x00EC)
-#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0)
-#define VIC_FIQ_IN_STACK VIC_REG(0x00F4)
-#define VIC_TEST_BUS_SEL VIC_REG(0x00F8)
-#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC)
-#else
-#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0)
-#define VIC_IRQ_IN_STACK VIC_REG(0x00E4)
-#define VIC_TEST_BUS_SEL VIC_REG(0x00E8)
-#endif
-
-#define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4))
-#define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4))
-
-#if defined(CONFIG_ARCH_MSM7X30)
-#define VIC_NUM_REGS 4
-#else
-#define VIC_NUM_REGS 2
-#endif
-
-#if VIC_NUM_REGS == 2
-#define DPRINT_REGS(base_reg, format, ...) \
- printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \
- readl(base_reg ## 0), readl(base_reg ## 1))
-#define DPRINT_ARRAY(array, format, ...) \
- printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \
- array[0], array[1])
-#elif VIC_NUM_REGS == 4
-#define DPRINT_REGS(base_reg, format, ...) \
- printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \
- readl(base_reg ## 0), readl(base_reg ## 1), \
- readl(base_reg ## 2), readl(base_reg ## 3))
-#define DPRINT_ARRAY(array, format, ...) \
- printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \
- array[0], array[1], \
- array[2], array[3])
-#else
-#error "VIC_NUM_REGS set to illegal value"
-#endif
-
-static uint32_t msm_irq_smsm_wake_enable[2];
-static struct {
- uint32_t int_en[2];
- uint32_t int_type;
- uint32_t int_polarity;
- uint32_t int_select;
-} msm_irq_shadow_reg[VIC_NUM_REGS];
-static uint32_t msm_irq_idle_disable[VIC_NUM_REGS];
-
-#define SMSM_FAKE_IRQ (0xff)
-static uint8_t msm_irq_to_smsm[NR_IRQS] = {
- [INT_MDDI_EXT] = 1,
- [INT_MDDI_PRI] = 2,
- [INT_MDDI_CLIENT] = 3,
- [INT_USB_OTG] = 4,
-
- [INT_PWB_I2C] = 5,
- [INT_SDC1_0] = 6,
- [INT_SDC1_1] = 7,
- [INT_SDC2_0] = 8,
-
- [INT_SDC2_1] = 9,
- [INT_ADSP_A9_A11] = 10,
- [INT_UART1] = 11,
- [INT_UART2] = 12,
-
- [INT_UART3] = 13,
- [INT_UART1_RX] = 14,
- [INT_UART2_RX] = 15,
- [INT_UART3_RX] = 16,
-
- [INT_UART1DM_IRQ] = 17,
- [INT_UART1DM_RX] = 18,
- [INT_KEYSENSE] = 19,
-#if !defined(CONFIG_ARCH_MSM7X30)
- [INT_AD_HSSD] = 20,
-#endif
-
- [INT_NAND_WR_ER_DONE] = 21,
- [INT_NAND_OP_DONE] = 22,
- [INT_TCHSCRN1] = 23,
- [INT_TCHSCRN2] = 24,
-
- [INT_TCHSCRN_SSBI] = 25,
- [INT_USB_HS] = 26,
- [INT_UART2DM_RX] = 27,
- [INT_UART2DM_IRQ] = 28,
-
- [INT_SDC4_1] = 29,
- [INT_SDC4_0] = 30,
- [INT_SDC3_1] = 31,
- [INT_SDC3_0] = 32,
-
- /* fake wakeup interrupts */
- [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ,
- [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ,
- [INT_A9_M2A_0] = SMSM_FAKE_IRQ,
- [INT_A9_M2A_1] = SMSM_FAKE_IRQ,
- [INT_A9_M2A_5] = SMSM_FAKE_IRQ,
- [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
- [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
- [INT_ADSP_A11] = SMSM_FAKE_IRQ,
-#ifdef CONFIG_ARCH_QSD8X50
- [INT_SIRC_0] = SMSM_FAKE_IRQ,
- [INT_SIRC_1] = SMSM_FAKE_IRQ,
-#endif
-};
-
-static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val)
-{
- int i;
-
- for (i = 0; i < VIC_NUM_REGS; i++)
- writel(val, base + (i * 4));
-}
-
-static void msm_irq_ack(unsigned int irq)
-{
- void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_CLEAR0, irq);
- irq = 1 << (irq & 31);
- writel(irq, reg);
-}
-
-static void msm_irq_mask(unsigned int irq)
-{
- void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENCLEAR0, irq);
- unsigned index = VIC_INT_TO_REG_INDEX(irq);
- uint32_t mask = 1UL << (irq & 31);
- int smsm_irq = msm_irq_to_smsm[irq];
-
- msm_irq_shadow_reg[index].int_en[0] &= ~mask;
- writel(mask, reg);
- if (smsm_irq == 0)
- msm_irq_idle_disable[index] &= ~mask;
- else {
- mask = 1UL << (smsm_irq - 1);
- msm_irq_smsm_wake_enable[0] &= ~mask;
- }
-}
-
-static void msm_irq_unmask(unsigned int irq)
-{
- void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENSET0, irq);
- unsigned index = VIC_INT_TO_REG_INDEX(irq);
- uint32_t mask = 1UL << (irq & 31);
- int smsm_irq = msm_irq_to_smsm[irq];
-
- msm_irq_shadow_reg[index].int_en[0] |= mask;
- writel(mask, reg);
-
- if (smsm_irq == 0)
- msm_irq_idle_disable[index] |= mask;
- else {
- mask = 1UL << (smsm_irq - 1);
- msm_irq_smsm_wake_enable[0] |= mask;
- }
-}
-
-static int msm_irq_set_wake(unsigned int irq, unsigned int on)
-{
- unsigned index = VIC_INT_TO_REG_INDEX(irq);
- uint32_t mask = 1UL << (irq & 31);
- int smsm_irq = msm_irq_to_smsm[irq];
-
- if (smsm_irq == 0) {
- printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", irq);
- return -EINVAL;
- }
- if (on)
- msm_irq_shadow_reg[index].int_en[1] |= mask;
- else
- msm_irq_shadow_reg[index].int_en[1] &= ~mask;
-
- if (smsm_irq == SMSM_FAKE_IRQ)
- return 0;
-
- mask = 1UL << (smsm_irq - 1);
- if (on)
- msm_irq_smsm_wake_enable[1] |= mask;
- else
- msm_irq_smsm_wake_enable[1] &= ~mask;
- return 0;
-}
-
-static int msm_irq_set_type(unsigned int irq, unsigned int flow_type)
-{
- void __iomem *treg = VIC_INT_TO_REG_ADDR(VIC_INT_TYPE0, irq);
- void __iomem *preg = VIC_INT_TO_REG_ADDR(VIC_INT_POLARITY0, irq);
- unsigned index = VIC_INT_TO_REG_INDEX(irq);
- int b = 1 << (irq & 31);
- uint32_t polarity;
- uint32_t type;
-
- polarity = msm_irq_shadow_reg[index].int_polarity;
- if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
- polarity |= b;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
- polarity &= ~b;
- writel(polarity, preg);
- msm_irq_shadow_reg[index].int_polarity = polarity;
-
- type = msm_irq_shadow_reg[index].int_type;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- type |= b;
- irq_desc[irq].handle_irq = handle_edge_irq;
- }
- if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) {
- type &= ~b;
- irq_desc[irq].handle_irq = handle_level_irq;
- }
- writel(type, treg);
- msm_irq_shadow_reg[index].int_type = type;
- return 0;
-}
-
-static struct irq_chip msm_irq_chip = {
- .name = "msm",
- .disable = msm_irq_mask,
- .ack = msm_irq_ack,
- .mask = msm_irq_mask,
- .unmask = msm_irq_unmask,
- .set_wake = msm_irq_set_wake,
- .set_type = msm_irq_set_type,
-};
-
-void __init msm_init_irq(void)
-{
- unsigned n;
-
- /* select level interrupts */
- msm_irq_write_all_regs(VIC_INT_TYPE0, 0);
-
- /* select highlevel interrupts */
- msm_irq_write_all_regs(VIC_INT_POLARITY0, 0);
-
- /* select IRQ for all INTs */
- msm_irq_write_all_regs(VIC_INT_SELECT0, 0);
-
- /* disable all INTs */
- msm_irq_write_all_regs(VIC_INT_EN0, 0);
-
- /* don't use vic */
- writel(0, VIC_CONFIG);
-
- /* enable interrupt controller */
- writel(3, VIC_INT_MASTEREN);
-
- for (n = 0; n < NR_MSM_IRQS; n++) {
- set_irq_chip(n, &msm_irq_chip);
- set_irq_handler(n, handle_level_irq);
- set_irq_flags(n, IRQF_VALID);
- }
-}
diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c
index 6c8d5f8..a29c1e6d 100644
--- a/arch/arm/mach-msm/irq.c
+++ b/arch/arm/mach-msm/irq.c
@@ -16,102 +16,473 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <asm/cacheflush.h>
+
#include <mach/hardware.h>
#include <mach/msm_iomap.h>
+#include <mach/fiq.h>
+
+#include "sirc.h"
+#include "smd_private.h"
+
+enum {
+ IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0,
+ IRQ_DEBUG_SLEEP_INT = 1U << 1,
+ IRQ_DEBUG_SLEEP_ABORT = 1U << 2,
+ IRQ_DEBUG_SLEEP = 1U << 3,
+ IRQ_DEBUG_SLEEP_REQUEST = 1U << 4,
+};
+static int msm_irq_debug_mask;
+module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
#define VIC_REG(off) (MSM_VIC_BASE + (off))
+#define __bank(irq) (((irq) / 32) & 0x3)
-#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */
-#define VIC_INT_EN0 VIC_REG(0x0010)
-#define VIC_INT_EN1 VIC_REG(0x0014)
-#define VIC_INT_ENCLEAR0 VIC_REG(0x0020)
-#define VIC_INT_ENCLEAR1 VIC_REG(0x0024)
-#define VIC_INT_ENSET0 VIC_REG(0x0030)
-#define VIC_INT_ENSET1 VIC_REG(0x0034)
-#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */
-#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */
-#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */
+#define VIC_INT_SELECT(n) VIC_REG(0x0000+((n) * 4)) /* 1: FIQ, 0: IRQ */
+#define VIC_INT_EN(n) VIC_REG(0x0010+((n) * 4))
+#define VIC_INT_ENCLEAR(n) VIC_REG(0x0020+((n) * 4))
+#define VIC_INT_ENSET(n) VIC_REG(0x0030+((n) * 4))
+#define VIC_INT_TYPE(n) VIC_REG(0x0040+((n) * 4)) /* 1: EDGE, 0: LEVEL */
+#define VIC_INT_POLARITY(n) VIC_REG(0x0050+((n) * 4)) /* 1: NEG, 0: POS */
#define VIC_NO_PEND_VAL VIC_REG(0x0060)
+
+#if defined(CONFIG_ARCH_MSM_SCORPION)
+#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064)
+#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */
+#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */
+#else
#define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */
-#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */
#define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */
-#define VIC_IRQ_STATUS0 VIC_REG(0x0080)
-#define VIC_IRQ_STATUS1 VIC_REG(0x0084)
-#define VIC_FIQ_STATUS0 VIC_REG(0x0090)
-#define VIC_FIQ_STATUS1 VIC_REG(0x0094)
-#define VIC_RAW_STATUS0 VIC_REG(0x00A0)
-#define VIC_RAW_STATUS1 VIC_REG(0x00A4)
-#define VIC_INT_CLEAR0 VIC_REG(0x00B0)
-#define VIC_INT_CLEAR1 VIC_REG(0x00B4)
-#define VIC_SOFTINT0 VIC_REG(0x00C0)
-#define VIC_SOFTINT1 VIC_REG(0x00C4)
+#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */
+#endif
+#define VIC_IRQ_STATUS(n) VIC_REG(0x0080+((n) * 4))
+#define VIC_FIQ_STATUS(n) VIC_REG(0x0090+((n) * 4))
+#define VIC_RAW_STATUS(n) VIC_REG(0x00A0+((n) * 4))
+#define VIC_INT_CLEAR(n) VIC_REG(0x00B0+((n) * 4))
+#define VIC_SOFTINT(n) VIC_REG(0x00C0+((n) * 4))
#define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */
#define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */
#define VIC_IRQ_VEC_WR VIC_REG(0x00D8)
+
+#if defined(CONFIG_ARCH_MSM_SCORPION)
+#define VIC_FIQ_VEC_RD VIC_REG(0x00DC)
+#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0)
+#define VIC_FIQ_VEC_WR VIC_REG(0x00E4)
+#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8)
+#define VIC_IRQ_IN_STACK VIC_REG(0x00EC)
+#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0)
+#define VIC_FIQ_IN_STACK VIC_REG(0x00F4)
+#define VIC_TEST_BUS_SEL VIC_REG(0x00F8)
+#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC)
+#else
#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0)
#define VIC_IRQ_IN_STACK VIC_REG(0x00E4)
#define VIC_TEST_BUS_SEL VIC_REG(0x00E8)
+#endif
#define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4))
#define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4))
+#if defined(CONFIG_ARCH_MSM7X30)
+#define VIC_NUM_BANKS 4
+#else
+#define VIC_NUM_BANKS 2
+#endif
+
+static uint32_t msm_irq_smsm_wake_enable[2];
+static struct {
+ uint32_t int_en[2];
+ uint32_t int_type;
+ uint32_t int_polarity;
+ uint32_t int_select;
+} msm_irq_shadow_reg[VIC_NUM_BANKS];
+static uint32_t msm_irq_idle_disable[VIC_NUM_BANKS];
+
+#ifndef CONFIG_ARCH_MSM_SCORPION
+#define INT_INFO_SMSM_ID SMEM_SMSM_INT_INFO
+struct smsm_interrupt_info *smsm_int_info;
+#else
+#define INT_INFO_SMSM_ID SMEM_APPS_DEM_SLAVE_DATA
+struct msm_dem_slave_data *smsm_int_info;
+#endif
+
+
+#define SMSM_FAKE_IRQ (0xff)
+static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = {
+ [INT_MDDI_EXT] = 1,
+ [INT_MDDI_PRI] = 2,
+ [INT_MDDI_CLIENT] = 3,
+ [INT_USB_OTG] = 4,
+
+ /* [INT_PWB_I2C] = 5 -- not usable */
+ [INT_SDC1_0] = 6,
+ [INT_SDC1_1] = 7,
+ [INT_SDC2_0] = 8,
+
+ [INT_SDC2_1] = 9,
+ [INT_ADSP_A9_A11] = 10,
+ [INT_UART1] = 11,
+ [INT_UART2] = 12,
+
+ [INT_UART3] = 13,
+ [INT_UART1_RX] = 14,
+ [INT_UART2_RX] = 15,
+ [INT_UART3_RX] = 16,
+
+ [INT_UART1DM_IRQ] = 17,
+ [INT_UART1DM_RX] = 18,
+ [INT_KEYSENSE] = 19,
+#if !defined(CONFIG_ARCH_MSM7X30)
+ [INT_AD_HSSD] = 20,
+#endif
+
+ [INT_NAND_WR_ER_DONE] = 21,
+ [INT_NAND_OP_DONE] = 22,
+ [INT_TCHSCRN1] = 23,
+ [INT_TCHSCRN2] = 24,
+
+ [INT_TCHSCRN_SSBI] = 25,
+ [INT_USB_HS] = 26,
+ [INT_UART2DM_RX] = 27,
+ [INT_UART2DM_IRQ] = 28,
+
+ [INT_SDC4_1] = 29,
+ [INT_SDC4_0] = 30,
+ [INT_SDC3_1] = 31,
+ [INT_SDC3_0] = 32,
+
+ /* fake wakeup interrupts */
+ [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ,
+ [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ,
+ [INT_A9_M2A_0] = SMSM_FAKE_IRQ,
+ [INT_A9_M2A_1] = SMSM_FAKE_IRQ,
+ [INT_A9_M2A_5] = SMSM_FAKE_IRQ,
+ [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
+ [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
+ [INT_ADSP_A11] = SMSM_FAKE_IRQ,
+
+#if defined(CONFIG_ARCH_QSD8X50)
+ [INT_SIRC_0] = SMSM_FAKE_IRQ,
+ [INT_SIRC_1] = SMSM_FAKE_IRQ,
+#endif
+};
+
static void msm_irq_ack(unsigned int irq)
{
- void __iomem *reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0);
+ void __iomem *reg = VIC_INT_CLEAR(__bank(irq));
irq = 1 << (irq & 31);
writel(irq, reg);
}
static void msm_irq_mask(unsigned int irq)
{
- void __iomem *reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0);
- writel(1 << (irq & 31), reg);
+ void __iomem *reg = VIC_INT_ENCLEAR(__bank(irq));
+ unsigned index = __bank(irq);
+ uint32_t mask = 1UL << (irq & 31);
+ int smsm_irq = msm_irq_to_smsm[irq];
+
+ msm_irq_shadow_reg[index].int_en[0] &= ~mask;
+ writel(mask, reg);
+ if (smsm_irq == 0)
+ msm_irq_idle_disable[index] &= ~mask;
+ else {
+ mask = 1UL << (smsm_irq - 1);
+ msm_irq_smsm_wake_enable[0] &= ~mask;
+ }
}
static void msm_irq_unmask(unsigned int irq)
{
- void __iomem *reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0);
- writel(1 << (irq & 31), reg);
+ void __iomem *reg = VIC_INT_ENSET(__bank(irq));
+ unsigned index = __bank(irq);
+ uint32_t mask = 1UL << (irq & 31);
+ int smsm_irq = msm_irq_to_smsm[irq];
+
+ msm_irq_shadow_reg[index].int_en[0] |= mask;
+ writel(mask, reg);
+
+ if (smsm_irq == 0)
+ msm_irq_idle_disable[index] |= mask;
+ else {
+ mask = 1UL << (smsm_irq - 1);
+ msm_irq_smsm_wake_enable[0] |= mask;
+ }
}
static int msm_irq_set_wake(unsigned int irq, unsigned int on)
{
- return -EINVAL;
+ unsigned index = __bank(irq);
+ uint32_t mask = 1UL << (irq & 31);
+ int smsm_irq = msm_irq_to_smsm[irq];
+
+ if (smsm_irq == 0) {
+ printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", irq);
+ return -EINVAL;
+ }
+ if (on)
+ msm_irq_shadow_reg[index].int_en[1] |= mask;
+ else
+ msm_irq_shadow_reg[index].int_en[1] &= ~mask;
+
+ if (smsm_irq == SMSM_FAKE_IRQ)
+ return 0;
+
+ mask = 1UL << (smsm_irq - 1);
+ if (on)
+ msm_irq_smsm_wake_enable[1] |= mask;
+ else
+ msm_irq_smsm_wake_enable[1] &= ~mask;
+ return 0;
}
static int msm_irq_set_type(unsigned int irq, unsigned int flow_type)
{
- void __iomem *treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0);
- void __iomem *preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0);
+ void __iomem *treg = VIC_INT_TYPE(__bank(irq));
+ void __iomem *preg = VIC_INT_POLARITY(__bank(irq));
+ unsigned index = __bank(irq);
int b = 1 << (irq & 31);
+ uint32_t polarity;
+ uint32_t type;
+ polarity = msm_irq_shadow_reg[index].int_polarity;
if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
- writel(readl(preg) | b, preg);
+ polarity |= b;
if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
- writel(readl(preg) & (~b), preg);
+ polarity &= ~b;
+ writel(polarity, preg);
+ msm_irq_shadow_reg[index].int_polarity = polarity;
+ type = msm_irq_shadow_reg[index].int_type;
if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- writel(readl(treg) | b, treg);
+ type |= b;
irq_desc[irq].handle_irq = handle_edge_irq;
}
if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) {
- writel(readl(treg) & (~b), treg);
+ type &= ~b;
irq_desc[irq].handle_irq = handle_level_irq;
}
+ writel(type, treg);
+ msm_irq_shadow_reg[index].int_type = type;
return 0;
}
+int msm_irq_pending(void)
+{
+ int i;
+
+ for (i = 0; i < VIC_NUM_BANKS; ++i)
+ if (readl(VIC_IRQ_STATUS(i)))
+ return 1;
+ return 0;
+}
+
+static void print_vic_irq_stat(void)
+{
+ int i;
+
+ for (i = 0; i < VIC_NUM_BANKS; i++)
+ printk(" %x", readl(VIC_IRQ_STATUS(i)));
+ printk("\n");
+}
+
+static void print_irq_array(uint32_t *arr, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ printk(" %x", arr[i]);
+ printk("\n");
+}
+
+int msm_irq_idle_sleep_allowed(void)
+{
+ int i;
+
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) {
+ printk(KERN_INFO "%s: disable", __func__);
+ print_irq_array(msm_irq_idle_disable, VIC_NUM_BANKS);
+ }
+
+ for (i = 0; i < VIC_NUM_BANKS; ++i)
+ if (msm_irq_idle_disable[i])
+ return 0;
+ return !!smsm_int_info;
+}
+
+/* If arm9_wake is set: pass control to the other core.
+ * If from_idle is not set: disable non-wakeup interrupts.
+ */
+void msm_irq_enter_sleep1(bool arm9_wake, int from_idle)
+{
+ if (!arm9_wake || !smsm_int_info)
+ return;
+ smsm_int_info->interrupt_mask = msm_irq_smsm_wake_enable[!from_idle];
+ smsm_int_info->pending_interrupts = 0;
+}
+
+int msm_irq_enter_sleep2(bool arm9_wake, int from_idle)
+{
+ int limit = 10;
+ uint32_t pending[VIC_NUM_BANKS];
+ int i;
+ uint32_t any = 0;
+
+ if (from_idle && !arm9_wake)
+ return 0;
+
+ /* edge triggered interrupt may get lost if this mode is used */
+ WARN_ON_ONCE(!arm9_wake && !from_idle);
+
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) {
+ printk(KERN_INFO "%s: change irq, pend", __func__);
+ print_vic_irq_stat();
+ }
+
+ for (i = 0; i < VIC_NUM_BANKS; ++i) {
+ pending[i] = readl(VIC_IRQ_STATUS(i));
+ pending[i] &= msm_irq_shadow_reg[i].int_en[!from_idle];
+ /* Clear INT_A9_M2A_5 since requesting sleep triggers it */
+ if (i == (INT_A9_M2A_5 / 32))
+ pending[i] &= ~(1U << (INT_A9_M2A_5 % 32));
+ any |= pending[i];
+ }
+
+ if (any) {
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) {
+ printk(KERN_INFO "%s abort", __func__);
+ print_irq_array(pending, VIC_NUM_BANKS);
+ }
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < VIC_NUM_BANKS; ++i)
+ writel(0, VIC_INT_EN(i));
+
+ while (limit-- > 0) {
+ int pend_irq;
+ int irq = readl(VIC_IRQ_VEC_RD);
+ if (irq == -1)
+ break;
+ pend_irq = readl(VIC_IRQ_VEC_PEND_RD);
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT)
+ printk(KERN_INFO "msm_irq_enter_sleep cleared "
+ "int %d (%d)\n", irq, pend_irq);
+ }
+
+ if (arm9_wake) {
+ msm_irq_set_type(INT_A9_M2A_6, IRQF_TRIGGER_RISING);
+ msm_irq_ack(INT_A9_M2A_6);
+ writel(1U << INT_A9_M2A_6, VIC_INT_ENSET(0));
+ } else {
+ for (i = 0; i < VIC_NUM_BANKS; ++i)
+ writel(msm_irq_shadow_reg[i].int_en[1],
+ VIC_INT_ENSET(i));
+ }
+
+ return 0;
+}
+
+void msm_irq_exit_sleep1(void)
+{
+ int i;
+
+ msm_irq_ack(INT_A9_M2A_6);
+ msm_irq_ack(INT_PWB_I2C);
+ for (i = 0; i < VIC_NUM_BANKS; i++) {
+ writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE(i));
+ writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY(i));
+ writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN(i));
+ writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT(i));
+ }
+ writel(3, VIC_INT_MASTEREN);
+ if (!smsm_int_info) {
+ printk(KERN_ERR "msm_irq_exit_sleep <SM NO INT_INFO>\n");
+ return;
+ }
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) {
+ printk(KERN_INFO "%s %x %x %x now", __func__,
+ smsm_int_info->interrupt_mask,
+ smsm_int_info->pending_interrupts,
+ smsm_int_info->wakeup_reason);
+ print_vic_irq_stat();
+ }
+}
+
+void msm_irq_exit_sleep2(void)
+{
+ int i;
+ uint32_t pending;
+
+ if (!smsm_int_info) {
+ printk(KERN_ERR "msm_irq_exit_sleep <SM NO INT_INFO>\n");
+ return;
+ }
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) {
+ printk(KERN_INFO "%s %x %x %x now", __func__,
+ smsm_int_info->interrupt_mask,
+ smsm_int_info->pending_interrupts,
+ smsm_int_info->wakeup_reason);
+ print_vic_irq_stat();
+ }
+ pending = smsm_int_info->pending_interrupts;
+ for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) {
+ unsigned bank = __bank(i);
+ uint32_t reg_mask = 1UL << (i & 31);
+ int smsm_irq = msm_irq_to_smsm[i];
+ uint32_t smsm_mask;
+ if (smsm_irq == 0)
+ continue;
+ smsm_mask = 1U << (smsm_irq - 1);
+ if (!(pending & smsm_mask))
+ continue;
+ pending &= ~smsm_mask;
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) {
+ printk(KERN_INFO "%s: irq %d still pending %x now",
+ __func__, i, pending);
+ print_vic_irq_stat();
+ }
+#if 0 /* debug intetrrupt trigger */
+ if (readl(VIC_IRQ_STATUS(bank)) & reg_mask)
+ writel(reg_mask, VIC_INT_CLEAR(bank));
+#endif
+ if (readl(VIC_IRQ_STATUS(bank)) & reg_mask)
+ continue;
+ writel(reg_mask, VIC_SOFTINT(bank));
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) {
+ printk(KERN_INFO "%s: irq %d need trigger, now",
+ __func__, i);
+ print_vic_irq_stat();
+ }
+ }
+}
+
+void msm_irq_exit_sleep3(void)
+{
+ if (!smsm_int_info) {
+ printk(KERN_ERR "msm_irq_exit_sleep <SM NO INT_INFO>\n");
+ return;
+ }
+ if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) {
+ printk(KERN_INFO "%s %x %x %x state %x now", __func__,
+ smsm_int_info->interrupt_mask,
+ smsm_int_info->pending_interrupts,
+ smsm_int_info->wakeup_reason,
+ smsm_get_state(SMSM_STATE_MODEM));
+ print_vic_irq_stat();
+ }
+}
+
static struct irq_chip msm_irq_chip = {
.name = "msm",
+ .disable = msm_irq_mask,
.ack = msm_irq_ack,
.mask = msm_irq_mask,
.unmask = msm_irq_unmask,
@@ -123,31 +494,157 @@
{
unsigned n;
- /* select level interrupts */
- writel(0, VIC_INT_TYPE0);
- writel(0, VIC_INT_TYPE1);
+ for (n = 0; n < VIC_NUM_BANKS; ++n) {
+ /* select level interrupts */
+ writel(0, VIC_INT_TYPE(n));
- /* select highlevel interrupts */
- writel(0, VIC_INT_POLARITY0);
- writel(0, VIC_INT_POLARITY1);
+ /* select highlevel interrupts */
+ writel(0, VIC_INT_POLARITY(n));
- /* select IRQ for all INTs */
- writel(0, VIC_INT_SELECT0);
- writel(0, VIC_INT_SELECT1);
+ /* select IRQ for all INTs */
+ writel(0, VIC_INT_SELECT(n));
- /* disable all INTs */
- writel(0, VIC_INT_EN0);
- writel(0, VIC_INT_EN1);
+ /* disable all INTs */
+ writel(0, VIC_INT_EN(n));
+ }
/* don't use 1136 vic */
writel(0, VIC_CONFIG);
/* enable interrupt controller */
- writel(1, VIC_INT_MASTEREN);
+ writel(3, VIC_INT_MASTEREN);
for (n = 0; n < NR_MSM_IRQS; n++) {
set_irq_chip(n, &msm_irq_chip);
set_irq_handler(n, handle_level_irq);
set_irq_flags(n, IRQF_VALID);
}
+
+ msm_init_sirc();
}
+
+static int __init msm_init_irq_late(void)
+{
+ smsm_int_info = smem_alloc(INT_INFO_SMSM_ID, sizeof(*smsm_int_info));
+ if (!smsm_int_info)
+ pr_err("set_wakeup_mask NO INT_INFO (%d)\n", INT_INFO_SMSM_ID);
+ return 0;
+}
+late_initcall(msm_init_irq_late);
+
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+void msm_trigger_irq(int irq)
+{
+ void __iomem *reg = VIC_SOFTINT(__bank(irq));
+ uint32_t mask = 1UL << (irq & 31);
+ writel(mask, reg);
+}
+
+void msm_fiq_enable(int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ irq_desc[irq].chip->unmask(irq);
+ local_irq_restore(flags);
+}
+
+void msm_fiq_disable(int irq)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ irq_desc[irq].chip->mask(irq);
+ local_irq_restore(flags);
+}
+
+static void _msm_fiq_select(int irq)
+{
+ void __iomem *reg = VIC_INT_SELECT(__bank(irq));
+ unsigned index = __bank(irq);
+ uint32_t mask = 1UL << (irq & 31);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ msm_irq_shadow_reg[index].int_select |= mask;
+ writel(msm_irq_shadow_reg[index].int_select, reg);
+ local_irq_restore(flags);
+}
+
+static void _msm_fiq_unselect(int irq)
+{
+ void __iomem *reg = VIC_INT_SELECT(__bank(irq));
+ unsigned index = __bank(irq);
+ uint32_t mask = 1UL << (irq & 31);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ msm_irq_shadow_reg[index].int_select &= (!mask);
+ writel(msm_irq_shadow_reg[index].int_select, reg);
+ local_irq_restore(flags);
+}
+
+void msm_fiq_select(int irq)
+{
+ if (irq < FIRST_SIRC_IRQ)
+ _msm_fiq_select(irq);
+ else if (irq < FIRST_GPIO_IRQ)
+ sirc_fiq_select(irq, true);
+ else
+ pr_err("unsupported fiq %d", irq);
+}
+
+void msm_fiq_unselect(int irq)
+{
+ if (irq < FIRST_SIRC_IRQ)
+ _msm_fiq_unselect(irq);
+ else if (irq < FIRST_GPIO_IRQ)
+ sirc_fiq_select(irq, false);
+ else
+ pr_err("unsupported fiq %d", irq);
+}
+
+/* set_fiq_handler originally from arch/arm/kernel/fiq.c */
+static void set_fiq_handler(void *start, unsigned int length)
+{
+ memcpy((void *)0xffff001c, start, length);
+ flush_icache_range(0xffff001c, 0xffff001c + length);
+ if (!vectors_high())
+ flush_icache_range(0x1c, 0x1c + length);
+}
+
+extern unsigned char fiq_glue, fiq_glue_end;
+
+static void (*fiq_func)(void *data, void *regs, void *svc_sp);
+static void *fiq_data;
+static void *fiq_stack;
+
+void fiq_glue_setup(void *func, void *data, void *sp);
+
+int msm_fiq_set_handler(void (*func)(void *data, void *regs, void *svc_sp),
+ void *data)
+{
+ unsigned long flags;
+ int ret = -ENOMEM;
+
+ if (!fiq_stack)
+ fiq_stack = kzalloc(THREAD_SIZE, GFP_KERNEL);
+ if (!fiq_stack)
+ return -ENOMEM;
+
+ local_irq_save(flags);
+ if (fiq_func == 0) {
+ fiq_func = func;
+ fiq_data = data;
+ fiq_glue_setup(func, data, fiq_stack + THREAD_START_SP);
+ set_fiq_handler(&fiq_glue, (&fiq_glue_end - &fiq_glue));
+ ret = 0;
+ }
+ local_irq_restore(flags);
+ return ret;
+}
+
+void msm_fiq_exit_sleep(void)
+{
+ if (fiq_stack)
+ fiq_glue_setup(fiq_func, fiq_data, fiq_stack + THREAD_START_SP);
+}
+#endif
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
new file mode 100644
index 0000000..3be79d8
--- /dev/null
+++ b/arch/arm/mach-msm/memory.c
@@ -0,0 +1,29 @@
+/* arch/arm/mach-msm/memory.c
+ *
+ * Copyright (C) 2007 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/mm.h>
+#include <linux/mm_types.h>
+#include <asm/pgtable.h>
+
+int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
+ unsigned long pfn, unsigned long size, pgprot_t prot)
+{
+ unsigned long pfn_addr = pfn << PAGE_SHIFT;
+ if ((pfn_addr >= 0x88000000) && (pfn_addr < 0xD0000000)) {
+ prot = pgprot_device(prot);
+ printk("remapping device %lx\n", prot);
+ }
+ return remap_pfn_range(vma, addr, pfn, size, prot);
+}
diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c
new file mode 100644
index 0000000..8b81741
--- /dev/null
+++ b/arch/arm/mach-msm/msm_vibrator.c
@@ -0,0 +1,134 @@
+/* include/asm/mach-msm/htc_pwrsink.h
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2007 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/platform_device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <../../../drivers/staging/android/timed_output.h>
+#include <linux/sched.h>
+
+#include <mach/msm_rpcrouter.h>
+
+#define PM_LIBPROG 0x30000061
+#if defined(CONFIG_ARCH_MSM7X30)
+#define PM_LIBVERS 0x00030001
+#elif defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+#define PM_LIBVERS 0xfb837d0b
+#else
+#define PM_LIBVERS MSM_RPC_VERS(1,1)
+#endif
+
+#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30)
+#define HTC_PROCEDURE_SET_VIB_ON_OFF 22
+#else
+#define HTC_PROCEDURE_SET_VIB_ON_OFF 21
+#endif
+#define PMIC_VIBRATOR_LEVEL (3000)
+
+static struct work_struct vibrator_work;
+static struct hrtimer vibe_timer;
+static spinlock_t vibe_lock;
+static int vibe_state;
+
+static void set_pmic_vibrator(int on)
+{
+ static struct msm_rpc_endpoint *vib_endpoint;
+ struct set_vib_on_off_req {
+ struct rpc_request_hdr hdr;
+ uint32_t data;
+ } req;
+
+ if (!vib_endpoint) {
+ vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0);
+ if (IS_ERR(vib_endpoint)) {
+ printk(KERN_ERR "init vib rpc failed!\n");
+ vib_endpoint = 0;
+ return;
+ }
+ }
+
+ if (on)
+ req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL);
+ else
+ req.data = cpu_to_be32(0);
+
+ msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req,
+ sizeof(req), 5 * HZ);
+}
+
+static void update_vibrator(struct work_struct *work)
+{
+ set_pmic_vibrator(vibe_state);
+}
+
+static void vibrator_enable(struct timed_output_dev *dev, int value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vibe_lock, flags);
+ hrtimer_cancel(&vibe_timer);
+
+ if (value == 0)
+ vibe_state = 0;
+ else {
+ value = (value > 15000 ? 15000 : value);
+ vibe_state = 1;
+ hrtimer_start(&vibe_timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&vibe_lock, flags);
+
+ schedule_work(&vibrator_work);
+}
+
+static int vibrator_get_time(struct timed_output_dev *dev)
+{
+ if (hrtimer_active(&vibe_timer)) {
+ ktime_t r = hrtimer_get_remaining(&vibe_timer);
+ return r.tv.sec * 1000 + r.tv.nsec / 1000000;
+ } else
+ return 0;
+}
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
+{
+ vibe_state = 0;
+ schedule_work(&vibrator_work);
+ return HRTIMER_NORESTART;
+}
+
+static struct timed_output_dev pmic_vibrator = {
+ .name = "vibrator",
+ .get_time = vibrator_get_time,
+ .enable = vibrator_enable,
+};
+
+void __init msm_init_pmic_vibrator(void)
+{
+ INIT_WORK(&vibrator_work, update_vibrator);
+
+ spin_lock_init(&vibe_lock);
+ vibe_state = 0;
+ hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vibe_timer.function = vibrator_timer_func;
+
+ timed_output_dev_register(&pmic_vibrator);
+}
+
+MODULE_DESCRIPTION("timed output pmic vibrator device");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c
new file mode 100644
index 0000000..4361e49
--- /dev/null
+++ b/arch/arm/mach-msm/nand_partitions.c
@@ -0,0 +1,128 @@
+/* arch/arm/mach-msm/nand_partitions.c
+ *
+ * Code to extract partition information from ATAG set up by the
+ * bootloader.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/flash.h>
+#include <asm/io.h>
+
+#include <asm/setup.h>
+
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <mach/msm_iomap.h>
+
+#include <mach/board.h>
+
+
+/* configuration tags specific to msm */
+
+#define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */
+
+struct msm_ptbl_entry
+{
+ char name[16];
+ __u32 offset;
+ __u32 size;
+ __u32 flags;
+};
+
+#define MSM_MAX_PARTITIONS 8
+
+static struct mtd_partition msm_nand_partitions[MSM_MAX_PARTITIONS];
+static char msm_nand_names[MSM_MAX_PARTITIONS * 16];
+
+extern struct flash_platform_data msm_nand_data;
+
+static int __init parse_tag_msm_partition(const struct tag *tag)
+{
+ struct mtd_partition *ptn = msm_nand_partitions;
+ char *name = msm_nand_names;
+ struct msm_ptbl_entry *entry = (void *) &tag->u;
+ unsigned count, n;
+ unsigned have_kpanic = 0;
+
+ count = (tag->hdr.size - 2) /
+ (sizeof(struct msm_ptbl_entry) / sizeof(__u32));
+
+ if (count > MSM_MAX_PARTITIONS)
+ count = MSM_MAX_PARTITIONS;
+
+ for (n = 0; n < count; n++) {
+ memcpy(name, entry->name, 15);
+ name[15] = 0;
+
+ if (!strcmp(name, "kpanic"))
+ have_kpanic = 1;
+
+ ptn->name = name;
+ ptn->offset = entry->offset;
+ ptn->size = entry->size;
+
+ name += 16;
+ entry++;
+ ptn++;
+ }
+
+#ifdef CONFIG_VIRTUAL_KPANIC_PARTITION
+ if (!have_kpanic) {
+ int i;
+ uint64_t kpanic_off = 0;
+
+ if (count == MSM_MAX_PARTITIONS) {
+ printk("Cannot create virtual 'kpanic' partition\n");
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ ptn = &msm_nand_partitions[i];
+ if (!strcmp(ptn->name, CONFIG_VIRTUAL_KPANIC_SRC)) {
+ ptn->size -= CONFIG_VIRTUAL_KPANIC_PSIZE;
+ kpanic_off = ptn->offset + ptn->size;
+ break;
+ }
+ }
+ if (i == count) {
+ printk(KERN_ERR "Partition %s not found\n",
+ CONFIG_VIRTUAL_KPANIC_SRC);
+ goto out;
+ }
+
+ ptn = &msm_nand_partitions[count];
+ ptn->name ="kpanic";
+ ptn->offset = kpanic_off;
+ ptn->size = CONFIG_VIRTUAL_KPANIC_PSIZE;
+
+ printk("Virtual mtd partition '%s' created @%llx (%llu)\n",
+ ptn->name, ptn->offset, ptn->size);
+
+ count++;
+ }
+out:
+#endif /* CONFIG_VIRTUAL_KPANIC_SRC */
+ msm_nand_data.nr_parts = count;
+ msm_nand_data.parts = msm_nand_partitions;
+
+ return 0;
+}
+
+__tagtable(ATAG_MSM_PARTITION, parse_tag_msm_partition);
diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c
new file mode 100644
index 0000000..8fd3689
--- /dev/null
+++ b/arch/arm/mach-msm/pm.c
@@ -0,0 +1,794 @@
+/* arch/arm/mach-msm/pm.c
+ *
+ * MSM Power Management Routines
+ *
+ * Copyright (C) 2007 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/module.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/earlysuspend.h>
+#include <mach/msm_iomap.h>
+#include <mach/system.h>
+#include <asm/io.h>
+
+#include "smd_private.h"
+#include "acpuclock.h"
+#include "proc_comm.h"
+#include "clock.h"
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+
+enum {
+ MSM_PM_DEBUG_SUSPEND = 1U << 0,
+ MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1,
+ MSM_PM_DEBUG_STATE = 1U << 2,
+ MSM_PM_DEBUG_CLOCK = 1U << 3,
+ MSM_PM_DEBUG_RESET_VECTOR = 1U << 4,
+ MSM_PM_DEBUG_SMSM_STATE = 1U << 5,
+ MSM_PM_DEBUG_IDLE = 1U << 6,
+ MSM_PM_DEBUG_CLOCK_VOTE = 1U << 7
+};
+static int msm_pm_debug_mask = MSM_PM_DEBUG_CLOCK_VOTE;
+module_param_named(debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+ MSM_PM_SLEEP_MODE_APPS_SLEEP,
+ MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT,
+ MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+};
+static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE;
+module_param_named(sleep_mode, msm_pm_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP);
+static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE;
+module_param_named(idle_sleep_mode, msm_pm_idle_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP);
+static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
+module_param_named(idle_sleep_min_time, msm_pm_idle_sleep_min_time, int, S_IRUGO | S_IWUSR | S_IWGRP);
+static int msm_pm_idle_spin_time = CONFIG_MSM7X00A_IDLE_SPIN_TIME;
+module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#if defined(CONFIG_ARCH_MSM7X30)
+#define A11S_CLK_SLEEP_EN (MSM_GCC_BASE + 0x020)
+#define A11S_PWRDOWN (MSM_ACC_BASE + 0x01c)
+#define A11S_SECOP (MSM_TCSR_BASE + 0x038)
+#else
+#define A11S_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
+#define A11S_PWRDOWN (MSM_CSR_BASE + 0x440)
+#define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108)
+#define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508)
+#endif
+
+
+#define DEM_MASTER_BITS_PER_CPU 6
+
+/* Power Master State Bits - Per CPU */
+#define DEM_MASTER_SMSM_RUN \
+ (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+#define DEM_MASTER_SMSM_RSA \
+ (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
+ (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+#define DEM_MASTER_SMSM_SLEEP_EXIT \
+ (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+#define DEM_MASTER_SMSM_READY \
+ (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+#define DEM_MASTER_SMSM_SLEEP \
+ (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
+
+/* Power Slave State Bits */
+#define DEM_SLAVE_SMSM_RUN (0x0001)
+#define DEM_SLAVE_SMSM_PWRC (0x0002)
+#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
+#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
+#define DEM_SLAVE_SMSM_WFPI (0x0010)
+#define DEM_SLAVE_SMSM_SLEEP (0x0020)
+#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
+#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
+#define DEM_SLAVE_SMSM_RESET (0x0100)
+#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
+
+#ifndef CONFIG_ARCH_MSM_SCORPION
+#define PM_SMSM_WRITE_STATE SMSM_STATE_APPS
+#define PM_SMSM_READ_STATE SMSM_STATE_MODEM
+
+#define PM_SMSM_WRITE_RUN SMSM_RUN
+#define PM_SMSM_READ_RUN SMSM_RUN
+#else
+#define PM_SMSM_WRITE_STATE SMSM_STATE_APPS_DEM
+#define PM_SMSM_READ_STATE SMSM_STATE_POWER_MASTER_DEM
+
+#define PM_SMSM_WRITE_RUN DEM_SLAVE_SMSM_RUN
+#define PM_SMSM_READ_RUN DEM_MASTER_SMSM_RUN
+#endif
+
+int msm_pm_collapse(void);
+int msm_arch_idle(void);
+void msm_pm_collapse_exit(void);
+
+int64_t msm_timer_enter_idle(void);
+void msm_timer_exit_idle(int low_power);
+int msm_irq_idle_sleep_allowed(void);
+int msm_irq_pending(void);
+int clks_print_running(void);
+
+static int axi_rate;
+static int sleep_axi_rate;
+static struct clk *axi_clk;
+static uint32_t *msm_pm_reset_vector;
+
+static uint32_t msm_pm_max_sleep_time;
+
+#ifdef CONFIG_MSM_IDLE_STATS
+enum msm_pm_time_stats_id {
+ MSM_PM_STAT_REQUESTED_IDLE,
+ MSM_PM_STAT_IDLE_SPIN,
+ MSM_PM_STAT_IDLE_WFI,
+ MSM_PM_STAT_IDLE_SLEEP,
+ MSM_PM_STAT_IDLE_FAILED_SLEEP,
+ MSM_PM_STAT_NOT_IDLE,
+ MSM_PM_STAT_COUNT
+};
+
+static struct msm_pm_time_stats {
+ const char *name;
+ int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int count;
+ int64_t total_time;
+} msm_pm_stats[MSM_PM_STAT_COUNT] = {
+ [MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request",
+ [MSM_PM_STAT_IDLE_SPIN].name = "idle-spin",
+ [MSM_PM_STAT_IDLE_WFI].name = "idle-wfi",
+ [MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep",
+ [MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep",
+ [MSM_PM_STAT_NOT_IDLE].name = "not-idle",
+};
+
+static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
+{
+ int i;
+ int64_t bt;
+ msm_pm_stats[id].total_time += t;
+ msm_pm_stats[id].count++;
+ bt = t;
+ do_div(bt, CONFIG_MSM_IDLE_STATS_FIRST_BUCKET);
+ if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
+ (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
+ i = DIV_ROUND_UP(fls((uint32_t)bt),
+ CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
+ else
+ i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
+ msm_pm_stats[id].bucket[i]++;
+ if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i])
+ msm_pm_stats[id].min_time[i] = t;
+ if (t > msm_pm_stats[id].max_time[i])
+ msm_pm_stats[id].max_time[i] = t;
+}
+#endif
+
+static int
+msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear,
+ uint32_t wait_any_set, uint32_t wait_any_clear)
+{
+ int i;
+ uint32_t state;
+
+ for (i = 0; i < 100000; i++) {
+ state = smsm_get_state(PM_SMSM_READ_STATE);
+ if (((wait_all_set || wait_all_clear) &&
+ !(~state & wait_all_set) && !(state & wait_all_clear)) ||
+ (state & wait_any_set) || (~state & wait_any_clear))
+ return 0;
+ udelay(1);
+ }
+ pr_err("msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", wait_all_set,
+ wait_all_clear, wait_any_set, wait_any_clear, state);
+ return -ETIMEDOUT;
+}
+
+static void
+msm_pm_enter_prep_hw(void)
+{
+#if defined(CONFIG_ARCH_MSM7X30)
+ writel(1, A11S_PWRDOWN);
+ writel(4, A11S_SECOP);
+#else
+#if defined(CONFIG_ARCH_QSD8X50)
+ writel(0x1b, A11S_CLK_SLEEP_EN);
+#else
+ writel(0x1f, A11S_CLK_SLEEP_EN);
+#endif
+ writel(1, A11S_PWRDOWN);
+ writel(0, A11S_STANDBY_CTL);
+
+#if defined(CONFIG_ARCH_MSM_ARM11)
+ writel(0, A11RAMBACKBIAS);
+#endif
+#endif
+}
+
+static void
+msm_pm_exit_restore_hw(void)
+{
+#if defined(CONFIG_ARCH_MSM7X30)
+ writel(0, A11S_SECOP);
+ writel(0, A11S_PWRDOWN);
+#else
+ writel(0x00, A11S_CLK_SLEEP_EN);
+ writel(0, A11S_PWRDOWN);
+#endif
+}
+
+#ifdef CONFIG_MSM_FIQ_SUPPORT
+void msm_fiq_exit_sleep(void);
+#else
+static inline void msm_fiq_exit_sleep(void) { }
+#endif
+
+static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle)
+{
+ uint32_t saved_vector[2];
+ int collapsed;
+ void msm_irq_enter_sleep1(bool arm9_wake, int from_idle);
+ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle);
+ void msm_irq_exit_sleep1(void);
+ void msm_irq_exit_sleep2(void);
+ void msm_irq_exit_sleep3(void);
+ void msm_gpio_enter_sleep(int from_idle);
+ void msm_gpio_exit_sleep(void);
+ void smd_sleep_exit(void);
+ uint32_t enter_state;
+ uint32_t enter_wait_set = 0;
+ uint32_t enter_wait_clear = 0;
+ uint32_t exit_state;
+ uint32_t exit_wait_clear = 0;
+ uint32_t exit_wait_any_set = 0;
+ unsigned long pm_saved_acpu_clk_rate = 0;
+ int ret;
+ int rv = -EINTR;
+ bool invalid_inital_state = false;
+
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
+ printk(KERN_INFO "msm_sleep(): mode %d delay %u idle %d\n",
+ sleep_mode, sleep_delay, from_idle);
+
+#ifndef CONFIG_ARCH_MSM_SCORPION
+ switch (sleep_mode) {
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ enter_state = SMSM_PWRC;
+ enter_wait_set = SMSM_RSA;
+ exit_state = SMSM_WFPI;
+ exit_wait_clear = SMSM_RSA;
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
+ enter_state = SMSM_PWRC_SUSPEND;
+ enter_wait_set = SMSM_RSA;
+ exit_state = SMSM_WFPI;
+ exit_wait_clear = SMSM_RSA;
+ break;
+ case MSM_PM_SLEEP_MODE_APPS_SLEEP:
+ enter_state = SMSM_SLEEP;
+ exit_state = SMSM_SLEEPEXIT;
+ exit_wait_any_set = SMSM_SLEEPEXIT;
+ break;
+ default:
+ enter_state = 0;
+ exit_state = 0;
+ }
+#else
+ switch (sleep_mode) {
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ enter_state = DEM_SLAVE_SMSM_PWRC;
+ enter_wait_set = DEM_MASTER_SMSM_RSA;
+ exit_state = DEM_SLAVE_SMSM_WFPI;
+ exit_wait_any_set =
+ DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
+ enter_state = DEM_SLAVE_SMSM_PWRC_SUSPEND;
+ enter_wait_set = DEM_MASTER_SMSM_RSA;
+ exit_state = DEM_SLAVE_SMSM_WFPI;
+ exit_wait_any_set =
+ DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
+ break;
+ case MSM_PM_SLEEP_MODE_APPS_SLEEP:
+ enter_state = DEM_SLAVE_SMSM_SLEEP;
+ enter_wait_set = DEM_MASTER_SMSM_SLEEP;
+ exit_state = DEM_SLAVE_SMSM_SLEEP_EXIT;
+ exit_wait_any_set = DEM_MASTER_SMSM_SLEEP_EXIT;
+ break;
+ default:
+ enter_state = 0;
+ exit_state = 0;
+ }
+#endif
+
+ clk_enter_sleep(from_idle);
+ msm_irq_enter_sleep1(!!enter_state, from_idle);
+ msm_gpio_enter_sleep(from_idle);
+
+ if (enter_state) {
+ /* Make sure last sleep request did not end with a timeout */
+ ret = msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0);
+ if (ret) {
+ printk(KERN_ERR "msm_sleep(): invalid inital state\n");
+ invalid_inital_state = true;
+ }
+
+ if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP)
+ sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */
+ ret = smsm_set_sleep_duration(sleep_delay);
+ if (ret) {
+ printk(KERN_ERR "msm_sleep(): smsm_set_sleep_duration %x failed\n", enter_state);
+ enter_state = 0;
+ exit_state = 0;
+ }
+ if ((!from_idle && (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK_VOTE)) ||
+ (from_idle && (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE)))
+ clks_print_running();
+
+ ret = smsm_change_state(PM_SMSM_WRITE_STATE, PM_SMSM_WRITE_RUN, enter_state);
+ if (ret) {
+ printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state);
+ enter_state = 0;
+ exit_state = 0;
+ }
+ ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0);
+ if (ret || invalid_inital_state) {
+ printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(PM_SMSM_READ_STATE));
+ goto enter_failed;
+ }
+ }
+ if (msm_irq_enter_sleep2(!!enter_state, from_idle))
+ goto enter_failed;
+
+ if (enter_state) {
+ msm_pm_enter_prep_hw();
+
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
+ printk(KERN_INFO "msm_sleep(): enter "
+ "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
+ "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
+ readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
+ }
+
+ if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) {
+ pm_saved_acpu_clk_rate = acpuclk_power_collapse();
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
+ printk(KERN_INFO "msm_sleep(): %ld enter power collapse"
+ "\n", pm_saved_acpu_clk_rate);
+ if (pm_saved_acpu_clk_rate == 0)
+ goto ramp_down_failed;
+
+ /* Drop AXI request when the screen is on */
+ if (axi_rate)
+ clk_set_rate(axi_clk, sleep_axi_rate);
+ }
+ if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) {
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
+ smsm_print_sleep_info();
+ saved_vector[0] = msm_pm_reset_vector[0];
+ saved_vector[1] = msm_pm_reset_vector[1];
+ msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
+ msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit);
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_RESET_VECTOR)
+ printk(KERN_INFO "msm_sleep(): vector %x %x -> "
+ "%x %x\n", saved_vector[0], saved_vector[1],
+ msm_pm_reset_vector[0], msm_pm_reset_vector[1]);
+ collapsed = msm_pm_collapse();
+ msm_pm_reset_vector[0] = saved_vector[0];
+ msm_pm_reset_vector[1] = saved_vector[1];
+ if (collapsed) {
+ cpu_init();
+ __asm__("cpsie a");
+ msm_fiq_exit_sleep();
+ local_fiq_enable();
+ rv = 0;
+ }
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_POWER_COLLAPSE)
+ printk(KERN_INFO "msm_pm_collapse(): returned %d\n",
+ collapsed);
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
+ smsm_print_sleep_info();
+ } else {
+ msm_arch_idle();
+ rv = 0;
+ }
+
+ if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) {
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
+ printk(KERN_INFO "msm_sleep(): exit power collapse %ld"
+ "\n", pm_saved_acpu_clk_rate);
+ if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0)
+ printk(KERN_ERR "msm_sleep(): clk_set_rate %ld "
+ "failed\n", pm_saved_acpu_clk_rate);
+
+ /* Restore axi rate if needed */
+ if (axi_rate)
+ clk_set_rate(axi_clk, axi_rate);
+ }
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
+ printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, "
+ "A11S_PWRDOWN %x, smsm_get_state %x\n",
+ readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN),
+ smsm_get_state(PM_SMSM_READ_STATE));
+ramp_down_failed:
+ msm_irq_exit_sleep1();
+enter_failed:
+ if (enter_state) {
+ msm_pm_exit_restore_hw();
+
+ smsm_change_state(PM_SMSM_WRITE_STATE, enter_state, exit_state);
+ msm_pm_wait_state(0, exit_wait_clear, exit_wait_any_set, 0);
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
+ printk(KERN_INFO "msm_sleep(): sleep exit "
+ "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
+ "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
+ readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
+ smsm_print_sleep_info();
+ }
+ msm_irq_exit_sleep2();
+ if (enter_state) {
+ smsm_change_state(PM_SMSM_WRITE_STATE, exit_state, PM_SMSM_WRITE_RUN);
+ msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0);
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
+ printk(KERN_INFO "msm_sleep(): sleep exit "
+ "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
+ "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
+ readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
+ }
+ msm_irq_exit_sleep3();
+ msm_gpio_exit_sleep();
+ smd_sleep_exit();
+ clk_exit_sleep();
+ return rv;
+}
+
+static int msm_pm_idle_spin(void)
+{
+ int spin;
+ spin = msm_pm_idle_spin_time >> 10;
+ while (spin-- > 0) {
+ if (msm_irq_pending()) {
+ return -1;
+ }
+ udelay(1);
+ }
+ return 0;
+}
+
+void arch_idle(void)
+{
+ int ret;
+ int64_t sleep_time;
+ int low_power = 0;
+#ifdef CONFIG_MSM_IDLE_STATS
+ int64_t t1;
+ static int64_t t2;
+ int exit_stat;
+#endif
+ int allow_sleep =
+ msm_pm_idle_sleep_mode < MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT &&
+#ifdef CONFIG_HAS_WAKELOCK
+ !has_wake_lock(WAKE_LOCK_IDLE) &&
+#endif
+ msm_irq_idle_sleep_allowed();
+ if (msm_pm_reset_vector == NULL)
+ return;
+
+ sleep_time = msm_timer_enter_idle();
+#ifdef CONFIG_MSM_IDLE_STATS
+ t1 = ktime_to_ns(ktime_get());
+ msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
+ msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, sleep_time);
+#endif
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE)
+ printk(KERN_INFO "arch_idle: sleep time %llu, allow_sleep %d\n",
+ sleep_time, allow_sleep);
+ if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) {
+ unsigned long saved_rate;
+ if (acpuclk_get_wfi_rate() && msm_pm_idle_spin() < 0) {
+#ifdef CONFIG_MSM_IDLE_STATS
+ exit_stat = MSM_PM_STAT_IDLE_SPIN;
+#endif
+ goto abort_idle;
+ }
+ saved_rate = acpuclk_wait_for_irq();
+
+
+ if (saved_rate && msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
+ printk(KERN_DEBUG "arch_idle: clk %ld -> swfi\n",
+ saved_rate);
+
+ /*
+ * If there is a wfi speed specified and we failed to ramp, do not
+ * go into wfi.
+ */
+ if (acpuclk_get_wfi_rate() && !saved_rate)
+ while (!msm_irq_pending())
+ udelay(1);
+ else
+ msm_arch_idle();
+
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
+ printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n",
+ saved_rate);
+ if (acpuclk_set_rate(saved_rate, 1) < 0)
+ printk(KERN_ERR "msm_sleep(): clk_set_rate %ld "
+ "failed\n", saved_rate);
+#ifdef CONFIG_MSM_IDLE_STATS
+ exit_stat = MSM_PM_STAT_IDLE_WFI;
+#endif
+ } else {
+ if (msm_pm_idle_spin() < 0) {
+#ifdef CONFIG_MSM_IDLE_STATS
+ exit_stat = MSM_PM_STAT_IDLE_SPIN;
+#endif
+ goto abort_idle;
+ }
+
+ low_power = 1;
+ do_div(sleep_time, NSEC_PER_SEC / 32768);
+ if (sleep_time > 0x6DDD000) {
+ printk("sleep_time too big %lld\n", sleep_time);
+ sleep_time = 0x6DDD000;
+ }
+ ret = msm_sleep(msm_pm_idle_sleep_mode, sleep_time, 1);
+#ifdef CONFIG_MSM_IDLE_STATS
+ if (ret)
+ exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP;
+ else
+ exit_stat = MSM_PM_STAT_IDLE_SLEEP;
+#endif
+ }
+abort_idle:
+ msm_timer_exit_idle(low_power);
+#ifdef CONFIG_MSM_IDLE_STATS
+ t2 = ktime_to_ns(ktime_get());
+ msm_pm_add_stat(exit_stat, t2 - t1);
+#endif
+}
+
+static int msm_pm_enter(suspend_state_t state)
+{
+ msm_sleep(msm_pm_sleep_mode, msm_pm_max_sleep_time, 0);
+ return 0;
+}
+
+static struct platform_suspend_ops msm_pm_ops = {
+ .enter = msm_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+#if defined(CONFIG_ARCH_MSM7X00A)
+static uint32_t restart_reason = 0x776655AA;
+#else
+static uint32_t restart_reason = 0;
+#endif
+
+static void msm_pm_power_off(void)
+{
+ msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
+ for (;;) ;
+}
+
+static bool console_flushed;
+
+void msm_pm_flush_console(void)
+{
+ if (console_flushed)
+ return;
+ console_flushed = true;
+
+ printk("\n");
+ printk(KERN_EMERG "Restarting %s\n", linux_banner);
+ if (!try_acquire_console_sem()) {
+ release_console_sem();
+ return;
+ }
+
+ mdelay(50);
+
+ local_irq_disable();
+ if (try_acquire_console_sem())
+ printk(KERN_EMERG "msm_restart: Console was locked! Busting\n");
+ else
+ printk(KERN_EMERG "msm_restart: Console was locked!\n");
+ release_console_sem();
+}
+
+static void msm_pm_restart(char str)
+{
+ msm_pm_flush_console();
+
+ /* If there's a hard reset hook and the restart_reason
+ * is the default, prefer that to the (slower) proc_comm
+ * reset command.
+ */
+ if ((restart_reason == 0x776655AA) && msm_hw_reset_hook) {
+ msm_hw_reset_hook();
+ } else {
+ msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
+ }
+ for (;;) ;
+}
+
+static int msm_reboot_call(struct notifier_block *this, unsigned long code, void *_cmd)
+{
+ if((code == SYS_RESTART) && _cmd) {
+ char *cmd = _cmd;
+ if (!strcmp(cmd, "bootloader")) {
+ restart_reason = 0x77665500;
+ } else if (!strcmp(cmd, "recovery")) {
+ restart_reason = 0x77665502;
+ } else if (!strcmp(cmd, "eraseflash")) {
+ restart_reason = 0x776655EF;
+ } else if (!strncmp(cmd, "oem-", 4)) {
+ unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
+ restart_reason = 0x6f656d00 | code;
+ } else if (!strcmp(cmd, "force-hard")) {
+ restart_reason = 0x776655AA;
+ } else {
+ restart_reason = 0x77665501;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_reboot_notifier =
+{
+ .notifier_call = msm_reboot_call,
+};
+
+#ifdef CONFIG_MSM_IDLE_STATS
+static int msm_pm_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i, j;
+ char *p = page;
+
+ for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) {
+ int64_t bucket_time;
+ int64_t s;
+ uint32_t ns;
+ s = msm_pm_stats[i].total_time;
+ ns = do_div(s, NSEC_PER_SEC);
+ p += sprintf(p,
+ "%s:\n"
+ " count: %7d\n"
+ " total_time: %lld.%09u\n",
+ msm_pm_stats[i].name,
+ msm_pm_stats[i].count,
+ s, ns);
+ bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
+ for (j = 0; j < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; j++) {
+ s = bucket_time;
+ ns = do_div(s, NSEC_PER_SEC);
+ p += sprintf(p, " <%2lld.%09u: %7d (%lld-%lld)\n",
+ s, ns, msm_pm_stats[i].bucket[j],
+ msm_pm_stats[i].min_time[j],
+ msm_pm_stats[i].max_time[j]);
+ bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
+ }
+ p += sprintf(p, " >=%2lld.%09u: %7d (%lld-%lld)\n",
+ s, ns, msm_pm_stats[i].bucket[j],
+ msm_pm_stats[i].min_time[j],
+ msm_pm_stats[i].max_time[j]);
+ }
+ *start = page + off;
+
+ len = p - page;
+ if (len > off)
+ len -= off;
+ else
+ len = 0;
+
+ return len < count ? len : count;
+}
+#endif
+
+void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
+{
+ int64_t max_sleep_time_bs = max_sleep_time_ns;
+
+ /* Convert from ns -> BS units */
+ do_div(max_sleep_time_bs, NSEC_PER_SEC / 32768);
+
+ if (max_sleep_time_bs > 0x6DDD000)
+ msm_pm_max_sleep_time = (uint32_t) 0x6DDD000;
+ else
+ msm_pm_max_sleep_time = (uint32_t) max_sleep_time_bs;
+
+ if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
+ printk("%s: Requested %lldns (%lldbs), Giving %ubs\n",
+ __func__, max_sleep_time_ns,
+ max_sleep_time_bs,
+ msm_pm_max_sleep_time);
+}
+EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
+
+#if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION)
+/* axi 128 screen on, 61mhz screen off */
+static void axi_early_suspend(struct early_suspend *handler) {
+ axi_rate = 0;
+ clk_set_rate(axi_clk, axi_rate);
+}
+
+static void axi_late_resume(struct early_suspend *handler) {
+ axi_rate = 128000000;
+ sleep_axi_rate = 120000000;
+ clk_set_rate(axi_clk, axi_rate);
+}
+
+static struct early_suspend axi_screen_suspend = {
+ .suspend = axi_early_suspend,
+ .resume = axi_late_resume,
+};
+#endif
+
+static void __init msm_pm_axi_init(void)
+{
+#if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION)
+ axi_clk = clk_get(NULL, "ebi1_clk");
+ if (IS_ERR(axi_clk)) {
+ int result = PTR_ERR(axi_clk);
+ pr_err("clk_get(ebi1_clk) returned %d\n", result);
+ return;
+ }
+ axi_rate = 128000000;
+ sleep_axi_rate = 120000000;
+ clk_set_rate(axi_clk, axi_rate);
+ register_early_suspend(&axi_screen_suspend);
+#else
+ axi_rate = 0;
+#endif
+}
+
+static int __init msm_pm_init(void)
+{
+ pm_power_off = msm_pm_power_off;
+ arm_pm_restart = msm_pm_restart;
+ msm_pm_max_sleep_time = 0;
+ msm_pm_axi_init();
+
+ register_reboot_notifier(&msm_reboot_notifier);
+
+ msm_pm_reset_vector = ioremap(RESET_VECTOR, PAGE_SIZE);
+ if (msm_pm_reset_vector == NULL) {
+ printk(KERN_ERR "msm_pm_init: failed to map reset vector\n");
+ return -ENODEV;
+ }
+
+ suspend_set_ops(&msm_pm_ops);
+
+#ifdef CONFIG_MSM_IDLE_STATS
+ create_proc_read_entry("msm_pm_stats", S_IRUGO,
+ NULL, msm_pm_read_proc, NULL);
+#endif
+ return 0;
+}
+
+__initcall(msm_pm_init);
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
new file mode 100644
index 0000000..159a7a6
--- /dev/null
+++ b/arch/arm/mach-msm/pm.h
@@ -0,0 +1,31 @@
+/* arch/arm/mach-msm/pm.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: San Mehat <san@android.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 __ARCH_ARM_MACH_MSM_PM_H
+#define __ARCH_ARM_MACH_MSM_PM_H
+
+#include <asm/arch/msm_iomap.h>
+
+#define A11S_CLK_SLEEP_EN_ADDR MSM_CSR_BASE + 0x11c
+
+#define CLK_SLEEP_EN_ARM11_CORE 0x01
+#define CLK_SLEEP_EN_ARM11_AHB 0x02
+#define CLK_SLEEP_EN_ID_BRIDGE 0x04
+#define CLK_SLEEP_EN_DMA_BRIDGE 0x08
+#define CLK_SLEEP_EN_PBUS 0x10
+#define CLK_SLEEP_EN_DEBUG_TIME 0x20
+#define CLK_SLEEP_EN_GP_TIMER 0x40
+#endif
diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c
new file mode 100644
index 0000000..e6dc9fe
--- /dev/null
+++ b/arch/arm/mach-msm/pmic.c
@@ -0,0 +1,530 @@
+/* arch/arm/mach-msm/qdsp6/pmic.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+
+#include "pmic.h"
+
+#include <mach/msm_rpcrouter.h>
+
+#define LIB_NULL_PROC 0
+#define LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC 1
+#define LP_MODE_CONTROL_PROC 2
+#define VREG_SET_LEVEL_PROC 3
+#define VREG_PULL_DOWN_SWITCH_PROC 4
+#define SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC 5
+#define SECURE_MPP_CONFIG_I_SINK_PROC 6
+#define RTC_START_PROC 7
+#define RTC_STOP_PROC 8
+#define RTC_GET_TIME_PROC 9
+#define RTC_ENABLE_ALARM_PROC 10
+#define RTC_DISABLE_ALARM_PROC 11
+#define RTC_GET_ALARM_TIME_PROC 12
+#define RTC_GET_ALARM_STATUS_PROC 13
+#define RTC_SET_TIME_ADJUST_PROC 14
+#define RTC_GET_TIME_ADJUST_PROC 15
+#define SET_LED_INTENSITY_PROC 16
+#define FLASH_LED_SET_CURRENT_PROC 17
+#define FLASH_LED_SET_MODE_PROC 18
+#define FLASH_LED_SET_POLARITY_PROC 19
+#define SPEAKER_CMD_PROC 20
+#define SET_SPEAKER_GAIN_PROC 21
+#define VIB_MOT_SET_VOLT_PROC 22
+#define VIB_MOT_SET_MODE_PROC 23
+#define VIB_MOT_SET_POLARITY_PROC 24
+#define VID_EN_PROC 25
+#define VID_IS_EN_PROC 26
+#define VID_LOAD_DETECT_EN_PROC 27
+#define MIC_EN_PROC 28
+#define MIC_IS_EN_PROC 29
+#define MIC_SET_VOLT_PROC 30
+#define MIC_GET_VOLT_PROC 31
+#define SPKR_EN_RIGHT_CHAN_PROC 32
+#define SPKR_IS_RIGHT_CHAN_EN_PROC 33
+#define SPKR_EN_LEFT_CHAN_PROC 34
+#define SPKR_IS_LEFT_CHAN_EN_PROC 35
+#define SET_SPKR_CONFIGURATION_PROC 36
+#define GET_SPKR_CONFIGURATION_PROC 37
+#define SPKR_GET_GAIN_PROC 38
+#define SPKR_IS_EN_PROC 39
+#define SPKR_EN_MUTE_PROC 40
+#define SPKR_IS_MUTE_EN_PROC 41
+#define SPKR_SET_DELAY_PROC 42
+#define SPKR_GET_DELAY_PROC 43
+#define SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC 44
+#define SET_SPEAKER_DELAY_PROC 45
+#define SPEAKER_1K6_ZIN_ENABLE_PROC 46
+#define SPKR_SET_MUX_HPF_CORNER_FREQ_PROC 47
+#define SPKR_GET_MUX_HPF_CORNER_FREQ_PROC 48
+#define SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC 49
+#define SPKR_EN_STEREO_PROC 50
+#define SPKR_IS_STEREO_EN_PROC 51
+#define SPKR_SELECT_USB_WITH_HPF_20HZ_PROC 52
+#define SPKR_IS_USB_WITH_HPF_20HZ_PROC 53
+#define SPKR_BYPASS_MUX_PROC 54
+#define SPKR_IS_MUX_BYPASSED_PROC 55
+#define SPKR_EN_HPF_PROC 56
+#define SPKR_IS_HPF_EN_PROC 57
+#define SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC 58
+#define SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC 59
+#define SPKR_ADD_RIGHT_LEFT_CHAN_PROC 60
+#define SPKR_SET_GAIN_PROC 61
+#define SPKR_EN_PROC 62
+
+
+/* rpc related */
+#define PMIC_RPC_TIMEOUT (5*HZ)
+
+#define PMIC_RPC_PROG 0x30000061
+#define PMIC_RPC_VER 0x00010001
+
+/* error bit flags defined by modem side */
+#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001)
+#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002)
+#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004)
+#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008)
+#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010)
+
+#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F)
+
+#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080)
+#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100)
+
+#define PMIC_BUFF_SIZE 256
+
+static DEFINE_MUTEX(pmic_mutex);
+static struct msm_rpc_endpoint *pmic_ept;
+
+
+static int modem_to_linux_err(uint err)
+{
+ if (err == 0)
+ return 0;
+
+ if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE)
+ return -EINVAL;
+
+ if (err & PM_ERR_FLAG__SBI_OPT_ERR)
+ return -EIO;
+
+ if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED)
+ return -ENOSYS;
+
+ return -EPERM;
+}
+
+
+/*
+ * 1) network byte order
+ * 2) RPC request header(40 bytes) and RPC reply header (24 bytes)
+ * 3) each transaction consists of a request and reply
+ * 3) PROC (comamnd) layer has its own sub-protocol defined
+ * 4) sub-protocol can be grouped to follwoing 7 cases:
+ * a) set one argument, no get
+ * b) set two argument, no get
+ * c) set three argument, no get
+ * d) set a struct, no get
+ * e) set a argument followed by a struct, no get
+ * f) set a argument, get a argument
+ * g) no set, get either a argument or a struct
+ */
+
+/* Returns number of reply bytes (minus reply header size) or
+ * negative value on error.
+ */
+static int pmic_rpc(int proc, void *msg, int msglen, void *rep, int replen)
+{
+ int r;
+ mutex_lock(&pmic_mutex);
+
+ if (!pmic_ept) {
+ pmic_ept = msm_rpc_connect(PMIC_RPC_PROG, PMIC_RPC_VER, 0);
+ if (!pmic_ept) {
+ pr_err("pmic: cannot connect to rpc server\n");
+ r = -ENODEV;
+ goto done;
+ }
+ }
+ r = msm_rpc_call_reply(pmic_ept, proc, msg, msglen,
+ rep, replen, PMIC_RPC_TIMEOUT);
+ if (r >= 0) {
+ if (r < sizeof(struct rpc_reply_hdr)) {
+ r = -EIO;
+ goto done;
+ }
+ r -= sizeof(struct rpc_reply_hdr);
+ }
+done:
+ mutex_unlock(&pmic_mutex);
+ return r;
+}
+
+struct pmic_reply {
+ struct rpc_reply_hdr hdr;
+ uint32_t status;
+ uint32_t data;
+};
+
+/**
+ * pmic_rpc_set_only() - set arguments and no get
+ * @data0: first argumrnt
+ * @data1: second argument
+ * @data2: third argument
+ * @data3: fourth argument
+ * @num: number of argument
+ * @proc: command/request id
+ *
+ * This function covers case a, b, and c
+ */
+static int pmic_rpc_set_only(uint data0, uint data1, uint data2, uint data3,
+ int num, int proc)
+{
+ struct {
+ struct rpc_request_hdr hdr;
+ uint32_t data[4];
+ } msg;
+ struct pmic_reply rep;
+ int r;
+
+ if (num > 4)
+ return -EINVAL;
+
+ msg.data[0] = cpu_to_be32(data0);
+ msg.data[1] = cpu_to_be32(data1);
+ msg.data[2] = cpu_to_be32(data2);
+ msg.data[3] = cpu_to_be32(data3);
+
+ r = pmic_rpc(proc, &msg,
+ sizeof(struct rpc_request_hdr) + num * sizeof(uint32_t),
+ &rep, sizeof(rep));
+ if (r < 0)
+ return r;
+ if (r < sizeof(uint32_t))
+ return -EIO;
+
+ return modem_to_linux_err(be32_to_cpu(rep.status));
+}
+
+/**
+ * pmic_rpc_set_struct() - set the whole struct
+ * @xflag: indicates an extra argument
+ * @xdata: the extra argument
+ * @*data: starting address of struct
+ * @size: size of struct
+ * @proc: command/request id
+ *
+ * This fucntion covers case d and e
+ */
+static int pmic_rpc_set_struct(int xflag, uint xdata, uint *data, uint size,
+ int proc)
+{
+ struct {
+ struct rpc_request_hdr hdr;
+ uint32_t data[32+2];
+ } msg;
+ struct pmic_reply rep;
+ int n = 0;
+
+ size = (size + 3) & (~3);
+ if (size > (32 * sizeof(uint32_t)))
+ return -EINVAL;
+
+ if (xflag)
+ msg.data[n++] = cpu_to_be32(xdata);
+
+ msg.data[n++] = cpu_to_be32(1);
+ while (size > 0) {
+ size -= 4;
+ msg.data[n++] = cpu_to_be32(*data++);
+ }
+
+ n = pmic_rpc(proc, &msg,
+ sizeof(struct rpc_request_hdr) + n * sizeof(uint32_t),
+ &rep, sizeof(rep));
+ if (n < 0)
+ return n;
+ if (n < sizeof(uint32_t))
+ return -EIO;
+
+ return modem_to_linux_err(be32_to_cpu(rep.status));
+}
+
+int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id)
+{
+ return pmic_rpc_set_only(cmd, id, 0, 0, 2, LP_MODE_CONTROL_PROC);
+}
+EXPORT_SYMBOL(pmic_lp_mode_control);
+
+int pmic_secure_mpp_control_digital_output(enum mpp_which which,
+ enum mpp_dlogic_level level,
+ enum mpp_dlogic_out_ctrl out)
+{
+ return pmic_rpc_set_only(which, level, out, 0, 3,
+ SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC);
+}
+EXPORT_SYMBOL(pmic_secure_mpp_control_digital_output);
+
+int pmic_secure_mpp_config_i_sink(enum mpp_which which,
+ enum mpp_i_sink_level level,
+ enum mpp_i_sink_switch onoff)
+{
+ return pmic_rpc_set_only(which, level, onoff, 0, 3,
+ SECURE_MPP_CONFIG_I_SINK_PROC);
+}
+EXPORT_SYMBOL(pmic_secure_mpp_config_i_sink);
+
+int pmic_secure_mpp_config_digital_input(enum mpp_which which,
+ enum mpp_dlogic_level level,
+ enum mpp_dlogic_in_dbus dbus)
+{
+ return pmic_rpc_set_only(which, level, dbus, 0, 3,
+ SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC);
+}
+EXPORT_SYMBOL(pmic_secure_mpp_config_digital_input);
+
+int pmic_rtc_start(struct rtc_time *time)
+{
+ return pmic_rpc_set_struct(0, 0, (uint *)time, sizeof(*time),
+ RTC_START_PROC);
+}
+EXPORT_SYMBOL(pmic_rtc_start);
+
+int pmic_rtc_stop(void)
+{
+ return pmic_rpc_set_only(0, 0, 0, 0, 0, RTC_STOP_PROC);
+}
+EXPORT_SYMBOL(pmic_rtc_stop);
+
+int pmic_rtc_enable_alarm(enum rtc_alarm alarm,
+ struct rtc_time *time)
+{
+ return pmic_rpc_set_struct(1, alarm, (uint *)time, sizeof(*time),
+ RTC_ENABLE_ALARM_PROC);
+}
+EXPORT_SYMBOL(pmic_rtc_enable_alarm);
+
+int pmic_rtc_disable_alarm(enum rtc_alarm alarm)
+{
+ return pmic_rpc_set_only(alarm, 0, 0, 0, 1, RTC_DISABLE_ALARM_PROC);
+}
+EXPORT_SYMBOL(pmic_rtc_disable_alarm);
+
+int pmic_rtc_set_time_adjust(uint adjust)
+{
+ return pmic_rpc_set_only(adjust, 0, 0, 0, 1,
+ RTC_SET_TIME_ADJUST_PROC);
+}
+EXPORT_SYMBOL(pmic_rtc_set_time_adjust);
+
+/*
+ * generic speaker
+ */
+int pmic_speaker_cmd(const enum spkr_cmd cmd)
+{
+ return pmic_rpc_set_only(cmd, 0, 0, 0, 1, SPEAKER_CMD_PROC);
+}
+EXPORT_SYMBOL(pmic_speaker_cmd);
+
+int pmic_set_spkr_configuration(struct spkr_config_mode *cfg)
+{
+ return pmic_rpc_set_struct(0, 0, (uint *)cfg, sizeof(*cfg),
+ SET_SPKR_CONFIGURATION_PROC);
+}
+EXPORT_SYMBOL(pmic_set_spkr_configuration);
+
+int pmic_spkr_en_right_chan(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_RIGHT_CHAN_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_right_chan);
+
+int pmic_spkr_en_left_chan(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_LEFT_CHAN_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_left_chan);
+
+int pmic_set_speaker_gain(enum spkr_gain gain)
+{
+ return pmic_rpc_set_only(gain, 0, 0, 0, 1, SET_SPEAKER_GAIN_PROC);
+}
+EXPORT_SYMBOL(pmic_set_speaker_gain);
+
+int pmic_set_speaker_delay(enum spkr_dly delay)
+{
+ return pmic_rpc_set_only(delay, 0, 0, 0, 1, SET_SPEAKER_DELAY_PROC);
+}
+EXPORT_SYMBOL(pmic_set_speaker_delay);
+
+int pmic_speaker_1k6_zin_enable(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1,
+ SPEAKER_1K6_ZIN_ENABLE_PROC);
+}
+EXPORT_SYMBOL(pmic_speaker_1k6_zin_enable);
+
+int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq)
+{
+ return pmic_rpc_set_only(freq, 0, 0, 0, 1,
+ SPKR_SET_MUX_HPF_CORNER_FREQ_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_set_mux_hpf_corner_freq);
+
+int pmic_spkr_select_usb_with_hpf_20hz(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1,
+ SPKR_SELECT_USB_WITH_HPF_20HZ_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_select_usb_with_hpf_20hz);
+
+int pmic_spkr_bypass_mux(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_BYPASS_MUX_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_bypass_mux);
+
+int pmic_spkr_en_hpf(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_HPF_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_hpf);
+
+int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1,
+ SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_sink_curr_from_ref_volt_cir);
+
+/*
+ * speaker indexed by left_right
+ */
+int pmic_spkr_en(enum spkr_left_right left_right, uint enable)
+{
+ return pmic_rpc_set_only(left_right, enable, 0, 0, 2, SPKR_EN_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en);
+
+int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain)
+{
+ return pmic_rpc_set_only(left_right, gain, 0, 0, 2, SPKR_SET_GAIN_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_set_gain);
+
+int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay)
+{
+ return pmic_rpc_set_only(left_right, delay, 0, 0, 2,
+ SPKR_SET_DELAY_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_set_delay);
+
+int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled)
+{
+ return pmic_rpc_set_only(left_right, enabled, 0, 0, 2,
+ SPKR_EN_MUTE_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_mute);
+
+/*
+ * mic
+ */
+int pmic_mic_en(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, MIC_EN_PROC);
+}
+EXPORT_SYMBOL(pmic_mic_en);
+
+int pmic_mic_set_volt(enum mic_volt vol)
+{
+ return pmic_rpc_set_only(vol, 0, 0, 0, 1, MIC_SET_VOLT_PROC);
+}
+EXPORT_SYMBOL(pmic_mic_set_volt);
+
+int pmic_vib_mot_set_volt(uint vol)
+{
+ return pmic_rpc_set_only(vol, 0, 0, 0, 1, VIB_MOT_SET_VOLT_PROC);
+}
+EXPORT_SYMBOL(pmic_vib_mot_set_volt);
+
+int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode)
+{
+ return pmic_rpc_set_only(mode, 0, 0, 0, 1, VIB_MOT_SET_MODE_PROC);
+}
+EXPORT_SYMBOL(pmic_vib_mot_set_mode);
+
+int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol)
+{
+ return pmic_rpc_set_only(pol, 0, 0, 0, 1, VIB_MOT_SET_POLARITY_PROC);
+}
+EXPORT_SYMBOL(pmic_vib_mot_set_polarity);
+
+int pmic_vid_en(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_EN_PROC);
+}
+EXPORT_SYMBOL(pmic_vid_en);
+
+int pmic_vid_load_detect_en(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_LOAD_DETECT_EN_PROC);
+}
+EXPORT_SYMBOL(pmic_vid_load_detect_en);
+
+int pmic_set_led_intensity(enum ledtype type, int level)
+{
+ return pmic_rpc_set_only(type, level, 0, 0, 2, SET_LED_INTENSITY_PROC);
+}
+EXPORT_SYMBOL(pmic_set_led_intensity);
+
+int pmic_flash_led_set_current(const uint16_t milliamps)
+{
+ return pmic_rpc_set_only(milliamps, 0, 0, 0, 1,
+ FLASH_LED_SET_CURRENT_PROC);
+}
+EXPORT_SYMBOL(pmic_flash_led_set_current);
+
+int pmic_flash_led_set_mode(enum flash_led_mode mode)
+{
+ return pmic_rpc_set_only((int)mode, 0, 0, 0, 1,
+ FLASH_LED_SET_MODE_PROC);
+}
+EXPORT_SYMBOL(pmic_flash_led_set_mode);
+
+int pmic_flash_led_set_polarity(enum flash_led_pol pol)
+{
+ return pmic_rpc_set_only((int)pol, 0, 0, 0, 1,
+ FLASH_LED_SET_POLARITY_PROC);
+}
+EXPORT_SYMBOL(pmic_flash_led_set_polarity);
+
+int pmic_spkr_add_right_left_chan(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1,
+ SPKR_ADD_RIGHT_LEFT_CHAN_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_add_right_left_chan);
+
+int pmic_spkr_en_stereo(uint enable)
+{
+ return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_STEREO_PROC);
+}
+EXPORT_SYMBOL(pmic_spkr_en_stereo);
+
diff --git a/arch/arm/mach-msm/pmic.h b/arch/arm/mach-msm/pmic.h
new file mode 100644
index 0000000..14ad789
--- /dev/null
+++ b/arch/arm/mach-msm/pmic.h
@@ -0,0 +1,310 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_PMIC_H
+#define __ARCH_ARM_MACH_PMIC_H
+
+#include "proc_comm.h"
+
+enum spkr_left_right {
+ LEFT_SPKR,
+ RIGHT_SPKR,
+};
+
+enum spkr_gain {
+ SPKR_GAIN_MINUS16DB, /* -16 db */
+ SPKR_GAIN_MINUS12DB, /* -12 db */
+ SPKR_GAIN_MINUS08DB, /* -08 db */
+ SPKR_GAIN_MINUS04DB, /* -04 db */
+ SPKR_GAIN_00DB, /* 00 db */
+ SPKR_GAIN_PLUS04DB, /* +04 db */
+ SPKR_GAIN_PLUS08DB, /* +08 db */
+ SPKR_GAIN_PLUS12DB, /* +12 db */
+};
+
+enum spkr_dly {
+ SPKR_DLY_10MS, /* ~10 ms delay */
+ SPKR_DLY_100MS, /* ~100 ms delay */
+};
+
+enum spkr_hpf_corner_freq {
+ SPKR_FREQ_1_39KHZ, /* 1.39 kHz */
+ SPKR_FREQ_0_64KHZ, /* 0.64 kHz */
+ SPKR_FREQ_0_86KHZ, /* 0.86 kHz */
+ SPKR_FREQ_0_51KHZ, /* 0.51 kHz */
+ SPKR_FREQ_1_06KHZ, /* 1.06 kHz */
+ SPKR_FREQ_0_57KHZ, /* 0.57 kHz */
+ SPKR_FREQ_0_73KHZ, /* 0.73 kHz */
+ SPKR_FREQ_0_47KHZ, /* 0.47 kHz */
+ SPKR_FREQ_1_20KHZ, /* 1.20 kHz */
+ SPKR_FREQ_0_60KHZ, /* 0.60 kHz */
+ SPKR_FREQ_0_76KHZ, /* 0.76 kHz */
+ SPKR_FREQ_0_49KHZ, /* 0.49 kHz */
+ SPKR_FREQ_0_95KHZ, /* 0.95 kHz */
+ SPKR_FREQ_0_54KHZ, /* 0.54 kHz */
+ SPKR_FREQ_0_68KHZ, /* 0.68 kHz */
+ SPKR_FREQ_0_45KHZ, /* 0.45 kHz */
+};
+
+/* Turn the speaker on or off and enables or disables mute.*/
+enum spkr_cmd {
+ SPKR_DISABLE, /* Enable Speaker */
+ SPKR_ENABLE, /* Disable Speaker */
+ SPKR_MUTE_OFF, /* turn speaker mute off, SOUND ON */
+ SPKR_MUTE_ON, /* turn speaker mute on, SOUND OFF */
+ SPKR_OFF, /* turn speaker OFF (speaker disable and mute on) */
+ SPKR_ON, /* turn speaker ON (speaker enable and mute off) */
+ SPKR_SET_FREQ_CMD, /* set speaker frequency */
+ SPKR_GET_FREQ_CMD, /* get speaker frequency */
+ SPKR_SET_GAIN_CMD, /* set speaker gain */
+ SPKR_GET_GAIN_CMD, /* get speaker gain */
+ SPKR_SET_DELAY_CMD, /* set speaker delay */
+ SPKR_GET_DELAY_CMD, /* get speaker delay */
+ SPKR_SET_PDM_MODE,
+ SPKR_SET_PWM_MODE,
+};
+
+struct spkr_config_mode {
+ uint32_t is_right_chan_en;
+ uint32_t is_left_chan_en;
+ uint32_t is_right_left_chan_added;
+ uint32_t is_stereo_en;
+ uint32_t is_usb_with_hpf_20hz;
+ uint32_t is_mux_bypassed;
+ uint32_t is_hpf_en;
+ uint32_t is_sink_curr_from_ref_volt_cir_en;
+};
+
+enum mic_volt {
+ MIC_VOLT_2_00V, /* 2.00 V */
+ MIC_VOLT_1_93V, /* 1.93 V */
+ MIC_VOLT_1_80V, /* 1.80 V */
+ MIC_VOLT_1_73V, /* 1.73 V */
+};
+
+enum ledtype {
+ LED_LCD,
+ LED_KEYPAD,
+};
+
+enum flash_led_mode {
+ FLASH_LED_MODE__MANUAL,
+ FLASH_LED_MODE__DBUS1,
+ FLASH_LED_MODE__DBUS2,
+ FLASH_LED_MODE__DBUS3,
+};
+
+enum flash_led_pol {
+ FLASH_LED_POL__ACTIVE_HIGH,
+ FLASH_LED_POL__ACTIVE_LOW,
+};
+
+enum switch_cmd {
+ OFF_CMD,
+ ON_CMD
+};
+
+enum vreg_lp_id {
+ PM_VREG_LP_MSMA_ID,
+ PM_VREG_LP_MSMP_ID,
+ PM_VREG_LP_MSME1_ID,
+ PM_VREG_LP_GP3_ID,
+ PM_VREG_LP_MSMC_ID,
+ PM_VREG_LP_MSME2_ID,
+ PM_VREG_LP_GP4_ID,
+ PM_VREG_LP_GP1_ID,
+ PM_VREG_LP_RFTX_ID,
+ PM_VREG_LP_RFRX1_ID,
+ PM_VREG_LP_RFRX2_ID,
+ PM_VREG_LP_WLAN_ID,
+ PM_VREG_LP_MMC_ID,
+ PM_VREG_LP_RUIM_ID,
+ PM_VREG_LP_MSMC0_ID,
+ PM_VREG_LP_GP2_ID,
+ PM_VREG_LP_GP5_ID,
+ PM_VREG_LP_GP6_ID,
+ PM_VREG_LP_MPLL_ID,
+ PM_VREG_LP_RFUBM_ID,
+ PM_VREG_LP_RFA_ID,
+ PM_VREG_LP_CDC2_ID,
+ PM_VREG_LP_RFTX2_ID,
+ PM_VREG_LP_USIM_ID,
+ PM_VREG_LP_USB2P6_ID,
+ PM_VREG_LP_TCXO_ID,
+ PM_VREG_LP_USB3P3_ID,
+
+ PM_VREG_LP_MSME_ID = PM_VREG_LP_MSME1_ID,
+ /* backward compatible enums only */
+ PM_VREG_LP_CAM_ID = PM_VREG_LP_GP1_ID,
+ PM_VREG_LP_MDDI_ID = PM_VREG_LP_GP2_ID,
+ PM_VREG_LP_RUIM2_ID = PM_VREG_LP_GP3_ID,
+ PM_VREG_LP_AUX_ID = PM_VREG_LP_GP4_ID,
+ PM_VREG_LP_AUX2_ID = PM_VREG_LP_GP5_ID,
+ PM_VREG_LP_BT_ID = PM_VREG_LP_GP6_ID,
+ PM_VREG_LP_MSMC_LDO_ID = PM_VREG_LP_MSMC_ID,
+ PM_VREG_LP_MSME1_LDO_ID = PM_VREG_LP_MSME1_ID,
+ PM_VREG_LP_MSME2_LDO_ID = PM_VREG_LP_MSME2_ID,
+ PM_VREG_LP_RFA1_ID = PM_VREG_LP_RFRX2_ID,
+ PM_VREG_LP_RFA2_ID = PM_VREG_LP_RFTX2_ID,
+ PM_VREG_LP_XO_ID = PM_VREG_LP_TCXO_ID
+};
+
+enum mpp_which {
+ PM_MPP_1,
+ PM_MPP_2,
+ PM_MPP_3,
+ PM_MPP_4,
+ PM_MPP_5,
+ PM_MPP_6,
+ PM_MPP_7,
+ PM_MPP_8,
+ PM_MPP_9,
+ PM_MPP_10,
+ PM_MPP_11,
+ PM_MPP_12,
+ PM_MPP_13,
+ PM_MPP_14,
+ PM_MPP_15,
+ PM_MPP_16,
+ PM_MPP_17,
+ PM_MPP_18,
+ PM_MPP_19,
+ PM_MPP_20,
+ PM_MPP_21,
+ PM_MPP_22,
+
+ PM_NUM_MPP_HAN = PM_MPP_4 + 1,
+ PM_NUM_MPP_KIP = PM_MPP_4 + 1,
+ PM_NUM_MPP_EPIC = PM_MPP_4 + 1,
+ PM_NUM_MPP_PM7500 = PM_MPP_22 + 1,
+ PM_NUM_MPP_PM6650 = PM_MPP_12 + 1,
+ PM_NUM_MPP_PM6658 = PM_MPP_12 + 1,
+ PM_NUM_MPP_PANORAMIX = PM_MPP_2 + 1,
+ PM_NUM_MPP_PM6640 = PM_NUM_MPP_PANORAMIX,
+ PM_NUM_MPP_PM6620 = PM_NUM_MPP_PANORAMIX
+};
+
+enum mpp_dlogic_level {
+ PM_MPP__DLOGIC__LVL_MSME,
+ PM_MPP__DLOGIC__LVL_MSMP,
+ PM_MPP__DLOGIC__LVL_RUIM,
+ PM_MPP__DLOGIC__LVL_MMC,
+ PM_MPP__DLOGIC__LVL_VDD,
+};
+
+enum mpp_dlogic_in_dbus {
+ PM_MPP__DLOGIC_IN__DBUS_NONE,
+ PM_MPP__DLOGIC_IN__DBUS1,
+ PM_MPP__DLOGIC_IN__DBUS2,
+ PM_MPP__DLOGIC_IN__DBUS3,
+};
+
+enum mpp_dlogic_out_ctrl {
+ PM_MPP__DLOGIC_OUT__CTRL_LOW,
+ PM_MPP__DLOGIC_OUT__CTRL_HIGH,
+ PM_MPP__DLOGIC_OUT__CTRL_MPP,
+ PM_MPP__DLOGIC_OUT__CTRL_NOT_MPP,
+};
+
+enum mpp_i_sink_level {
+ PM_MPP__I_SINK__LEVEL_5mA,
+ PM_MPP__I_SINK__LEVEL_10mA,
+ PM_MPP__I_SINK__LEVEL_15mA,
+ PM_MPP__I_SINK__LEVEL_20mA,
+ PM_MPP__I_SINK__LEVEL_25mA,
+ PM_MPP__I_SINK__LEVEL_30mA,
+ PM_MPP__I_SINK__LEVEL_35mA,
+ PM_MPP__I_SINK__LEVEL_40mA,
+};
+
+enum mpp_i_sink_switch {
+ PM_MPP__I_SINK__SWITCH_DIS,
+ PM_MPP__I_SINK__SWITCH_ENA,
+ PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_HIGH,
+ PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_LOW,
+};
+
+enum pm_vib_mot_mode {
+ PM_VIB_MOT_MODE__MANUAL,
+ PM_VIB_MOT_MODE__DBUS1,
+ PM_VIB_MOT_MODE__DBUS2,
+ PM_VIB_MOT_MODE__DBUS3,
+};
+
+enum pm_vib_mot_pol {
+ PM_VIB_MOT_POL__ACTIVE_HIGH,
+ PM_VIB_MOT_POL__ACTIVE_LOW,
+};
+
+struct rtc_time {
+ uint sec;
+};
+
+enum rtc_alarm {
+ PM_RTC_ALARM_1,
+};
+
+
+int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id);
+int pmic_secure_mpp_control_digital_output(enum mpp_which which,
+ enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out);
+int pmic_secure_mpp_config_i_sink(enum mpp_which which,
+ enum mpp_i_sink_level level, enum mpp_i_sink_switch onoff);
+int pmic_secure_mpp_config_digital_input(enum mpp_which which,
+ enum mpp_dlogic_level level, enum mpp_dlogic_in_dbus dbus);
+int pmic_speaker_cmd(const enum spkr_cmd cmd);
+int pmic_set_spkr_configuration(struct spkr_config_mode *cfg);
+int pmic_spkr_en_right_chan(uint enable);
+int pmic_spkr_en_left_chan(uint enable);
+int pmic_spkr_en(enum spkr_left_right left_right, uint enabled);
+int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain);
+int pmic_set_speaker_gain(enum spkr_gain gain);
+int pmic_set_speaker_delay(enum spkr_dly delay);
+int pmic_speaker_1k6_zin_enable(uint enable);
+int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq);
+int pmic_spkr_select_usb_with_hpf_20hz(uint enable);
+int pmic_spkr_bypass_mux(uint enable);
+int pmic_spkr_en_hpf(uint enable);
+int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable);
+int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay);
+int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled);
+int pmic_mic_en(uint enable);
+int pmic_mic_set_volt(enum mic_volt vol);
+int pmic_set_led_intensity(enum ledtype type, int level);
+int pmic_flash_led_set_current(uint16_t milliamps);
+int pmic_flash_led_set_mode(enum flash_led_mode mode);
+int pmic_flash_led_set_polarity(enum flash_led_pol pol);
+int pmic_spkr_add_right_left_chan(uint enable);
+int pmic_spkr_en_stereo(uint enable);
+int pmic_vib_mot_set_volt(uint vol);
+int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode);
+int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol);
+int pmic_vid_en(uint enable);
+int pmic_vid_load_detect_en(uint enable);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile
new file mode 100644
index 0000000..991d4a7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/Makefile
@@ -0,0 +1,17 @@
+obj-y += adsp.o
+ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y)
+obj-y += adsp_info.o
+obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o
+else
+obj-y += adsp_6225.o
+endif
+
+obj-y += adsp_driver.o
+obj-y += adsp_video_verify_cmd.o
+obj-y += adsp_videoenc_verify_cmd.o
+obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o
+obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o
+obj-y += adsp_lpm_verify_cmd.o
+obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o
+obj-y += snd.o
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c
new file mode 100644
index 0000000..9dc8945
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp.c
@@ -0,0 +1,1183 @@
+/* arch/arm/mach-msm/qdsp5/adsp.c
+ *
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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.
+ *
+ */
+
+/* TODO:
+ * - move shareable rpc code outside of adsp.c
+ * - general solution for virt->phys patchup
+ * - queue IDs should be relative to modules
+ * - disallow access to non-associated queues
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+
+static struct wake_lock adsp_wake_lock;
+static inline void prevent_suspend(void)
+{
+ wake_lock(&adsp_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+ wake_unlock(&adsp_wake_lock);
+}
+
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include "adsp.h"
+
+#define INT_ADSP INT_ADSP_A9_A11
+
+static struct adsp_info adsp_info;
+static struct msm_rpc_endpoint *rpc_cb_server_client;
+static struct msm_adsp_module *adsp_modules;
+static int adsp_open_count;
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+
+static uint32_t current_image = -1;
+
+void adsp_set_image(struct adsp_info *info, uint32_t image)
+{
+ current_image = image;
+}
+
+/*
+ * Checks whether the module_id is available in the
+ * module_entries table.If module_id is available returns `0`.
+ * If module_id is not available returns `-ENXIO`.
+ */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int32_t adsp_validate_module(uint32_t module_id)
+{
+ uint32_t *ptr;
+ uint32_t module_index;
+ uint32_t num_mod_entries;
+
+ ptr = adsp_info.init_info_ptr->module_entries;
+ num_mod_entries = adsp_info.init_info_ptr->module_table_size;
+
+ for (module_index = 0; module_index < num_mod_entries; module_index++)
+ if (module_id == ptr[module_index])
+ return 0;
+
+ return -ENXIO;
+}
+#else
+static inline int32_t adsp_validate_module(uint32_t module_id) { return 0; }
+#endif
+
+uint32_t adsp_get_module(struct adsp_info *info, uint32_t task)
+{
+ BUG_ON(current_image == -1UL);
+ return info->task_to_module[current_image][task];
+}
+
+uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id)
+{
+ BUG_ON(current_image == -1UL);
+ return info->queue_offset[current_image][queue_id];
+}
+
+static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module,
+ struct msm_adsp_module *adsp_module)
+{
+ int rc;
+ struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+ struct rpc_reply_hdr *rpc_rsp;
+
+ msm_rpc_setup_req(&rpc_req.hdr,
+ RPC_ADSP_RTOS_ATOM_PROG,
+ msm_rpc_get_vers(adsp_module->rpc_client),
+ RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+ rpc_req.gotit = cpu_to_be32(1);
+ rpc_req.cmd = cpu_to_be32(cmd);
+ rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+ rpc_req.module = cpu_to_be32(module);
+ rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req));
+ if (rc < 0) {
+ pr_err("adsp: could not send RPC request: %d\n", rc);
+ return rc;
+ }
+
+ rc = msm_rpc_read(adsp_module->rpc_client,
+ (void **)&rpc_rsp, -1, (5*HZ));
+ if (rc < 0) {
+ pr_err("adsp: error receiving RPC reply: %d (%d)\n",
+ rc, -ERESTARTSYS);
+ return rc;
+ }
+
+ if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) {
+ pr_err("adsp: RPC call was denied!\n");
+ kfree(rpc_rsp);
+ return -EPERM;
+ }
+
+ if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) !=
+ RPC_ACCEPTSTAT_SUCCESS) {
+ pr_err("adsp error: RPC call was not successful (%d)\n",
+ be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat));
+ kfree(rpc_rsp);
+ return -EINVAL;
+ }
+
+ kfree(rpc_rsp);
+ return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int get_module_index(uint32_t id)
+{
+ int mod_idx;
+ for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++)
+ if (adsp_info.module[mod_idx].id == id)
+ return mod_idx;
+
+ return -ENXIO;
+}
+#endif
+
+static struct msm_adsp_module *find_adsp_module_by_id(
+ struct adsp_info *info, uint32_t id)
+{
+ if (id > info->max_module_id) {
+ return NULL;
+ } else {
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ id = get_module_index(id);
+ if (id < 0)
+ return NULL;
+#endif
+ return info->id_to_module[id];
+ }
+}
+
+static struct msm_adsp_module *find_adsp_module_by_name(
+ struct adsp_info *info, const char *name)
+{
+ unsigned n;
+ for (n = 0; n < info->module_count; n++)
+ if (!strcmp(name, adsp_modules[n].name))
+ return adsp_modules + n;
+ return NULL;
+}
+
+static int adsp_rpc_init(struct msm_adsp_module *adsp_module)
+{
+ /* remove the original connect once compatible support is complete */
+ adsp_module->rpc_client = msm_rpc_connect(
+ RPC_ADSP_RTOS_ATOM_PROG,
+ RPC_ADSP_RTOS_ATOM_VERS,
+ MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE);
+
+ if (IS_ERR(adsp_module->rpc_client)) {
+ int rc = PTR_ERR(adsp_module->rpc_client);
+ adsp_module->rpc_client = 0;
+ pr_err("adsp: could not open rpc client: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+/*
+ * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get
+ * queue offsets and module entries (init info) as part of the event.
+ */
+static void msm_get_init_info(void)
+{
+ int rc;
+ struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+
+ adsp_info.init_info_rpc_client = msm_rpc_connect(
+ RPC_ADSP_RTOS_ATOM_PROG,
+ RPC_ADSP_RTOS_ATOM_VERS,
+ MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE);
+ if (IS_ERR(adsp_info.init_info_rpc_client)) {
+ rc = PTR_ERR(adsp_info.init_info_rpc_client);
+ adsp_info.init_info_rpc_client = 0;
+ pr_err("adsp: could not open rpc client: %d\n", rc);
+ return;
+ }
+
+ msm_rpc_setup_req(&rpc_req.hdr,
+ RPC_ADSP_RTOS_ATOM_PROG,
+ msm_rpc_get_vers(adsp_info.init_info_rpc_client),
+ RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+ rpc_req.gotit = cpu_to_be32(1);
+ rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO);
+ rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+ rpc_req.module = 0;
+
+ rc = msm_rpc_write(adsp_info.init_info_rpc_client,
+ &rpc_req, sizeof(rpc_req));
+ if (rc < 0)
+ pr_err("adsp: could not send RPC request: %d\n", rc);
+}
+#endif
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **out,
+ struct msm_adsp_ops *ops, void *driver_data)
+{
+ struct msm_adsp_module *module;
+ int rc = 0;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ static uint32_t init_info_cmd_sent;
+ if (!init_info_cmd_sent) {
+ msm_get_init_info();
+ init_waitqueue_head(&adsp_info.init_info_wait);
+ rc = wait_event_timeout(adsp_info.init_info_wait,
+ adsp_info.init_info_state == ADSP_STATE_INIT_INFO,
+ 5 * HZ);
+ if (!rc) {
+ pr_info("adsp: INIT_INFO failed\n");
+ return -ETIMEDOUT;
+ }
+ init_info_cmd_sent++;
+ }
+#endif
+
+ module = find_adsp_module_by_name(&adsp_info, name);
+ if (!module)
+ return -ENODEV;
+
+ mutex_lock(&module->lock);
+ pr_info("adsp: opening module %s\n", module->name);
+ if (module->open_count++ == 0 && module->clk)
+ clk_enable(module->clk);
+
+ mutex_lock(&adsp_open_lock);
+ if (adsp_open_count++ == 0) {
+ enable_irq(INT_ADSP);
+ prevent_suspend();
+ }
+ mutex_unlock(&adsp_open_lock);
+
+ if (module->ops) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ rc = adsp_rpc_init(module);
+ if (rc)
+ goto done;
+
+ module->ops = ops;
+ module->driver_data = driver_data;
+ *out = module;
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ module->id, module);
+ if (rc) {
+ module->ops = NULL;
+ module->driver_data = NULL;
+ *out = NULL;
+ pr_err("adsp: REGISTER_APP failed\n");
+ goto done;
+ }
+
+ pr_info("adsp: module %s has been registered\n", module->name);
+
+done:
+ mutex_lock(&adsp_open_lock);
+ if (rc && --adsp_open_count == 0) {
+ disable_irq(INT_ADSP);
+ allow_suspend();
+ }
+ if (rc && --module->open_count == 0 && module->clk)
+ clk_disable(module->clk);
+ mutex_unlock(&adsp_open_lock);
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_get);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module);
+
+void msm_adsp_put(struct msm_adsp_module *module)
+{
+ unsigned long flags;
+
+ mutex_lock(&module->lock);
+ if (--module->open_count == 0 && module->clk)
+ clk_disable(module->clk);
+ if (module->ops) {
+ pr_info("adsp: closing module %s\n", module->name);
+
+ /* lock to ensure a dsp event cannot be delivered
+ * during or after removal of the ops and driver_data
+ */
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+ module->ops = NULL;
+ module->driver_data = NULL;
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+
+ if (module->state != ADSP_STATE_DISABLED) {
+ pr_info("adsp: disabling module %s\n", module->name);
+ msm_adsp_disable_locked(module);
+ }
+
+ msm_rpc_close(module->rpc_client);
+ module->rpc_client = 0;
+ if (--adsp_open_count == 0) {
+ disable_irq(INT_ADSP);
+ allow_suspend();
+ pr_info("adsp: disable interrupt\n");
+ }
+ } else {
+ pr_info("adsp: module %s is already closed\n", module->name);
+ }
+ mutex_unlock(&module->lock);
+}
+EXPORT_SYMBOL(msm_adsp_put);
+
+/* this should be common code with rpc_servers.c */
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+ uint32_t xid, uint32_t accept_status)
+{
+ int rc = 0;
+ uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+ struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+ reply->xid = cpu_to_be32(xid);
+ reply->type = cpu_to_be32(1); /* reply */
+ reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+ reply->data.acc_hdr.verf_flavor = 0;
+ reply->data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf));
+ if (rc < 0)
+ pr_err("adsp: could not write RPC response: %d\n", rc);
+ return rc;
+}
+
+int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ uint32_t ctrl_word;
+ uint32_t dsp_q_addr;
+ uint32_t dsp_addr;
+ uint32_t cmd_id = 0;
+ int cnt = 0;
+ int ret_status = 0;
+ unsigned long flags;
+ struct adsp_info *info = module->info;
+
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+ if (module->state != ADSP_STATE_ENABLED) {
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ pr_err("adsp: module %s not enabled before write\n",
+ module->name);
+ return -ENODEV;
+ }
+ if (adsp_validate_module(module->id)) {
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ pr_info("adsp: module id validation failed %s %d\n",
+ module->name, module->id);
+ return -ENXIO;
+ }
+ dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr);
+ dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M;
+
+ /* Poll until the ADSP is ready to accept a command.
+ * Wait for 100us, return error if it's not responding.
+ * If this returns an error, we need to disable ALL modules and
+ * then retry.
+ */
+ while (((ctrl_word = readl(info->write_ctrl)) &
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_V) {
+ if (cnt > 100) {
+ pr_err("adsp: timeout waiting for DSP write ready\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ pr_warning("adsp: waiting for DSP write ready\n");
+ udelay(1);
+ cnt++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Clear the command bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. This notifies the DSP that
+ * we are about to send a command on this particular queue. The
+ * DSP will in response change its state.
+ */
+ writel(1, info->send_irq);
+
+ /* Poll until the adsp responds to the interrupt; this does not
+ * generate an interrupt from the adsp. This should happen within
+ * 5ms.
+ */
+ cnt = 0;
+ while ((readl(info->write_ctrl) &
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) ==
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) {
+ if (cnt > 5000) {
+ pr_err("adsp: timeout waiting for adsp ack\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ udelay(1);
+ cnt++;
+ }
+
+ /* Read the ctrl word */
+ ctrl_word = readl(info->write_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) {
+ ret_status = -EAGAIN;
+ goto fail;
+ }
+
+ /* Ctrl word status bits were 00, no error in the ctrl word */
+
+ /* Get the DSP buffer address */
+ dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE;
+
+ if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint16_t *buf_ptr = (uint16_t *) cmd_buf;
+ uint16_t *dsp_addr16 = (uint16_t *)dsp_addr;
+ cmd_size /= sizeof(uint16_t);
+
+ /* Save the command ID */
+ cmd_id = (uint32_t) buf_ptr[0];
+
+ /* Copy the command to DSP memory */
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr16++ = *buf_ptr++;
+ } else {
+ uint32_t *buf_ptr = (uint32_t *) cmd_buf;
+ uint32_t *dsp_addr32 = (uint32_t *)dsp_addr;
+ cmd_size /= sizeof(uint32_t);
+
+ /* Save the command ID */
+ cmd_id = buf_ptr[0];
+
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr32++ = *buf_ptr++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Set the command bits to write done */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V;
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. It does not respond with
+ * an interrupt, and we do not need to wait for it to
+ * acknowledge, because it will hold the mutex lock until it's
+ * ready to receive more commands again.
+ */
+ writel(1, info->send_irq);
+
+ module->num_commands++;
+
+fail:
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ return ret_status;
+}
+EXPORT_SYMBOL(msm_adsp_write);
+
+int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ int rc, retries = 0;
+ do {
+ rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size);
+ if (rc == -EAGAIN)
+ udelay(10);
+ } while(rc == -EAGAIN && retries++ < 100);
+ if (retries > 50)
+ pr_warning("adsp: %s command took %d attempts: rc %d\n",
+ module->name, retries, rc);
+ return rc;
+}
+
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+static void *modem_event_addr;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static void read_modem_event(void *buf, size_t size)
+{
+ uint32_t *dptr = buf;
+ struct rpc_adsp_rtos_modem_to_app_args_t *sptr;
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr;
+ size_t len = size / 4;
+
+ if (len < 3) {
+ pr_err("%s: invalid length %d\n", __func__, len);
+ return;
+ }
+
+ sptr = modem_event_addr;
+ pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+ dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event);
+ dptr[1] = be32_to_cpu(pkt_ptr->module);
+ dptr[2] = be32_to_cpu(pkt_ptr->image);
+}
+#else
+static void read_modem_event(void *buf, size_t size)
+{
+ uint32_t *dptr = buf;
+ struct rpc_adsp_rtos_modem_to_app_args_t *sptr =
+ modem_event_addr;
+ size_t len = size / 4;
+ if (len < 3) {
+ pr_err("%s: invalid length %d\n", __func__, len);
+ return;
+ }
+ dptr[0] = be32_to_cpu(sptr->event);
+ dptr[1] = be32_to_cpu(sptr->module);
+ dptr[2] = be32_to_cpu(sptr->image);
+}
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+#endif /* CONFIG_MSM_ADSP_REPORT_EVENTS */
+
+static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req)
+{
+ struct rpc_adsp_rtos_modem_to_app_args_t *args =
+ (struct rpc_adsp_rtos_modem_to_app_args_t *)req;
+ uint32_t event;
+ uint32_t proc_id;
+ uint32_t module_id;
+ uint32_t image;
+ struct msm_adsp_module *module;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr =
+ &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+ event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event);
+ proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id);
+ module_id = be32_to_cpu(pkt_ptr->module);
+ image = be32_to_cpu(pkt_ptr->image);
+
+ if (be32_to_cpu(args->mtoa_pkt.desc_field) == RPC_ADSP_RTOS_INIT_INFO) {
+ struct queue_to_offset_type *qptr;
+ struct queue_to_offset_type *qtbl;
+ uint32_t *mptr;
+ uint32_t *mtbl;
+ uint32_t q_idx;
+ uint32_t num_entries;
+ uint32_t entries_per_image;
+ struct adsp_rtos_mp_mtoa_init_info_type *iptr;
+ struct adsp_rtos_mp_mtoa_init_info_type *sptr;
+ int32_t i_no, e_idx;
+
+ pr_info("adsp:INIT_INFO Event\n");
+ sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.
+ mp_mtoa_init_packet;
+
+ iptr = adsp_info.init_info_ptr;
+ iptr->image_count = be32_to_cpu(sptr->image_count);
+ iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets);
+ num_entries = iptr->num_queue_offsets;
+ qptr = &sptr->queue_offsets_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ qtbl = &iptr->queue_offsets_tbl[i_no][0];
+ for (e_idx = 0; e_idx < num_entries; e_idx++) {
+ qtbl[e_idx].offset = be32_to_cpu(qptr->offset);
+ qtbl[e_idx].queue = be32_to_cpu(qptr->queue);
+ q_idx = be32_to_cpu(qptr->queue);
+ iptr->queue_offsets[i_no][q_idx] =
+ qtbl[e_idx].offset;
+ qptr++;
+ }
+ }
+
+ num_entries = be32_to_cpu(sptr->num_task_module_entries);
+ iptr->num_task_module_entries = num_entries;
+ entries_per_image = num_entries / iptr->image_count;
+ mptr = &sptr->task_to_module_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ mtbl = &iptr->task_to_module_tbl[i_no][0];
+ for (e_idx = 0; e_idx < entries_per_image; e_idx++) {
+ mtbl[e_idx] = be32_to_cpu(*mptr);
+ mptr++;
+ }
+ }
+
+ iptr->module_table_size = be32_to_cpu(sptr->module_table_size);
+ mptr = &sptr->module_entries[0];
+ for (i_no = 0; i_no < iptr->module_table_size; i_no++)
+ iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]);
+ adsp_info.init_info_state = ADSP_STATE_INIT_INFO;
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ wake_up(&adsp_info.init_info_wait);
+
+ return;
+ }
+#else
+ event = be32_to_cpu(args->event);
+ proc_id = be32_to_cpu(args->proc_id);
+ module_id = be32_to_cpu(args->module);
+ image = be32_to_cpu(args->image);
+#endif
+
+ pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n",
+ event, proc_id, module_id, image);
+
+ module = find_adsp_module_by_id(&adsp_info, module_id);
+ if (!module) {
+ pr_err("adsp: module %d is not supported!\n", module_id);
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_GARBAGE_ARGS);
+ return;
+ }
+
+ mutex_lock(&module->lock);
+ switch (event) {
+ case RPC_ADSP_RTOS_MOD_READY:
+ pr_info("adsp: module %s: READY\n", module->name);
+ module->state = ADSP_STATE_ENABLED;
+ wake_up(&module->state_wait);
+ adsp_set_image(module->info, image);
+ break;
+ case RPC_ADSP_RTOS_MOD_DISABLE:
+ pr_info("adsp: module %s: DISABLED\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_SERVICE_RESET:
+ pr_info("adsp: module %s: SERVICE_RESET\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_CMD_SUCCESS:
+ pr_info("adsp: module %s: CMD_SUCCESS\n", module->name);
+ break;
+ case RPC_ADSP_RTOS_CMD_FAIL:
+ pr_info("adsp: module %s: CMD_FAIL\n", module->name);
+ break;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ case RPC_ADSP_RTOS_DISABLE_FAIL:
+ pr_info("adsp: module %s: DISABLE_FAIL\n", module->name);
+ break;
+#endif
+ default:
+ pr_info("adsp: unknown event %d\n", event);
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_GARBAGE_ARGS);
+ mutex_unlock(&module->lock);
+ return;
+ }
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ mutex_unlock(&module->lock);
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+ if (module->ops != NULL && module->ops->event != NULL) {
+ modem_event_addr = (uint32_t *)req;
+ module->ops->event(module->driver_data, EVENT_MSG_ID,
+ EVENT_LEN, read_modem_event);
+ }
+#endif
+}
+
+static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req)
+{
+ switch (req->procedure) {
+ case RPC_ADSP_RTOS_MTOA_NULL_PROC:
+ rpc_send_accepted_void_reply(rpc_cb_server_client,
+ req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ break;
+ case RPC_ADSP_RTOS_MODEM_TO_APP_PROC:
+ handle_adsp_rtos_mtoa_app(req);
+ break;
+ default:
+ pr_err("adsp: unknowned proc %d\n", req->procedure);
+ rpc_send_accepted_void_reply(
+ rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_PROC_UNAVAIL);
+ break;
+ }
+ return 0;
+}
+
+/* this should be common code with rpc_servers.c */
+static int adsp_rpc_thread(void *data)
+{
+ void *buffer;
+ struct rpc_request_hdr *req;
+ int rc;
+
+ do {
+ rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1);
+ if (rc < 0) {
+ pr_err("adsp: could not read rpc: %d\n", rc);
+ break;
+ }
+ req = (struct rpc_request_hdr *)buffer;
+
+ req->type = be32_to_cpu(req->type);
+ req->xid = be32_to_cpu(req->xid);
+ req->rpc_vers = be32_to_cpu(req->rpc_vers);
+ req->prog = be32_to_cpu(req->prog);
+ req->vers = be32_to_cpu(req->vers);
+ req->procedure = be32_to_cpu(req->procedure);
+
+ if (req->type != 0)
+ goto bad_rpc;
+ if (req->rpc_vers != 2)
+ goto bad_rpc;
+ if (req->prog != RPC_ADSP_RTOS_MTOA_PROG)
+ goto bad_rpc;
+ if (req->vers != RPC_ADSP_RTOS_MTOA_VERS)
+ goto bad_rpc;
+
+ handle_adsp_rtos_mtoa(req);
+ kfree(buffer);
+ continue;
+
+bad_rpc:
+ pr_err("adsp: bogus rpc from modem\n");
+ kfree(buffer);
+ } while (1);
+
+ do_exit(0);
+}
+
+static size_t read_event_len;
+static void *read_event_addr;
+
+static void read_event_16(void *buf, size_t size)
+{
+ uint16_t *dst = buf;
+ uint16_t *src = read_event_addr;
+ size_t len = size / 2;
+ if (len > read_event_len)
+ len = read_event_len;
+ else if (len < read_event_len)
+ pr_warning("%s: event bufer length too small (%d < %d)\n",
+ __func__, len, read_event_len);
+ while (len--)
+ *dst++ = *src++;
+}
+
+static void read_event_32(void *buf, size_t size)
+{
+ uint32_t *dst = buf;
+ uint32_t *src = read_event_addr;
+ size_t len = size / 4;
+ if (len > read_event_len)
+ len = read_event_len;
+ else if (len < read_event_len)
+ pr_warning("%s: event bufer length too small (%d < %d)\n",
+ __func__, len, read_event_len);
+ while (len--)
+ *dst++ = *src++;
+}
+
+static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(
+ struct adsp_info *info, void *dsp_addr)
+{
+ struct msm_adsp_module *module;
+ unsigned rtos_task_id;
+ unsigned msg_id;
+ unsigned msg_length;
+ void (*func)(void *, size_t);
+
+ if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint32_t *dsp_addr32 = dsp_addr;
+ uint32_t tmp = *dsp_addr32++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M);
+ read_event_len = tmp >> 16;
+ read_event_addr = dsp_addr32;
+ msg_length = read_event_len * sizeof(uint32_t);
+ func = read_event_32;
+ } else {
+ uint16_t *dsp_addr16 = dsp_addr;
+ uint16_t tmp = *dsp_addr16++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M;
+ read_event_len = *dsp_addr16++;
+ read_event_addr = dsp_addr16;
+ msg_length = read_event_len * sizeof(uint16_t);
+ func = read_event_16;
+ }
+
+ if (rtos_task_id > info->max_task_id) {
+ pr_err("adsp: bogus task id %d\n", rtos_task_id);
+ return 0;
+ }
+ module = find_adsp_module_by_id(info,
+ adsp_get_module(info, rtos_task_id));
+
+ if (!module) {
+ pr_err("adsp: no module for task id %d\n", rtos_task_id);
+ return 0;
+ }
+
+ module->num_events++;
+
+ if (!module->ops) {
+ pr_err("adsp: module %s is not open\n", module->name);
+ return 0;
+ }
+
+ module->ops->event(module->driver_data, msg_id, msg_length, func);
+ return 0;
+}
+
+static int adsp_get_event(struct adsp_info *info)
+{
+ uint32_t ctrl_word;
+ uint32_t ready;
+ void *dsp_addr;
+ uint32_t cmd_type;
+ int cnt;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+ /* Whenever the DSP has a message, it updates this control word
+ * and generates an interrupt. When we receive the interrupt, we
+ * read this register to find out what ADSP task the command is
+ * comming from.
+ *
+ * The ADSP should *always* be ready on the first call, but the
+ * irq handler calls us in a loop (to handle back-to-back command
+ * processing), so we give the DSP some time to return to the
+ * ready state. The DSP will not issue another IRQ for events
+ * pending between the first IRQ and the event queue being drained,
+ * unfortunately.
+ */
+
+ for (cnt = 0; cnt < 10; cnt++) {
+ ctrl_word = readl(info->read_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) ==
+ ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V)
+ goto ready;
+
+ udelay(10);
+ }
+ pr_warning("adsp: not ready after 100uS\n");
+ rc = -EBUSY;
+ goto done;
+
+ready:
+ /* Here we check to see if there are pending messages. If there are
+ * none, we siply return -EAGAIN to indicate that there are no more
+ * messages pending.
+ */
+ ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M;
+ if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) &&
+ (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) {
+ rc = -EAGAIN;
+ goto done;
+ }
+
+ /* DSP says that there are messages waiting for the host to read */
+
+ /* Get the Command Type */
+ cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M;
+
+ /* Get the DSP buffer address */
+ dsp_addr = (void *)((ctrl_word &
+ ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE);
+
+ /* We can only handle Task-to-Host messages */
+ if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) {
+ pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type);
+ rc = -EIO;
+ goto done;
+ }
+
+ adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr);
+
+ ctrl_word = readl(info->read_ctrl);
+ ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M;
+
+ /* Write ctrl word to the DSP */
+ writel(ctrl_word, info->read_ctrl);
+
+ /* Generate an interrupt to the DSP */
+ writel(1, info->send_irq);
+
+done:
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ return rc;
+}
+
+static irqreturn_t adsp_irq_handler(int irq, void *data)
+{
+ struct adsp_info *info = &adsp_info;
+ int cnt = 0;
+ for (cnt = 0; cnt < 10; cnt++)
+ if (adsp_get_event(info) < 0)
+ break;
+ if (cnt > info->event_backlog_max)
+ info->event_backlog_max = cnt;
+ info->events_received += cnt;
+ if (cnt == 10)
+ pr_err("adsp: too many (%d) events for single irq!\n", cnt);
+ return IRQ_HANDLED;
+}
+
+int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate)
+{
+ if (module->clk && clk_rate)
+ return clk_set_rate(module->clk, clk_rate);
+
+ return -EINVAL;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n",
+ module->name, module->state, module->id);
+
+ mutex_lock(&module->lock);
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE,
+ module->id, module);
+ if (rc)
+ break;
+ module->state = ADSP_STATE_ENABLING;
+ mutex_unlock(&module->lock);
+ rc = wait_event_timeout(module->state_wait,
+ module->state != ADSP_STATE_ENABLING,
+ 1 * HZ);
+ mutex_lock(&module->lock);
+ if (module->state == ADSP_STATE_ENABLED) {
+ rc = 0;
+ } else {
+ pr_err("adsp: module '%s' enable timed out\n",
+ module->name);
+ rc = -ETIMEDOUT;
+ }
+ break;
+ case ADSP_STATE_ENABLING:
+ pr_warning("adsp: module '%s' enable in progress\n",
+ module->name);
+ break;
+ case ADSP_STATE_ENABLED:
+ pr_warning("adsp: module '%s' already enabled\n",
+ module->name);
+ break;
+ case ADSP_STATE_DISABLING:
+ pr_err("adsp: module '%s' disable in progress\n",
+ module->name);
+ rc = -EBUSY;
+ break;
+ }
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_enable);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ pr_warning("adsp: module '%s' already disabled\n",
+ module->name);
+ break;
+ case ADSP_STATE_ENABLING:
+ case ADSP_STATE_ENABLED:
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE,
+ module->id, module);
+ module->state = ADSP_STATE_DISABLED;
+ }
+ return rc;
+}
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+ int rc;
+ pr_info("msm_adsp_disable() '%s'\n", module->name);
+ mutex_lock(&module->lock);
+ rc = msm_adsp_disable_locked(module);
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable);
+
+static int msm_adsp_probe(struct platform_device *pdev)
+{
+ unsigned count;
+ int rc, i;
+ int max_module_id;
+
+ pr_info("adsp: probe\n");
+
+ wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp");
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ adsp_info.init_info_ptr = kzalloc(
+ (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL);
+ if (!adsp_info.init_info_ptr)
+ return -ENOMEM;
+#endif
+
+ rc = adsp_init_info(&adsp_info);
+ if (rc)
+ return rc;
+ adsp_info.send_irq += (uint32_t) MSM_AD5_BASE;
+ adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE;
+ adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE;
+ count = adsp_info.module_count;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ max_module_id = count;
+#else
+ max_module_id = adsp_info.max_module_id + 1;
+#endif
+
+ adsp_modules = kzalloc(
+ sizeof(struct msm_adsp_module) * count +
+ sizeof(void *) * max_module_id, GFP_KERNEL);
+ if (!adsp_modules)
+ return -ENOMEM;
+
+ adsp_info.id_to_module = (void *) (adsp_modules + count);
+
+ spin_lock_init(&adsp_cmd_lock);
+
+ rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING,
+ "adsp", 0);
+ if (rc < 0)
+ goto fail_request_irq;
+ disable_irq(INT_ADSP);
+
+ rpc_cb_server_client = msm_rpc_open();
+ if (IS_ERR(rpc_cb_server_client)) {
+ rpc_cb_server_client = NULL;
+ rc = PTR_ERR(rpc_cb_server_client);
+ pr_err("adsp: could not create rpc server (%d)\n", rc);
+ goto fail_rpc_open;
+ }
+
+ rc = msm_rpc_register_server(rpc_cb_server_client,
+ RPC_ADSP_RTOS_MTOA_PROG,
+ RPC_ADSP_RTOS_MTOA_VERS);
+ if (rc) {
+ pr_err("adsp: could not register callback server (%d)\n", rc);
+ goto fail_rpc_register;
+ }
+
+ /* start the kernel thread to process the callbacks */
+ kthread_run(adsp_rpc_thread, NULL, "kadspd");
+
+ for (i = 0; i < count; i++) {
+ struct msm_adsp_module *mod = adsp_modules + i;
+ mutex_init(&mod->lock);
+ init_waitqueue_head(&mod->state_wait);
+ mod->info = &adsp_info;
+ mod->name = adsp_info.module[i].name;
+ mod->id = adsp_info.module[i].id;
+ if (adsp_info.module[i].clk_name)
+ mod->clk = clk_get(NULL, adsp_info.module[i].clk_name);
+ else
+ mod->clk = NULL;
+ if (mod->clk && adsp_info.module[i].clk_rate)
+ clk_set_rate(mod->clk, adsp_info.module[i].clk_rate);
+ mod->verify_cmd = adsp_info.module[i].verify_cmd;
+ mod->patch_event = adsp_info.module[i].patch_event;
+ INIT_HLIST_HEAD(&mod->pmem_regions);
+ mod->pdev.name = adsp_info.module[i].pdev_name;
+ mod->pdev.id = -1;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ adsp_info.id_to_module[i] = mod;
+#else
+ adsp_info.id_to_module[mod->id] = mod;
+#endif
+ platform_device_register(&mod->pdev);
+ }
+
+ msm_adsp_publish_cdevs(adsp_modules, count);
+
+ return 0;
+
+fail_rpc_register:
+ msm_rpc_close(rpc_cb_server_client);
+ rpc_cb_server_client = NULL;
+fail_rpc_open:
+ enable_irq(INT_ADSP);
+ free_irq(INT_ADSP, 0);
+fail_request_irq:
+ kfree(adsp_modules);
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ kfree(adsp_info.init_info_ptr);
+#endif
+ return rc;
+}
+
+static struct platform_driver msm_adsp_driver = {
+ .probe = msm_adsp_probe,
+ .driver = {
+ .name = MSM_ADSP_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init adsp_init(void)
+{
+ return platform_driver_register(&msm_adsp_driver);
+}
+
+device_initcall(adsp_init);
diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h
new file mode 100644
index 0000000..0e5c9abd
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp.h
@@ -0,0 +1,369 @@
+/* arch/arm/mach-msm/qdsp5/adsp.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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 _ARCH_ARM_MACH_MSM_ADSP_H
+#define _ARCH_ARM_MACH_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_adsp.h>
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len);
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len);
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+
+
+struct adsp_event;
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+
+struct adsp_module_info {
+ const char *name;
+ const char *pdev_name;
+ uint32_t id;
+ const char *clk_name;
+ unsigned long clk_rate;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+#define ADSP_EVENT_MAX_SIZE 496
+#define EVENT_LEN 12
+#define EVENT_MSG_ID ((uint16_t)~0)
+
+struct adsp_event {
+ struct list_head list;
+ uint32_t size; /* always in bytes */
+ uint16_t msg_id;
+ uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
+ int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
+ union {
+ uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
+ uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
+ } data;
+};
+
+struct adsp_info {
+ uint32_t send_irq;
+ uint32_t read_ctrl;
+ uint32_t write_ctrl;
+
+ uint32_t max_msg16_size;
+ uint32_t max_msg32_size;
+
+ uint32_t max_task_id;
+ uint32_t max_module_id;
+ uint32_t max_queue_id;
+ uint32_t max_image_id;
+
+ /* for each image id, a map of queue id to offset */
+ uint32_t **queue_offset;
+
+ /* for each image id, a map of task id to module id */
+ uint32_t **task_to_module;
+
+ /* for each module id, map of module id to module */
+ struct msm_adsp_module **id_to_module;
+
+ uint32_t module_count;
+ struct adsp_module_info *module;
+
+ /* stats */
+ uint32_t events_received;
+ uint32_t event_backlog_max;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ /* rpc_client for init_info */
+ struct msm_rpc_endpoint *init_info_rpc_client;
+ struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr;
+ wait_queue_head_t init_info_wait;
+ unsigned init_info_state;
+#endif
+};
+
+#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a
+#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b
+#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0
+#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0
+#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2
+#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) /* must be actual vers */
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:00010000"
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x71d1094b, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0xee3a9966, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:71d1094b"
+#elif CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x20f17fd3, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0x75babbd6, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:20f17fd3"
+#else
+#error "Unknown AMSS version"
+#endif
+
+enum rpc_adsp_rtos_proc_type {
+ RPC_ADSP_RTOS_PROC_NONE = 0,
+ RPC_ADSP_RTOS_PROC_MODEM = 1,
+ RPC_ADSP_RTOS_PROC_APPS = 2,
+};
+
+enum {
+ RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ RPC_ADSP_RTOS_CMD_ENABLE,
+ RPC_ADSP_RTOS_CMD_DISABLE,
+ RPC_ADSP_RTOS_CMD_KERNEL_COMMAND,
+ RPC_ADSP_RTOS_CMD_16_COMMAND,
+ RPC_ADSP_RTOS_CMD_32_COMMAND,
+ RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+ RPC_ADSP_RTOS_CMD_REMOTE_EVENT,
+ RPC_ADSP_RTOS_CMD_SET_STATE,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
+ RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
+#endif
+};
+
+enum rpc_adsp_rtos_mod_status_type {
+ RPC_ADSP_RTOS_MOD_READY,
+ RPC_ADSP_RTOS_MOD_DISABLE,
+ RPC_ADSP_RTOS_SERVICE_RESET,
+ RPC_ADSP_RTOS_CMD_FAIL,
+ RPC_ADSP_RTOS_CMD_SUCCESS,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ RPC_ADSP_RTOS_INIT_INFO,
+ RPC_ADSP_RTOS_DISABLE_FAIL,
+#endif
+};
+
+struct rpc_adsp_rtos_app_to_modem_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+ uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+ uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+};
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+enum qdsp_image_type {
+ QDSP_IMAGE_COMBO,
+ QDSP_IMAGE_GAUDIO,
+ QDSP_IMAGE_QTV_LP,
+ QDSP_IMAGE_MAX,
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+ QDSP_IMAGE_32BIT_DUMMY = 0x10000
+};
+
+struct adsp_rtos_mp_mtoa_header_type {
+ enum rpc_adsp_rtos_mod_status_type event;
+ enum rpc_adsp_rtos_proc_type proc_id;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Event Info*/
+struct adsp_rtos_mp_mtoa_type {
+ uint32_t module;
+ uint32_t image;
+ uint32_t apps_okts;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Init Info */
+#define IMG_MAX 8
+#define ENTRIES_MAX 64
+
+struct queue_to_offset_type {
+ uint32_t queue;
+ uint32_t offset;
+};
+
+struct adsp_rtos_mp_mtoa_init_info_type {
+ uint32_t image_count;
+ uint32_t num_queue_offsets;
+ struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX];
+ uint32_t num_task_module_entries;
+ uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX];
+
+ uint32_t module_table_size;
+ uint32_t module_entries[ENTRIES_MAX];
+ /*
+ * queue_offsets[] is to store only queue_offsets
+ */
+ uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX];
+};
+
+struct adsp_rtos_mp_mtoa_s_type {
+ struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
+
+ uint32_t desc_field;
+ union {
+ struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet;
+ struct adsp_rtos_mp_mtoa_type mp_mtoa_packet;
+ } adsp_rtos_mp_mtoa_data;
+};
+
+struct rpc_adsp_rtos_modem_to_app_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ struct adsp_rtos_mp_mtoa_s_type mtoa_pkt;
+};
+#else
+struct rpc_adsp_rtos_modem_to_app_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ uint32_t event; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+ uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+ uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+ uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */
+};
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+
+#define ADSP_STATE_DISABLED 0
+#define ADSP_STATE_ENABLING 1
+#define ADSP_STATE_ENABLED 2
+#define ADSP_STATE_DISABLING 3
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define ADSP_STATE_INIT_INFO 4
+#endif
+
+struct msm_adsp_module {
+ struct mutex lock;
+ const char *name;
+ unsigned id;
+ struct adsp_info *info;
+
+ struct msm_rpc_endpoint *rpc_client;
+ struct msm_adsp_ops *ops;
+ void *driver_data;
+
+ /* statistics */
+ unsigned num_commands;
+ unsigned num_events;
+
+ wait_queue_head_t state_wait;
+ unsigned state;
+
+ struct platform_device pdev;
+ struct clk *clk;
+ int open_count;
+
+ struct mutex pmem_regions_lock;
+ struct hlist_head pmem_regions;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
+extern int adsp_init_info(struct adsp_info *info);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define QDSP_RTOS_NO_QUEUE 0xfffffffe
+#else
+#define QDSP_RTOS_NO_QUEUE 0xffffffff
+#endif
+
+/*
+ * Constants used to communicate with the ADSP RTOS
+ */
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU
+
+/* Combination of MUTEX and CMD bits to check if the DSP is busy */
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U
+
+/* RTOS to Host processor command mask values */
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U
+
+/* Combination of FLAG and COMMAND bits to check if MSG ready */
+#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+
+#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU
+#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U
+
+/* Base address of DSP and DSP hardware registers */
+#define QDSP_RAMC_OFFSET 0x400000
+
+#endif /* _ARCH_ARM_MACH_MSM_ADSP_H */
diff --git a/arch/arm/mach-msm/qdsp5/adsp_6210.c b/arch/arm/mach-msm/qdsp5/adsp_6210.c
new file mode 100644
index 0000000..628c247
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6210.c
@@ -0,0 +1,283 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6210.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3be, /* QDSP_mpuAfeQueue */
+ 0x3ee, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3c2, /* QDSP_uPAudPPCmd1Queue */
+ 0x3c6, /* QDSP_uPAudPPCmd2Queue */
+ 0x3ca, /* QDSP_uPAudPPCmd3Queue */
+ 0x3da, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x3de, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x3e2, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x3e6, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x3ea, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x3ce, /* QDSP_uPAudPreProcCmdQueue */
+ 0x3d6, /* QDSP_uPAudRecBitStreamQueue */
+ 0x3d2, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x585, /* QDSP_lpmCommandQueue */
+ 0x52d, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x541, /* QDSP_mpuModmathCmdQueue */
+ 0x555, /* QDSP_mpuVDecCmdQueue */
+ 0x559, /* QDSP_mpuVDecPktQueue */
+ 0x551, /* QDSP_mpuVEncCmdQueue */
+ 0x535, /* QDSP_rxMpuDecCmdQueue */
+ 0x539, /* QDSP_rxMpuDecPktQueue */
+ 0x53d, /* QDSP_txMpuEncQueue */
+ 0x55d, /* QDSP_uPAudPPCmd1Queue */
+ 0x561, /* QDSP_uPAudPPCmd2Queue */
+ 0x565, /* QDSP_uPAudPPCmd3Queue */
+ 0x575, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x579, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x569, /* QDSP_uPAudPreProcCmdQueue */
+ 0x571, /* QDSP_uPAudRecBitStreamQueue */
+ 0x56d, /* QDSP_uPAudRecCmdQueue */
+ 0x581, /* QDSP_uPJpegActionCmdQueue */
+ 0x57d, /* QDSP_uPJpegCfgCmdQueue */
+ 0x531, /* QDSP_uPVocProcQueue */
+ 0x545, /* QDSP_vfeCommandQueue */
+ 0x54d, /* QDSP_vfeCommandScaleQueue */
+ 0x549 /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x40c, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x410, /* QDSP_mpuVDecCmdQueue */
+ 0x414, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x41c, /* QDSP_uPAudPPCmd1Queue */
+ 0x420, /* QDSP_uPAudPPCmd2Queue */
+ 0x424, /* QDSP_uPAudPPCmd3Queue */
+ 0x430, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPreProcCmdQueue */
+ 0x42c, /* QDSP_uPAudRecBitStreamQueue */
+ 0x428, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Tables to convert tasks to modules */
+static uint32_t *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPPTASK),
+ QDSP_MODULE(AUDRECTASK),
+ QDSP_MODULE(AUDPREPROCTASK),
+ QDSP_MODULE(VFETASK),
+ QDSP_MODULE(QCAMTASK),
+ QDSP_MODULE(LPMTASK),
+ QDSP_MODULE(JPEGTASK),
+ QDSP_MODULE(VIDEOTASK),
+ QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_6220.c b/arch/arm/mach-msm/qdsp5/adsp_6220.c
new file mode 100644
index 0000000..c4c5a55
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6220.c
@@ -0,0 +1,284 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6220.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3f0, /* QDSP_mpuAfeQueue */
+ 0x420, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3f4, /* QDSP_uPAudPPCmd1Queue */
+ 0x3f8, /* QDSP_uPAudPPCmd2Queue */
+ 0x3fc, /* QDSP_uPAudPPCmd3Queue */
+ 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x400, /* QDSP_uPAudPreProcCmdQueue */
+ 0x408, /* QDSP_uPAudRecBitStreamQueue */
+ 0x404, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x6f2, /* QDSP_lpmCommandQueue */
+ 0x69e, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x6b2, /* QDSP_mpuModmathCmdQueue */
+ 0x6c6, /* QDSP_mpuVDecCmdQueue */
+ 0x6ca, /* QDSP_mpuVDecPktQueue */
+ 0x6c2, /* QDSP_mpuVEncCmdQueue */
+ 0x6a6, /* QDSP_rxMpuDecCmdQueue */
+ 0x6aa, /* QDSP_rxMpuDecPktQueue */
+ 0x6ae, /* QDSP_txMpuEncQueue */
+ 0x6ce, /* QDSP_uPAudPPCmd1Queue */
+ 0x6d2, /* QDSP_uPAudPPCmd2Queue */
+ 0x6d6, /* QDSP_uPAudPPCmd3Queue */
+ 0x6e6, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x6da, /* QDSP_uPAudPreProcCmdQueue */
+ 0x6e2, /* QDSP_uPAudRecBitStreamQueue */
+ 0x6de, /* QDSP_uPAudRecCmdQueue */
+ 0x6ee, /* QDSP_uPJpegActionCmdQueue */
+ 0x6ea, /* QDSP_uPJpegCfgCmdQueue */
+ 0x6a2, /* QDSP_uPVocProcQueue */
+ 0x6b6, /* QDSP_vfeCommandQueue */
+ 0x6be, /* QDSP_vfeCommandScaleQueue */
+ 0x6ba /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x430, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x434, /* QDSP_mpuVDecCmdQueue */
+ 0x438, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x440, /* QDSP_uPAudPPCmd1Queue */
+ 0x444, /* QDSP_uPAudPPCmd2Queue */
+ 0x448, /* QDSP_uPAudPPCmd3Queue */
+ 0x454, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x43c, /* QDSP_uPAudPreProcCmdQueue */
+ 0x450, /* QDSP_uPAudRecBitStreamQueue */
+ 0x44c, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK),
+ QDSP_MODULE(AUDPPTASK),
+ QDSP_MODULE(AUDPREPROCTASK),
+ QDSP_MODULE(AUDRECTASK),
+ QDSP_MODULE(VFETASK),
+ QDSP_MODULE(QCAMTASK),
+ QDSP_MODULE(LPMTASK),
+ QDSP_MODULE(JPEGTASK),
+ QDSP_MODULE(VIDEOTASK),
+ QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c
new file mode 100644
index 0000000..5078afb
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6225.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC_UMTS,
+ QDSP_MODULE_VOC_CDMA,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 30U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3f0, /* QDSP_mpuAfeQueue */
+ 0x420, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3f4, /* QDSP_uPAudPPCmd1Queue */
+ 0x3f8, /* QDSP_uPAudPPCmd2Queue */
+ 0x3fc, /* QDSP_uPAudPPCmd3Queue */
+ 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x400, /* QDSP_uPAudPreProcCmdQueue */
+ 0x408, /* QDSP_uPAudRecBitStreamQueue */
+ 0x404, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x714, /* QDSP_lpmCommandQueue */
+ 0x6bc, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x6d0, /* QDSP_mpuModmathCmdQueue */
+ 0x6e8, /* QDSP_mpuVDecCmdQueue */
+ 0x6ec, /* QDSP_mpuVDecPktQueue */
+ 0x6e4, /* QDSP_mpuVEncCmdQueue */
+ 0x6c4, /* QDSP_rxMpuDecCmdQueue */
+ 0x6c8, /* QDSP_rxMpuDecPktQueue */
+ 0x6cc, /* QDSP_txMpuEncQueue */
+ 0x6f0, /* QDSP_uPAudPPCmd1Queue */
+ 0x6f4, /* QDSP_uPAudPPCmd2Queue */
+ 0x6f8, /* QDSP_uPAudPPCmd3Queue */
+ 0x708, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x6fc, /* QDSP_uPAudPreProcCmdQueue */
+ 0x704, /* QDSP_uPAudRecBitStreamQueue */
+ 0x700, /* QDSP_uPAudRecCmdQueue */
+ 0x710, /* QDSP_uPJpegActionCmdQueue */
+ 0x70c, /* QDSP_uPJpegCfgCmdQueue */
+ 0x6c0, /* QDSP_uPVocProcQueue */
+ 0x6d8, /* QDSP_vfeCommandQueue */
+ 0x6e0, /* QDSP_vfeCommandScaleQueue */
+ 0x6dc, /* QDSP_vfeCommandTableQueue */
+ 0x6d4, /* QDSP_uPDiagQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3fe, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x402, /* QDSP_mpuVDecCmdQueue */
+ 0x406, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x40e, /* QDSP_uPAudPPCmd1Queue */
+ 0x412, /* QDSP_uPAudPPCmd2Queue */
+ 0x416, /* QDSP_uPAudPPCmd3Queue */
+ 0x422, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x40a, /* QDSP_uPAudPreProcCmdQueue */
+ 0x41e, /* QDSP_uPAudRecBitStreamQueue */
+ 0x41a, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+ .clk_name = clkname, .clk_rate = clkrate, \
+ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+ adsp_vfe_patch_event),
+ QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+ QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd,
+ adsp_jpeg_patch_event),
+ QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+ adsp_video_verify_cmd, NULL),
+ QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+ adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c
new file mode 100644
index 0000000..8197765
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c
@@ -0,0 +1,642 @@
+/* arch/arm/mach-msm/qdsp5/adsp_driver.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "adsp.h"
+
+#include <linux/msm_adsp.h>
+#include <linux/android_pmem.h>
+
+struct adsp_pmem_region {
+ struct hlist_node list;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ struct file *file;
+};
+
+struct adsp_device {
+ struct msm_adsp_module *module;
+
+ spinlock_t event_queue_lock;
+ wait_queue_head_t event_wait;
+ struct list_head event_queue;
+ int abort;
+
+ const char *name;
+ struct device *device;
+ struct cdev cdev;
+};
+
+static struct adsp_device *inode_to_device(struct inode *inode);
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = __v >= __r->vaddr && \
+ __e <= __r->vaddr + __r->len; \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+static int adsp_pmem_check(struct msm_adsp_module *module,
+ void *vaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region_elt;
+ struct hlist_node *node;
+ struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ printk(KERN_ERR "adsp: module %s:"
+ " region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ module->name,
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int adsp_pmem_add(struct msm_adsp_module *module,
+ struct adsp_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct adsp_pmem_region *region;
+ int rc = -EINVAL;
+
+ mutex_lock(&module->pmem_regions_lock);
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ INIT_HLIST_NODE(®ion->list);
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ goto end;
+ }
+
+ rc = adsp_pmem_check(module, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ goto end;
+ }
+
+ region->vaddr = info->vaddr;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+
+ hlist_add_head(®ion->list, &module->pmem_regions);
+end:
+ mutex_unlock(&module->pmem_regions_lock);
+ return rc;
+}
+
+static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long len, struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ void *vaddr = *addr;
+ struct adsp_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ printk(KERN_ERR "adsp: module %s: "
+ "multiple hits for vaddr %p, len %ld\n",
+ module->name, vaddr, len);
+ hlist_for_each_entry(region_elt, node,
+ &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len)
+ printk(KERN_ERR "\t%p, %ld --> %p\n",
+ region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
+ " lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ *kvaddr = region->kvaddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+static int adsp_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ /* call the per module verifier */
+ if (module->verify_cmd)
+ return module->verify_cmd(module, queue_id, cmd_data,
+ cmd_size);
+ else
+ printk(KERN_INFO "adsp: no packet verifying function "
+ "for task %s\n", module->name);
+ return 0;
+}
+
+static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
+{
+ struct adsp_command_t cmd;
+ unsigned char buf[256];
+ void *cmd_data;
+ long rc;
+
+ if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
+ return -EFAULT;
+
+ if (cmd.len > 256) {
+ cmd_data = kmalloc(cmd.len, GFP_USER);
+ if (!cmd_data)
+ return -ENOMEM;
+ } else {
+ cmd_data = buf;
+ }
+
+ if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
+ rc = -EFAULT;
+ goto end;
+ }
+
+ mutex_lock(&adev->module->pmem_regions_lock);
+ if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
+ printk(KERN_ERR "module %s: verify failed.\n",
+ adev->module->name);
+ rc = -EINVAL;
+ goto end;
+ }
+ rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
+end:
+ mutex_unlock(&adev->module->pmem_regions_lock);
+
+ if (cmd.len > 256)
+ kfree(cmd_data);
+
+ return rc;
+}
+
+static int adsp_events_pending(struct adsp_device *adev)
+{
+ unsigned long flags;
+ int yes;
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ yes = !list_empty(&adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ return yes || adev->abort;
+}
+
+static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
+ struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ unsigned long paddr = (unsigned long)(*addr);
+ struct adsp_pmem_region *region_elt;
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (paddr >= region_elt->paddr &&
+ paddr < region_elt->paddr + region_elt->len) {
+ *region = region_elt;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
+{
+ struct adsp_pmem_region *region;
+ unsigned long paddr = (unsigned long)(*addr);
+ unsigned long *vaddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_paddr(module, addr, ®ion);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
+ module->name, vaddr);
+ return ret;
+ }
+
+ *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
+ return 0;
+}
+
+static int adsp_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ /* call the per-module msg verifier */
+ if (module->patch_event)
+ return module->patch_event(module, event);
+ return 0;
+}
+
+static long adsp_get_event(struct adsp_device *adev, void __user *arg)
+{
+ unsigned long flags;
+ struct adsp_event *data = NULL;
+ struct adsp_event_t evt;
+ int timeout;
+ long rc = 0;
+
+ if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
+ return -EFAULT;
+
+ timeout = (int)evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ adev->event_wait, adsp_events_pending(adev),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ adev->event_wait, adsp_events_pending(adev));
+ }
+ if (rc < 0)
+ return rc;
+
+ if (adev->abort)
+ return -ENODEV;
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ if (!list_empty(&adev->event_queue)) {
+ data = list_first_entry(&adev->event_queue,
+ struct adsp_event, list);
+ list_del(&data->list);
+ }
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+
+ if (!data)
+ return -EAGAIN;
+
+ /* DSP messages are type 0; they may contain physical addresses */
+ if (data->type == 0)
+ adsp_patch_event(adev->module, data);
+
+ /* map adsp_event --> adsp_event_t */
+ if (evt.len < data->size) {
+ rc = -ETOOSMALL;
+ goto end;
+ }
+ if (data->msg_id != EVENT_MSG_ID) {
+ if (copy_to_user((void *)(evt.data), data->data.msg16,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ } else {
+ if (copy_to_user((void *)(evt.data), data->data.msg32,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+ evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
+ evt.msg_id = data->msg_id;
+ evt.flags = data->is16;
+ evt.len = data->size;
+ if (copy_to_user(arg, &evt, sizeof(evt)))
+ rc = -EFAULT;
+end:
+ kfree(data);
+ return rc;
+}
+
+static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct adsp_device *adev = filp->private_data;
+
+ switch (cmd) {
+ case ADSP_IOCTL_ENABLE:
+ return msm_adsp_enable(adev->module);
+
+ case ADSP_IOCTL_DISABLE:
+ return msm_adsp_disable(adev->module);
+
+ case ADSP_IOCTL_DISABLE_EVENT_RSP:
+ return 0;
+
+ case ADSP_IOCTL_DISABLE_ACK:
+ pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
+ break;
+
+ case ADSP_IOCTL_WRITE_COMMAND:
+ return adsp_write_cmd(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_GET_EVENT:
+ return adsp_get_event(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_SET_CLKRATE: {
+#if CONFIG_MSM_AMSS_VERSION==6350
+ unsigned long clk_rate;
+ if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
+ return -EFAULT;
+ return adsp_set_clkrate(adev->module, clk_rate);
+#endif
+ }
+
+ case ADSP_IOCTL_REGISTER_PMEM: {
+ struct adsp_pmem_info info;
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ return -EFAULT;
+ return adsp_pmem_add(adev->module, &info);
+ }
+
+ case ADSP_IOCTL_ABORT_EVENT_READ:
+ adev->abort = 1;
+ wake_up(&adev->event_wait);
+ break;
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int adsp_release(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev = filp->private_data;
+ struct msm_adsp_module *module = adev->module;
+ struct hlist_node *node, *tmp;
+ struct adsp_pmem_region *region;
+
+ pr_info("adsp_release() '%s'\n", adev->name);
+
+ /* clear module before putting it to avoid race with open() */
+ adev->module = NULL;
+
+ mutex_lock(&module->pmem_regions_lock);
+ hlist_for_each_safe(node, tmp, &module->pmem_regions) {
+ region = hlist_entry(node, struct adsp_pmem_region, list);
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ mutex_unlock(&module->pmem_regions_lock);
+ BUG_ON(!hlist_empty(&module->pmem_regions));
+
+ msm_adsp_put(module);
+ return 0;
+}
+
+static void adsp_event(void *driver_data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct adsp_device *adev = driver_data;
+ struct adsp_event *event;
+ unsigned long flags;
+
+ if (len > ADSP_EVENT_MAX_SIZE) {
+ pr_err("adsp_event: event too large (%d bytes)\n", len);
+ return;
+ }
+
+ event = kmalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event) {
+ pr_err("adsp_event: cannot allocate buffer\n");
+ return;
+ }
+
+ if (id != EVENT_MSG_ID) {
+ event->type = 0;
+ event->is16 = 0;
+ event->msg_id = id;
+ event->size = len;
+
+ getevent(event->data.msg16, len);
+ } else {
+ event->type = 1;
+ event->is16 = 1;
+ event->msg_id = id;
+ event->size = len;
+ getevent(event->data.msg32, len);
+ }
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ list_add_tail(&event->list, &adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ wake_up(&adev->event_wait);
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = adsp_event,
+};
+
+static int adsp_open(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev;
+ int rc;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ adev = inode_to_device(inode);
+ if (!adev)
+ return -ENODEV;
+
+ pr_info("adsp_open() name = '%s'\n", adev->name);
+
+ rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
+ if (rc)
+ return rc;
+
+ pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
+ filp->private_data = adev;
+ adev->abort = 0;
+ INIT_HLIST_HEAD(&adev->module->pmem_regions);
+ mutex_init(&adev->module->pmem_regions_lock);
+
+ return 0;
+}
+
+static unsigned adsp_device_count;
+static struct adsp_device *adsp_devices;
+
+static struct adsp_device *inode_to_device(struct inode *inode)
+{
+ unsigned n = MINOR(inode->i_rdev);
+ if (n < adsp_device_count) {
+ if (adsp_devices[n].device)
+ return adsp_devices + n;
+ }
+ return NULL;
+}
+
+static dev_t adsp_devno;
+static struct class *adsp_class;
+
+static struct file_operations adsp_fops = {
+ .owner = THIS_MODULE,
+ .open = adsp_open,
+ .unlocked_ioctl = adsp_ioctl,
+ .release = adsp_release,
+};
+
+static void adsp_create(struct adsp_device *adev, const char *name,
+ struct device *parent, dev_t devt)
+{
+ struct device *dev;
+ int rc;
+
+ dev = device_create(adsp_class, parent, devt, "%s", name);
+ if (IS_ERR(dev))
+ return;
+
+ init_waitqueue_head(&adev->event_wait);
+ INIT_LIST_HEAD(&adev->event_queue);
+ spin_lock_init(&adev->event_queue_lock);
+
+ cdev_init(&adev->cdev, &adsp_fops);
+ adev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&adev->cdev, devt, 1);
+ if (rc < 0) {
+ device_destroy(adsp_class, devt);
+ } else {
+ adev->device = dev;
+ adev->name = name;
+ }
+}
+
+void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
+{
+ int rc;
+
+ adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
+ if (!adsp_devices)
+ return;
+
+ adsp_class = class_create(THIS_MODULE, "adsp");
+ if (IS_ERR(adsp_class))
+ goto fail_create_class;
+
+ rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
+ if (rc < 0)
+ goto fail_alloc_region;
+
+ adsp_device_count = n;
+ for (n = 0; n < adsp_device_count; n++) {
+ adsp_create(adsp_devices + n,
+ modules[n].name, &modules[n].pdev.dev,
+ MKDEV(MAJOR(adsp_devno), n));
+ }
+
+ return;
+
+fail_alloc_region:
+ class_unregister(adsp_class);
+fail_create_class:
+ kfree(adsp_devices);
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_info.c b/arch/arm/mach-msm/qdsp5/adsp_info.c
new file mode 100644
index 0000000..b9c77d2
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_info.c
@@ -0,0 +1,121 @@
+/* arch/arm/mach-msm/adsp_info.c
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, 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 "adsp.h"
+
+/* Firmware modules */
+#define QDSP_MODULE_KERNEL 0x0106dd4e
+#define QDSP_MODULE_AFETASK 0x0106dd6f
+#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70
+#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71
+#define QDSP_MODULE_AUDPPTASK 0x0106dd72
+#define QDSP_MODULE_VIDEOTASK 0x0106dd73
+#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74
+#define QDSP_MODULE_PCM_DEC 0x0106dd75
+#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76
+#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77
+#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78
+#define QDSP_MODULE_HOSTPCM 0x0106dd79
+#define QDSP_MODULE_DTMF 0x0106dd7a
+#define QDSP_MODULE_AUDRECTASK 0x0106dd7b
+#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c
+#define QDSP_MODULE_SBC_ENC 0x0106dd7d
+#define QDSP_MODULE_VOC_UMTS 0x0106dd9a
+#define QDSP_MODULE_VOC_CDMA 0x0106dd98
+#define QDSP_MODULE_VOC_PCM 0x0106dd7f
+#define QDSP_MODULE_VOCENCTASK 0x0106dd80
+#define QDSP_MODULE_VOCDECTASK 0x0106dd81
+#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82
+#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83
+#define QDSP_MODULE_VFETASK 0x0106dd84
+#define QDSP_MODULE_WAV_ENC 0x0106dd85
+#define QDSP_MODULE_AACLC_ENC 0x0106dd86
+#define QDSP_MODULE_VIDEO_AMR 0x0106dd87
+#define QDSP_MODULE_VOC_AMR 0x0106dd88
+#define QDSP_MODULE_VOC_EVRC 0x0106dd89
+#define QDSP_MODULE_VOC_13K 0x0106dd8a
+#define QDSP_MODULE_VOC_FGV 0x0106dd8b
+#define QDSP_MODULE_DIAGTASK 0x0106dd8c
+#define QDSP_MODULE_JPEGTASK 0x0106dd8d
+#define QDSP_MODULE_LPMTASK 0x0106dd8e
+#define QDSP_MODULE_QCAMTASK 0x0106dd8f
+#define QDSP_MODULE_MODMATHTASK 0x0106dd90
+#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91
+#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92
+#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93
+#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94
+#define QDSP_MODULE_MIDI 0x0106dd95
+#define QDSP_MODULE_GAUDIO 0x0106dd96
+#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97
+#define QDSP_MODULE_MAX 0x7fffffff
+
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+#define QDSP_MODULE_32BIT_DUMMY 0x10000
+
+static uint32_t *qdsp_task_to_module[IMG_MAX];
+static uint32_t *qdsp_queue_offset_table[IMG_MAX];
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+ .clk_name = clkname, .clk_rate = clkrate, \
+ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+ adsp_vfe_patch_event),
+ QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+ QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd,
+ adsp_jpeg_patch_event),
+ QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+ adsp_video_verify_cmd, NULL),
+ QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+ adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ uint32_t img_num;
+
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_queue_offset_table[img_num] =
+ &info->init_info_ptr->queue_offsets[img_num][0];
+
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_task_to_module[img_num] =
+ &info->init_info_ptr->task_to_module_tbl[img_num][0];
+ info->max_task_id = 30;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_MAX_NUM_QUEUES;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
new file mode 100644
index 0000000..4f493ed
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
@@ -0,0 +1,31 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
+ *
+ * Verification code for aDSP JPEG events.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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 <mach/qdsp5/qdsp5jpegmsg.h>
+#include "adsp.h"
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) {
+ jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16;
+ return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr);
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
new file mode 100644
index 0000000..b33eba2
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
@@ -0,0 +1,182 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
+ *
+ * Verification code for aDSP JPEG packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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 <mach/qdsp5/qdsp5jpegcmdi.h>
+#include "adsp.h"
+
+static uint32_t dec_fmt;
+
+static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size,
+ uint32_t *chroma_size)
+{
+ uint32_t fmt, luma_width, luma_height;
+
+ fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M;
+ luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M)
+ >> 16;
+ luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M;
+ *luma_size = luma_width * luma_height;
+ if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2)
+ *chroma_size = *luma_size/2;
+ else
+ *chroma_size = *luma_size;
+}
+
+static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data;
+ uint32_t luma_size, chroma_size;
+ int i, num_frags;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) {
+ printk(KERN_ERR "adsp: module %s: JPEG ENC CFG invalid cmd_size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ get_sizes(cmd, &luma_size, &chroma_size);
+ num_frags = (cmd->process_cfg >> 10) & 0xf;
+ num_frags = ((num_frags == 1) ? num_frags : num_frags * 2);
+ for (i = 0; i < num_frags; i += 2) {
+ if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) ||
+ adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size))
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1,
+ cmd->op_buf_0_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1,
+ cmd->op_buf_1_cfg_part2))
+ return -1;
+ return 0;
+}
+
+static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data;
+ uint32_t div;
+
+ if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) {
+ printk(KERN_ERR "adsp: module %s: JPEG DEC CFG invalid cmd_size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1,
+ cmd->ip_stream_buf_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1,
+ cmd->op_stream_buf_0_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1,
+ cmd->op_stream_buf_1_cfg_part2))
+ return -1;
+ dec_fmt = cmd->op_data_format &
+ JPEG_CMD_DEC_OP_DATA_FORMAT_M;
+ div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3,
+ cmd->op_stream_buf_0_cfg_part2 / div) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3,
+ cmd->op_stream_buf_1_cfg_part2 / div))
+ return -1;
+ return 0;
+}
+
+static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch(cmd_id) {
+ case JPEG_CMD_ENC_CFG:
+ return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size);
+ case JPEG_CMD_DEC_CFG:
+ return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size);
+ default:
+ if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid JPEG CFG cmd_id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int verify_jpeg_action_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch (cmd_id) {
+ case JPEG_CMD_ENC_OP_CONSUMED:
+ {
+ jpeg_cmd_enc_op_consumed *cmd =
+ (jpeg_cmd_enc_op_consumed *)cmd_data;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+ printk(KERN_ERR "adsp: module %s: JPEG_CMD_ENC_OP_CONSUMED invalid size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr,
+ cmd->op_buf_size))
+ return -1;
+ }
+ break;
+ case JPEG_CMD_DEC_OP_CONSUMED:
+ {
+ uint32_t div;
+ jpeg_cmd_dec_op_consumed *cmd =
+ (jpeg_cmd_dec_op_consumed *)cmd_data;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+ printk(KERN_ERR "adsp: module %s: JPEG_CMD_DEC_OP_CONSUMED invalid size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
+ if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr,
+ cmd->luma_op_buf_size) ||
+ adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr,
+ cmd->luma_op_buf_size / div))
+ return -1;
+ }
+ break;
+ default:
+ if (cmd_id > 7) {
+ printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch(queue_id) {
+ case QDSP_uPJpegCfgCmdQueue:
+ return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size);
+ case QDSP_uPJpegActionCmdQueue:
+ return verify_jpeg_action_cmd(module, cmd_data, cmd_size);
+ default:
+ return -1;
+ }
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
new file mode 100644
index 0000000..1e23ef3
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
@@ -0,0 +1,65 @@
+/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
+ *
+ * Verificion code for aDSP LPM packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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 <mach/qdsp5/qdsp5lpmcmdi.h>
+#include "adsp.h"
+
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ uint32_t cmd_id, col_height, input_row_incr, output_row_incr,
+ input_size, output_size;
+ uint32_t size_mask = 0x0fff;
+ lpm_cmd_start *cmd;
+
+ if (queue_id != QDSP_lpmCommandQueue) {
+ printk(KERN_ERR "adsp: module %s: wrong queue id %d\n",
+ module->name, queue_id);
+ return -1;
+ }
+
+ cmd = (lpm_cmd_start *)cmd_data;
+ cmd_id = cmd->cmd_id;
+
+ if (cmd_id == LPM_CMD_START) {
+ if (cmd_size != sizeof(lpm_cmd_start)) {
+ printk(KERN_ERR "adsp: module %s: wrong size %d, expect %d\n",
+ module->name, cmd_size, sizeof(lpm_cmd_start));
+ return -1;
+ }
+ col_height = cmd->ip_data_cfg_part1 & size_mask;
+ input_row_incr = cmd->ip_data_cfg_part2 & size_mask;
+ output_row_incr = cmd->op_data_cfg_part1 & size_mask;
+ input_size = col_height * input_row_incr;
+ output_size = col_height * output_row_incr;
+ if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module,
+ (void **)(&cmd->ip_data_cfg_part4),
+ input_size)) ||
+ (cmd->op_data_cfg_part3 && adsp_pmem_fixup(module,
+ (void **)(&cmd->op_data_cfg_part3),
+ output_size)))
+ return -1;
+ } else if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
new file mode 100644
index 0000000..8f09ed2
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
@@ -0,0 +1,54 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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 <mach/qdsp5/qdsp5vfemsg.h>
+#include "adsp.h"
+
+static int patch_op_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16;
+ if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) ||
+ adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr))
+ return -1;
+ return 0;
+}
+
+static int patch_af_wb_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16;
+ return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf);
+}
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ switch(event->msg_id) {
+ case VFE_MSG_OP1:
+ case VFE_MSG_OP2:
+ return patch_op_event(module, event);
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_WB_EXP:
+ return patch_af_wb_event(module, event);
+ default:
+ break;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
new file mode 100644
index 0000000..d1f3fa8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
@@ -0,0 +1,239 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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 <mach/qdsp5/qdsp5vfecmdi.h>
+#include "adsp.h"
+
+static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr;
+static uint32_t af_size = 4228;
+static uint32_t awb_size = 8196;
+
+static inline int verify_cmd_op_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data;
+ void **addr_y = (void **)&cmd->op1_buf_y_addr;
+ void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr);
+
+ if (cmd_size != sizeof(vfe_cmd_op1_ack))
+ return -1;
+ if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) ||
+ (*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr)))
+ return -1;
+ return 0;
+}
+
+static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ int i;
+ vfe_cmd_stats_autofocus_cfg *cmd =
+ (vfe_cmd_stats_autofocus_cfg *)cmd_data;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg))
+ return -1;
+
+ for (i = 0; i < 3; i++) {
+ void **addr = (void **)(&cmd->af_stats_op_buf[i]);
+ if (*addr && adsp_pmem_fixup(module, addr, af_size))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_wb_exp_cfg *cmd =
+ (vfe_cmd_stats_wb_exp_cfg *)cmd_data;
+ int i;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg))
+ return -1;
+
+ for (i = 0; i < 3; i++) {
+ void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]);
+ if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data;
+ void **addr = (void **)&cmd->af_stats_op_buf;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_af_ack))
+ return -1;
+
+ if (*addr && adsp_pmem_fixup(module, addr, af_size))
+ return -1;
+ return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_wb_exp_ack *cmd =
+ (vfe_cmd_stats_wb_exp_ack *)cmd_data;
+ void **addr = (void **)&cmd->wb_exp_stats_op_buf;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack))
+ return -1;
+
+ if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+ return -1;
+ return 0;
+}
+
+static int verify_vfe_command(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch (cmd_id) {
+ case VFE_CMD_OP1_ACK:
+ return verify_cmd_op_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_OP2_ACK:
+ return verify_cmd_op_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_AUTOFOCUS_CFG:
+ return verify_cmd_stats_autofocus_cfg(module, cmd_data,
+ cmd_size);
+ case VFE_CMD_STATS_WB_EXP_CFG:
+ return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_AF_ACK:
+ return verify_cmd_stats_af_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_WB_EXP_ACK:
+ return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size);
+ default:
+ if (cmd_id > 29) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE command id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int verify_vfe_command_scale(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ // FIXME: check the size
+ if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE SCALE command id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ return 0;
+}
+
+
+static uint32_t get_size(uint32_t hw)
+{
+ uint32_t height, width;
+ uint32_t height_mask = 0x3ffc;
+ uint32_t width_mask = 0x3ffc000;
+
+ height = (hw & height_mask) >> 2;
+ width = (hw & width_mask) >> 14 ;
+ return height * width;
+}
+
+static int verify_vfe_command_table(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ int i;
+
+ switch (cmd_id) {
+ case VFE_CMD_AXI_IP_CFG:
+ {
+ vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data;
+ uint32_t size;
+ if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_IP_CFG) command size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+ size = get_size(cmd->ip_cfg_part2);
+
+ for (i = 0; i < 8; i++) {
+ void **addr = (void **)
+ &cmd->ip_buf_addr[i];
+ if (*addr && adsp_pmem_fixup(module, addr, size))
+ return -1;
+ }
+ }
+ case VFE_CMD_AXI_OP_CFG:
+ {
+ vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data;
+ void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr;
+
+ if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_OP_CFG) command size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+ size1_y = get_size(cmd->op1_y_cfg_part2);
+ size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2);
+ size2_y = get_size(cmd->op2_y_cfg_part2);
+ size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2);
+ for (i = 0; i < 8; i++) {
+ addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]);
+ addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]);
+ addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]);
+ addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]);
+/*
+ printk("module %s: [%d] %p %p %p %p\n",
+ module->name, i,
+ *addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr);
+*/
+ if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) ||
+ (*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) ||
+ (*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) ||
+ (*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr)))
+ return -1;
+ }
+ }
+ default:
+ if (cmd_id > 4) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE command id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_vfeCommandQueue:
+ return verify_vfe_command(module, cmd_data, cmd_size);
+ case QDSP_vfeCommandScaleQueue:
+ return verify_vfe_command_scale(module, cmd_data, cmd_size);
+ case QDSP_vfeCommandTableQueue:
+ return verify_vfe_command_table(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_ERR "adsp: module %s: unknown queue id %d\n",
+ module->name, queue_id);
+ return -1;
+ }
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
new file mode 100644
index 0000000..fdad055
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
@@ -0,0 +1,163 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VDEC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+ ##args); } \
+ while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+
+#include <mach/qdsp5/qdsp5vdeccmdi.h>
+#include "adsp.h"
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+ unsigned short low)
+{
+ return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+ unsigned short *low)
+{
+ *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+ *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+ unsigned short *low,
+ unsigned short size_high,
+ unsigned short size_low,
+ struct msm_adsp_module *module,
+ unsigned long *addr, unsigned long *size)
+{
+ void *phys_addr;
+ unsigned long phys_size;
+ unsigned long kvaddr;
+
+ phys_addr = high_low_short_to_ptr(*high, *low);
+ phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+ DLOG("virt %x %x\n", phys_addr, phys_size);
+ if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+ DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+ *high, *low, size_high, size_low, phys_addr, phys_size);
+ return -1;
+ }
+ ptr_to_high_low_short(phys_addr, high, low);
+ DLOG("phys %x %x\n", phys_addr, phys_size);
+ if (addr)
+ *addr = kvaddr;
+ if (size)
+ *size = phys_size;
+ return 0;
+}
+
+static int verify_vdec_pkt_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+ viddec_cmd_subframe_pkt *pkt;
+ unsigned long subframe_pkt_addr;
+ unsigned long subframe_pkt_size;
+ viddec_cmd_frame_header_packet *frame_header_pkt;
+ int i, num_addr, skip;
+ unsigned short *frame_buffer_high, *frame_buffer_low;
+ unsigned long frame_buffer_size;
+ unsigned short frame_buffer_size_high, frame_buffer_size_low;
+
+ DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+ if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
+ printk(KERN_INFO "adsp_video: unknown video packet %u\n",
+ cmd_id);
+ return 0;
+ }
+ if (cmd_size < sizeof(viddec_cmd_subframe_pkt))
+ return -1;
+
+ pkt = (viddec_cmd_subframe_pkt *)cmd_data;
+
+ if (pmem_fixup_high_low(&(pkt->subframe_packet_high),
+ &(pkt->subframe_packet_low),
+ pkt->subframe_packet_size_high,
+ pkt->subframe_packet_size_low,
+ module,
+ &subframe_pkt_addr,
+ &subframe_pkt_size))
+ return -1;
+
+ /* deref those ptrs and check if they are a frame header packet */
+ frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr;
+
+ switch (frame_header_pkt->packet_id) {
+ case 0xB201: /* h.264 */
+ num_addr = skip = 8;
+ break;
+ case 0x4D01: /* mpeg-4 and h.263 */
+ num_addr = 3;
+ skip = 0;
+ break;
+ default:
+ return 0;
+ }
+
+ frame_buffer_high = &frame_header_pkt->frame_buffer_0_high;
+ frame_buffer_low = &frame_header_pkt->frame_buffer_0_low;
+ frame_buffer_size = (frame_header_pkt->x_dimension *
+ frame_header_pkt->y_dimension * 3) / 2;
+ ptr_to_high_low_short((void *)frame_buffer_size,
+ &frame_buffer_size_high,
+ &frame_buffer_size_low);
+ for (i = 0; i < num_addr; i++) {
+ if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+ frame_buffer_size_high,
+ frame_buffer_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ frame_buffer_high += 2;
+ frame_buffer_low += 2;
+ }
+ /* Patch the output buffer. */
+ frame_buffer_high += 2*skip;
+ frame_buffer_low += 2*skip;
+ if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+ frame_buffer_size_high,
+ frame_buffer_size_low, module, NULL, NULL))
+ return -1;
+ return 0;
+}
+
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_mpuVDecPktQueue:
+ DLOG("\n");
+ return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_INFO "unknown video queue %u\n", queue_id);
+ return 0;
+ }
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c
new file mode 100644
index 0000000..ee37449
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c
@@ -0,0 +1,235 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VENC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 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/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+ ##args); } \
+ while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+#include <mach/qdsp5/qdsp5venccmdi.h>
+#include "adsp.h"
+
+
+static unsigned short x_dimension, y_dimension;
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+ unsigned short low)
+{
+ return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+ unsigned short *low)
+{
+ *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+ *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+ unsigned short *low,
+ unsigned short size_high,
+ unsigned short size_low,
+ struct msm_adsp_module *module,
+ unsigned long *addr, unsigned long *size)
+{
+ void *phys_addr;
+ unsigned long phys_size;
+ unsigned long kvaddr;
+
+ phys_addr = high_low_short_to_ptr(*high, *low);
+ phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+ DLOG("virt %x %x\n", phys_addr, phys_size);
+ if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+ DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+ *high, *low, size_high, size_low, phys_addr, phys_size);
+ return -1;
+ }
+ ptr_to_high_low_short(phys_addr, high, low);
+ DLOG("phys %x %x\n", phys_addr, phys_size);
+ if (addr)
+ *addr = kvaddr;
+ if (size)
+ *size = phys_size;
+ return 0;
+}
+
+static int verify_venc_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+ unsigned long frame_buf_size, luma_buf_size, chroma_buf_size;
+ unsigned short frame_buf_size_high, frame_buf_size_low;
+ unsigned short luma_buf_size_high, luma_buf_size_low;
+ unsigned short chroma_buf_size_high, chroma_buf_size_low;
+ videnc_cmd_cfg *config_cmd;
+ videnc_cmd_frame_start *frame_cmd;
+ videnc_cmd_dis *dis_cmd;
+
+ DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+ switch (cmd_id) {
+ case VIDENC_CMD_ACTIVE:
+ if (cmd_size < sizeof(videnc_cmd_active))
+ return -1;
+ break;
+ case VIDENC_CMD_IDLE:
+ if (cmd_size < sizeof(videnc_cmd_idle))
+ return -1;
+ x_dimension = y_dimension = 0;
+ break;
+ case VIDENC_CMD_STATUS_QUERY:
+ if (cmd_size < sizeof(videnc_cmd_status_query))
+ return -1;
+ break;
+ case VIDENC_CMD_RC_CFG:
+ if (cmd_size < sizeof(videnc_cmd_rc_cfg))
+ return -1;
+ break;
+ case VIDENC_CMD_INTRA_REFRESH:
+ if (cmd_size < sizeof(videnc_cmd_intra_refresh))
+ return -1;
+ break;
+ case VIDENC_CMD_DIGITAL_ZOOM:
+ if (cmd_size < sizeof(videnc_cmd_digital_zoom))
+ return -1;
+ break;
+ case VIDENC_CMD_DIS_CFG:
+ if (cmd_size < sizeof(videnc_cmd_dis_cfg))
+ return -1;
+ break;
+ case VIDENC_CMD_CFG:
+ if (cmd_size < sizeof(videnc_cmd_cfg))
+ return -1;
+ config_cmd = (videnc_cmd_cfg *)cmd_data;
+ x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8;
+ x_dimension = x_dimension*16;
+ y_dimension = (config_cmd->venc_frame_dim) & 0xFF;
+ y_dimension = y_dimension * 16;
+ break;
+ case VIDENC_CMD_FRAME_START:
+ if (cmd_size < sizeof(videnc_cmd_frame_start))
+ return -1;
+ frame_cmd = (videnc_cmd_frame_start *)cmd_data;
+ luma_buf_size = x_dimension * y_dimension;
+ chroma_buf_size = luma_buf_size>>1;
+ frame_buf_size = luma_buf_size + chroma_buf_size;
+ ptr_to_high_low_short((void *)luma_buf_size,
+ &luma_buf_size_high,
+ &luma_buf_size_low);
+ ptr_to_high_low_short((void *)chroma_buf_size,
+ &chroma_buf_size_high,
+ &chroma_buf_size_low);
+ ptr_to_high_low_short((void *)frame_buf_size,
+ &frame_buf_size_high,
+ &frame_buf_size_low);
+ /* Address of raw Y data. */
+ if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high,
+ &frame_cmd->input_luma_addr_low,
+ luma_buf_size_high,
+ luma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Address of raw CbCr data */
+ if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high,
+ &frame_cmd->input_chroma_addr_low,
+ chroma_buf_size_high,
+ chroma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Reference VOP */
+ if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high,
+ &frame_cmd->ref_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Encoded Packet Address */
+ if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high,
+ &frame_cmd->enc_pkt_buf_ptr_low,
+ frame_cmd->enc_pkt_buf_size_high,
+ frame_cmd->enc_pkt_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Unfiltered VOP Buffer Address */
+ if (pmem_fixup_high_low(
+ &frame_cmd->unfilt_recon_vop_buf_ptr_high,
+ &frame_cmd->unfilt_recon_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Filtered VOP Buffer Address */
+ if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high,
+ &frame_cmd->filt_recon_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ break;
+ case VIDENC_CMD_DIS:
+ if (cmd_size < sizeof(videnc_cmd_dis))
+ return -1;
+ dis_cmd = (videnc_cmd_dis *)cmd_data;
+ luma_buf_size = x_dimension * y_dimension;
+ ptr_to_high_low_short((void *)luma_buf_size,
+ &luma_buf_size_high,
+ &luma_buf_size_low);
+ /* Prev VFE Luma Output Address */
+ if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high,
+ &dis_cmd->vfe_out_prev_luma_addr_low,
+ luma_buf_size_high,
+ luma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ break;
+ default:
+ printk(KERN_INFO "adsp_video:unknown encoder video command %u\n",
+ cmd_id);
+ return 0;
+ }
+
+ return 0;
+}
+
+
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_mpuVEncCmdQueue:
+ DLOG("\n");
+ return verify_venc_cmd(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_INFO "unknown video queue %u\n", queue_id);
+ return 0;
+ }
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c
new file mode 100644
index 0000000..4232b9f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_aac.c
@@ -0,0 +1,1051 @@
+/* arch/arm/mach-msm/qdsp5/audio_aac.c
+ *
+ * aac audio decoder device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009 QUALCOMM USA, 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audmgr.h"
+
+#include <mach/msm_adsp.h>
+#include <mach/msm_audio_aac.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 32768
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AAC 5
+
+#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ struct msm_audio_aac_config aac_config;
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ dprintk("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audio_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ dprintk("audio_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audio_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audio_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ dprintk("%s: FLUSH_ACK\n", __func__);
+ audio->wflush = 0;
+ audio->rflush = 0;
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_aac = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ audpp_cmd_cfg_adec_params_aac cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.format = audio->aac_config.format;
+ cmd.audio_object = audio->aac_config.audio_object;
+ cmd.ep_config = audio->aac_config.ep_config;
+ cmd.aac_section_data_resilience_flag =
+ audio->aac_config.aac_section_data_resilience_flag;
+ cmd.aac_scalefactor_data_resilience_flag =
+ audio->aac_config.aac_scalefactor_data_resilience_flag;
+ cmd.aac_spectral_data_resilience_flag =
+ audio->aac_config.aac_spectral_data_resilience_flag;
+ cmd.sbr_on_flag = audio->aac_config.sbr_on_flag;
+ cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag;
+ cmd.channel_configuration = audio->aac_config.channel_configuration;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 1024); /* AAC frame size */
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audplay_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+/* printk("frame %d busy\n", audio->out_tail); */
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static int audaac_validate_usr_config(struct msm_audio_aac_config *config)
+{
+ int ret_val = -1;
+
+ if (config->format != AUDIO_AAC_FORMAT_ADTS &&
+ config->format != AUDIO_AAC_FORMAT_RAW &&
+ config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW &&
+ config->format != AUDIO_AAC_FORMAT_LOAS)
+ goto done;
+
+ if (config->audio_object != AUDIO_AAC_OBJECT_LC &&
+ config->audio_object != AUDIO_AAC_OBJECT_LTP &&
+ config->audio_object != AUDIO_AAC_OBJECT_ERLC)
+ goto done;
+
+ if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) {
+ if (config->ep_config > 3)
+ goto done;
+ if (config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_OFF &&
+ config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_ON)
+ goto done;
+ if (config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_OFF &&
+ config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_ON)
+ goto done;
+ if (config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_OFF &&
+ config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_ON)
+ goto done;
+ } else {
+ config->aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ config->aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ config->aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+ }
+
+ if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF &&
+ config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON)
+ goto done;
+
+ if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF &&
+ config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON)
+ goto done;
+
+ if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR)
+ goto done;
+
+ if (config->channel_configuration > 2)
+ goto done;
+
+ ret_val = 0;
+ done:
+ return ret_val;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audio_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ dprintk("%s: AUDIO_FLUSH\n", __func__);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running)
+ audpp_flush(audio->dec_id);
+ else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (config.channel_count == 1) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode ==
+ AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG:{
+ if (copy_to_user((void *)arg, &audio->aac_config,
+ sizeof(audio->aac_config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG:{
+ struct msm_audio_aac_config usr_config;
+
+ if (copy_from_user
+ (&usr_config, (void *)arg,
+ sizeof(usr_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (audaac_validate_usr_config(&usr_config) == 0) {
+ audio->aac_config = usr_config;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("ioctl: allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audio_aac: buf alloc fail\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audio_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ dprintk("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ dprintk("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audio_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ dprintk("audio_read: kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audio_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned dsize;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->reserved) {
+ dprintk("%s: append reserved byte %x\n",
+ __func__, audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > (frame->size - 1)) ?
+ frame->size - 1 : count;
+ cpy_ptr++;
+ dsize = 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > frame->size) ? frame->size : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ dprintk("%s: odd length buf reserve last byte %x\n",
+ __func__, audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audio_release()\n");
+
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ audio->reserved = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio the_aac_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_aac_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_aac, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ goto done;
+ }
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS;
+ audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC;
+ audio->aac_config.ep_config = 0;
+ audio->aac_config.aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ audio->aac_config.aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ audio->aac_config.aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+ audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON;
+ audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON;
+ audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR;
+ audio->aac_config.channel_configuration = 2;
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_aac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_aac_audio.lock);
+ mutex_init(&the_aac_audio.write_lock);
+ mutex_init(&the_aac_audio.read_lock);
+ spin_lock_init(&the_aac_audio.dsp_lock);
+ init_waitqueue_head(&the_aac_audio.write_wait);
+ init_waitqueue_head(&the_aac_audio.read_wait);
+ the_aac_audio.read_data = NULL;
+ return misc_register(&audio_aac_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
new file mode 100644
index 0000000..63fe2d0
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
@@ -0,0 +1,872 @@
+/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+ *
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1024 /* Hold minimum 700ms voice data */
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AMRNB 10
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+struct audpp_cmd_cfg_adec_params_amrnb {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed)) ;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audamrnb_send_data(struct audio *audio, unsigned needed);
+static void audamrnb_config_hostpcm(struct audio *audio);
+static void audamrnb_buffer_refresh(struct audio *audio);
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audamrnb_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_disable(struct audio *audio)
+{
+ dprintk("audamrnb_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrnb_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audamrnb_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audamrnb_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audamrnb_buffer_refresh(audio);
+ } else {
+ dprintk("audamrnb_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audamrnb_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audamrnb_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audamrnb_config_hostpcm(audio);
+ audamrnb_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audamrnb_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audamrnb_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audamrnb_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audamrnb_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ default:
+ pr_err("audamrnb_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrnb = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_amrnb cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audamrnb_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ);
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audamrnb_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audamrnb_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audamrnb_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+/* printk("frame %d busy\n", audio->out_tail); */
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audamrnb_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audamrnb_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audamrnb_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audamrnb_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audamrnb_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audamrnb_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audamrnb_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audamrnb_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+
+ case AUDIO_SET_CONFIG:{
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("audamrnb_ioctl: allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audamrnb_ioctl: no mem for pcm buf\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audamrnb_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ dprintk("audamrnb_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audamrnb_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audamrnb_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ }
+ }
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audamrnb_read: kick start pcm feedback again\n");
+ audamrnb_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audamrnb_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audamrnb_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ dprintk("audamrnb_write() \n");
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ dprintk("audamrnb_write() buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audamrnb_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audamrnb_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audamrnb_release()\n");
+
+ mutex_lock(&audio->lock);
+ audamrnb_disable(audio);
+ audamrnb_flush(audio);
+ audamrnb_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_amrnb_audio;
+
+static int audamrnb_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_amrnb_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_amrnb, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ audmgr_disable(&audio->audmgr);
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ goto done;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audamrnb_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_amrnb_fops = {
+ .owner = THIS_MODULE,
+ .open = audamrnb_open,
+ .release = audamrnb_release,
+ .read = audamrnb_read,
+ .write = audamrnb_write,
+ .unlocked_ioctl = audamrnb_ioctl,
+};
+
+struct miscdevice audio_amrnb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb",
+ .fops = &audio_amrnb_fops,
+};
+
+static int __init audamrnb_init(void)
+{
+ mutex_init(&the_amrnb_audio.lock);
+ mutex_init(&the_amrnb_audio.write_lock);
+ mutex_init(&the_amrnb_audio.read_lock);
+ spin_lock_init(&the_amrnb_audio.dsp_lock);
+ init_waitqueue_head(&the_amrnb_audio.write_wait);
+ init_waitqueue_head(&the_amrnb_audio.read_wait);
+ the_amrnb_audio.read_data = NULL;
+ return misc_register(&audio_amrnb_misc);
+}
+
+static void __exit audamrnb_exit(void)
+{
+ misc_deregister(&audio_amrnb_misc);
+}
+
+module_init(audamrnb_init);
+module_exit(audamrnb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-NB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c
new file mode 100644
index 0000000..8ee8d53
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c
@@ -0,0 +1,844 @@
+/* arch/arm/mach-msm/audio_evrc.c
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code also borrows from audio_aac.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+ printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Hold 30 packets of 24 bytes each*/
+#define BUFSZ 720
+#define DMASZ (BUFSZ * 2)
+
+#define AUDDEC_DEC_EVRC 12
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT 5
+/* DSP only accepts 5 buffers at most
+ * but support 2 buffers currently
+ */
+#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+static struct audio the_evrc_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audevrc_send_data(struct audio *audio, unsigned needed);
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audevrc_config_hostpcm(struct audio *audio);
+static void audevrc_buffer_refresh(struct audio *audio);
+
+/* must be called with audio->lock held */
+static int audevrc_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_EVRC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_disable(struct audio *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+
+static void audevrc_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr
+ == payload[2 + index * 2]) {
+ dprintk("audevrc_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audevrc_update_pcm_buf_entry: expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audevrc_buffer_refresh(audio);
+ } else {
+ dprintk("audevrc_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audevrc_send_data(audio, 1);
+ break;
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ dprintk("audevrc_update_pcm_buf_entry:======> \n");
+ audevrc_update_pcm_buf_entry(audio, msg);
+ break;
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audevrc_config_hostpcm(audio);
+ audevrc_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audevrc_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audevrc_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audevrc_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audevrc_dsp_event: ROUTING_ACK\n");
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ default:
+ pr_err("audevrc_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_evrc = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_evrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = sizeof(cmd);
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audevrc_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audevrc_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audevrc_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audevrc_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audevrc_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audevrc_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audevrc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audevrc_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audevrc_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audevrc_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_SET_CONFIG:{
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("audevrc_ioctl: allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err
+ ("audevrc_ioctl: no mem for pcm buf\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+ if (!audio->pcm_feedback) {
+ return 0;
+ /* PCM feedback is not enabled. Nothing to read */
+ }
+ mutex_lock(&audio->read_lock);
+ dprintk("audevrc_read() \n");
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped));
+ dprintk("audevrc_read() wait terminated \n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ dprintk("audevrc_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audevrc_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audevrc_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+
+ }
+ }
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audevrc_read: kick start pcm feedback again\n");
+ audevrc_buffer_refresh(audio);
+ }
+ mutex_unlock(&audio->read_lock);
+ if (buf > start)
+ rc = buf - start;
+ dprintk("audevrc_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audevrc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ mutex_lock(&audio->write_lock);
+ dprintk("audevrc_write() \n");
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audevrc_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audevrc_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audevrc_release()\n");
+
+ mutex_lock(&audio->lock);
+ audevrc_disable(audio);
+ audevrc_flush(audio);
+ audevrc_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_evrc_audio;
+
+static int audevrc_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_evrc_audio;
+ int rc;
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ return -EBUSY;
+ }
+
+ /* Acquire Lock */
+ mutex_lock(&audio->lock);
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto dma_fail;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto audmgr_fail;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_evrc, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ goto adsp_fail;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x3FFF;
+
+ audevrc_flush(audio);
+
+ audio->opened = 1;
+ file->private_data = audio;
+
+ mutex_unlock(&audio->lock);
+ return rc;
+
+adsp_fail:
+ audmgr_close(&audio->audmgr);
+audmgr_fail:
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+dma_fail:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_evrc_fops = {
+ .owner = THIS_MODULE,
+ .open = audevrc_open,
+ .release = audevrc_release,
+ .read = audevrc_read,
+ .write = audevrc_write,
+ .unlocked_ioctl = audevrc_ioctl,
+};
+
+struct miscdevice audio_evrc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc",
+ .fops = &audio_evrc_fops,
+};
+
+static int __init audevrc_init(void)
+{
+ mutex_init(&the_evrc_audio.lock);
+ mutex_init(&the_evrc_audio.write_lock);
+ mutex_init(&the_evrc_audio.read_lock);
+ spin_lock_init(&the_evrc_audio.dsp_lock);
+ init_waitqueue_head(&the_evrc_audio.write_wait);
+ init_waitqueue_head(&the_evrc_audio.read_wait);
+ the_evrc_audio.read_data = NULL;
+ return misc_register(&audio_evrc_misc);
+}
+
+static void __exit audevrc_exit(void)
+{
+ misc_deregister(&audio_evrc_misc);
+}
+
+module_init(audevrc_init);
+module_exit(audevrc_exit);
+
+MODULE_DESCRIPTION("MSM EVRC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c
new file mode 100644
index 0000000..2a67209
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_in.c
@@ -0,0 +1,967 @@
+/* arch/arm/mach-msm/qdsp5/audio_in.c
+ *
+ * pcm audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_SIZE (2052 * 2)
+#define MONO_DATA_SIZE (2048)
+#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
+#define DMASZ (FRAME_SIZE * FRAME_NUM)
+
+#define AGC_PARAM_SIZE (20)
+#define NS_PARAM_SIZE (6)
+#define IIR_PARAM_SIZE (48)
+#define DEBUG (0)
+
+#define AGC_ENABLE 0x0001
+#define NS_ENABLE 0x0002
+#define IIR_ENABLE 0x0004
+
+struct tx_agc_config {
+ uint16_t agc_params[AGC_PARAM_SIZE];
+};
+
+struct ns_config {
+ uint16_t ns_params[NS_PARAM_SIZE];
+};
+
+struct tx_iir_filter {
+ uint16_t num_bands;
+ uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct audpre_cmd_iir_config_type {
+ uint16_t cmd_id;
+ uint16_t active_flag;
+ uint16_t num_bands;
+ uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+
+ struct msm_adsp_module *audpre;
+ struct msm_adsp_module *audrec;
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t type; /* 0 for PCM ,1 for AAC */
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+
+ unsigned short samp_rate_index;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+
+ /* audpre settings */
+ int agc_enable;
+ struct tx_agc_config agc;
+
+ int ns_enable;
+ struct ns_config ns;
+
+ int iir_enable;
+ struct tx_iir_filter iir;
+};
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable);
+static int audio_in_encoder_config(struct audio_in *audio);
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+static void audio_flush(struct audio_in *audio);
+static int audio_dsp_set_agc(struct audio_in *audio);
+static int audio_dsp_set_ns(struct audio_in *audio);
+static int audio_dsp_set_tx_iir(struct audio_in *audio);
+
+static unsigned convert_dsp_samp_index(unsigned index)
+{
+ switch (index) {
+ case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000;
+ case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100;
+ case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000;
+ case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000;
+ case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050;
+ case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000;
+ case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000;
+ case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025;
+ case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000;
+ default: return AUDREC_CMD_SAMP_RATE_INDX_11025;
+ }
+}
+
+static unsigned convert_samp_rate(unsigned hz)
+{
+ switch (hz) {
+ case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000;
+ case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100;
+ case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000;
+ case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000;
+ case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050;
+ case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000;
+ case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000;
+ case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025;
+ case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000;
+ default: return RPC_AUD_DEF_SAMPLE_RATE_11025;
+ }
+}
+
+static unsigned convert_samp_index(unsigned index)
+{
+ switch (index) {
+ case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000;
+ case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100;
+ case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000;
+ case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000;
+ case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050;
+ case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000;
+ case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000;
+ case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025;
+ case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000;
+ default: return 11025;
+ }
+}
+
+/* must be called with audio->lock held */
+static int audio_in_enable(struct audio_in *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ if (audio->enabled)
+ return 0;
+
+ cfg.tx_rate = audio->samp_rate;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ else
+ cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audpre)) {
+ pr_err("audrec: msm_adsp_enable(audpre) failed\n");
+ return -ENODEV;
+ }
+ if (msm_adsp_enable(audio->audrec)) {
+ pr_err("audrec: msm_adsp_enable(audrec) failed\n");
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ audio_in_dsp_enable(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+
+ audio_in_dsp_enable(audio, 0);
+
+ wake_up(&audio->wait);
+
+ msm_adsp_disable(audio->audrec);
+ msm_adsp_disable(audio->audpre);
+ audmgr_disable(&audio->audmgr);
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audpre_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ uint16_t msg[6]; /* may be a 32-bit event, which we ignore */
+ getevent(msg, sizeof(msg));
+
+ switch (id) {
+ case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
+ pr_info("audpre: type %d, status_flag %d\n", msg[0], msg[1]);
+ break;
+ case AUDPREPROC_MSG_ERROR_MSG_ID:
+ pr_info("audpre: err_index %d\n", msg[0]);
+ break;
+ default:
+ pr_err("audpre: unknown event %d\n", id);
+ }
+}
+
+struct audio_frame {
+ uint16_t count_low;
+ uint16_t count_high;
+ uint16_t bytes;
+ uint16_t unknown;
+ unsigned char samples[];
+} __attribute__((packed));
+
+static void audio_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ /* XXX check for bogus frame size? */
+
+ frame = (void *) (((char *)audio->in[index].data) - sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->bytes;
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ audio->in_count++;
+
+ audio_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+ uint16_t msg[6]; /* may be a 32-bit event, which we ignore */
+ getevent(msg, sizeof(msg));
+
+ switch (id) {
+ case AUDREC_MSG_CMD_CFG_DONE_MSG:
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) {
+ pr_info("audpre: CFG ENABLED\n");
+ audio_dsp_set_agc(audio);
+ audio_dsp_set_ns(audio);
+ audio_dsp_set_tx_iir(audio);
+ audio_in_encoder_config(audio);
+ } else {
+ pr_info("audrec: CFG SLEEP\n");
+ audio->running = 0;
+ }
+ } else {
+ pr_info("audrec: CMD_CFG_DONE %x\n", msg[0]);
+ }
+ break;
+ case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+ pr_info("audrec: PARAM CFG DONE\n");
+ audio->running = 1;
+ break;
+ }
+ case AUDREC_MSG_FATAL_ERR_MSG:
+ pr_err("audrec: ERROR %x\n", msg[0]);
+ break;
+ case AUDREC_MSG_PACKET_READY_MSG:
+/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */
+ audio_in_get_dsp_frames(audio);
+ break;
+ default:
+ pr_err("audrec: unknown event %d\n", id);
+ }
+}
+
+struct msm_adsp_ops audpre_adsp_ops = {
+ .event = audpre_dsp_event,
+};
+
+struct msm_adsp_ops audrec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+
+#define audio_send_queue_pre(audio, cmd, len) \
+ msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len)
+#define audio_send_queue_recbs(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, \
+ QDSP_uPAudRecCmdQueue, cmd, len)
+
+static int audio_dsp_set_agc(struct audio_in *audio)
+{
+ audpreproc_cmd_cfg_agc_params cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+
+ if (audio->agc_enable) {
+ /* cmd.tx_agc_param_mask = 0xFE00 from sample code */
+ cmd.tx_agc_param_mask =
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+ cmd.tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+ memcpy(&cmd.static_gain, &audio->agc.agc_params[0],
+ sizeof(uint16_t) * 6);
+ /* cmd.param_mask = 0xFFF0 from sample code */
+ cmd.param_mask =
+ (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK);
+ memcpy(&cmd.aig_attackk, &audio->agc.agc_params[6],
+ sizeof(uint16_t) * 14);
+
+ } else {
+ cmd.tx_agc_param_mask =
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+ cmd.tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("tx_agc_param_mask = 0x%04x\n", cmd.tx_agc_param_mask);
+ pr_info("tx_agc_enable_flag = 0x%04x\n", cmd.tx_agc_enable_flag);
+ pr_info("static_gain = 0x%04x\n", cmd.static_gain);
+ pr_info("adaptive_gain_flag = 0x%04x\n", cmd.adaptive_gain_flag);
+ pr_info("expander_th = 0x%04x\n", cmd.expander_th);
+ pr_info("expander_slope = 0x%04x\n", cmd.expander_slope);
+ pr_info("compressor_th = 0x%04x\n", cmd.compressor_th);
+ pr_info("compressor_slope = 0x%04x\n", cmd.compressor_slope);
+ pr_info("param_mask = 0x%04x\n", cmd.param_mask);
+ pr_info("aig_attackk = 0x%04x\n", cmd.aig_attackk);
+ pr_info("aig_leak_down = 0x%04x\n", cmd.aig_leak_down);
+ pr_info("aig_leak_up = 0x%04x\n", cmd.aig_leak_up);
+ pr_info("aig_max = 0x%04x\n", cmd.aig_max);
+ pr_info("aig_min = 0x%04x\n", cmd.aig_min);
+ pr_info("aig_releasek = 0x%04x\n", cmd.aig_releasek);
+ pr_info("aig_leakrate_fast = 0x%04x\n", cmd.aig_leakrate_fast);
+ pr_info("aig_leakrate_slow = 0x%04x\n", cmd.aig_leakrate_slow);
+ pr_info("attackk_msw = 0x%04x\n", cmd.attackk_msw);
+ pr_info("attackk_lsw = 0x%04x\n", cmd.attackk_lsw);
+ pr_info("delay = 0x%04x\n", cmd.delay);
+ pr_info("releasek_msw = 0x%04x\n", cmd.releasek_msw);
+ pr_info("releasek_lsw = 0x%04x\n", cmd.releasek_lsw);
+ pr_info("rms_tav = 0x%04x\n", cmd.rms_tav);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_ns(struct audio_in *audio)
+{
+ audpreproc_cmd_cfg_ns_params cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+
+ if (audio->ns_enable) {
+ /* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */
+ cmd.ec_mode_new =
+ AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
+ AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
+ AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
+ memcpy(&cmd.dens_gamma_n, &audio->ns.ns_params,
+ sizeof(audio->ns.ns_params));
+ } else {
+ cmd.ec_mode_new =
+ AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("ec_mode_new = 0x%04x\n", cmd.ec_mode_new);
+ pr_info("dens_gamma_n = 0x%04x\n", cmd.dens_gamma_n);
+ pr_info("dens_nfe_block_size = 0x%04x\n", cmd.dens_nfe_block_size);
+ pr_info("dens_limit_ns = 0x%04x\n", cmd.dens_limit_ns);
+ pr_info("dens_limit_ns_d = 0x%04x\n", cmd.dens_limit_ns_d);
+ pr_info("wb_gamma_e = 0x%04x\n", cmd.wb_gamma_e);
+ pr_info("wb_gamma_n = 0x%04x\n", cmd.wb_gamma_n);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_tx_iir(struct audio_in *audio)
+{
+ struct audpre_cmd_iir_config_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+ if (audio->iir_enable) {
+ cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA;
+ cmd.num_bands = audio->iir.num_bands;
+ memcpy(&cmd.iir_params, &audio->iir.iir_params,
+ sizeof(audio->iir.iir_params));
+ } else {
+ cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("active_flag = 0x%04x\n", cmd.active_flag);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable)
+{
+ audrec_cmd_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_CFG;
+ cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
+ cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type);
+ cmd.type_1 = 0;
+
+ return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_encoder_config(struct audio_in *audio)
+{
+ audrec_cmd_arec0param_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ unsigned n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
+ cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16;
+ cmd.ptr_to_extpkt_buffer_lsw = audio->phys;
+ cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
+ cmd.samp_rate_index = audio->samp_rate_index;
+ cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */
+
+ /* FIXME have no idea why cmd.rec_quality is fixed
+ * as 0x1C00 from sample code
+ */
+ cmd.rec_quality = 0x1C00;
+
+ /* prepare buffer pointers:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ * AAC
+ * Mono/Stere: 768 + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ audio->in[n].data = data + 4;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ data += (4 + (audio->channel_mode ? 2048 : 1024));
+ else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ data += (4 + 768);
+ }
+
+ return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ audrec_cmd_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
+ /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
+ cmd.type = AUDREC_CMD_TYPE_0;
+ cmd.curr_rec_count_msw = read_cnt >> 16;
+ cmd.curr_rec_count_lsw = read_cnt;
+
+ return audio_send_queue_recbs(audio, &cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_enable_agc(struct audio_in *audio, int enable)
+{
+ if (audio->agc_enable != enable) {
+ audio->agc_enable = enable;
+ if (audio->running)
+ audio_dsp_set_agc(audio);
+ }
+}
+
+static void audio_enable_ns(struct audio_in *audio, int enable)
+{
+ if (audio->ns_enable != enable) {
+ audio->ns_enable = enable;
+ if (audio->running)
+ audio_dsp_set_ns(audio);
+ }
+}
+
+static void audio_enable_tx_iir(struct audio_in *audio, int enable)
+{
+ if (audio->iir_enable != enable) {
+ audio->iir_enable = enable;
+ if (audio->running)
+ audio_dsp_set_tx_iir(audio);
+ }
+}
+
+static void audio_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+}
+
+static long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_in_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_in_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.channel_count == 1) {
+ cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO;
+ } else if (cfg.channel_count == 2) {
+ cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ if (cfg.type == 0) {
+ cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+ } else if (cfg.type == 1) {
+ cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->samp_rate = convert_samp_rate(cfg.sample_rate);
+ audio->samp_rate_index =
+ convert_dsp_samp_index(cfg.sample_rate);
+ audio->channel_mode = cfg.channel_count;
+ audio->buffer_size =
+ audio->channel_mode ? STEREO_DATA_SIZE
+ : MONO_DATA_SIZE;
+ audio->type = cfg.type;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ cfg.sample_rate = convert_samp_index(audio->samp_rate);
+ if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO)
+ cfg.channel_count = 1;
+ else
+ cfg.channel_count = 2;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ cfg.type = 0;
+ else
+ cfg.type = 1;
+ cfg.unused[0] = 0;
+ cfg.unused[1] = 0;
+ cfg.unused[2] = 0;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped);
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ break;
+ } else {
+ pr_err("audio_in: short read\n");
+ break;
+ }
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ break; /* AAC only read one frame */
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static ssize_t audio_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static int audio_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ audio_in_disable(audio);
+ audio_flush(audio);
+ msm_adsp_put(audio->audrec);
+ msm_adsp_put(audio->audpre);
+ audio->audrec = NULL;
+ audio->audpre = NULL;
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio_in the_audio_in;
+
+static int audio_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_in;
+ int rc;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025;
+ audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025;
+ audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
+ audio->buffer_size = MONO_DATA_SIZE;
+ audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+ rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+ &audpre_adsp_ops, audio);
+ if (rc)
+ goto done;
+ rc = msm_adsp_get("AUDRECTASK", &audio->audrec,
+ &audrec_adsp_ops, audio);
+ if (rc)
+ goto done;
+
+ audio->dsp_cnt = 0;
+ audio->stopped = 0;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0, enable;
+ uint16_t enable_mask;
+#if DEBUG
+ int i;
+#endif
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPRE: {
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask)))
+ goto out_fault;
+
+ enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
+ audio_enable_agc(audio, enable);
+ enable = (enable_mask & NS_ENABLE) ? 1 : 0;
+ audio_enable_ns(audio, enable);
+ enable = (enable_mask & IIR_ENABLE) ? 1 : 0;
+ audio_enable_tx_iir(audio, enable);
+ break;
+ }
+ case AUDIO_SET_AGC: {
+ if (copy_from_user(&audio->agc, (void *) arg,
+ sizeof(audio->agc)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set agc\n");
+ for (i = 0; i < AGC_PARAM_SIZE; i++) \
+ pr_info("agc_params[%d] = 0x%04x\n", i,
+ audio->agc.agc_params[i]);
+#endif
+ break;
+ }
+ case AUDIO_SET_NS: {
+ if (copy_from_user(&audio->ns, (void *) arg,
+ sizeof(audio->ns)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set ns\n");
+ for (i = 0; i < NS_PARAM_SIZE; i++) \
+ pr_info("ns_params[%d] = 0x%04x\n",
+ i, audio->ns.ns_params[i]);
+#endif
+ break;
+ }
+ case AUDIO_SET_TX_IIR: {
+ if (copy_from_user(&audio->iir, (void *) arg,
+ sizeof(audio->iir)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set iir\n");
+ pr_info("iir.num_bands = 0x%04x\n", audio->iir.num_bands);
+ for (i = 0; i < IIR_PARAM_SIZE; i++) \
+ pr_info("iir_params[%d] = 0x%04x\n",
+ i, audio->iir.iir_params[i]);
+#endif
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+
+ goto out;
+
+out_fault:
+ rc = -EFAULT;
+out:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static int audpre_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_in;
+ file->private_data = audio;
+ return 0;
+}
+
+static struct file_operations audio_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+static struct file_operations audpre_fops = {
+ .owner = THIS_MODULE,
+ .open = audpre_open,
+ .unlocked_ioctl = audpre_ioctl,
+};
+
+struct miscdevice audio_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &audio_fops,
+};
+
+struct miscdevice audpre_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audpre",
+ .fops = &audpre_fops,
+};
+
+static int __init audio_in_init(void)
+{
+ int rc;
+ the_audio_in.data = dma_alloc_coherent(NULL, DMASZ,
+ &the_audio_in.phys, GFP_KERNEL);
+ if (!the_audio_in.data) {
+ printk(KERN_ERR "%s: Unable to allocate DMA buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&the_audio_in.lock);
+ mutex_init(&the_audio_in.read_lock);
+ spin_lock_init(&the_audio_in.dsp_lock);
+ init_waitqueue_head(&the_audio_in.wait);
+ rc = misc_register(&audio_in_misc);
+ if (!rc) {
+ rc = misc_register(&audpre_misc);
+ if (rc < 0)
+ misc_deregister(&audio_in_misc);
+ }
+ return rc;
+}
+
+device_initcall(audio_in_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c
new file mode 100644
index 0000000..f09bdcb
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c
@@ -0,0 +1,970 @@
+/* arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768
+#define BUFSZ_MIN 4096
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ pr_info("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_MP3;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ pr_info("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush) {
+ audio->buf_refresh = 1;
+ return;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ pr_info("audio_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ pr_info("audio_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ pr_info("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ pr_info("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ pr_info("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ pr_info("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ pr_info("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ dprintk("%s: FLUSH_ACK\n", __func__);
+ audio->wflush = 0;
+ audio->rflush = 0;
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+
+struct msm_adsp_ops audplay_adsp_ops = {
+ .event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V |
+ AUDDEC_DEC_MP3;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ audpp_cmd_cfg_adec_params_mp3 cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ pr_info("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 576); /* Mp3 frame size */
+ refresh_cmd.buf_read_count = 0;
+ pr_info("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ pr_info("audplay_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (audio->wflush) {
+ audio->out_needed = 1;
+ goto done;
+ }
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ pr_info("audio_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ dprintk("%s: AUDIO_FLUSH\n", __func__);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ audio->rflush = 0;
+ audio->wflush = 0;
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ audplay_buffer_refresh(audio);
+ }
+ break;
+
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ pr_info("ioctl: allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audio_mp3: malloc pcm \
+ buf failed\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback disabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ pr_info("audio_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since
+ * driver does not know frame size, read count
+ * must be greater or equal
+ * to size of PCM samples
+ */
+ pr_info("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ pr_info("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audio_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ pr_info("audio_read: kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ pr_info("audio_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned dsize;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->reserved) {
+ dprintk("%s: append reserved byte %x\n",
+ __func__, audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > (frame->size - 1)) ?
+ frame->size - 1 : count;
+ cpy_ptr++;
+ dsize = 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > frame->size) ? frame->size : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ dprintk("%s: odd length buf reserve last byte %x\n",
+ __func__, audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audio_release()\n");
+
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ audio->reserved = 0;
+ dma_free_coherent(NULL, audio->out_dma_sz, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+struct audio the_mp3_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_mp3_audio;
+ int rc;
+ unsigned pmem_sz;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ pmem_sz = DMASZ_MAX;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ audio->data = dma_alloc_coherent(NULL, pmem_sz,
+ &audio->phys, GFP_KERNEL);
+ if (audio->data)
+ break;
+ else if (pmem_sz == DMASZ_MIN) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+
+ dprintk("%s: allocated %d bytes DMA buffer\n", __func__, pmem_sz);
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc) {
+ dma_free_coherent(NULL, pmem_sz,
+ audio->data, audio->phys);
+ goto done;
+ }
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops,
+ audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ dma_free_coherent(NULL, pmem_sz,
+ audio->data, audio->phys);
+ audmgr_close(&audio->audmgr);
+ goto done;
+ }
+
+ audio->out_dma_sz = pmem_sz;
+ pmem_sz >>= 1; /* Shift by 1 to get size of ping pong buffer */
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = pmem_sz;
+
+ audio->out[1].data = audio->data + pmem_sz;
+ audio->out[1].addr = audio->phys + pmem_sz;
+ audio->out[1].size = pmem_sz;
+
+ audio->volume = 0x2000; /* equal to Q13 number 1.0 Unit Gain */
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &audio_mp3_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_mp3_audio.lock);
+ mutex_init(&the_mp3_audio.write_lock);
+ mutex_init(&the_mp3_audio.read_lock);
+ spin_lock_init(&the_mp3_audio.dsp_lock);
+ init_waitqueue_head(&the_mp3_audio.write_wait);
+ init_waitqueue_head(&the_mp3_audio.read_wait);
+ the_mp3_audio.read_data = NULL;
+ return misc_register(&audio_mp3_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c
new file mode 100644
index 0000000..fcb1f13
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_out.c
@@ -0,0 +1,850 @@
+/* arch/arm/mach-msm/qdsp5/audio_out.c
+ *
+ * pcm audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+#include <mach/htc_pwrsink.h>
+
+#include "evlog.h"
+
+#define LOG_AUDIO_EVENTS 1
+#define LOG_AUDIO_FAULTS 0
+
+enum {
+ EV_NULL,
+ EV_OPEN,
+ EV_WRITE,
+ EV_RETURN,
+ EV_IOCTL,
+ EV_WRITE_WAIT,
+ EV_WAIT_EVENT,
+ EV_FILL_BUFFER,
+ EV_SEND_BUFFER,
+ EV_DSP_EVENT,
+ EV_ENABLE,
+};
+
+#if (LOG_AUDIO_EVENTS != 1)
+static inline void LOG(unsigned id, unsigned arg) {}
+#else
+static const char *pcm_log_strings[] = {
+ "NULL",
+ "OPEN",
+ "WRITE",
+ "RETURN",
+ "IOCTL",
+ "WRITE_WAIT",
+ "WAIT_EVENT",
+ "FILL_BUFFER",
+ "SEND_BUFFER",
+ "DSP_EVENT",
+ "ENABLE",
+};
+
+DECLARE_LOG(pcm_log, 64, pcm_log_strings);
+
+static int __init _pcm_log_init(void)
+{
+ return ev_log_init(&pcm_log);
+}
+module_init(_pcm_log_init);
+
+#define LOG(id,arg) ev_log_write(&pcm_log, id, arg)
+#endif
+
+
+
+
+
+#define BUFSZ (960 * 5)
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_EQ_FLAG_DIS 0x0000
+#define AUDPP_CMD_EQ_FLAG_ENA -1
+#define AUDPP_CMD_IIR_FLAG_DIS 0x0000
+#define AUDPP_CMD_IIR_FLAG_ENA -1
+
+#define AUDPP_CMD_IIR_TUNING_FILTER 1
+#define AUDPP_CMD_EQUALIZER 2
+#define AUDPP_CMD_ADRC 3
+
+#define ADRC_ENABLE 0x0001
+#define EQ_ENABLE 0x0002
+#define IIR_ENABLE 0x0004
+
+struct adrc_filter {
+ uint16_t compression_th;
+ uint16_t compression_slope;
+ uint16_t rms_time;
+ uint16_t attack_const_lsw;
+ uint16_t attack_const_msw;
+ uint16_t release_const_lsw;
+ uint16_t release_const_msw;
+ uint16_t adrc_system_delay;
+};
+
+struct eqalizer {
+ uint16_t num_bands;
+ uint16_t eq_params[132];
+};
+
+struct rx_iir_filter {
+ uint16_t num_bands;
+ uint16_t iir_params[48];
+};
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ uint16_t eq_flag;
+ uint16_t num_bands;
+ uint16_t eq_params[132];
+} audpp_cmd_cfg_object_params_eq;
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ uint16_t active_flag;
+ uint16_t num_bands;
+ uint16_t iir_params[48];
+} audpp_cmd_cfg_object_params_rx_iir;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t wait;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ unsigned volume;
+
+ struct wake_lock wakelock;
+ struct wake_lock idlelock;
+
+ int adrc_enable;
+ struct adrc_filter adrc;
+
+ int eq_enable;
+ struct eqalizer eq;
+
+ int rx_iir_enable;
+ struct rx_iir_filter iir;
+};
+
+static void audio_prevent_sleep(struct audio *audio)
+{
+ printk(KERN_INFO "++++++++++++++++++++++++++++++\n");
+ wake_lock(&audio->wakelock);
+ wake_lock(&audio->idlelock);
+}
+
+static void audio_allow_sleep(struct audio *audio)
+{
+ wake_unlock(&audio->wakelock);
+ wake_unlock(&audio->idlelock);
+ printk(KERN_INFO "------------------------------\n");
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes);
+static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len);
+static int audio_dsp_set_adrc(struct audio *audio);
+static int audio_dsp_set_eq(struct audio *audio);
+static int audio_dsp_set_rx_iir(struct audio *audio);
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ pr_info("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ /* refuse to start if we're not ready */
+ if (!audio->out[0].used || !audio->out[1].used)
+ return -EIO;
+
+ /* we start buffers 0 and 1, so buffer 0 will be the
+ * next one the dsp will want
+ */
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM;
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ audio_prevent_sleep(audio);
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0) {
+ audio_allow_sleep(audio);
+ return rc;
+ }
+
+ if (audpp_enable(-1, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ audmgr_disable(&audio->audmgr);
+ audio_allow_sleep(audio);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ htc_pwrsink_set(PWRSINK_AUDIO, 100);
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ pr_info("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio_dsp_out_enable(audio, 0);
+
+ audpp_disable(-1, audio);
+
+ wake_up(&audio->wait);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ audio_allow_sleep(audio);
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+ struct buffer *frame;
+ unsigned long flags;
+
+ LOG(EV_DSP_EVENT, id);
+ switch (id) {
+ case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+ unsigned id = msg[2];
+ unsigned idx = msg[3] - 1;
+
+ /* pr_info("audio_dsp_event: HOST_PCM id %d idx %d\n", id, idx); */
+ if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+ pr_err("bogus id\n");
+ break;
+ }
+ if (idx > 1) {
+ pr_err("bogus buffer idx\n");
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (audio->running) {
+ atomic_add(audio->out[idx].used, &audio->out_bytes);
+ audio->out[idx].used = 0;
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ audio_dsp_send_buffer(
+ audio, audio->out_tail, frame->used);
+ audio->out_tail ^= 1;
+ } else {
+ audio->out_needed++;
+ }
+ wake_up(&audio->wait);
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ break;
+ }
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_info("audio_dsp_event: PCMDMAMISSED %d\n", msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ LOG(EV_ENABLE, 1);
+ pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(5, audio->volume, 0);
+ audio_dsp_set_adrc(audio);
+ audio_dsp_set_eq(audio);
+ audio_dsp_set_rx_iir(audio);
+ audio_dsp_out_enable(audio, 1);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ LOG(EV_ENABLE, 0);
+ pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes)
+{
+ audpp_cmd_pcm_intf cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+ if (yes) {
+ cmd.write_buf1LSW = audio->out[0].addr;
+ cmd.write_buf1MSW = audio->out[0].addr >> 16;
+ cmd.write_buf1_len = audio->out[0].size;
+ cmd.write_buf2LSW = audio->out[1].addr;
+ cmd.write_buf2MSW = audio->out[1].addr >> 16;
+ cmd.write_buf2_len = audio->out[1].size;
+ cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
+ cmd.weight_decoder_to_rx = audio->out_weight;
+ cmd.weight_arm_to_rx = 1;
+ cmd.partition_number_arm_to_dsp = 0;
+ cmd.sample_rate = audio->out_sample_rate;
+ cmd.channel_mode = audio->out_channel_mode;
+ }
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len)
+{
+ audpp_cmd_pcm_intf_send_buffer cmd;
+
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+ cmd.dsp_to_arm_buf_id = 0;
+ cmd.arm_to_dsp_buf_id = idx + 1;
+ cmd.arm_to_dsp_buf_len = len;
+
+ LOG(EV_SEND_BUFFER, idx);
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_adrc(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_adrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_ADRC;
+
+ if (audio->adrc_enable) {
+ cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+ cmd.compression_th = audio->adrc.compression_th;
+ cmd.compression_slope = audio->adrc.compression_slope;
+ cmd.rms_time = audio->adrc.rms_time;
+ cmd.attack_const_lsw = audio->adrc.attack_const_lsw;
+ cmd.attack_const_msw = audio->adrc.attack_const_msw;
+ cmd.release_const_lsw = audio->adrc.release_const_lsw;
+ cmd.release_const_msw = audio->adrc.release_const_msw;
+ cmd.adrc_system_delay = audio->adrc.adrc_system_delay;
+ } else {
+ cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+ }
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_eq(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_eq cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_EQUALIZER;
+
+ if (audio->eq_enable) {
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA;
+ cmd.num_bands = audio->eq.num_bands;
+ memcpy(&cmd.eq_params, audio->eq.eq_params,
+ sizeof(audio->eq.eq_params));
+ } else {
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS;
+ }
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_rx_iir(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_rx_iir cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+ if (audio->rx_iir_enable) {
+ cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+ cmd.num_bands = audio->iir.num_bands;
+ memcpy(&cmd.iir_params, audio->iir.iir_params,
+ sizeof(audio->iir.iir_params));
+ } else {
+ cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+ }
+
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static int audio_enable_adrc(struct audio *audio, int enable)
+{
+ if (audio->adrc_enable != enable) {
+ audio->adrc_enable = enable;
+ if (audio->running)
+ audio_dsp_set_adrc(audio);
+ }
+ return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable != enable) {
+ audio->eq_enable = enable;
+ if (audio->running)
+ audio_dsp_set_eq(audio);
+ }
+ return 0;
+}
+
+static int audio_enable_rx_iir(struct audio *audio, int enable)
+{
+ if (audio->rx_iir_enable != enable) {
+ audio->rx_iir_enable = enable;
+ if (audio->running)
+ audio_dsp_set_rx_iir(audio);
+ }
+ return 0;
+}
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->out_bytes);
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(6, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ LOG(EV_IOCTL, cmd);
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static inline int rt_policy(int policy)
+{
+ if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR))
+ return 1;
+ return 0;
+}
+
+static inline int task_has_rt_policy(struct task_struct *p)
+{
+ return rt_policy(p->policy);
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct sched_param s = { .sched_priority = 1 };
+ struct audio *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int old_prio = current->rt_priority;
+ int old_policy = current->policy;
+ int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE);
+ int rc = 0;
+
+ LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24));
+
+ /* just for this write, set us real-time */
+ if (!task_has_rt_policy(current)) {
+ struct cred *new = prepare_creds();
+ cap_raise(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ sched_setscheduler(current, SCHED_RR, &s);
+ }
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+
+ LOG(EV_WAIT_EVENT, 0);
+ rc = wait_event_interruptible(audio->wait,
+ (frame->used == 0) || (audio->stopped));
+ LOG(EV_WAIT_EVENT, 1);
+
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = count > frame->size ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ LOG(EV_FILL_BUFFER, audio->out_head ^ 1);
+ frame = audio->out + audio->out_tail;
+ if (frame->used && audio->out_needed) {
+ audio_dsp_send_buffer(audio, audio->out_tail, frame->used);
+ audio->out_tail ^= 1;
+ audio->out_needed--;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ mutex_unlock(&audio->write_lock);
+
+ /* restore scheduling policy and priority */
+ if (!rt_policy(old_policy)) {
+ struct sched_param v = { .sched_priority = old_prio };
+ sched_setscheduler(current, old_policy, &v);
+ if (likely(!cap_nice)) {
+ struct cred *new = prepare_creds();
+ cap_lower(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ sched_setscheduler(current, SCHED_RR, &s);
+ }
+ }
+
+ LOG(EV_RETURN,(buf > start) ? (buf - start) : rc);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ LOG(EV_OPEN, 0);
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ htc_pwrsink_set(PWRSINK_AUDIO, 0);
+ return 0;
+}
+
+struct audio the_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ audio->out_buffer_size = BUFSZ;
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->out_weight = 100;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+ LOG(EV_OPEN, 1);
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0, enable;
+ uint16_t enable_mask;
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg, sizeof(enable_mask)))
+ goto out_fault;
+
+ enable = (enable_mask & ADRC_ENABLE)? 1 : 0;
+ audio_enable_adrc(audio, enable);
+ enable = (enable_mask & EQ_ENABLE)? 1 : 0;
+ audio_enable_eq(audio, enable);
+ enable = (enable_mask & IIR_ENABLE)? 1 : 0;
+ audio_enable_rx_iir(audio, enable);
+ break;
+
+ case AUDIO_SET_ADRC:
+ if (copy_from_user(&audio->adrc, (void*) arg, sizeof(audio->adrc)))
+ goto out_fault;
+ break;
+
+ case AUDIO_SET_EQ:
+ if (copy_from_user(&audio->eq, (void*) arg, sizeof(audio->eq)))
+ goto out_fault;
+ break;
+
+ case AUDIO_SET_RX_IIR:
+ if (copy_from_user(&audio->iir, (void*) arg, sizeof(audio->iir)))
+ goto out_fault;
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ goto out;
+
+ out_fault:
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static int audpp_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_audio;
+
+ file->private_data = audio;
+ return 0;
+}
+
+static struct file_operations audio_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+static struct file_operations audpp_fops = {
+ .owner = THIS_MODULE,
+ .open = audpp_open,
+ .unlocked_ioctl = audpp_ioctl,
+};
+
+struct miscdevice audio_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &audio_fops,
+};
+
+struct miscdevice audpp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_ctl",
+ .fops = &audpp_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_audio.lock);
+ mutex_init(&the_audio.write_lock);
+ spin_lock_init(&the_audio.dsp_lock);
+ init_waitqueue_head(&the_audio.wait);
+ wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm");
+ wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+ return (misc_register(&audio_misc) || misc_register(&audpp_misc));
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
new file mode 100644
index 0000000..9571469
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
@@ -0,0 +1,855 @@
+/* arch/arm/mach-msm/qdsp5/audio_qcelp.c
+ *
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code is based in part on audio_mp3.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "audmgr.h"
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1080 /* QCELP 13K Hold 600ms packet data = 36 * 30 */
+#define BUF_COUNT 2
+#define DMASZ (BUFSZ * BUF_COUNT)
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT 5
+
+#define AUDDEC_DEC_QCELP 9
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[BUF_COUNT];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section - START */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* Host PCM section - END */
+
+ struct msm_adsp_module *audplay;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1; /* set when non-tunnel mode */
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+
+ uint16_t dec_id;
+};
+
+static struct audio the_qcelp_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audqcelp_send_data(struct audio *audio, unsigned needed);
+static void audqcelp_config_hostpcm(struct audio *audio);
+static void audqcelp_buffer_refresh(struct audio *audio);
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audqcelp_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_13K;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_disable(struct audio *audio)
+{
+ dprintk("audqcelp_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audqcelp_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audqcelp_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ pr_err(
+ "audqcelp_update_pcm_buf_entry: expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audqcelp_buffer_refresh(audio);
+ } else {
+ dprintk("audqcelp_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audqcelp_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audqcelp_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audqcelp_config_hostpcm(audio);
+ audqcelp_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audqcelp_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audqcelp_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audqcelp_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audqcelp_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ default:
+ pr_err("audqcelp_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_qcelp = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_v13k cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audqcelp_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audqcelp_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audqcelp_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+static void audqcelp_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audqcelp_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+}
+
+static void audqcelp_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audqcelp_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audqcelp_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audqcelp_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audqcelp_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audqcelp_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audqcelp_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+ break;
+ case AUDIO_SET_CONFIG:
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = BUF_COUNT;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ if (copy_from_user(&config, (void *)arg,
+ sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk(
+ "audqcelp_ioctl: allocate PCM buf %d\n",
+ config.buffer_count * config.buffer_size);
+ audio->read_data = dma_alloc_coherent(NULL,
+ config.buffer_size * config.buffer_count,
+ &audio->read_phys, GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err(
+ "audqcelp_ioctl: no mem for pcm buf\n"
+ );
+ rc = -ENOMEM;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audqcelp_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped));
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ not know frame size, read count must be greater or equal
+ to size of PCM samples */
+ dprintk("audqcelp_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audqcelp_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user(buf,
+ audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audqcelp_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ }
+ }
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audqcelp_read: kick start pcm feedback again\n");
+ audqcelp_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audqcelp_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audqcelp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ dprintk("audqcelp_write() \n");
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ dprintk("audqcelp_write() buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audqcelp_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audqcelp_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audqcelp_release()\n");
+
+ mutex_lock(&audio->lock);
+ audqcelp_disable(audio);
+ audqcelp_flush(audio);
+ audqcelp_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ if (audio->data)
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int audqcelp_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_qcelp_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto err;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_qcelp, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ audmgr_close(&audio->audmgr);
+ goto err;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audqcelp_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+err:
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_qcelp_fops = {
+ .owner = THIS_MODULE,
+ .open = audqcelp_open,
+ .release = audqcelp_release,
+ .read = audqcelp_read,
+ .write = audqcelp_write,
+ .unlocked_ioctl = audqcelp_ioctl,
+};
+
+struct miscdevice audio_qcelp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp",
+ .fops = &audio_qcelp_fops,
+};
+
+static int __init audqcelp_init(void)
+{
+ mutex_init(&the_qcelp_audio.lock);
+ mutex_init(&the_qcelp_audio.write_lock);
+ mutex_init(&the_qcelp_audio.read_lock);
+ spin_lock_init(&the_qcelp_audio.dsp_lock);
+ init_waitqueue_head(&the_qcelp_audio.write_wait);
+ init_waitqueue_head(&the_qcelp_audio.read_wait);
+ the_qcelp_audio.read_data = NULL;
+ return misc_register(&audio_qcelp_misc);
+}
+
+static void __exit audqcelp_exit(void)
+{
+ misc_deregister(&audio_qcelp_misc);
+}
+
+module_init(audqcelp_init);
+module_exit(audqcelp_exit);
+
+MODULE_DESCRIPTION("MSM QCELP 13K driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM");
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c
new file mode 100644
index 0000000..b7d8158
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr.c
@@ -0,0 +1,314 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.c
+ *
+ * interface to "audmgr" service on the baseband cpu
+ *
+ * Copyright (C) 2008 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/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <asm/atomic.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#define STATE_CLOSED 0
+#define STATE_DISABLED 1
+#define STATE_ENABLING 2
+#define STATE_ENABLED 3
+#define STATE_DISABLING 4
+#define STATE_ERROR 5
+
+static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid)
+{
+ uint32_t rep[6];
+
+ rep[0] = cpu_to_be32(xid);
+ rep[1] = cpu_to_be32(1);
+ rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ rep[4] = 0;
+ rep[5] = 0;
+
+ msm_rpc_write(ept, rep, sizeof(rep));
+}
+
+static void process_audmgr_callback(struct audmgr *am,
+ struct rpc_audmgr_cb_func_ptr *args,
+ int len)
+{
+ if (len < (sizeof(uint32_t) * 3))
+ return;
+ if (be32_to_cpu(args->set_to_one) != 1)
+ return;
+
+ switch (be32_to_cpu(args->status)) {
+ case RPC_AUDMGR_STATUS_READY:
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ am->handle = be32_to_cpu(args->u.handle);
+ pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle);
+ break;
+ case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
+ uint32_t volume;
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ volume = be32_to_cpu(args->u.volume);
+ pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume);
+ am->state = STATE_ENABLED;
+ wake_up(&am->wait);
+ break;
+ }
+ case RPC_AUDMGR_STATUS_PENDING:
+ pr_err("audmgr: PENDING?\n");
+ break;
+ case RPC_AUDMGR_STATUS_SUSPEND:
+ pr_err("audmgr: SUSPEND?\n");
+ break;
+ case RPC_AUDMGR_STATUS_FAILURE:
+ pr_err("audmgr: FAILURE\n");
+ break;
+ case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
+ pr_err("audmgr: VOLUME_CHANGE?\n");
+ break;
+ case RPC_AUDMGR_STATUS_DISABLED:
+ pr_err("audmgr: DISABLED\n");
+ am->state = STATE_DISABLED;
+ wake_up(&am->wait);
+ break;
+ case RPC_AUDMGR_STATUS_ERROR:
+ pr_err("audmgr: ERROR?\n");
+ am->state = STATE_ERROR;
+ wake_up(&am->wait);
+ break;
+ default:
+ break;
+ }
+}
+
+static void process_rpc_request(uint32_t proc, uint32_t xid,
+ void *data, int len, void *private)
+{
+ struct audmgr *am = private;
+ uint32_t *x = data;
+
+ if (0) {
+ int n = len / 4;
+ pr_info("rpc_call proc %d:", proc);
+ while (n--)
+ printk(" %08x", be32_to_cpu(*x++));
+ printk("\n");
+ }
+
+ if (proc == AUDMGR_CB_FUNC_PTR)
+ process_audmgr_callback(am, data, len);
+ else
+ pr_err("audmgr: unknown rpc proc %d\n", proc);
+ rpc_ack(am->ept, xid);
+}
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_VERSION 2
+
+#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
+#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
+#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
+#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
+
+static int audmgr_rpc_thread(void *data)
+{
+ struct audmgr *am = data;
+ struct rpc_request_hdr *hdr = NULL;
+ uint32_t type;
+ int len;
+
+ pr_info("audmgr_rpc_thread() start\n");
+
+ while (!kthread_should_stop()) {
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1);
+ if (len < 0) {
+ pr_err("audmgr: rpc read failed (%d)\n", len);
+ break;
+ }
+ if (len < RPC_COMMON_HDR_SZ)
+ continue;
+
+ type = be32_to_cpu(hdr->type);
+ if (type == RPC_TYPE_REPLY) {
+ struct rpc_reply_hdr *rep = (void *) hdr;
+ uint32_t status;
+ if (len < RPC_REPLY_HDR_SZ)
+ continue;
+ status = be32_to_cpu(rep->reply_stat);
+ if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
+ status = be32_to_cpu(rep->data.acc_hdr.accept_stat);
+ pr_info("audmgr: rpc_reply status %d\n", status);
+ } else {
+ pr_info("audmgr: rpc_reply denied!\n");
+ }
+ /* process reply */
+ continue;
+ }
+
+ if (len < RPC_REQUEST_HDR_SZ)
+ continue;
+
+ process_rpc_request(be32_to_cpu(hdr->procedure),
+ be32_to_cpu(hdr->xid),
+ (void *) (hdr + 1),
+ len - sizeof(*hdr),
+ data);
+ }
+ pr_info("audmgr_rpc_thread() exit\n");
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ am->task = NULL;
+ wake_up(&am->wait);
+ return 0;
+}
+
+struct audmgr_enable_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_audmgr_enable_client_args args;
+};
+
+struct audmgr_disable_msg {
+ struct rpc_request_hdr hdr;
+ uint32_t handle;
+};
+
+int audmgr_open(struct audmgr *am)
+{
+ int rc;
+
+ if (am->state != STATE_CLOSED)
+ return 0;
+
+ am->ept = msm_rpc_connect(AUDMGR_PROG,
+ AUDMGR_VERS,
+ MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE);
+
+ init_waitqueue_head(&am->wait);
+
+ if (IS_ERR(am->ept)) {
+ rc = PTR_ERR(am->ept);
+ am->ept = NULL;
+ pr_err("audmgr: failed to connect to audmgr svc\n");
+ return rc;
+ }
+
+ am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc");
+ if (IS_ERR(am->task)) {
+ rc = PTR_ERR(am->task);
+ am->task = NULL;
+ msm_rpc_close(am->ept);
+ am->ept = NULL;
+ return rc;
+ }
+
+ am->state = STATE_DISABLED;
+ return 0;
+}
+EXPORT_SYMBOL(audmgr_open);
+
+int audmgr_close(struct audmgr *am)
+{
+ return -EBUSY;
+}
+EXPORT_SYMBOL(audmgr_close);
+
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg)
+{
+ struct audmgr_enable_msg msg;
+ int rc;
+
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ if (am->state == STATE_DISABLING)
+ pr_err("audmgr: state is DISABLING in enable?\n");
+ am->state = STATE_ENABLING;
+
+ msg.args.set_to_one = cpu_to_be32(1);
+ msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate);
+ msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate);
+ msg.args.def_method = cpu_to_be32(cfg->def_method);
+ msg.args.codec_type = cpu_to_be32(cfg->codec);
+ msg.args.snd_method = cpu_to_be32(cfg->snd_method);
+ msg.args.cb_func = cpu_to_be32(0x11111111);
+ msg.args.client_data = cpu_to_be32(0x11223344);
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_ENABLE_CLIENT);
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while enabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_enable);
+
+int audmgr_disable(struct audmgr *am)
+{
+ struct audmgr_disable_msg msg;
+ int rc;
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_DISABLE_CLIENT);
+ msg.handle = cpu_to_be32(am->handle);
+
+ am->state = STATE_DISABLING;
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while disabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_disable);
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.h b/arch/arm/mach-msm/qdsp5/audmgr.h
new file mode 100644
index 0000000..cd2d0c6
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr.h
@@ -0,0 +1,215 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_H
+
+#if CONFIG_MSM_AMSS_VERSION==6350
+#include "audmgr_new.h"
+#else
+
+enum rpc_aud_def_sample_rate_type {
+ RPC_AUD_DEF_SAMPLE_RATE_NONE,
+ RPC_AUD_DEF_SAMPLE_RATE_8000,
+ RPC_AUD_DEF_SAMPLE_RATE_11025,
+ RPC_AUD_DEF_SAMPLE_RATE_12000,
+ RPC_AUD_DEF_SAMPLE_RATE_16000,
+ RPC_AUD_DEF_SAMPLE_RATE_22050,
+ RPC_AUD_DEF_SAMPLE_RATE_24000,
+ RPC_AUD_DEF_SAMPLE_RATE_32000,
+ RPC_AUD_DEF_SAMPLE_RATE_44100,
+ RPC_AUD_DEF_SAMPLE_RATE_48000,
+ RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+ RPC_AUD_DEF_METHOD_NONE,
+ RPC_AUD_DEF_METHOD_KEY_BEEP,
+ RPC_AUD_DEF_METHOD_PLAYBACK,
+ RPC_AUD_DEF_METHOD_VOICE,
+ RPC_AUD_DEF_METHOD_RECORD,
+ RPC_AUD_DEF_METHOD_HOST_PCM,
+ RPC_AUD_DEF_METHOD_MIDI_OUT,
+ RPC_AUD_DEF_METHOD_RECORD_SBC,
+ RPC_AUD_DEF_METHOD_DTMF_RINGER,
+ RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+ RPC_AUD_DEF_CODEC_NONE,
+ RPC_AUD_DEF_CODEC_DTMF,
+ RPC_AUD_DEF_CODEC_MIDI,
+ RPC_AUD_DEF_CODEC_MP3,
+ RPC_AUD_DEF_CODEC_PCM,
+ RPC_AUD_DEF_CODEC_AAC,
+ RPC_AUD_DEF_CODEC_WMA,
+ RPC_AUD_DEF_CODEC_RA,
+ RPC_AUD_DEF_CODEC_ADPCM,
+ RPC_AUD_DEF_CODEC_GAUDIO,
+ RPC_AUD_DEF_CODEC_VOC_EVRC,
+ RPC_AUD_DEF_CODEC_VOC_13K,
+ RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+ RPC_AUD_DEF_CODEC_VOC_AMR,
+ RPC_AUD_DEF_CODEC_VOC_EFR,
+ RPC_AUD_DEF_CODEC_VOC_FR,
+ RPC_AUD_DEF_CODEC_VOC_HR,
+ RPC_AUD_DEF_CODEC_VOC,
+ RPC_AUD_DEF_CODEC_SBC,
+ RPC_AUD_DEF_CODEC_VOC_PCM,
+ RPC_AUD_DEF_CODEC_AMR_WB,
+ RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+ RPC_AUD_DEF_CODEC_MAX,
+};
+
+enum rpc_snd_method_type {
+ RPC_SND_METHOD_VOICE = 0,
+ RPC_SND_METHOD_KEY_BEEP,
+ RPC_SND_METHOD_MESSAGE,
+ RPC_SND_METHOD_RING,
+ RPC_SND_METHOD_MIDI,
+ RPC_SND_METHOD_AUX,
+ RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+ RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_1,
+ RPC_VOC_CODEC_STEREO_HEADSET,
+ RPC_VOC_CODEC_ON_CHIP_AUX,
+ RPC_VOC_CODEC_BT_OFF_BOARD,
+ RPC_VOC_CODEC_BT_A2DP,
+ RPC_VOC_CODEC_OFF_BOARD,
+ RPC_VOC_CODEC_SDAC,
+ RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TTY_ON_CHIP_1,
+ RPC_VOC_CODEC_TTY_OFF_BOARD,
+ RPC_VOC_CODEC_TTY_VCO,
+ RPC_VOC_CODEC_TTY_HCO,
+ RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+ RPC_VOC_CODEC_MAX,
+ RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+ RPC_AUDMGR_STATUS_READY,
+ RPC_AUDMGR_STATUS_CODEC_CONFIG,
+ RPC_AUDMGR_STATUS_PENDING,
+ RPC_AUDMGR_STATUS_SUSPEND,
+ RPC_AUDMGR_STATUS_FAILURE,
+ RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+ RPC_AUDMGR_STATUS_DISABLED,
+ RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+ uint32_t set_to_one;
+ uint32_t tx_sample_rate;
+ uint32_t rx_sample_rate;
+ uint32_t def_method;
+ uint32_t codec_type;
+ uint32_t snd_method;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT 2
+#define AUDMGR_DISABLE_CLIENT 3
+#define AUDMGR_SUSPEND_EVENT_RSP 4
+#define AUDMGR_REGISTER_OPERATION_LISTENER 5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
+#define AUDMGR_REGISTER_CODEC_LISTENER 7
+#define AUDMGR_GET_RX_SAMPLE_RATE 8
+#define AUDMGR_GET_TX_SAMPLE_RATE 9
+#define AUDMGR_SET_DEVICE_MODE 10
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_PROG_VERS "rs30000013:46255756"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0x46255756
+#else
+#define AUDMGR_PROG_VERS "rs30000013:e94e8f0c"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0xe94e8f0c
+#endif
+
+struct rpc_audmgr_cb_func_ptr {
+ uint32_t cb_id;
+ uint32_t set_to_one;
+ uint32_t status;
+ union {
+ uint32_t handle;
+ uint32_t volume;
+
+ } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR 1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x5fa922a9
+#else
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x21570ba7
+#endif
+
+struct audmgr {
+ wait_queue_head_t wait;
+ uint32_t handle;
+ struct msm_rpc_endpoint *ept;
+ struct task_struct *task;
+ int state;
+};
+
+struct audmgr_config {
+ uint32_t tx_rate;
+ uint32_t rx_rate;
+ uint32_t def_method;
+ uint32_t codec;
+ uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_pause(unsigned id, int pause);
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
+#endif
diff --git a/arch/arm/mach-msm/qdsp5/audmgr_new.h b/arch/arm/mach-msm/qdsp5/audmgr_new.h
new file mode 100644
index 0000000..4381242
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr_new.h
@@ -0,0 +1,213 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+
+enum rpc_aud_def_sample_rate_type {
+ RPC_AUD_DEF_SAMPLE_RATE_NONE,
+ RPC_AUD_DEF_SAMPLE_RATE_8000,
+ RPC_AUD_DEF_SAMPLE_RATE_11025,
+ RPC_AUD_DEF_SAMPLE_RATE_12000,
+ RPC_AUD_DEF_SAMPLE_RATE_16000,
+ RPC_AUD_DEF_SAMPLE_RATE_22050,
+ RPC_AUD_DEF_SAMPLE_RATE_24000,
+ RPC_AUD_DEF_SAMPLE_RATE_32000,
+ RPC_AUD_DEF_SAMPLE_RATE_44100,
+ RPC_AUD_DEF_SAMPLE_RATE_48000,
+ RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+ RPC_AUD_DEF_METHOD_NONE,
+ RPC_AUD_DEF_METHOD_KEY_BEEP,
+ RPC_AUD_DEF_METHOD_PLAYBACK,
+ RPC_AUD_DEF_METHOD_VOICE,
+ RPC_AUD_DEF_METHOD_RECORD,
+ RPC_AUD_DEF_METHOD_HOST_PCM,
+ RPC_AUD_DEF_METHOD_MIDI_OUT,
+ RPC_AUD_DEF_METHOD_RECORD_SBC,
+ RPC_AUD_DEF_METHOD_DTMF_RINGER,
+ RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+ RPC_AUD_DEF_CODEC_NONE,
+ RPC_AUD_DEF_CODEC_DTMF,
+ RPC_AUD_DEF_CODEC_MIDI,
+ RPC_AUD_DEF_CODEC_MP3,
+ RPC_AUD_DEF_CODEC_PCM,
+ RPC_AUD_DEF_CODEC_AAC,
+ RPC_AUD_DEF_CODEC_WMA,
+ RPC_AUD_DEF_CODEC_RA,
+ RPC_AUD_DEF_CODEC_ADPCM,
+ RPC_AUD_DEF_CODEC_GAUDIO,
+ RPC_AUD_DEF_CODEC_VOC_EVRC,
+ RPC_AUD_DEF_CODEC_VOC_13K,
+ RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+ RPC_AUD_DEF_CODEC_VOC_AMR,
+ RPC_AUD_DEF_CODEC_VOC_EFR,
+ RPC_AUD_DEF_CODEC_VOC_FR,
+ RPC_AUD_DEF_CODEC_VOC_HR,
+ RPC_AUD_DEF_CODEC_VOC_CDMA,
+ RPC_AUD_DEF_CODEC_VOC_CDMA_WB,
+ RPC_AUD_DEF_CODEC_VOC_UMTS,
+ RPC_AUD_DEF_CODEC_VOC_UMTS_WB,
+ RPC_AUD_DEF_CODEC_SBC,
+ RPC_AUD_DEF_CODEC_VOC_PCM,
+ RPC_AUD_DEF_CODEC_AMR_WB,
+ RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+ RPC_AUD_DEF_CODEC_AAC_BSAC,
+ RPC_AUD_DEF_CODEC_MAX,
+ RPC_AUD_DEF_CODEC_AMR_NB,
+ RPC_AUD_DEF_CODEC_13K,
+ RPC_AUD_DEF_CODEC_EVRC,
+ RPC_AUD_DEF_CODEC_MAX_002,
+};
+
+enum rpc_snd_method_type {
+ RPC_SND_METHOD_VOICE = 0,
+ RPC_SND_METHOD_KEY_BEEP,
+ RPC_SND_METHOD_MESSAGE,
+ RPC_SND_METHOD_RING,
+ RPC_SND_METHOD_MIDI,
+ RPC_SND_METHOD_AUX,
+ RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+ RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_1,
+ RPC_VOC_CODEC_STEREO_HEADSET,
+ RPC_VOC_CODEC_ON_CHIP_AUX,
+ RPC_VOC_CODEC_BT_OFF_BOARD,
+ RPC_VOC_CODEC_BT_A2DP,
+ RPC_VOC_CODEC_OFF_BOARD,
+ RPC_VOC_CODEC_SDAC,
+ RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TTY_ON_CHIP_1,
+ RPC_VOC_CODEC_TTY_OFF_BOARD,
+ RPC_VOC_CODEC_TTY_VCO,
+ RPC_VOC_CODEC_TTY_HCO,
+ RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+ RPC_VOC_CODEC_MAX,
+ RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+ RPC_AUDMGR_STATUS_READY,
+ RPC_AUDMGR_STATUS_CODEC_CONFIG,
+ RPC_AUDMGR_STATUS_PENDING,
+ RPC_AUDMGR_STATUS_SUSPEND,
+ RPC_AUDMGR_STATUS_FAILURE,
+ RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+ RPC_AUDMGR_STATUS_DISABLED,
+ RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+ uint32_t set_to_one;
+ uint32_t tx_sample_rate;
+ uint32_t rx_sample_rate;
+ uint32_t def_method;
+ uint32_t codec_type;
+ uint32_t snd_method;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT 2
+#define AUDMGR_DISABLE_CLIENT 3
+#define AUDMGR_SUSPEND_EVENT_RSP 4
+#define AUDMGR_REGISTER_OPERATION_LISTENER 5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
+#define AUDMGR_REGISTER_CODEC_LISTENER 7
+#define AUDMGR_GET_RX_SAMPLE_RATE 8
+#define AUDMGR_GET_TX_SAMPLE_RATE 9
+#define AUDMGR_SET_DEVICE_MODE 10
+
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS MSM_RPC_VERS(1,0)
+
+struct rpc_audmgr_cb_func_ptr {
+ uint32_t cb_id;
+ uint32_t status; /* Audmgr status */
+ uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */
+ uint32_t disc;
+ /* disc = AUDMGR_STATUS_READY => data=handle
+ disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle
+ disc = AUDMGR_STATUS_DISABLED => data =status_disabled
+ disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */
+ union {
+ uint32_t handle;
+ uint32_t volume;
+ uint32_t status_disabled;
+ uint32_t volume_change;
+ } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR 1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
+
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0xf8e3e2d9
+
+struct audmgr {
+ wait_queue_head_t wait;
+ uint32_t handle;
+ struct msm_rpc_endpoint *ept;
+ struct task_struct *task;
+ int state;
+};
+
+struct audmgr_config {
+ uint32_t tx_rate;
+ uint32_t rx_rate;
+ uint32_t def_method;
+ uint32_t codec;
+ uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+int audpp_pause(unsigned id, int pause);
+int audpp_flush(unsigned id);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5/audpp.c b/arch/arm/mach-msm/qdsp5/audpp.c
new file mode 100644
index 0000000..32c2847
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audpp.c
@@ -0,0 +1,429 @@
+
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * Copyright (C) 2008 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/module.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#include "evlog.h"
+
+
+enum {
+ EV_NULL,
+ EV_ENABLE,
+ EV_DISABLE,
+ EV_EVENT,
+ EV_DATA,
+};
+
+static const char *dsp_log_strings[] = {
+ "NULL",
+ "ENABLE",
+ "DISABLE",
+ "EVENT",
+ "DATA",
+};
+
+DECLARE_LOG(dsp_log, 64, dsp_log_strings);
+
+static int __init _dsp_log_init(void)
+{
+ return ev_log_init(&dsp_log);
+}
+module_init(_dsp_log_init);
+#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
+
+static DEFINE_MUTEX(audpp_lock);
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+#define AUDPP_AVSYNC_INFO_SIZE 7
+
+struct audpp_state {
+ struct msm_adsp_module *mod;
+ audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
+ void *private[AUDPP_CLNT_MAX_COUNT];
+ struct mutex *lock;
+ unsigned open_count;
+ unsigned enabled;
+
+ /* which channels are actually enabled */
+ unsigned avsync_mask;
+
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
+};
+
+struct audpp_state the_audpp_state = {
+ .lock = &audpp_lock,
+};
+
+int audpp_send_queue1(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd1Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue1);
+
+int audpp_send_queue2(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd2Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue2);
+
+int audpp_send_queue3(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd3Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue3);
+
+static int audpp_dsp_config(int enable)
+{
+ audpp_cmd_cfg cmd;
+
+ cmd.cmd_id = AUDPP_CMD_CFG;
+ cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
+ uint16_t *msg)
+{
+ unsigned n;
+ for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
+ if (audpp->func[n])
+ audpp->func[n] (audpp->private[n], id, msg);
+ }
+}
+
+static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
+ unsigned id, uint16_t *msg)
+{
+ if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
+ audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
+}
+
+static void audpp_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audpp_state *audpp = data;
+ uint16_t msg[8];
+
+ if (id == AUDPP_MSG_AVSYNC_MSG) {
+ getevent(audpp->avsync, sizeof(audpp->avsync));
+
+ /* mask off any channels we're not watching to avoid
+ * cases where we might get one last update after
+ * disabling avsync and end up in an odd state when
+ * we next read...
+ */
+ audpp->avsync[0] &= audpp->avsync_mask;
+ return;
+ }
+
+ getevent(msg, sizeof(msg));
+
+ LOG(EV_EVENT, (id << 16) | msg[0]);
+ LOG(EV_DATA, (msg[1] << 16) | msg[2]);
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned cid = msg[0];
+ pr_info("audpp: status %d %d %d\n", cid, msg[1],
+ msg[2]);
+ if ((cid < 5) && audpp->func[cid])
+ audpp->func[cid] (audpp->private[cid], id, msg);
+ break;
+ }
+ case AUDPP_MSG_HOST_PCM_INTF_MSG:
+ if (audpp->func[5])
+ audpp->func[5] (audpp->private[5], id, msg);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_err("audpp: DMA missed obj=%x\n", msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ pr_info("audpp: ENABLE\n");
+ audpp->enabled = 1;
+ audpp_broadcast(audpp, id, msg);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ pr_info("audpp: DISABLE\n");
+ audpp->enabled = 0;
+ audpp_broadcast(audpp, id, msg);
+ } else {
+ pr_err("audpp: invalid config msg %d\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ audpp_broadcast(audpp, id, msg);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+ default:
+ pr_info("audpp: unhandled msg id %x\n", id);
+ }
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = audpp_dsp_event,
+};
+
+static void audpp_fake_event(struct audpp_state *audpp, int id,
+ unsigned event, unsigned arg)
+{
+ uint16_t msg[1];
+ msg[0] = arg;
+ audpp->func[id] (audpp->private[id], event, msg);
+}
+
+int audpp_enable(int id, audpp_event_func func, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int res = 0;
+
+ if (id < -1 || id > 4)
+ return -EINVAL;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ if (audpp->func[id]) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ audpp->func[id] = func;
+ audpp->private[id] = private;
+
+ LOG(EV_ENABLE, 1);
+ if (audpp->open_count++ == 0) {
+ pr_info("audpp: enable\n");
+ res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+ if (res < 0) {
+ pr_err("audpp: cannot open AUDPPTASK\n");
+ audpp->open_count = 0;
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ goto out;
+ }
+ LOG(EV_ENABLE, 2);
+ msm_adsp_enable(audpp->mod);
+ audpp_dsp_config(1);
+ } else {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (audpp->enabled)
+ audpp_fake_event(audpp, id,
+ AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
+ local_irq_restore(flags);
+ }
+
+ res = 0;
+out:
+ mutex_unlock(audpp->lock);
+ return res;
+}
+EXPORT_SYMBOL(audpp_enable);
+
+void audpp_disable(int id, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ unsigned long flags;
+
+ if (id < -1 || id > 4)
+ return;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ LOG(EV_DISABLE, 1);
+ if (!audpp->func[id])
+ goto out;
+ if (audpp->private[id] != private)
+ goto out;
+
+ local_irq_save(flags);
+ audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ local_irq_restore(flags);
+
+ if (--audpp->open_count == 0) {
+ pr_info("audpp: disable\n");
+ LOG(EV_DISABLE, 2);
+ audpp_dsp_config(0);
+ msm_adsp_disable(audpp->mod);
+ msm_adsp_put(audpp->mod);
+ audpp->mod = NULL;
+ }
+out:
+ mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+void audpp_avsync(int id, unsigned rate)
+{
+ unsigned long flags;
+ audpp_cmd_avsync cmd;
+
+ if (BAD_ID(id))
+ return;
+
+ local_irq_save(flags);
+ if (rate)
+ the_audpp_state.avsync_mask |= (1 << id);
+ else
+ the_audpp_state.avsync_mask &= (~(1 << id));
+ the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
+ local_irq_restore(flags);
+
+ cmd.cmd_id = AUDPP_CMD_AVSYNC;
+ cmd.object_number = id;
+ cmd.interrupt_interval_lsw = rate;
+ cmd.interrupt_interval_msw = rate >> 16;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_avsync);
+
+unsigned audpp_avsync_sample_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_sample_count);
+
+unsigned audpp_avsync_byte_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_byte_count);
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_VOLUME_PAN 0
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
+{
+ /* cmd, obj_cfg[7], cmd_type, volume, pan */
+ uint16_t cmd[11];
+
+ if (id > 6)
+ return -EINVAL;
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd[8] = AUDPP_CMD_VOLUME_PAN;
+ cmd[9] = volume;
+ cmd[10] = pan;
+
+ return audpp_send_queue3(cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_set_volume_and_pan);
+
+int audpp_pause(unsigned id, int pause)
+{
+ /* pause 1 = pause 0 = resume */
+ u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(pause_cmd, 0, sizeof(pause_cmd));
+
+ pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ if (pause == 1)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+ else if (pause == 0)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
+ else
+ return -EINVAL;
+
+ return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
+}
+EXPORT_SYMBOL(audpp_pause);
+
+int audpp_flush(unsigned id)
+{
+ u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(flush_cmd, 0, sizeof(flush_cmd));
+
+ flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
+
+ return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
+}
+EXPORT_SYMBOL(audpp_flush);
diff --git a/arch/arm/mach-msm/qdsp5/evlog.h b/arch/arm/mach-msm/qdsp5/evlog.h
new file mode 100644
index 0000000..5c0edf1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/evlog.h
@@ -0,0 +1,133 @@
+/* arch/arm/mach-msm/qdsp5/evlog.h
+ *
+ * simple event log debugging facility
+ *
+ * Copyright (C) 2008 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/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/debugfs.h>
+
+#define EV_LOG_ENTRY_NAME(n) n##_entry
+
+#define DECLARE_LOG(_name, _size, _str) \
+static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \
+static struct ev_log _name = { \
+ .name = #_name, \
+ .strings = _str, \
+ .num_strings = ARRAY_SIZE(_str), \
+ .entry = EV_LOG_ENTRY_NAME(_name), \
+ .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \
+}
+
+struct ev_entry {
+ ktime_t when;
+ uint32_t id;
+ uint32_t arg;
+};
+
+struct ev_log {
+ struct ev_entry *entry;
+ unsigned max;
+ unsigned next;
+ unsigned fault;
+ const char **strings;
+ unsigned num_strings;
+ const char *name;
+};
+
+static char ev_buf[4096];
+
+static ssize_t ev_log_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ev_log *log = file->private_data;
+ struct ev_entry *entry;
+ unsigned long flags;
+ int size = 0;
+ unsigned n, id, max;
+ ktime_t now, t;
+
+ max = log->max;
+ now = ktime_get();
+ local_irq_save(flags);
+ n = (log->next - 1) & (max - 1);
+ entry = log->entry;
+ while (n != log->next) {
+ t = ktime_sub(now, entry[n].when);
+ id = entry[n].id;
+ if (id) {
+ const char *str;
+ if (id < log->num_strings)
+ str = log->strings[id];
+ else
+ str = "UNKNOWN";
+ size += scnprintf(ev_buf + size, 4096 - size,
+ "%8d.%03d %08x %s\n",
+ t.tv.sec, t.tv.nsec / 1000000,
+ entry[n].arg, str);
+ }
+ n = (n - 1) & (max - 1);
+ }
+ log->fault = 0;
+ local_irq_restore(flags);
+ return simple_read_from_buffer(buf, count, ppos, ev_buf, size);
+}
+
+static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg)
+{
+ struct ev_entry *entry;
+ unsigned long flags;
+ local_irq_save(flags);
+
+ if (log->fault) {
+ if (log->fault == 1)
+ goto done;
+ log->fault--;
+ }
+
+ entry = log->entry + log->next;
+ entry->when = ktime_get();
+ entry->id = id;
+ entry->arg = arg;
+ log->next = (log->next + 1) & (log->max - 1);
+done:
+ local_irq_restore(flags);
+}
+
+static void ev_log_freeze(struct ev_log *log, unsigned count)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ log->fault = count;
+ local_irq_restore(flags);
+}
+
+static int ev_log_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations ev_log_ops = {
+ .read = ev_log_read,
+ .open = ev_log_open,
+};
+
+static int ev_log_init(struct ev_log *log)
+{
+ debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops);
+ return 0;
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c
new file mode 100644
index 0000000..037d7ff
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/snd.c
@@ -0,0 +1,279 @@
+/* arch/arm/mach-msm/qdsp5/snd.c
+ *
+ * interface to "snd" service on the baseband cpu
+ *
+ * Copyright (C) 2008 HTC 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_rpcrouter.h>
+
+struct snd_ctxt {
+ struct mutex lock;
+ int opened;
+ struct msm_rpc_endpoint *ept;
+ struct msm_snd_endpoints *snd_epts;
+};
+
+static struct snd_ctxt the_snd;
+
+#define RPC_SND_PROG 0x30000002
+#define RPC_SND_CB_PROG 0x31000002
+#if CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_SND_VERS 0x94756085 /* 2490720389 */
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || \
+ (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */
+#elif CONFIG_MSM_AMSS_VERSION == 6350
+#define RPC_SND_VERS MSM_RPC_VERS(1,0)
+#endif
+
+#define SND_SET_DEVICE_PROC 2
+#define SND_SET_VOLUME_PROC 3
+
+struct rpc_snd_set_device_args {
+ uint32_t device;
+ uint32_t ear_mute;
+ uint32_t mic_mute;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+struct rpc_snd_set_volume_args {
+ uint32_t device;
+ uint32_t method;
+ uint32_t volume;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+struct snd_set_device_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_snd_set_device_args args;
+};
+
+struct snd_set_volume_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_snd_set_volume_args args;
+};
+
+struct snd_endpoint *get_snd_endpoints(int *size);
+
+static inline int check_mute(int mute)
+{
+ return (mute == SND_MUTE_MUTED ||
+ mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL;
+}
+
+static int get_endpoint(struct snd_ctxt *snd, unsigned long arg)
+{
+ int rc = 0, index;
+ struct msm_snd_endpoint ept;
+
+ if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) {
+ pr_err("snd_ioctl get endpoint: invalid read pointer.\n");
+ return -EFAULT;
+ }
+
+ index = ept.id;
+ if (index < 0 || index >= snd->snd_epts->num) {
+ pr_err("snd_ioctl get endpoint: invalid index!\n");
+ return -EINVAL;
+ }
+
+ ept.id = snd->snd_epts->endpoints[index].id;
+ strncpy(ept.name,
+ snd->snd_epts->endpoints[index].name,
+ sizeof(ept.name));
+
+ if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) {
+ pr_err("snd_ioctl get endpoint: invalid write pointer.\n");
+ rc = -EFAULT;
+ }
+
+ return rc;
+}
+
+static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct snd_set_device_msg dmsg;
+ struct snd_set_volume_msg vmsg;
+ struct msm_snd_device_config dev;
+ struct msm_snd_volume_config vol;
+ struct snd_ctxt *snd = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&snd->lock);
+ switch (cmd) {
+ case SND_SET_DEVICE:
+ if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
+ pr_err("snd_ioctl set device: invalid pointer.\n");
+ rc = -EFAULT;
+ break;
+ }
+
+ dmsg.args.device = cpu_to_be32(dev.device);
+ dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
+ dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
+ if (check_mute(dev.ear_mute) < 0 ||
+ check_mute(dev.mic_mute) < 0) {
+ pr_err("snd_ioctl set device: invalid mute status.\n");
+ rc = -EINVAL;
+ break;
+ }
+ dmsg.args.cb_func = -1;
+ dmsg.args.client_data = 0;
+
+ pr_info("snd_set_device %d %d %d\n", dev.device,
+ dev.ear_mute, dev.mic_mute);
+
+ rc = msm_rpc_call(snd->ept,
+ SND_SET_DEVICE_PROC,
+ &dmsg, sizeof(dmsg), 5 * HZ);
+ break;
+
+ case SND_SET_VOLUME:
+ if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) {
+ pr_err("snd_ioctl set volume: invalid pointer.\n");
+ rc = -EFAULT;
+ break;
+ }
+
+ vmsg.args.device = cpu_to_be32(vol.device);
+ vmsg.args.method = cpu_to_be32(vol.method);
+ if (vol.method != SND_METHOD_VOICE) {
+ pr_err("snd_ioctl set volume: invalid method.\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ vmsg.args.volume = cpu_to_be32(vol.volume);
+ vmsg.args.cb_func = -1;
+ vmsg.args.client_data = 0;
+
+ pr_info("snd_set_volume %d %d %d\n", vol.device,
+ vol.method, vol.volume);
+
+ rc = msm_rpc_call(snd->ept,
+ SND_SET_VOLUME_PROC,
+ &vmsg, sizeof(vmsg), 5 * HZ);
+ break;
+
+ case SND_GET_NUM_ENDPOINTS:
+ if (copy_to_user((void __user *)arg,
+ &snd->snd_epts->num, sizeof(unsigned))) {
+ pr_err("snd_ioctl get endpoint: invalid pointer.\n");
+ rc = -EFAULT;
+ }
+ break;
+
+ case SND_GET_ENDPOINT:
+ rc = get_endpoint(snd, arg);
+ break;
+
+ default:
+ pr_err("snd_ioctl unknown command.\n");
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&snd->lock);
+
+ return rc;
+}
+
+static int snd_release(struct inode *inode, struct file *file)
+{
+ struct snd_ctxt *snd = file->private_data;
+
+ mutex_lock(&snd->lock);
+ snd->opened = 0;
+ mutex_unlock(&snd->lock);
+ return 0;
+}
+
+static int snd_open(struct inode *inode, struct file *file)
+{
+ struct snd_ctxt *snd = &the_snd;
+ int rc = 0;
+
+ mutex_lock(&snd->lock);
+ if (snd->opened == 0) {
+ if (snd->ept == NULL) {
+ snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(snd->ept)) {
+ rc = PTR_ERR(snd->ept);
+ snd->ept = NULL;
+ pr_err("snd: failed to connect snd svc\n");
+ goto err;
+ }
+ }
+ file->private_data = snd;
+ snd->opened = 1;
+ } else {
+ pr_err("snd already opened.\n");
+ rc = -EBUSY;
+ }
+
+err:
+ mutex_unlock(&snd->lock);
+ return rc;
+}
+
+static struct file_operations snd_fops = {
+ .owner = THIS_MODULE,
+ .open = snd_open,
+ .release = snd_release,
+ .unlocked_ioctl = snd_ioctl,
+};
+
+struct miscdevice snd_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_snd",
+ .fops = &snd_fops,
+};
+
+static int snd_probe(struct platform_device *pdev)
+{
+ struct snd_ctxt *snd = &the_snd;
+ mutex_init(&snd->lock);
+ snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
+ return misc_register(&snd_misc);
+}
+
+static struct platform_driver snd_plat_driver = {
+ .probe = snd_probe,
+ .driver = {
+ .name = "msm_snd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init snd_init(void)
+{
+ return platform_driver_register(&snd_plat_driver);
+}
+
+module_init(snd_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile
new file mode 100644
index 0000000..75016db
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/Makefile
@@ -0,0 +1,5 @@
+obj-y += adsp.o
+obj-y += adsp_audio.o
+obj-y += audio_glue.o
+obj-y += audio_out.o
+obj-y += marimba.o
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c
new file mode 100644
index 0000000..8d74241
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.c
@@ -0,0 +1,692 @@
+/* arch/arm/mach-msm/qdsp5v2/adsp.c
+ *
+ * Copyright (C) 2010 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/fs.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <mach/msm_iomap.h>
+
+#include "../dal.h"
+
+#include "adsp.h"
+#include "adsp_private.h"
+
+struct msm_adsp_queue {
+ const char *name;
+ uint32_t offset;
+ uint32_t max_size;
+ uint32_t flags;
+};
+
+struct msm_adsp_module {
+ msm_adsp_callback func;
+ void *cookie;
+
+ wait_queue_head_t wait;
+ struct msm_adsp *adsp;
+ uint32_t id;
+
+ unsigned active;
+
+ const char *name;
+ struct msm_adsp_module *next;
+ struct msm_adsp_queue queue[ADSP_QUEUES_MAX];
+};
+
+struct msm_adsp {
+ /* DSP "registers" */
+ void *read_ctrl;
+ void *write_ctrl;
+ void *send_irq;
+ void *base;
+
+ /* DAL client handle for DSP control service */
+ struct dal_client *client;
+
+ spinlock_t callback_lock;
+ spinlock_t write_lock;
+ spinlock_t event_lock;
+
+ wait_queue_head_t callback_wq;
+
+ /* list of all existing dsp modules */
+ struct msm_adsp_module *all_modules;
+
+ /* map from dsp rtos task IDs to modules */
+ struct msm_adsp_module *task_to_module[ADSP_TASKS_MAX];
+
+ /* used during initialization */
+ struct adsp_module_info tmpmodule;
+
+};
+
+static struct msm_adsp the_adsp;
+
+static struct msm_adsp_module *id_to_module(struct msm_adsp *adsp, unsigned id)
+{
+ struct msm_adsp_module *module;
+
+ for (module = adsp->all_modules; module; module = module->next)
+ if (module->id == id)
+ return module;
+ return NULL;
+}
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **module,
+ msm_adsp_callback func, void *cookie)
+{
+ struct msm_adsp *adsp = &the_adsp;
+ unsigned long flags;
+ int ret = -ENODEV;
+ struct msm_adsp_module *m;
+
+ for (m = adsp->all_modules; m; m = m->next) {
+ if (!strcmp(m->name, name)) {
+ spin_lock_irqsave(&m->adsp->callback_lock, flags);
+ if (m->func == 0) {
+ m->func = func;
+ m->cookie = cookie;
+ *module = m;
+ ret = 0;
+ } else {
+ ret = -EBUSY;
+ }
+ spin_unlock_irqrestore(&m->adsp->callback_lock, flags);
+ break;
+ }
+ }
+ return ret;
+}
+
+void msm_adsp_put(struct msm_adsp_module *m)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&m->adsp->callback_lock, flags);
+ m->func = 0;
+ m->cookie = 0;
+ spin_unlock_irqrestore(&m->adsp->callback_lock, flags);
+}
+
+
+int msm_adsp_lookup_queue(struct msm_adsp_module *module, const char *name)
+{
+ int n;
+ for (n = 0; n < ADSP_QUEUES_MAX; n++) {
+ if (!module->queue[n].name)
+ break;
+ if (!strcmp(name, module->queue[n].name))
+ return n;
+ }
+ return -ENODEV;
+}
+
+static int msm_adsp_command(struct msm_adsp_module *module, unsigned cmd_id)
+{
+ struct adsp_dal_cmd cmd;
+ int ret;
+
+ cmd.cmd = cmd_id;
+ cmd.proc_id = ADSP_PROC_APPS;
+ cmd.module = module->id;
+ cmd.cookie = 0;
+
+ ret = dal_call_f5(module->adsp->client, ADSP_DAL_COMMAND,
+ &cmd, sizeof(cmd));
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+ int ret;
+ /* XXX interlock? */
+
+ ret = msm_adsp_command(module, ADSP_CMD_ENABLE);
+ if (ret < 0) {
+ pr_err("msm_adsp_enable: error enabling %s %d\n",
+ module->name, ret);
+ return -EIO;
+ }
+ ret = wait_event_timeout(module->adsp->callback_wq,
+ module->active, 5 * HZ);
+ if (!ret) {
+ pr_err("msm_adsp_enable: timeout enabling %s\n",
+ module->name);
+ return -ETIMEDOUT;
+ }
+
+ printk("msm_adsp_enable: %s enabled.\n", module->name);
+ return 0;
+}
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+ /* XXX interlock? */
+ return msm_adsp_command(module, ADSP_CMD_DISABLE);
+}
+
+int msm_adsp_write(struct msm_adsp_module *module, unsigned queue_idx,
+ void *cmd_buf, size_t cmd_size)
+{
+ struct msm_adsp *adsp;
+ uint32_t val;
+ uint32_t dsp_q_addr;
+ uint32_t dsp_addr;
+ uint32_t cmd_id = 0;
+ int cnt = 0;
+ int ret = 0;
+ unsigned long flags;
+
+ if (!module || !cmd_size || (queue_idx >= ADSP_QUEUES_MAX))
+ return -EINVAL;
+
+ if (module->queue[queue_idx].name == NULL)
+ return -EINVAL;
+
+ adsp = module->adsp;
+
+ spin_lock_irqsave(&adsp->write_lock, flags);
+
+#if 0
+ if (module->state != ADSP_STATE_ENABLED) {
+ ret = -ENODEV;
+ goto done;
+ }
+#endif
+
+ dsp_q_addr = module->queue[queue_idx].offset;
+ dsp_q_addr &= ADSP_WRITE_CTRL_DSP_ADDR_M;
+
+ /* Poll until the ADSP is ready to accept a command.
+ * Wait for 100us, return error if it's not responding.
+ * If this returns an error, we need to disable ALL modules and
+ * then retry.
+ */
+ while (((val = readl(adsp->write_ctrl)) &
+ ADSP_WRITE_CTRL_READY_M) !=
+ ADSP_WRITE_CTRL_READY_V) {
+ if (cnt > 50) {
+ pr_err("timeout waiting for DSP write ready\n");
+ ret = -EIO;
+ goto done;
+ }
+ udelay(2);
+ cnt++;
+ }
+
+ /* Set the mutex bits */
+ val &= ~(ADSP_WRITE_CTRL_MUTEX_M);
+ val |= ADSP_WRITE_CTRL_MUTEX_NAVAIL_V;
+
+ /* Clear the command bits */
+ val &= ~(ADSP_WRITE_CTRL_CMD_M);
+
+ /* Set the queue address bits */
+ val &= ~(ADSP_WRITE_CTRL_DSP_ADDR_M);
+ val |= dsp_q_addr;
+
+ writel(val, adsp->write_ctrl);
+
+ /* Generate an interrupt to the DSP. This notifies the DSP that
+ * we are about to send a command on this particular queue. The
+ * DSP will in response change its state.
+ */
+ writel(1, adsp->send_irq);
+
+ /* Poll until the adsp responds to the interrupt; this does not
+ * generate an interrupt from the adsp. This should happen within
+ * 5ms.
+ */
+ cnt = 0;
+ while ((readl(adsp->write_ctrl) &
+ ADSP_WRITE_CTRL_MUTEX_M) ==
+ ADSP_WRITE_CTRL_MUTEX_NAVAIL_V) {
+ if (cnt > 2500) {
+ pr_err("timeout waiting for adsp ack\n");
+ ret = -EIO;
+ goto done;
+ }
+ udelay(2);
+ cnt++;
+ }
+
+ /* Read the ctrl word */
+ val = readl(adsp->write_ctrl);
+
+ if ((val & ADSP_WRITE_CTRL_STATUS_M) !=
+ ADSP_WRITE_CTRL_NO_ERR_V) {
+ ret = -EIO;
+ pr_err("failed to write queue %x, retry\n", dsp_q_addr);
+ goto done;
+ }
+
+ /* No error */
+ /* Get the DSP buffer address */
+ dsp_addr = (val & ADSP_WRITE_CTRL_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE;
+
+ if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint16_t *buf_ptr = (uint16_t *) cmd_buf;
+ uint16_t *dsp_addr16 = (uint16_t *)dsp_addr;
+ cmd_size /= sizeof(uint16_t);
+
+ /* Save the command ID */
+ cmd_id = (uint32_t) buf_ptr[0];
+
+ /* Copy the command to DSP memory */
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr16++ = *buf_ptr++;
+ } else {
+ uint32_t *buf_ptr = (uint32_t *) cmd_buf;
+ uint32_t *dsp_addr32 = (uint32_t *)dsp_addr;
+ cmd_size /= sizeof(uint32_t);
+
+ /* Save the command ID */
+ cmd_id = buf_ptr[0];
+
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr32++ = *buf_ptr++;
+ }
+
+ /* Set the mutex bits */
+ val &= ~(ADSP_WRITE_CTRL_MUTEX_M);
+ val |= ADSP_WRITE_CTRL_MUTEX_NAVAIL_V;
+
+ /* Set the command bits to write done */
+ val &= ~(ADSP_WRITE_CTRL_CMD_M);
+ val |= ADSP_WRITE_CTRL_CMD_WRITE_DONE_V;
+
+ /* Set the queue address bits */
+ val &= ~(ADSP_WRITE_CTRL_DSP_ADDR_M);
+ val |= dsp_q_addr;
+
+ writel(val, adsp->write_ctrl);
+
+ /* Generate an interrupt to the DSP. It does not respond with
+ * an interrupt, and we do not need to wait for it to
+ * acknowledge, because it will hold the mutex lock until it's
+ * ready to receive more commands again.
+ */
+ writel(1, adsp->send_irq);
+
+// module->num_commands++;
+
+done:
+ spin_unlock_irqrestore(&adsp->write_lock, flags);
+ return ret;
+}
+
+static int adsp_read_task_to_host(struct msm_adsp *adsp, void *dsp_addr)
+{
+ struct msm_adsp_module *module;
+ unsigned task_id;
+ unsigned msg_id;
+ unsigned msg_length;
+ unsigned n;
+ unsigned tmp;
+ union {
+ u32 data32[16];
+ u16 data16[32];
+ } u;
+
+ if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint32_t *dsp_addr32 = dsp_addr;
+ tmp = *dsp_addr32++;
+ task_id = (tmp & ADSP_READ_CTRL_TASK_ID_M) >> 8;
+ msg_id = (tmp & ADSP_READ_CTRL_MSG_ID_M);
+ tmp >>= 16;
+ if (tmp > 16) {
+ pr_err("adsp: message too large (%d x 32)\n", tmp);
+ tmp = 16;
+ }
+ msg_length = tmp * sizeof(uint32_t);
+ for (n = 0; n < tmp; n++)
+ u.data32[n] = *dsp_addr32++;
+ } else {
+ uint16_t *dsp_addr16 = dsp_addr;
+ tmp = *dsp_addr16++;
+ task_id = (tmp & ADSP_READ_CTRL_TASK_ID_M) >> 8;
+ msg_id = tmp & ADSP_READ_CTRL_MSG_ID_M;
+ tmp = *dsp_addr16++;
+ if (tmp > 32) {
+ pr_err("adsp: message too large (%d x 16)\n", tmp);
+ tmp = 32;
+ }
+ msg_length = tmp * sizeof(uint16_t);
+ for (n = 0; n < tmp; n++)
+ u.data16[n] = *dsp_addr16++;
+ }
+
+#if 0
+ pr_info("ADSP EVENT TASK %d MSG %d SIZE %d\n",
+ task_id, msg_id, msg_length);
+#endif
+ if (task_id > ADSP_TASKS_MAX) {
+ pr_err("adsp: bogus task id %d\n", task_id);
+ return 0;
+ }
+ module = adsp->task_to_module[task_id];
+
+ if (!module) {
+ pr_err("adsp: no module for task id %d\n", task_id);
+ return 0;
+ }
+
+ if (!module->func) {
+ pr_err("module %s is not open\n", module->name);
+ return 0;
+ }
+
+ module->func(msg_id, u.data32, msg_length, module->cookie);
+ return 0;
+}
+
+static int adsp_get_event(struct msm_adsp *adsp)
+{
+ uint32_t val;
+ uint32_t ready;
+ void *dsp_addr;
+ uint32_t cmd_type;
+ int cnt;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&adsp->event_lock, flags);
+
+ /* Whenever the DSP has a message, it updates this control word
+ * and generates an interrupt. When we receive the interrupt, we
+ * read this register to find out what ADSP task the command is
+ * comming from.
+ *
+ * The ADSP should *always* be ready on the first call, but the
+ * irq handler calls us in a loop (to handle back-to-back command
+ * processing), so we give the DSP some time to return to the
+ * ready state. The DSP will not issue another IRQ for events
+ * pending between the first IRQ and the event queue being drained,
+ * unfortunately.
+ */
+
+ for (cnt = 0; cnt < 50; cnt++) {
+ val = readl(adsp->read_ctrl);
+
+ if ((val & ADSP_READ_CTRL_FLAG_M) ==
+ ADSP_READ_CTRL_FLAG_UP_CONT_V)
+ goto ready;
+
+ udelay(2);
+ }
+ pr_err("adsp_get_event: not ready after 100uS\n");
+ rc = -EBUSY;
+ goto done;
+
+ready:
+ /* Here we check to see if there are pending messages. If there are
+ * none, we siply return -EAGAIN to indicate that there are no more
+ * messages pending.
+ */
+ ready = val & ADSP_READ_CTRL_READY_M;
+ if ((ready != ADSP_READ_CTRL_READY_V) &&
+ (ready != ADSP_READ_CTRL_CONT_V)) {
+ rc = -EAGAIN;
+ goto done;
+ }
+
+ /* DSP says that there are messages waiting for the host to read */
+
+ /* Get the Command Type */
+ cmd_type = val & ADSP_READ_CTRL_CMD_TYPE_M;
+
+ /* Get the DSP buffer address */
+ dsp_addr = (void *)((val &
+ ADSP_READ_CTRL_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE);
+
+ /* We can only handle Task-to-Host messages */
+ if (cmd_type != ADSP_READ_CTRL_CMD_TASK_TO_H_V) {
+ rc = -EIO;
+ goto done;
+ }
+
+ adsp_read_task_to_host(adsp, dsp_addr);
+
+ val = readl(adsp->read_ctrl);
+ val &= ~ADSP_READ_CTRL_READY_M;
+
+ /* Write ctrl word to the DSP */
+ writel(val, adsp->read_ctrl);
+
+ /* Generate an interrupt to the DSP */
+ writel(1, adsp->send_irq);
+
+done:
+ spin_unlock_irqrestore(&adsp->event_lock, flags);
+ return rc;
+}
+
+static irqreturn_t adsp_irq_handler(int irq, void *data)
+{
+ struct msm_adsp *adsp = &the_adsp;
+ int count = 0;
+ for (count = 0; count < 15; count++)
+ if (adsp_get_event(adsp) < 0)
+ break;
+#if 0
+ if (count > adsp->event_backlog_max)
+ adsp->event_backlog_max = count;
+ adsp->events_received += count;
+#endif
+ if (count == 15)
+ pr_err("too many (%d) events for single irq!\n", count);
+ return IRQ_HANDLED;
+}
+
+static void adsp_dal_callback(void *data, int len, void *cookie)
+{
+ struct msm_adsp *adsp = cookie;
+ struct adsp_dal_event *e = data;
+ struct msm_adsp_module *m;
+#if 0
+ pr_info("adsp: h %08x c %08x l %08x\n",
+ e->evt_handle, e->evt_cookie, e->evt_length);
+ pr_info(" : e %08x v %08x p %08x\n",
+ e->event, e->version, e->proc_id);
+ pr_info(" : m %08x i %08x a %08x\n",
+ e->u.info.module, e->u.info.image, e->u.info.apps_okts);
+#endif
+
+ switch (e->event) {
+ case ADSP_EVT_INIT_INFO:
+ memcpy(&adsp->tmpmodule, &e->u.module,
+ sizeof(adsp->tmpmodule));
+ break;
+ case ADSP_EVT_MOD_READY:
+ m = id_to_module(adsp, e->u.info.module);
+ if (m) {
+ pr_info("adsp: %s READY\n", m->name);
+ m->active = 1;
+ }
+ break;
+ case ADSP_EVT_MOD_DISABLE:
+ /* does not actually happen in adsp5v2 */
+ m = id_to_module(adsp, e->u.info.module);
+ if (m)
+ pr_info("adsp: %s DISABLED\n", m->name);
+ break;
+ case ADSP_EVT_DISABLE_FAIL:
+ m = id_to_module(adsp, e->u.info.module);
+ if (m)
+ pr_info("adsp: %s DISABLE FAILED\n", m->name);
+ break;
+ default:
+ pr_err("adsp_dal_callback: unknown event %d\n", e->event);
+ }
+ wake_up(&adsp->callback_wq);
+}
+
+static void adsp_add_module(struct msm_adsp *adsp, struct adsp_module_info *mi)
+{
+ struct msm_adsp_module *module;
+ int n;
+
+ if (mi->task_id >= ADSP_TASKS_MAX) {
+ pr_err("adsp: module '%s' task id %d is invalid\n",
+ mi->name, mi->task_id);
+ return;
+ }
+ if (mi->q_cnt > ADSP_QUEUES_MAX) {
+ pr_err("adsp: module '%s' q_cnt %d is invalid\n",
+ mi->name, mi->q_cnt);
+ return;
+ }
+
+ module = kzalloc(sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return;
+
+ module->name = kstrdup(mi->name, GFP_KERNEL);
+ if (!module->name)
+ goto fail_module_name;
+
+ for (n = 0; n < mi->q_cnt; n++) {
+ struct msm_adsp_queue *queue = module->queue + n;
+ queue->name = kstrdup(mi->queue[n].name, GFP_KERNEL);
+ if (!queue->name)
+ goto fail_queue_name;
+ queue->offset = mi->queue[n].offset;
+ queue->max_size = mi->queue[n].max_size;
+ queue->flags = mi->queue[n].flag;
+ }
+
+ init_waitqueue_head(&module->wait);
+ module->id = mi->uuid;
+ module->adsp = adsp;
+
+ module->next = adsp->all_modules;
+ adsp->all_modules = module;
+
+ adsp->task_to_module[mi->task_id] = module;
+#if 0
+ pr_info("adsp: module '%s' id 0x%x task %d\n",
+ module->name, module->id, mi->task_id);
+ for (n = 0; (n < ADSP_TASKS_MAX) && module->queue[n].name; n++)
+ pr_info(" queue '%s' off 0x%x size %d flags %x",
+ module->queue[n].name, module->queue[n].offset,
+ module->queue[n].max_size, module->queue[n].flags);
+#endif
+ return;
+
+fail_queue_name:
+ for (n = 0; n < mi->q_cnt; n++)
+ if (module->queue[n].name)
+ kfree(module->queue[n].name);
+fail_module_name:
+ kfree(module);
+}
+
+static int adsp_probe(struct platform_device *pdev) {
+ struct msm_adsp *adsp = &the_adsp;
+ struct adsp_dal_cmd cmd;
+ int ret, n;
+
+ pr_info("*** adsp_probe() ***\n");
+
+ adsp->base = MSM_AD5_BASE;
+ adsp->read_ctrl = adsp->base + ADSP_READ_CTRL_OFFSET;
+ adsp->write_ctrl = adsp->base + ADSP_WRITE_CTRL_OFFSET;
+ adsp->send_irq = adsp->base + ADSP_SEND_IRQ_OFFSET;
+
+ adsp->client = dal_attach(ADSP_DAL_DEVICE, ADSP_DAL_PORT,
+ adsp_dal_callback, adsp);
+ if (!adsp->client) {
+ pr_err("adsp_probe: cannot attach to dal device\n");
+ return -ENODEV;
+ }
+
+ cmd.cmd = ADSP_CMD_GET_INIT_INFO;
+ cmd.proc_id = ADSP_PROC_APPS;
+ cmd.module = 0;
+ cmd.cookie = 0;
+
+ for (n = 0; n < 64; n++) {
+ adsp->tmpmodule.uuid = 0xffffffff;
+ ret = dal_call_f5(adsp->client, ADSP_DAL_COMMAND,
+ &cmd, sizeof(cmd));
+ if (ret) {
+ pr_err("adsp_probe() get info dal call failed\n");
+ break;
+ }
+ ret = wait_event_timeout(adsp->callback_wq,
+ (adsp->tmpmodule.uuid != 0xffffffff),
+ 5*HZ);
+ if (ret == 0) {
+ pr_err("adsp_probe() timed out getting module info\n");
+ break;
+ }
+ if (adsp->tmpmodule.uuid == 0x7fffffff)
+ break;
+ if (adsp->tmpmodule.task_id == 0xffff)
+ continue;
+// adsp_print_module(&adsp->tmpmodule);
+ adsp_add_module(adsp, &adsp->tmpmodule);
+ }
+
+ ret = request_irq(INT_AD5A_MPROC_APPS_0, adsp_irq_handler,
+ IRQF_TRIGGER_RISING, "adsp", 0);
+ if (ret < 0)
+ return ret;
+
+ pr_info("*** adsp_probe() done ***\n");
+ return 0;
+}
+
+static struct platform_driver adsp_driver = {
+ .probe = adsp_probe,
+ .driver = {
+ .name = "SMD_DAL00",
+ .owner = THIS_MODULE,
+ },
+};
+
+extern int msm_codec_init(void);
+
+static int __init adsp_init(void)
+{
+ struct msm_adsp *adsp = &the_adsp;
+
+ pr_info("*** adsp_init() ***\n");
+
+ init_waitqueue_head(&adsp->callback_wq);
+ spin_lock_init(&adsp->callback_lock);
+ spin_lock_init(&adsp->write_lock);
+ spin_lock_init(&adsp->event_lock);
+
+ msm_codec_init();
+
+ return platform_driver_register(&adsp_driver);
+}
+
+module_init(adsp_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.h b/arch/arm/mach-msm/qdsp5v2/adsp.h
new file mode 100644
index 0000000..c37588f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.h
@@ -0,0 +1,42 @@
+/* arch/arm/mach-msm/qdsp5v2/adsp.h
+ *
+ * Copyright (C) 2010 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.
+ *
+ */
+
+#ifndef _MSM_ADSP_5V2_H_
+#define _MSM_ADSP_5V2_H_
+
+struct msm_adsp_module;
+
+typedef void (*msm_adsp_callback)(unsigned id, void *event,
+ size_t len, void *cookie);
+
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **module,
+ msm_adsp_callback callback, void *cookie);
+
+void msm_adsp_put(struct msm_adsp_module *module);
+
+/* find queue index for a named module command queue */
+int msm_adsp_lookup_queue(struct msm_adsp_module *module, const char *name);
+
+int msm_adsp_enable(struct msm_adsp_module *module);
+int msm_adsp_disable(struct msm_adsp_module *module);
+
+/* write is safe to call from atomic context. All other msm_adsp_*
+ * calls may block.
+ */
+int msm_adsp_write(struct msm_adsp_module *module, unsigned queue_idx,
+ void *data, size_t len);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.c b/arch/arm/mach-msm/qdsp5v2/adsp_audio.c
new file mode 100644
index 0000000..80dde0a1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.c
@@ -0,0 +1,460 @@
+/* arch/arm/mach-msm/qdsp5v2/adsp_audio.c
+ *
+ * Copyright (C) 2010 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/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "adsp.h"
+#include "adsp_module_afe.h"
+#include "adsp_module_audpp.h"
+#include "adsp_module_audplay.h"
+
+#include "adsp_audio.h"
+
+
+#define AUDDEC_DEC_PCM 0
+
+/* Decoder status received from AUDPPTASK */
+#define STATUS_SLEEP 0
+#define STATUS_INIT 1
+#define STATUS_CONFIG 2
+#define STATUS_PLAY 3
+
+
+#define MAX_AUDPLAY_TASKS 5
+
+struct audplay {
+ struct msm_adsp_module *module;
+ wait_queue_head_t wait;
+ int q1;
+ int active;
+ int id;
+ int status;
+ struct audpp *audpp;
+
+ void (*callback)(void *cookie);
+ void *cookie;
+};
+
+struct audpp {
+ struct msm_adsp_module *module;
+ wait_queue_head_t wait;
+ struct mutex lock;
+ int q1, q2, q3;
+ unsigned count;
+ struct audplay audplay[MAX_AUDPLAY_TASKS];
+};
+
+struct afe_info {
+ struct msm_adsp_module *module;
+ wait_queue_head_t wait;
+ struct mutex lock;
+ unsigned count;
+ u8 active[AFE_DEVICE_ID_MAX + 1];
+};
+
+struct afe_info the_afe_info;
+static struct audpp the_audpp;
+
+
+static void afe_callback(unsigned id, void *event, size_t len, void *cookie)
+{
+ struct afe_info *afe = cookie;
+ struct afe_msg_codec_config_ack *msg = event;
+
+ printk("afe_callback id=%d len=%d\n", id, len);
+
+ if (id != AFE_MSG_CODEC_CONFIG_ACK)
+ return;
+
+ if (msg->device_id > AFE_DEVICE_ID_MAX)
+ return;
+
+ if (msg->device_activity == AFE_MSG_CODEC_CONFIG_ENABLED)
+ afe->active[msg->device_id] = 1;
+ else
+ afe->active[msg->device_id] = 0;
+
+ wake_up(&afe->wait);
+}
+
+int afe_enable(unsigned device, unsigned rate, unsigned channels)
+{
+ struct afe_info *afe = &the_afe_info;
+ struct afe_cmd_codec_config cmd;
+ int ret = 0;
+
+ /* rate must be one of the following:
+ * 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+ */
+ cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD;
+ cmd.device_id = device;
+ cmd.activity = 1;
+ cmd.sample_rate = rate / 1000;
+ cmd.channel_mode = channels;
+ cmd.volume = AFE_VOLUME_UNITY;
+ cmd.reserved = 0;
+
+ mutex_lock(&afe->lock);
+
+ if (!afe->module) {
+ ret = msm_adsp_get("AFE", &afe->module, afe_callback, afe);
+ if (ret)
+ goto done;
+ }
+
+ if (afe->active[device]) {
+ pr_err("afe_enable: device %d already enabled\n", device);
+ ret = -EBUSY;
+ goto done;
+ }
+
+ if (++afe->count == 1) {
+ pr_info("AFE ENABLE!\n");
+ ret = msm_adsp_enable(afe->module);
+ if (ret < 0) {
+ pr_err("afe_enable: cannot enable module\n");
+ afe->count--;
+ goto done;
+ }
+ }
+
+ ret = msm_adsp_write(afe->module, 0, &cmd, sizeof(cmd));
+ if (ret < 0) {
+ printk("afe_enable: command write failed\n");
+ goto done;
+ }
+
+ ret = wait_event_timeout(afe->wait, afe->active[device], 5 * HZ);
+ if (!ret) {
+ pr_err("afe_enable: command timeout\n");
+ ret = -EIO;
+ } else {
+ printk("afe_enable: device %d active\n", cmd.device_id);
+ }
+done:
+ mutex_unlock(&afe->lock);
+ return ret;
+}
+
+int afe_disable(unsigned device)
+{
+ struct afe_info *afe = &the_afe_info;
+ struct afe_cmd_codec_config cmd;
+ int ret = 0;
+
+ memset(&cmd, sizeof(cmd), 0);
+ cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD;
+ cmd.device_id = device;
+ cmd.activity = 0;
+
+ mutex_lock(&afe->lock);
+
+ if (!afe->active[device]) {
+ pr_err("afe_disable: device %d already disabled\n", device);
+ goto done;
+ }
+
+ ret = msm_adsp_write(afe->module, 0, &cmd, sizeof(cmd));
+ if (ret < 0) {
+ printk("afe_disable: command write failed\n");
+ goto done;
+ }
+
+ ret = wait_event_timeout(afe->wait, !afe->active[device], 5 * HZ);
+ if (!ret) {
+ pr_err("afe_disable: command timeout\n");
+ ret = -EIO;
+ } else {
+ printk("afe_disable: device %d inactive\n", cmd.device_id);
+ if (--afe->count == 0) {
+ pr_info("AFE DISABLE!\n");
+ msm_adsp_disable(afe->module);
+ }
+ }
+done:
+ mutex_unlock(&afe->lock);
+ return ret;
+}
+
+static void audpp_callback(unsigned id, void *event, size_t len, void *cookie)
+{
+ struct audpp *audpp = cookie;
+
+ if (id == AUDPP_MSG_STATUS_MSG) {
+ struct audpp_msg_status_msg *msg = event;
+ pr_info("audpp STATUS id=%d status=%d reason=%d\n",
+ msg->dec_id, msg->status, msg->reason);
+ if (msg->dec_id < MAX_AUDPLAY_TASKS) {
+ audpp->audplay[msg->dec_id].status = msg->status;
+ wake_up(&audpp->audplay[msg->dec_id].wait);
+ }
+
+ } else {
+ pr_info("audpp cb %d %d\n", id, len);
+ }
+}
+
+static int audpp_get(struct audpp *audpp)
+{
+ int ret = 0;
+
+ if (++audpp->count > 1)
+ return 0;
+
+ ret = msm_adsp_get("AUDPP", &audpp->module, audpp_callback, audpp);
+ if (ret < 0) {
+ pr_err("audpp_get: could not get AUDPP\n");
+ goto fail_get_module;
+ }
+
+ audpp->q1 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd1");
+ audpp->q2 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd2");
+ audpp->q3 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd3");
+ if ((audpp->q1 < 0) || (audpp->q2 < 0) || (audpp->q3 < 0)) {
+ pr_err("audpp_get: could not get queues\n");
+ ret = -ENODEV;
+ goto fail_enable_module;
+ }
+
+ ret = msm_adsp_enable(audpp->module);
+ if (ret < 0)
+ goto fail_enable_module;
+
+ return 0;
+
+fail_enable_module:
+ msm_adsp_put(audpp->module);
+ audpp->module = NULL;
+fail_get_module:
+ audpp->count--;
+ return ret;
+}
+
+static void audpp_put(struct audpp *audpp)
+{
+ if (--audpp->count > 0)
+ return;
+
+ msm_adsp_disable(audpp->module);
+ msm_adsp_put(audpp->module);
+ audpp->module = NULL;
+}
+
+
+static void audplay_callback(unsigned id, void *event, size_t len, void *cookie)
+{
+ struct audplay *audplay = cookie;
+ if (id == AUDPLAY_MSG_DEC_NEEDS_DATA) {
+#if 0
+ struct audplay_msg_dec_needs_data *msg = event;
+ pr_info("audplay NEEDDATA id=%d off=%d sz=%d %d %d %d %d\n",
+ msg->dec_id, msg->adecDataReadPtrOffset,
+ msg->adecDataBufSize, msg->bitstream_free_len,
+ msg->bitstream_write_ptr, msg->bitstream_buf_start,
+ msg->bitstream_buf_len);
+#endif
+ audplay->callback(audplay->cookie);
+ } else {
+ pr_info("audplay cb %d %d\n", id, len);
+ }
+}
+
+struct audplay *audplay_get(void (*cb)(void *cookie), void *cookie)
+{
+ struct audpp *audpp = &the_audpp;
+ struct audplay *audplay = 0;
+ char buf[32];
+ unsigned n;
+ int ret;
+
+ mutex_lock(&audpp->lock);
+
+ for (n = 0; n < MAX_AUDPLAY_TASKS; n++)
+ if (audpp->audplay[n].active == 0) break;
+
+ if (n == MAX_AUDPLAY_TASKS)
+ goto done;
+
+ if (audpp_get(audpp))
+ goto done;
+
+ audplay = audpp->audplay + n;
+ sprintf(buf, "AUDPLAY%d", n);
+ ret = msm_adsp_get(buf, &audplay->module, audplay_callback, audplay);
+ if (ret < 0)
+ goto fail_audplay_get;
+
+ sprintf(buf,"AudPlay%dBitStreamCtrl", n);
+ audplay->q1 = msm_adsp_lookup_queue(audplay->module, buf);
+ if (audplay->q1 < 0)
+ goto fail_audplay_enable;
+
+ ret = msm_adsp_enable(audplay->module);
+ if (ret < 0)
+ goto fail_audplay_enable;
+
+ audplay->active = 1;
+ audplay->callback = cb;
+ audplay->cookie = cookie;
+ goto done;
+
+fail_audplay_enable:
+ msm_adsp_put(audplay->module);
+ audplay->module = NULL;
+ audplay->callback = NULL;
+fail_audplay_get:
+ audplay = NULL;
+ audpp_put(audpp);
+done:
+ mutex_unlock(&audpp->lock);
+ return audplay;
+}
+
+void audplay_put(struct audplay *audplay)
+{
+ mutex_lock(&audplay->audpp->lock);
+ audplay->active = 0;
+ msm_adsp_disable(audplay->module);
+ msm_adsp_put(audplay->module);
+ audplay->module = NULL;
+ audplay->callback = NULL;
+ audpp_put(audplay->audpp);
+ mutex_unlock(&audplay->audpp->lock);
+}
+
+static void inline audplay_send_q1(struct audplay *audplay, void *cmd, int len)
+{
+ msm_adsp_write(audplay->module, audplay->q1, cmd, len);
+}
+
+static void inline audpp_send_q1(struct audpp *audpp, void *cmd, int len)
+{
+ msm_adsp_write(audpp->module, audpp->q1, cmd, len);
+}
+
+static void inline audpp_send_q2(struct audpp *audpp, void *cmd, int len)
+{
+ msm_adsp_write(audpp->module, audpp->q2, cmd, len);
+}
+
+static void inline audpp_send_q3(struct audpp *audpp, void *cmd, int len)
+{
+ msm_adsp_write(audpp->module, audpp->q3, cmd, len);
+}
+
+
+void audplay_config_pcm(struct audplay *audplay,
+ unsigned rate, unsigned width, unsigned channels)
+{
+ struct audpp_cmd_cfg_adec_params_wav cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1;
+ cmd.common.dec_id = audplay->id;
+ cmd.common.input_sampling_frequency = rate;
+ cmd.stereo_cfg = channels;
+ cmd.pcm_width = 1;
+ cmd.sign = 0; /* really? */
+ audpp_send_q2(audplay->audpp, &cmd, sizeof(cmd)); /* sizeof(cmd)?!*/
+}
+
+void audplay_dsp_config(struct audplay *audplay, int enable)
+{
+ struct audpp_cmd_cfg_dec_type cmd;
+ int next;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC;
+ if (enable) {
+ cmd.dec_cfg |= AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM;
+ next = STATUS_INIT;
+ } else {
+ cmd.dec_cfg |= AUDPP_CMD_DIS_DEC_V;
+ next = STATUS_SLEEP;
+ }
+ cmd.dm_mode = 0;
+ cmd.stream_id = audplay->id;
+
+ mutex_lock(&audplay->audpp->lock);
+ audpp_send_q1(audplay->audpp, &cmd, sizeof(cmd));
+ wait_event_timeout(audplay->wait, audplay->status == next, 5 * HZ);
+ mutex_unlock(&audplay->audpp->lock);
+}
+
+void audplay_send_data(struct audplay *audplay, unsigned phys, unsigned len)
+{
+ struct audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audplay->id;
+ cmd.buf_ptr = phys;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+
+ mutex_lock(&audplay->audpp->lock);
+ audplay_send_q1(audplay, &cmd, sizeof(cmd));
+ wait_event_timeout(audplay->wait, audplay->status == STATUS_PLAY, 5 * HZ);
+ mutex_unlock(&audplay->audpp->lock);
+}
+
+void audplay_mix_select(struct audplay *audplay, unsigned mix)
+{
+ struct audpp_cmd_cfg_dev_mixer_params cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER;
+ cmd.stream_id = audplay->id;
+ cmd.mixer_cmd = mix;
+ audpp_send_q1(audplay->audpp, &cmd, sizeof(cmd));
+}
+
+void audplay_volume_pan(struct audplay *audplay, unsigned volume, unsigned pan)
+{
+#define AUDPP_CMD_VOLUME_PAN 0
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+ uint16_t cmd[7];
+ cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ cmd[1] = AUDPP_CMD_POPP_STREAM;
+ cmd[2] = audplay->id;
+ cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd[4] = AUDPP_CMD_VOLUME_PAN;
+ cmd[5] = volume;
+ cmd[6] = pan;
+ audpp_send_q3(audplay->audpp, cmd, sizeof(cmd));
+}
+
+
+
+void adsp_audio_init(void)
+{
+ struct afe_info *afe = &the_afe_info;
+ struct audpp *audpp = &the_audpp;
+ int n;
+
+ mutex_init(&audpp->lock);
+ init_waitqueue_head(&audpp->wait);
+ for (n = 0; n < MAX_AUDPLAY_TASKS; n++) {
+ struct audplay *audplay = audpp->audplay + n;
+ audplay->id = n;
+ audplay->audpp = audpp;
+ init_waitqueue_head(&audplay->wait);
+ }
+
+ mutex_init(&afe->lock);
+ init_waitqueue_head(&afe->wait);
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h
new file mode 100644
index 0000000..4d52e7c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h
@@ -0,0 +1,44 @@
+/* arch/arm/mach-msm/qdsp5v2/adsp_audio.h
+ *
+ * Copyright (C) 2010 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.
+ *
+ */
+
+#ifndef _MSM_ADSP_AUDIO_H_
+#define _MSM_ADSP_AUDIO_H_
+
+struct audplay;
+struct audpp;
+
+struct audplay *audplay_get(void (*cb)(void *cookie), void *cookie);
+void audplay_put(struct audplay *audplay);
+
+void audplay_send_data(struct audplay *audplay, unsigned phys, unsigned len);
+
+void audplay_dsp_config(struct audplay *audplay, int enable);
+void audplay_config_pcm(struct audplay *audplay,
+ unsigned rate, unsigned width, unsigned channels);
+
+
+void audplay_mix_select(struct audplay *audplay, unsigned mix);
+void audplay_volume_pan(struct audplay *audplay, unsigned volume, unsigned pan);
+
+
+int afe_enable(unsigned device, unsigned rate, unsigned channels);
+int afe_disable(unsigned device);
+
+
+int msm_codec_output(int enable);
+
+void adsp_audio_init(void);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h
new file mode 100644
index 0000000..eaa6139
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef __ADSP_MODULE_AFE_H
+#define __ADSP_MODULE_AFE_H
+
+#define AFE_DEVICE_MI2S_CODEC_RX 1 /* internal codec rx path */
+#define AFE_DEVICE_MI2S_CODEC_TX 2 /* internal codec tx path */
+#define AFE_DEVICE_AUX_CODEC_RX 3 /* external codec rx path */
+#define AFE_DEVICE_AUX_CODEC_TX 4 /* external codec tx path */
+#define AFE_DEVICE_MI2S_HDMI_RX 5 /* HDMI/FM block rx path */
+#define AFE_DEVICE_MI2S_HDMI_TX 6 /* HDMI/FM block tx path */
+#define AFE_DEVICE_ID_MAX 7
+
+#define AFE_VOLUME_UNITY 0x4000 /* Q14 format */
+
+#define AFE_CMD_CODEC_CONFIG_CMD 0x1
+#define AFE_CMD_CODEC_CONFIG_LEN sizeof(struct afe_cmd_codec_config)
+
+struct afe_cmd_codec_config{
+ uint16_t cmd_id;
+ uint16_t device_id;
+ uint16_t activity;
+ uint16_t sample_rate;
+ uint16_t channel_mode;
+ uint16_t volume;
+ uint16_t reserved;
+} __attribute__ ((packed));
+
+#define AFE_CMD_AUX_CODEC_CONFIG_CMD 0x3
+#define AFE_CMD_AUX_CODEC_CONFIG_LEN sizeof(struct afe_cmd_aux_codec_config)
+
+struct afe_cmd_aux_codec_config{
+ uint16_t cmd_id;
+ uint16_t dma_path_ctl;
+ uint16_t pcm_ctl;
+ uint16_t eight_khz_int_mode;
+ uint16_t aux_codec_intf_ctl;
+ uint16_t data_format_padding_info;
+} __attribute__ ((packed));
+
+#define AFE_MSG_CODEC_CONFIG_ACK 0x0001
+#define AFE_MSG_CODEC_CONFIG_ACK_LEN \
+ sizeof(struct afe_msg_codec_config_ack)
+
+#define AFE_MSG_CODEC_CONFIG_ENABLED 0x1
+#define AFE_MSG_CODEC_CONFIG_DISABLED 0xFFFF
+
+struct afe_msg_codec_config_ack {
+ uint16_t device_id;
+ uint16_t device_activity;
+} __attribute__((packed));
+
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h
new file mode 100644
index 0000000..7a67ded
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1992-2009, Code Aurora Forum. 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.
+ *
+ */
+
+#ifndef __ADSP_MODULE_AUDPLAY
+#define __ADSP_MODULE_AUDPLAY
+
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \
+ sizeof(struct audplay_cmd_bitstream_data_avail)
+
+/* Type specification of dec_data_avail message sent to AUDPLAYTASK
+*/
+struct audplay_cmd_bitstream_data_avail{
+ /*command ID*/
+ unsigned int cmd_id;
+
+ /* Decoder ID for which message is being sent */
+ unsigned int decoder_id;
+
+ /* Start address of data in ARM global memory */
+ unsigned int buf_ptr;
+
+ /* Number of 16-bit words of bit-stream data contiguously
+ * available at the above-mentioned address
+ */
+ unsigned int buf_size;
+
+ /* Partition number used by audPlayTask to communicate with DSP's RTOS
+ * kernel
+ */
+ unsigned int partition_number;
+
+} __attribute__((packed));
+
+#define AUDPLAY_CMD_CHANNEL_INFO 0x0001
+#define AUDPLAY_CMD_CHANNEL_INFO_LEN \
+ sizeof(struct audplay_cmd_channel_info)
+
+struct audplay_cmd_channel_select {
+ unsigned int cmd_id;
+ unsigned int stream_id;
+ unsigned int channel_select;
+} __attribute__((packed));
+
+struct audplay_cmd_threshold_update {
+ unsigned int cmd_id;
+ unsigned int threshold_update;
+ unsigned int threshold_value;
+} __attribute__((packed));
+
+union audplay_cmd_channel_info {
+ struct audplay_cmd_channel_select ch_select;
+ struct audplay_cmd_threshold_update thr_update;
+};
+
+#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003
+#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \
+ sizeof(struct audplay_cmd_hpcm_buf_cfg)
+
+struct audplay_cmd_hpcm_buf_cfg {
+ unsigned int cmd_id;
+ unsigned int hostpcm_config;
+ unsigned int feedback_frequency;
+ unsigned int byte_swap;
+ unsigned int max_buffers;
+ unsigned int partition_number;
+} __attribute__((packed));
+
+#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004
+#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \
+ sizeof(struct audplay_cmd_buffer_update)
+
+struct audplay_cmd_buffer_refresh {
+ unsigned int cmd_id;
+ unsigned int num_buffers;
+ unsigned int buf_read_count;
+ unsigned int buf0_address;
+ unsigned int buf0_length;
+ unsigned int buf1_address;
+ unsigned int buf1_length;
+} __attribute__((packed));
+
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2 0x0005
+#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2_LEN \
+ sizeof(struct audplay_cmd_bitstream_data_avail_nt2)
+
+/* Type specification of dec_data_avail message sent to AUDPLAYTASK
+ * for NT2 */
+struct audplay_cmd_bitstream_data_avail_nt2 {
+ /*command ID*/
+ unsigned int cmd_id;
+
+ /* Decoder ID for which message is being sent */
+ unsigned int decoder_id;
+
+ /* Start address of data in ARM global memory */
+ unsigned int buf_ptr;
+
+ /* Number of 16-bit words of bit-stream data contiguously
+ * available at the above-mentioned address
+ */
+ unsigned int buf_size;
+
+ /* Partition number used by audPlayTask to communicate with DSP's RTOS
+ * kernel
+ */
+ unsigned int partition_number;
+
+ /* bitstream write pointer */
+ unsigned int dspBitstreamWritePtr;
+
+} __attribute__((packed));
+
+#define AUDPLAY_CMD_OUTPORT_FLUSH 0x0006
+
+struct audplay_cmd_outport_flush {
+ unsigned int cmd_id;
+} __attribute__((packed));
+
+
+/* messages from dsp to apps */
+
+#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001
+#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \
+ sizeof(audplay_msg_dec_needs_data)
+
+struct audplay_msg_dec_needs_data {
+ /* reserved*/
+ unsigned int dec_id;
+
+ /*The read pointer offset of external memory till which bitstream
+ has been dmed in*/
+ unsigned int adecDataReadPtrOffset;
+
+ /*The buffer size of external memory. */
+ unsigned int adecDataBufSize;
+
+ unsigned int bitstream_free_len;
+ unsigned int bitstream_write_ptr;
+ unsigned int bitstream_buf_start;
+ unsigned int bitstream_buf_len;
+} __attribute__((packed));
+
+#define AUDPLAY_UP_STREAM_INFO 0x0003
+#define AUDPLAY_UP_STREAM_INFO_LEN \
+ sizeof(struct audplay_msg_stream_info)
+
+struct audplay_msg_stream_info {
+ unsigned int decoder_id;
+ unsigned int channel_info;
+ unsigned int sample_freq;
+ unsigned int bitstream_info;
+ unsigned int bit_rate;
+} __attribute__((packed));
+
+#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004
+#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \
+ sizeof(struct audplay_msg_buffer_update)
+
+struct audplay_msg_buffer_update {
+ unsigned int buffer_write_count;
+ unsigned int num_of_buffer;
+ unsigned int buf0_address;
+ unsigned int buf0_length;
+ unsigned int buf1_address;
+ unsigned int buf1_length;
+} __attribute__((packed));
+
+#define AUDPLAY_UP_OUTPORT_FLUSH_ACK 0x0005
+
+#define ADSP_MESSAGE_ID 0xFFFF
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h
new file mode 100644
index 0000000..8f278cf
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h
@@ -0,0 +1,1250 @@
+/*
+ * Copyright (c) 1992-2009, Code Aurora Forum. 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.
+ *
+ */
+
+#ifndef __ADSP_MODULE_AUDPP
+#define __ADSP_MODULE_AUDPP
+
+/*
+ * ARM to AUDPPTASK Commands
+ *
+ * ARM uses three command queues to communicate with AUDPPTASK
+ * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands
+ * Location : MEMA
+ * Buffer Size : 6 words
+ * No of buffers in a queue : 20 for gaming audio and 5 for other images
+ * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier
+ * Location : MEMA
+ * Buffer Size : 23
+ * No of buffers in a queue : 2
+ * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands
+ * Location : MEMA
+ * Buffer Size : 145
+ * No of buffers in a queue : 3
+ */
+
+/*
+ * Commands Related to uPAudPPCmd1Queue
+ */
+
+/*
+ * Command Structure to enable or disable the active decoders
+ */
+
+#define AUDPP_CMD_CFG_DEC_TYPE 0x0001
+#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(struct audpp_cmd_cfg_dec_type)
+
+/* Enable the decoder */
+#define AUDPP_CMD_DEC_TYPE_M 0x000F
+
+#define AUDPP_CMD_ENA_DEC_V 0x4000
+#define AUDPP_CMD_DIS_DEC_V 0x0000
+#define AUDPP_CMD_DEC_STATE_M 0x4000
+
+#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000
+#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000
+
+
+/* Type specification of cmd_cfg_dec */
+
+struct audpp_cmd_cfg_dec_type {
+ unsigned short cmd_id;
+ unsigned short stream_id;
+ unsigned short dec_cfg;
+ unsigned short dm_mode;
+} __attribute__((packed));
+
+/*
+ * Command Structure to Pause , Resume and flushes the selected audio decoders
+ */
+
+#define AUDPP_CMD_DEC_CTRL 0x0002
+#define AUDPP_CMD_DEC_CTRL_LEN sizeof(struct audpp_cmd_dec_ctrl)
+
+/* Decoder control commands for pause, resume and flush */
+#define AUDPP_CMD_FLUSH_V 0x2000
+
+#define AUDPP_CMD_PAUSE_V 0x4000
+#define AUDPP_CMD_RESUME_V 0x0000
+
+#define AUDPP_CMD_UPDATE_V 0x8000
+#define AUDPP_CMD_IGNORE_V 0x0000
+
+
+/* Type Spec for decoder control command*/
+
+struct audpp_cmd_dec_ctrl{
+ unsigned short cmd_id;
+ unsigned short stream_id;
+ unsigned short dec_ctrl;
+} __attribute__((packed));
+
+/*
+ * Command Structure to Configure the AVSync FeedBack Mechanism
+ */
+
+#define AUDPP_CMD_AVSYNC 0x0003
+#define AUDPP_CMD_AVSYNC_LEN sizeof(struct audpp_cmd_avsync)
+
+struct audpp_cmd_avsync{
+ unsigned short cmd_id;
+ unsigned short stream_id;
+ unsigned short interrupt_interval;
+ unsigned short sample_counter_dlsw;
+ unsigned short sample_counter_dmsw;
+ unsigned short sample_counter_msw;
+ unsigned short byte_counter_dlsw;
+ unsigned short byte_counter_dmsw;
+ unsigned short byte_counter_msw;
+} __attribute__((packed));
+
+/*
+ * Command Structure to enable or disable(sleep) the AUDPPTASK
+ */
+
+#define AUDPP_CMD_CFG 0x0004
+#define AUDPP_CMD_CFG_LEN sizeof(struct audpp_cmd_cfg)
+
+#define AUDPP_CMD_CFG_SLEEP 0x0000
+#define AUDPP_CMD_CFG_ENABLE 0xFFFF
+
+struct audpp_cmd_cfg {
+ unsigned short cmd_id;
+ unsigned short cfg;
+} __attribute__((packed));
+
+/*
+ * Command Structure to Inject or drop the specified no of samples
+ */
+
+#define AUDPP_CMD_ADJUST_SAMP 0x0005
+#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(struct audpp_cmd_adjust_samp)
+
+#define AUDPP_CMD_SAMP_DROP -1
+#define AUDPP_CMD_SAMP_INSERT 0x0001
+
+#define AUDPP_CMD_NUM_SAMPLES 0x0001
+
+struct audpp_cmd_adjust_samp {
+ unsigned short cmd_id;
+ unsigned short object_no;
+ signed short sample_insert_or_drop;
+ unsigned short num_samples;
+} __attribute__((packed));
+
+/*
+ * Command Structure to Configure AVSync Feedback Mechanism
+ */
+
+#define AUDPP_CMD_ROUTING_MODE 0x0007
+#define AUDPP_CMD_ROUTING_MODE_LEN \
+sizeof(struct audpp_cmd_routing_mode)
+
+struct audpp_cmd_routing_mode {
+ unsigned short cmd_id;
+ unsigned short object_number;
+ unsigned short routing_mode;
+} __attribute__((packed));
+
+/*
+ * Commands Related to uPAudPPCmd2Queue
+ */
+
+/*
+ * Command Structure to configure Per decoder Parameters (Common)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000
+#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_common)
+
+#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000
+#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000
+
+#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000
+#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000
+
+/* Sampling frequency*/
+#define AUDPP_CMD_SAMP_RATE_96000 0x0000
+#define AUDPP_CMD_SAMP_RATE_88200 0x0001
+#define AUDPP_CMD_SAMP_RATE_64000 0x0002
+#define AUDPP_CMD_SAMP_RATE_48000 0x0003
+#define AUDPP_CMD_SAMP_RATE_44100 0x0004
+#define AUDPP_CMD_SAMP_RATE_32000 0x0005
+#define AUDPP_CMD_SAMP_RATE_24000 0x0006
+#define AUDPP_CMD_SAMP_RATE_22050 0x0007
+#define AUDPP_CMD_SAMP_RATE_16000 0x0008
+#define AUDPP_CMD_SAMP_RATE_12000 0x0009
+#define AUDPP_CMD_SAMP_RATE_11025 0x000A
+#define AUDPP_CMD_SAMP_RATE_8000 0x000B
+
+
+/*
+ * Type specification of cmd_adec_cfg sent to all decoder
+ */
+
+struct audpp_cmd_cfg_adec_params_common {
+ unsigned short cmd_id;
+ unsigned short dec_id;
+ unsigned short length;
+ unsigned short reserved;
+ unsigned short input_sampling_frequency;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure Per decoder Parameters (Wav)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_wav)
+
+
+#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002
+
+#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000
+#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001
+#define AUDPP_CMD_WAV_PCM_WIDTH_24 0x0002
+
+struct audpp_cmd_cfg_adec_params_wav {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+ unsigned short pcm_width;
+ unsigned short sign;
+} __attribute__((packed));
+
+/*
+ * Command Structure for CMD_CFG_DEV_MIXER
+ */
+
+#define AUDPP_CMD_CFG_DEV_MIXER_PARAMS_LEN \
+ sizeof(struct audpp_cmd_cfg_dev_mixer_params)
+
+#define AUDPP_CMD_CFG_DEV_MIXER 0x0008
+
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_NONE 0x0000
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_0 0x0001
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_1 0x0002
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_2 0x0004
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_3 0x0008
+#define AUDPP_CMD_CFG_DEV_MIXER_DEV_4 0x0010
+
+struct audpp_cmd_cfg_dev_mixer_params {
+ unsigned short cmd_id;
+ unsigned short stream_id;
+ unsigned short mixer_cmd;
+} __attribute__((packed));
+
+
+/*
+ * Command Structure to configure Per decoder Parameters (ADPCM)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_adpcm)
+
+
+#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002
+
+struct audpp_cmd_cfg_adec_params_adpcm {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+ unsigned short block_size;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure Per decoder Parameters (WMA)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_wma)
+
+struct audpp_cmd_cfg_adec_params_wma {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short armdatareqthr;
+ unsigned short channelsdecoded;
+ unsigned short wmabytespersec;
+ unsigned short wmasamplingfreq;
+ unsigned short wmaencoderopts;
+} __attribute__((packed));
+
+
+/*
+ * Command Structure to configure Per decoder Parameters (MP3)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_mp3)
+
+struct audpp_cmd_cfg_adec_params_mp3 {
+ struct audpp_cmd_cfg_adec_params_common common;
+} __attribute__((packed));
+
+
+/*
+ * Command Structure to configure Per decoder Parameters (AAC)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_aac)
+
+
+#define AUDPP_CMD_AAC_FORMAT_ADTS -1
+#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000
+#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001
+#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002
+
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004
+#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011
+
+#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001
+#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000
+
+#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001
+#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000
+
+struct audpp_cmd_cfg_adec_params_aac {
+ struct audpp_cmd_cfg_adec_params_common common;
+ signed short format;
+ unsigned short audio_object;
+ unsigned short ep_config;
+ unsigned short aac_section_data_resilience_flag;
+ unsigned short aac_scalefactor_data_resilience_flag;
+ unsigned short aac_spectral_data_resilience_flag;
+ unsigned short sbr_on_flag;
+ unsigned short sbr_ps_on_flag;
+ unsigned short channel_configuration;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure Per decoder Parameters (V13K)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_v13k)
+
+
+#define AUDPP_CMD_STEREO_CFG_MONO 0x0001
+#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002
+
+struct audpp_cmd_cfg_adec_params_v13k {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed));
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_evrc)
+
+struct audpp_cmd_cfg_adec_params_evrc {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__ ((packed));
+
+/*
+ * Command Structure to configure Per decoder Parameters (AMRWB)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_amrwb)
+
+struct audpp_cmd_cfg_adec_params_amrwb {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure Per decoder Parameters (WMAPRO)
+ */
+
+#define AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN \
+ sizeof(struct audpp_cmd_cfg_adec_params_wmapro)
+
+struct audpp_cmd_cfg_adec_params_wmapro {
+ struct audpp_cmd_cfg_adec_params_common common;
+ unsigned short armdatareqthr;
+ uint8_t validbitspersample;
+ uint8_t numchannels;
+ unsigned short formattag;
+ unsigned short samplingrate;
+ unsigned short avgbytespersecond;
+ unsigned short asfpacketlength;
+ unsigned short channelmask;
+ unsigned short encodeopt;
+ unsigned short advancedencodeopt;
+ uint32_t advancedencodeopt2;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure the HOST PCM interface
+ */
+
+#define AUDPP_CMD_PCM_INTF 0x0001
+#define AUDPP_CMD_PCM_INTF_2 0x0002
+#define AUDPP_CMD_PCM_INTF_LEN sizeof(struct audpp_cmd_pcm_intf)
+
+#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001
+#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002
+
+/* These two values differentiate the two types of commands that could be issued
+ * Interface configuration command and Buffer update command */
+
+#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000
+#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1
+
+#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F
+#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008
+#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004
+
+/* These flags control the enabling and disabling of the interface together
+ * with host interface bit mask. */
+
+#define AUDPP_CMD_PCM_INTF_ENA_V -1
+#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000
+
+
+#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0
+#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1
+
+
+#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5
+#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6
+
+struct audpp_cmd_pcm_intf {
+ unsigned short cmd_id;
+ unsigned short stream;
+ unsigned short stream_id;
+ signed short config;
+ unsigned short intf_type;
+
+ /* DSP -> ARM Configuration */
+ unsigned short read_buf1LSW;
+ unsigned short read_buf1MSW;
+ unsigned short read_buf1_len;
+
+ unsigned short read_buf2LSW;
+ unsigned short read_buf2MSW;
+ unsigned short read_buf2_len;
+ /* 0:HOST_PCM_INTF disable
+ ** 0xFFFF: HOST_PCM_INTF enable
+ */
+ signed short dsp_to_arm_flag;
+ unsigned short partition_number;
+
+ /* ARM -> DSP Configuration */
+ unsigned short write_buf1LSW;
+ unsigned short write_buf1MSW;
+ unsigned short write_buf1_len;
+
+ unsigned short write_buf2LSW;
+ unsigned short write_buf2MSW;
+ unsigned short write_buf2_len;
+
+ /* 0:HOST_PCM_INTF disable
+ ** 0xFFFF: HOST_PCM_INTF enable
+ */
+ signed short arm_to_rx_flag;
+ unsigned short weight_decoder_to_rx;
+ unsigned short weight_arm_to_rx;
+
+ unsigned short partition_number_arm_to_dsp;
+ unsigned short sample_rate;
+ unsigned short channel_mode;
+} __attribute__((packed));
+
+/*
+ ** BUFFER UPDATE COMMAND
+ */
+#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \
+ sizeof(struct audpp_cmd_pcm_intf_send_buffer)
+
+struct audpp_cmd_pcm_intf_send_buffer {
+ unsigned short cmd_id;
+ unsigned short stream;
+ unsigned short stream_id;
+ /* set config = 0xFFFF for configuration*/
+ signed short config;
+ unsigned short intf_type;
+ unsigned short dsp_to_arm_buf_id;
+ unsigned short arm_to_dsp_buf_id;
+ unsigned short arm_to_dsp_buf_len;
+} __attribute__((packed));
+
+
+/*
+ * Commands Related to uPAudPPCmd3Queue
+ */
+
+/*
+ * Command Structure to configure post processing params (Commmon)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_common)
+
+#define AUDPP_CMD_OBJ0_UPDATE 0x8000
+#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000
+
+
+#define AUDPP_CMD_OBJ2_UPDATE 0x8000
+#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ3_UPDATE 0x8000
+#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_OBJ4_UPDATE 0x8000
+#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_HPCM_UPDATE 0x8000
+#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000
+#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000
+
+#define AUDPP_CMD_POPP_STREAM 0xFFFF
+#define AUDPP_CMD_COPP_STREAM 0x0000
+
+struct audpp_cmd_cfg_object_params_common{
+ unsigned short cmd_id;
+ unsigned short stream;
+ unsigned short stream_id;
+ unsigned short obj_cfg;
+ unsigned short command_type;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing params (Volume)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_volume)
+
+struct audpp_cmd_cfg_object_params_volume {
+ struct audpp_cmd_cfg_object_params_common common;
+ unsigned short volume;
+ unsigned short pan;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing params (PCM Filter)
+ */
+
+struct numerator {
+ unsigned short numerator_b0_filter_lsw;
+ unsigned short numerator_b0_filter_msw;
+ unsigned short numerator_b1_filter_lsw;
+ unsigned short numerator_b1_filter_msw;
+ unsigned short numerator_b2_filter_lsw;
+ unsigned short numerator_b2_filter_msw;
+} __attribute__((packed));
+
+struct denominator {
+ unsigned short denominator_a0_filter_lsw;
+ unsigned short denominator_a0_filter_msw;
+ unsigned short denominator_a1_filter_lsw;
+ unsigned short denominator_a1_filter_msw;
+} __attribute__((packed));
+
+struct shift_factor {
+ unsigned short shift_factor_0;
+} __attribute__((packed));
+
+struct pan {
+ unsigned short pan_filter_0;
+} __attribute__((packed));
+
+struct filter_1 {
+ struct numerator numerator_filter;
+ struct denominator denominator_filter;
+ struct shift_factor shift_factor_filter;
+ struct pan pan_filter;
+} __attribute__((packed));
+
+struct filter_2 {
+ struct numerator numerator_filter[2];
+ struct denominator denominator_filter[2];
+ struct shift_factor shift_factor_filter[2];
+ struct pan pan_filter[2];
+} __attribute__((packed));
+
+struct filter_3 {
+ struct numerator numerator_filter[3];
+ struct denominator denominator_filter[3];
+ struct shift_factor shift_factor_filter[3];
+ struct pan pan_filter[3];
+} __attribute__((packed));
+
+struct filter_4 {
+ struct numerator numerator_filter[4];
+ struct denominator denominator_filter[4];
+ struct shift_factor shift_factor_filter[4];
+ struct pan pan_filter[4];
+} __attribute__((packed));
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_pcm)
+
+
+struct audpp_cmd_cfg_object_params_pcm {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short active_flag;
+ unsigned short num_bands;
+ union {
+ struct filter_1 filter_1_params;
+ struct filter_2 filter_2_params;
+ struct filter_3 filter_3_params;
+ struct filter_4 filter_4_params;
+ } __attribute__((packed)) params_filter;
+} __attribute__((packed));
+
+
+/*
+ * Command Structure to configure post processing parameters (equalizer)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_eqalizer)
+
+struct eq_numerator {
+ unsigned short numerator_coeff_0_lsw;
+ unsigned short numerator_coeff_0_msw;
+ unsigned short numerator_coeff_1_lsw;
+ unsigned short numerator_coeff_1_msw;
+ unsigned short numerator_coeff_2_lsw;
+ unsigned short numerator_coeff_2_msw;
+} __attribute__((packed));
+
+struct eq_denominator {
+ unsigned short denominator_coeff_0_lsw;
+ unsigned short denominator_coeff_0_msw;
+ unsigned short denominator_coeff_1_lsw;
+ unsigned short denominator_coeff_1_msw;
+} __attribute__((packed));
+
+struct eq_shiftfactor {
+ unsigned short shift_factor;
+} __attribute__((packed));
+
+struct eq_coeff_1 {
+ struct eq_numerator numerator;
+ struct eq_denominator denominator;
+ struct eq_shiftfactor shiftfactor;
+} __attribute__((packed));
+
+struct eq_coeff_2 {
+ struct eq_numerator numerator[2];
+ struct eq_denominator denominator[2];
+ struct eq_shiftfactor shiftfactor[2];
+} __attribute__((packed));
+
+struct eq_coeff_3 {
+ struct eq_numerator numerator[3];
+ struct eq_denominator denominator[3];
+ struct eq_shiftfactor shiftfactor[3];
+} __attribute__((packed));
+
+struct eq_coeff_4 {
+ struct eq_numerator numerator[4];
+ struct eq_denominator denominator[4];
+ struct eq_shiftfactor shiftfactor[4];
+} __attribute__((packed));
+
+struct eq_coeff_5 {
+ struct eq_numerator numerator[5];
+ struct eq_denominator denominator[5];
+ struct eq_shiftfactor shiftfactor[5];
+} __attribute__((packed));
+
+struct eq_coeff_6 {
+ struct eq_numerator numerator[6];
+ struct eq_denominator denominator[6];
+ struct eq_shiftfactor shiftfactor[6];
+} __attribute__((packed));
+
+struct eq_coeff_7 {
+ struct eq_numerator numerator[7];
+ struct eq_denominator denominator[7];
+ struct eq_shiftfactor shiftfactor[7];
+} __attribute__((packed));
+
+struct eq_coeff_8 {
+ struct eq_numerator numerator[8];
+ struct eq_denominator denominator[8];
+ struct eq_shiftfactor shiftfactor[8];
+} __attribute__((packed));
+
+struct eq_coeff_9 {
+ struct eq_numerator numerator[9];
+ struct eq_denominator denominator[9];
+ struct eq_shiftfactor shiftfactor[9];
+} __attribute__((packed));
+
+struct eq_coeff_10 {
+ struct eq_numerator numerator[10];
+ struct eq_denominator denominator[10];
+ struct eq_shiftfactor shiftfactor[10];
+} __attribute__((packed));
+
+struct eq_coeff_11 {
+ struct eq_numerator numerator[11];
+ struct eq_denominator denominator[11];
+ struct eq_shiftfactor shiftfactor[11];
+} __attribute__((packed));
+
+struct eq_coeff_12 {
+ struct eq_numerator numerator[12];
+ struct eq_denominator denominator[12];
+ struct eq_shiftfactor shiftfactor[12];
+} __attribute__((packed));
+
+
+struct audpp_cmd_cfg_object_params_eqalizer {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short eq_flag;
+ unsigned short num_bands;
+ union {
+ struct eq_coeff_1 eq_coeffs_1;
+ struct eq_coeff_2 eq_coeffs_2;
+ struct eq_coeff_3 eq_coeffs_3;
+ struct eq_coeff_4 eq_coeffs_4;
+ struct eq_coeff_5 eq_coeffs_5;
+ struct eq_coeff_6 eq_coeffs_6;
+ struct eq_coeff_7 eq_coeffs_7;
+ struct eq_coeff_8 eq_coeffs_8;
+ struct eq_coeff_9 eq_coeffs_9;
+ struct eq_coeff_10 eq_coeffs_10;
+ struct eq_coeff_11 eq_coeffs_11;
+ struct eq_coeff_12 eq_coeffs_12;
+ } __attribute__((packed)) eq_coeff;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing parameters (ADRC)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_adrc)
+
+
+#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000
+#define AUDPP_CMD_ADRC_FLAG_ENA -1
+
+struct audpp_cmd_cfg_object_params_adrc {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short adrc_flag;
+ unsigned short compression_th;
+ unsigned short compression_slope;
+ unsigned short rms_time;
+ unsigned short attack_const_lsw;
+ unsigned short attack_const_msw;
+ unsigned short release_const_lsw;
+ unsigned short release_const_msw;
+ unsigned short adrc_delay;
+};
+
+/*
+ * Command Structure to configure post processing parameters (MB - ADRC)
+ */
+
+#define AUDPP_MAX_MBADRC_BANDS 5
+
+struct adrc_config {
+ uint16_t subband_enable;
+ uint16_t adrc_sub_mute;
+ uint16_t rms_time;
+ uint16_t compression_th;
+ uint16_t compression_slope;
+ uint16_t attack_const_lsw;
+ uint16_t attack_const_msw;
+ uint16_t release_const_lsw;
+ uint16_t release_const_msw;
+ uint16_t makeup_gain;
+};
+
+struct audpp_cmd_cfg_object_params_mbadrc {
+ struct audpp_cmd_cfg_object_params_common common;
+ uint16_t enable;
+ uint16_t num_bands;
+ uint16_t down_samp_level;
+ uint16_t adrc_delay;
+ uint16_t ext_buf_size;
+ uint16_t ext_partition;
+ uint16_t ext_buf_msw;
+ uint16_t ext_buf_lsw;
+ struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS];
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing parameters(Spectrum Analizer)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_spectram)
+
+
+struct audpp_cmd_cfg_object_params_spectram {
+ struct audpp_cmd_cfg_object_params_common common;
+ unsigned short sample_interval;
+ unsigned short num_coeff;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing parameters (QConcert)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_qconcert)
+
+
+#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1
+#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000
+
+#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001
+#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002
+
+#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF
+#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027
+
+
+#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF
+
+
+struct audpp_cmd_cfg_object_params_qconcert {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short enable_flag;
+ signed short op_mode;
+ signed short gain;
+ signed short expansion;
+ signed short delay;
+ unsigned short stages_per_mode;
+ unsigned short reverb_enable;
+ unsigned short decay_msw;
+ unsigned short decay_lsw;
+ unsigned short decay_time_ratio_msw;
+ unsigned short decay_time_ratio_lsw;
+ unsigned short reflection_delay_time;
+ unsigned short late_reverb_gain;
+ unsigned short late_reverb_delay;
+ unsigned short delay_buff_size_msw;
+ unsigned short delay_buff_size_lsw;
+ unsigned short partition_num;
+ unsigned short delay_buff_start_msw;
+ unsigned short delay_buff_start_lsw;
+} __attribute__((packed));
+
+/*
+ * Command Structure to configure post processing parameters (Side Chain)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_sidechain)
+
+
+#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000
+#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1
+
+struct audpp_cmd_cfg_object_params_sidechain {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short active_flag;
+ unsigned short num_bands;
+ union {
+ struct filter_1 filter_1_params;
+ struct filter_2 filter_2_params;
+ struct filter_3 filter_3_params;
+ struct filter_4 filter_4_params;
+ } __attribute__((packed)) params_filter;
+} __attribute__((packed));
+
+
+/*
+ * Command Structure to configure post processing parameters (QAFX)
+ */
+
+#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \
+ sizeof(struct audpp_cmd_cfg_object_params_qafx)
+
+#define AUDPP_CMD_QAFX_ENA_DISA 0x0000
+#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1
+#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001
+
+#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100
+#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010
+#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000
+
+#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102
+#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103
+#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107
+
+#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010
+#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011
+#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012
+#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013
+#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014
+
+
+struct audpp_cmd_cfg_object_params_qafx {
+ struct audpp_cmd_cfg_object_params_common common;
+ signed short enable;
+ unsigned short command_type;
+ unsigned short num_commands;
+ unsigned short commands;
+} __attribute__((packed));
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (REVERB) (Common)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG 0x0001
+#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \
+ sizeof(struct audpp_cmd_reverb_config_common)
+
+#define AUDPP_CMD_ENA_ENA 0xFFFF
+#define AUDPP_CMD_ENA_DIS 0x0000
+#define AUDPP_CMD_ENA_CFG 0x0001
+
+#define AUDPP_CMD_CMD_TYPE_ENV 0x0104
+#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015
+#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000
+
+
+struct audpp_cmd_reverb_config_common {
+ unsigned short cmd_id;
+ unsigned short enable;
+ unsigned short cmd_type;
+} __attribute__((packed));
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (ENV-0x0104)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \
+ sizeof(struct audpp_cmd_reverb_config_env_104)
+
+struct audpp_cmd_reverb_config_env_104 {
+ struct audpp_cmd_reverb_config_common common;
+ unsigned short env_gain;
+ unsigned short decay_msw;
+ unsigned short decay_lsw;
+ unsigned short decay_timeratio_msw;
+ unsigned short decay_timeratio_lsw;
+ unsigned short delay_time;
+ unsigned short reverb_gain;
+ unsigned short reverb_delay;
+} __attribute__((packed));
+
+/*
+ * Command Structure to enable , disable or configure the reverberation effect
+ * (ENV-0x0015)
+ */
+
+#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \
+ sizeof(struct audpp_cmd_reverb_config_env_15)
+
+struct audpp_cmd_reverb_config_env_15 {
+ struct audpp_cmd_reverb_config_common common;
+ unsigned short object_num;
+ unsigned short absolute_gain;
+} __attribute__((packed));
+
+
+/* messages from dsp to apps */
+
+
+/*
+ * AUDPPTASK uses audPPuPRlist to send messages to the ARM
+ * Location : MEMA
+ * Buffer Size : 45
+ * No of Buffers in a queue : 5 for gaming audio and 1 for other images
+ */
+
+/*
+ * MSG to Informs the ARM os Success/Failure of bringing up the decoder
+ */
+
+#define AUDPP_MSG_STATUS_MSG 0x0001
+#define AUDPP_MSG_STATUS_MSG_LEN \
+ sizeof(struct audpp_msg_status_msg)
+
+#define AUDPP_MSG_STATUS_SLEEP 0x0000
+#define AUDPP_MSG_STATUS_INIT 0x0001
+#define AUDPP_MSG_STATUS_CFG 0x0002
+#define AUDPP_MSG_STATUS_PLAY 0x0003
+
+#define AUDPP_MSG_REASON_NONE 0x0000
+#define AUDPP_MSG_REASON_MEM 0x0001
+#define AUDPP_MSG_REASON_NODECODER 0x0002
+
+struct audpp_msg_status_msg {
+ unsigned short dec_id;
+ unsigned short status;
+ unsigned short reason;
+} __attribute__((packed));
+
+/*
+ * MSG to communicate the spectrum analyzer output bands to the ARM
+ */
+#define AUDPP_MSG_SPA_BANDS 0x0002
+#define AUDPP_MSG_SPA_BANDS_LEN \
+ sizeof(struct audpp_msg_spa_bands)
+
+struct audpp_msg_spa_bands {
+ unsigned short current_object;
+ unsigned short spa_band_1;
+ unsigned short spa_band_2;
+ unsigned short spa_band_3;
+ unsigned short spa_band_4;
+ unsigned short spa_band_5;
+ unsigned short spa_band_6;
+ unsigned short spa_band_7;
+ unsigned short spa_band_8;
+ unsigned short spa_band_9;
+ unsigned short spa_band_10;
+ unsigned short spa_band_11;
+ unsigned short spa_band_12;
+ unsigned short spa_band_13;
+ unsigned short spa_band_14;
+ unsigned short spa_band_15;
+ unsigned short spa_band_16;
+ unsigned short spa_band_17;
+ unsigned short spa_band_18;
+ unsigned short spa_band_19;
+ unsigned short spa_band_20;
+ unsigned short spa_band_21;
+ unsigned short spa_band_22;
+ unsigned short spa_band_23;
+ unsigned short spa_band_24;
+ unsigned short spa_band_25;
+ unsigned short spa_band_26;
+ unsigned short spa_band_27;
+ unsigned short spa_band_28;
+ unsigned short spa_band_29;
+ unsigned short spa_band_30;
+ unsigned short spa_band_31;
+ unsigned short spa_band_32;
+} __attribute__((packed));
+
+/*
+ * MSG to communicate the PCM I/O buffer status to ARM
+ */
+#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003
+#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \
+ sizeof(struct audpp_msg_host_pcm_intf_msg)
+
+#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000
+#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001
+#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002
+#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003
+
+#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000
+#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001
+#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002
+#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003
+#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004
+#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005
+#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006
+#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007
+#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008
+#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009
+#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A
+#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B
+
+#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001
+#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002
+
+struct audpp_msg_host_pcm_intf_msg {
+ unsigned short obj_num;
+ unsigned short numbers_of_samples;
+ unsigned short host_pcm_id;
+ unsigned short buf_indx;
+ unsigned short samp_freq_indx;
+ unsigned short channel_mode;
+} __attribute__((packed));
+
+
+/*
+ * MSG to communicate 3D position of the source and listener , source volume
+ * source rolloff, source orientation
+ */
+
+#define AUDPP_MSG_QAFX_POS 0x0004
+#define AUDPP_MSG_QAFX_POS_LEN \
+ sizeof(struct audpp_msg_qafx_pos)
+
+struct audpp_msg_qafx_pos {
+ unsigned short current_object;
+ unsigned short x_pos_lis_msw;
+ unsigned short x_pos_lis_lsw;
+ unsigned short y_pos_lis_msw;
+ unsigned short y_pos_lis_lsw;
+ unsigned short z_pos_lis_msw;
+ unsigned short z_pos_lis_lsw;
+ unsigned short x_fwd_msw;
+ unsigned short x_fwd_lsw;
+ unsigned short y_fwd_msw;
+ unsigned short y_fwd_lsw;
+ unsigned short z_fwd_msw;
+ unsigned short z_fwd_lsw;
+ unsigned short x_up_msw;
+ unsigned short x_up_lsw;
+ unsigned short y_up_msw;
+ unsigned short y_up_lsw;
+ unsigned short z_up_msw;
+ unsigned short z_up_lsw;
+ unsigned short x_vel_lis_msw;
+ unsigned short x_vel_lis_lsw;
+ unsigned short y_vel_lis_msw;
+ unsigned short y_vel_lis_lsw;
+ unsigned short z_vel_lis_msw;
+ unsigned short z_vel_lis_lsw;
+ unsigned short threed_enable_flag;
+ unsigned short volume;
+ unsigned short x_pos_source_msw;
+ unsigned short x_pos_source_lsw;
+ unsigned short y_pos_source_msw;
+ unsigned short y_pos_source_lsw;
+ unsigned short z_pos_source_msw;
+ unsigned short z_pos_source_lsw;
+ unsigned short max_dist_0_msw;
+ unsigned short max_dist_0_lsw;
+ unsigned short min_dist_0_msw;
+ unsigned short min_dist_0_lsw;
+ unsigned short roll_off_factor;
+ unsigned short mute_after_max_flag;
+ unsigned short x_vel_source_msw;
+ unsigned short x_vel_source_lsw;
+ unsigned short y_vel_source_msw;
+ unsigned short y_vel_source_lsw;
+ unsigned short z_vel_source_msw;
+ unsigned short z_vel_source_lsw;
+} __attribute__((packed));
+
+/*
+ * MSG to provide AVSYNC feedback from DSP to ARM
+ */
+
+#define AUDPP_MSG_AVSYNC_MSG 0x0005
+#define AUDPP_MSG_AVSYNC_MSG_LEN \
+ sizeof(struct audpp_msg_avsync_msg)
+
+struct audpp_msg_avsync_msg {
+ unsigned short active_flag;
+ unsigned short num_samples_counter0_HSW;
+ unsigned short num_samples_counter0_MSW;
+ unsigned short num_samples_counter0_LSW;
+ unsigned short num_bytes_counter0_HSW;
+ unsigned short num_bytes_counter0_MSW;
+ unsigned short num_bytes_counter0_LSW;
+ unsigned short samp_freq_obj_0;
+ unsigned short samp_freq_obj_1;
+ unsigned short samp_freq_obj_2;
+ unsigned short samp_freq_obj_3;
+ unsigned short samp_freq_obj_4;
+ unsigned short samp_freq_obj_5;
+ unsigned short samp_freq_obj_6;
+ unsigned short samp_freq_obj_7;
+ unsigned short samp_freq_obj_8;
+ unsigned short samp_freq_obj_9;
+ unsigned short samp_freq_obj_10;
+ unsigned short samp_freq_obj_11;
+ unsigned short samp_freq_obj_12;
+ unsigned short samp_freq_obj_13;
+ unsigned short samp_freq_obj_14;
+ unsigned short samp_freq_obj_15;
+ unsigned short num_samples_counter4_HSW;
+ unsigned short num_samples_counter4_MSW;
+ unsigned short num_samples_counter4_LSW;
+ unsigned short num_bytes_counter4_HSW;
+ unsigned short num_bytes_counter4_MSW;
+ unsigned short num_bytes_counter4_LSW;
+} __attribute__((packed));
+
+/*
+ * MSG to provide PCM DMA Missed feedback from the DSP to ARM
+ */
+
+#define AUDPP_MSG_PCMDMAMISSED 0x0006
+#define AUDPP_MSG_PCMDMAMISSED_LEN \
+ sizeof(struct audpp_msg_pcmdmamissed);
+
+struct audpp_msg_pcmdmamissed {
+ /*
+ ** Bit 0 0 = PCM DMA not missed for object 0
+ ** 1 = PCM DMA missed for object0
+ ** Bit 1 0 = PCM DMA not missed for object 1
+ ** 1 = PCM DMA missed for object1
+ ** Bit 2 0 = PCM DMA not missed for object 2
+ ** 1 = PCM DMA missed for object2
+ ** Bit 3 0 = PCM DMA not missed for object 3
+ ** 1 = PCM DMA missed for object3
+ ** Bit 4 0 = PCM DMA not missed for object 4
+ ** 1 = PCM DMA missed for object4
+ */
+ unsigned short pcmdmamissed;
+} __attribute__((packed));
+
+/*
+ * MSG to AUDPP enable or disable feedback form DSP to ARM
+ */
+
+#define AUDPP_MSG_CFG_MSG 0x0007
+#define AUDPP_MSG_CFG_MSG_LEN \
+ sizeof(struct audpp_msg_cfg_msg)
+
+#define AUDPP_MSG_ENA_ENA 0xFFFF
+#define AUDPP_MSG_ENA_DIS 0x0000
+
+struct audpp_msg_cfg_msg {
+ /* Enabled - 0xffff
+ ** Disabled - 0
+ */
+ unsigned short enabled;
+} __attribute__((packed));
+
+/*
+ * MSG to communicate the reverb per object volume
+ */
+
+#define AUDPP_MSG_QREVERB_VOLUME 0x0008
+#define AUDPP_MSG_QREVERB_VOLUME_LEN \
+ sizeof(struct audpp_msg_qreverb_volume)
+
+
+struct audpp_msg_qreverb_volume {
+ unsigned short obj_0_gain;
+ unsigned short obj_1_gain;
+ unsigned short obj_2_gain;
+ unsigned short obj_3_gain;
+ unsigned short obj_4_gain;
+ unsigned short hpcm_obj_volume;
+} __attribute__((packed));
+
+#define AUDPP_MSG_ROUTING_ACK 0x0009
+#define AUDPP_MSG_ROUTING_ACK_LEN \
+ sizeof(struct audpp_msg_routing_ack)
+
+struct audpp_msg_routing_ack {
+ unsigned short dec_id;
+ unsigned short routing_mode;
+} __attribute__((packed));
+
+#define AUDPP_MSG_FLUSH_ACK 0x000A
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_private.h b/arch/arm/mach-msm/qdsp5v2/adsp_private.h
new file mode 100644
index 0000000..badfb0d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_private.h
@@ -0,0 +1,163 @@
+/* arch/arm/mach-msm/qdsp5v2/adsp_private.h
+ *
+ * Copyright (C) 2010 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.
+ *
+ */
+
+#ifndef _MSM_ADSP_5V2_PRIVATE_H_
+#define _MSM_ADSP_5V2_PRIVATE_H_
+
+/* adsp rtos / hardware memory map */
+
+#define QDSP_RAMC_OFFSET 0x00400000
+#define ADSP_READ_CTRL_OFFSET 0x00400038
+#define ADSP_WRITE_CTRL_OFFSET 0x00400034
+#define ADSP_SEND_IRQ_OFFSET 0x00c00200
+
+
+/* adsp rtos hardware / shared memory interface */
+
+#define ADSP_WRITE_CTRL_MUTEX_M 0x80000000U
+#define ADSP_WRITE_CTRL_MUTEX_NAVAIL_V 0x80000000U
+#define ADSP_WRITE_CTRL_MUTEX_AVAIL_V 0x00000000U
+
+#define ADSP_WRITE_CTRL_CMD_M 0x70000000U
+#define ADSP_WRITE_CTRL_CMD_WRITE_REQ_V 0x00000000U
+#define ADSP_WRITE_CTRL_CMD_WRITE_DONE_V 0x10000000U
+#define ADSP_WRITE_CTRL_CMD_NO_CMD_V 0x70000000U
+
+#define ADSP_WRITE_CTRL_STATUS_M 0x0E000000U
+#define ADSP_WRITE_CTRL_NO_ERR_V 0x00000000U
+#define ADSP_WRITE_CTRL_NO_FREE_BUF_V 0x02000000U
+
+#define ADSP_WRITE_CTRL_DSP_ADDR_M 0x00FFFFFFU
+
+#define ADSP_WRITE_CTRL_HTOD_CMD_ID_M 0x00FFFFFFU
+
+/* Combination of MUTEX and CMD bits to check if the DSP is busy */
+#define ADSP_WRITE_CTRL_READY_M 0xF0000000U
+#define ADSP_WRITE_CTRL_READY_V 0x70000000U
+
+/* RTOS to Host processor command mask values */
+#define ADSP_READ_CTRL_FLAG_M 0x80000000U
+#define ADSP_READ_CTRL_FLAG_UP_WAIT_V 0x00000000U
+#define ADSP_READ_CTRL_FLAG_UP_CONT_V 0x80000000U
+
+#define ADSP_READ_CTRL_CMD_M 0x60000000U
+#define ADSP_READ_CTRL_READ_DONE_V 0x00000000U
+#define ADSP_READ_CTRL_READ_REQ_V 0x20000000U
+#define ADSP_READ_CTRL_NO_CMD_V 0x60000000U
+
+/* Combination of FLAG and COMMAND bits to check if MSG ready */
+#define ADSP_READ_CTRL_READY_M 0xE0000000U
+#define ADSP_READ_CTRL_READY_V 0xA0000000U
+#define ADSP_READ_CTRL_CONT_V 0xC0000000U
+#define ADSP_READ_CTRL_DONE_V 0xE0000000U
+
+#define ADSP_READ_CTRL_STATUS_M 0x18000000U
+#define ADSP_READ_CTRL_NO_ERR_V 0x00000000U
+
+#define ADSP_READ_CTRL_IN_PROG_M 0x04000000U
+#define ADSP_READ_CTRL_NO_READ_IN_PROG_V 0x00000000U
+#define ADSP_READ_CTRL_READ_IN_PROG_V 0x04000000U
+
+#define ADSP_READ_CTRL_CMD_TYPE_M 0x03000000U
+#define ADSP_READ_CTRL_CMD_TASK_TO_H_V 0x00000000U
+
+#define ADSP_READ_CTRL_DSP_ADDR_M 0x00FFFFFFU
+
+#define ADSP_READ_CTRL_MSG_ID_M 0x000000FFU
+#define ADSP_READ_CTRL_TASK_ID_M 0x0000FF00U
+
+
+/* modem adsp management DAL service interface */
+
+#define ADSP_DAL_DEVICE 0x0200009A
+#define ADSP_DAL_PORT "SMD_DAL00"
+#define ADSP_DAL_COMMAND (DAL_OP_FIRST_DEVICE_API | 0x80000000)
+
+struct adsp_dal_cmd {
+ uint32_t cmd;
+ uint32_t proc_id;
+ uint32_t module;
+ void *cookie;
+};
+
+#define ADSP_PROC_NONE 0
+#define ADSP_PROC_MODEM 1
+#define ADSP_PROC_APPS 2
+
+#define ADSP_CMD_ENABLE 1
+#define ADSP_CMD_DISABLE 2
+#define ADSP_CMD_DISABLE_EVENT_RSP 6
+#define ADSP_CMD_GET_INIT_INFO 11
+
+#define ADSP_EVT_MOD_READY 0
+#define ADSP_EVT_MOD_DISABLE 1
+#define ADSP_EVT_INIT_INFO 6
+#define ADSP_EVT_DISABLE_FAIL 7
+
+#define ADSP_TASKS_MAX 64
+#define ADSP_QUEUES_MAX 4
+
+#define MODULE_NAME_MAX 32
+#define QUEUE_NAME_MAX 32
+
+#define ADSP_QUEUE_FLAG_16BIT 0
+#define ADSP_QUEUE_FLAG_32BIT 1
+
+struct adsp_queue_info {
+ uint8_t name[QUEUE_NAME_MAX];
+ uint32_t offset; /* Queue Offset in DSP memory */
+ uint16_t idx; /* Global queue identifier */
+ uint16_t max_size; /* Max allowed size in bytes for a queue */
+ uint16_t flag; /* queue is 32bit Vs 16 bits */
+ uint16_t rvd1;
+ uint32_t rvd2;
+};
+
+struct adsp_module_info
+{
+ uint8_t name[MODULE_NAME_MAX];
+ uint32_t uuid;
+ uint16_t task_id;
+ uint16_t q_cnt;
+ struct adsp_queue_info queue[ADSP_QUEUES_MAX];
+ uint32_t rvd1;
+ uint32_t rvd2;
+};
+
+struct adsp_evt_info {
+ uint32_t module;
+ uint32_t image;
+ uint32_t apps_okts; /* wtf is an okts? */
+};
+
+struct adsp_dal_event {
+ /* DAL common event header */
+ uint32_t evt_handle;
+ uint32_t evt_cookie;
+ uint32_t evt_length;
+
+ /* ADSP event header */
+ uint32_t event;
+ uint32_t version;
+ uint32_t proc_id;
+
+ /* payload */
+ union {
+ struct adsp_module_info module;
+ struct adsp_evt_info info;
+ } u;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_glue.c b/arch/arm/mach-msm/qdsp5v2/audio_glue.c
new file mode 100644
index 0000000..49b9841
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_glue.c
@@ -0,0 +1,540 @@
+/* arch/arm/mach-msm/qdsp5v2/audio_glue.c
+ *
+ * Copyright (C) 2010 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/module.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+/* the audio codec control consists of
+ * - mi2s transports (x3)
+ * - lpa (low power audio) frontend for mi2s tx
+ * - various related clocks
+ */
+struct msm_codec {
+ void *tx_base;
+ void *rx_base;
+ void *lpa_base;
+
+ struct clk *rx_mclk;
+ struct clk *rx_sclk;
+ struct clk *tx_mclk;
+ struct clk *tx_sclk;
+
+ struct clk *lpa_codec_clk;
+ struct clk *lpa_core_clk;
+ struct clk *lpa_pclk;
+
+ struct clk *adsp_clk;
+};
+
+#define LPA_MAX_BUF_SIZE 0x30000
+
+#define LPA_CONTROL 0x00000000
+#define LPA_CODEC 0x00000004
+#define LPA_HLB_MIN_ADDR 0x00000008
+#define LPA_HLB_MAX_ADDR 0x0000000C
+#define LPA_HLB_WPTR 0x00000010
+#define LPA_HLB_VOLUME_CONTROL 0x00000014
+#define LPA_LLB_MIN_ADDR 0x00000018
+#define LPA_LLB_MAX_ADDR 0x0000001C
+#define LPA_SB_MIN_ADDR 0x00000020
+#define LPA_SB_MAX_ADDR 0x00000024
+#define LPA_INTR_ENABLE 0x00000028
+#define LPA_INTR_STATUS 0x0000002C
+#define LPA_WMARK_ASSIGN 0x00000030
+#define LPA_WMARK_0_LLB 0x00000034
+#define LPA_WMARK_1_LLB 0x00000038
+#define LPA_WMARK_2_LLB 0x0000003C
+#define LPA_WMARK_3_LLB 0x00000040
+#define LPA_WMARK_HLB 0x00000044
+#define LPA_WMARK_SB 0x00000048
+#define LPA_RDPTR_LLB 0x0000004C
+#define LPA_RDPTR_HLB 0x00000050
+#define LPA_WRPTR_SB 0x00000054
+#define LPA_UTC_CONFIG 0x00000058
+#define LPA_UTC_INTR_LOW 0x0000005C
+#define LPA_UTC_INTR_HIGH 0x00000060
+#define LPA_UTC_LOW 0x00000064
+#define LPA_UTC_HIGH 0x00000068
+#define LPA_MISR 0x0000006C
+#define LPA_STATUS 0x00000070
+#define LPA_ACK 0x00000074
+#define LPA_MEMORY_CONTROL 0x00000078
+#define LPA_MEMORY_STATUS 0x0000007C
+#define LPA_MEMORY_TIME_CONTROL 0x00000080
+#define LPA_ACC_LV 0x00000084
+#define LPA_ACC_HV 0x0000008c
+#define LPA_RESETS 0x00000090
+#define LPA_TESTBUS 0x00000094
+
+#define LPA_AICTL 0x00000100
+
+/* OBUF_CODEC */
+#define LPA_CODEC_LOAD 0x200000
+#define LPA_CODEC_INTF_EN 0x100000
+#define LPA_CODEC_CFG_MASK 0x0FC07F
+
+#define LPA_SAMPLE_RATE_8KHZ 0x000000
+#define LPA_SAMPLE_RATE_11P025KHZ 0x010000
+#define LPA_SAMPLE_RATE_16KHZ 0x020000
+#define LPA_SAMPLE_RATE_22P05KHZ 0x030000
+#define LPA_SAMPLE_RATE_32KHZ 0x040000
+#define LPA_SAMPLE_RATE_44P1KHZ 0x050000
+#define LPA_SAMPLE_RATE_48KHZ 0x060000
+#define LPA_SAMPLE_RATE_64KHZ 0x070000
+#define LPA_SAMPLE_RATE_96KHZ 0x080000
+
+#define LPA_BITS_PER_CHAN_16BITS 0x000000
+#define LPA_BITS_PER_CHAN_24BITS 0x004000
+#define LPA_BITS_PER_CHAN_32BITS 0x008000
+#define LPA_BITS_PER_CHAN_RESERVED 0x00C000
+
+#define LPA_INTF_SDAC 0x000010
+#define LPA_INTF_MI2S 0x000020
+#define LPA_INTF_WB_CODEC 0x000030
+
+/* WB_CODEC & SDAC can only support 16bit mono/stereo.
+ * MI2S can bit format and number of channel
+ */
+#define LPA_NUM_CHAN_MONO 0x000000
+#define LPA_NUM_CHAN_STEREO 0x000001
+#define LPA_NUM_CHAN_5P1 0x000002
+#define LPA_NUM_CHAN_7P1 0x000003
+#define LPA_NUM_CHAN_4_CHANNEL 0x000004
+
+/* OBUF_CONTROL */
+#define LPA_CONTROL_TEST_EN 0x100
+#define LPA_CONTROL_LLB_CLR_CMD 0x080
+#define LPA_CONTROL_SB_SAT_EN 0x040
+#define LPA_CONTROL_LLB_SAT_EN 0x020
+#define LPA_CONTROL_LLB_ACC_EN 0x008
+#define LPA_CONTROL_HLB_EN 0x004
+#define LPA_CONTROL_LLB_EN 0x002
+#define LPA_CONTROL_SB_EN 0x001
+
+/* OBUF_RESET definition */
+#define LPA_RESETS_MISR 0x1
+#define LPA_RESETS_OVERALL 0x2
+
+/* OBUF_STATUS definition */
+#define LPA_STATUS_RESET_DONE 0x80000
+#define LPA_STATUS_LLB_CLR 0x40000
+
+/* OBUF_HLB_MIN_ADDR definition */
+#define LPA_HLB_MIN_ADDR_LOAD 0x40000
+#define LPA_HLB_MIN_ADDR_SEG_MASK 0x3e000
+
+/* OBUF_HLB_MAX_ADDR definition */
+#define LPA_HLB_MAX_ADDR_SEG_MASK 0x3fff8
+
+/* OBUF_LLB_MIN_ADDR definition */
+#define LPA_LLB_MIN_ADDR_LOAD 0x40000
+#define LPA_LLB_MIN_ADDR_SEG_BMSK 0x3e000
+
+/* OBUF_LLB_MAX_ADDR definition */
+#define LPA_LLB_MAX_ADDR_SEG_MASK 0x3ff8
+#define LPA_LLB_MAX_ADDR_SEG_SHFT 0x3
+
+/* OBUF_SB_MIN_ADDR definition */
+#define LPA_SB_MIN_ADDR_LOAD 0x4000
+#define LPA_SB_MIN_ADDR_SEG_BMSK 0x3e00
+
+/* OBUF_SB_MAX_ADDR definition */
+#define LPA_SB_MAX_ADDR_SEG_BMSK 0x3ff8
+
+/* OBUF_MEMORY_CONTROL definition */
+#define LPA_MEM_CTL_PWRUP 0xfff
+
+/* OBUF_INTR_ENABLE definition */
+#define LPA_INTR_EN 0x3
+
+/* OBUF_WMARK_ASSIGN definition */
+#define LPA_WMARK_ASSIGN_BMSK 0xF
+#define LPA_WMARK_ASSIGN_DONE 0xF
+
+/* OBUF_WMARK_n_LLB definition */
+#define LPA_WMARK_n_LLB_ADDR(n) (0x00000034 + 0x4 * (n))
+#define LPA_WMARK_CTRL_MASK 0x0c0000
+#define LPA_WMARK_CTRL_SHFT 0x12
+#define LPA_WMARK_MAP_MASK 0xf00000
+#define LPA_WMARK_MAP_SHFT 0x14
+
+#define LPA_WMARK_CTL_DISABLED 0x0
+#define LPA_WMARK_CTL_NON_BLOCK 0x1
+#define LPA_WMARK_CTL_ZERO_INSERT 0x2
+#define LPA_WMARK_CTL_RESERVED 0x3
+
+/* OBUF_UTC_CONFIG definition */
+#define LPA_UTC_CONFIG_MAP_MASK 0xf0
+#define LPA_UTC_CONFIG_MAP_SHFT 0x4
+#define LPA_UTC_CONFIG_EN 0x1
+#define LPA_UTC_CONFIG_NO_INTR 0xF
+
+/* OBUF_ACK definition */
+#define LPA_ACK_RESET_DONE 0x80000
+
+
+#define LPA_BUF_ID_HLB 0 /* HLB buffer */
+#define LPA_BUF_ID_LLB 1 /* LLB buffer */
+#define LPA_BUF_ID_SB 2 /* SB buffer */
+#define LPA_BUF_ID_UTC 3
+
+
+/* from board file in qct tree */
+
+#define LPA_HLB_SIZE 0x2BFF8
+
+#define LPA_ID_DSP 0
+#define LPA_ID_APP 2
+
+#if 0
+#define CFG_LLB_MIN_ADDR 0x0000
+#define CFG_LLB_MAX_ADDR 0x3ff8
+#define CFG_SB_MIN_ADDR 0
+#define CFG_SB_MAX_ADDR 0
+#else
+#define CFG_LLB_MIN_ADDR 0x0000
+#define CFG_LLB_MAX_ADDR 0x37f8
+#define CFG_SB_MIN_ADDR 0x3800
+#define CFG_SB_MAX_ADDR 0x3ff8
+#endif
+
+#define CFG_HLB_MIN_ADDR 0x00000
+#define CFG_HLB_MAX_ADDR 0x2BFF8
+
+/* 7x30 MI2S Registers */
+
+/* MI2S Registers are named from the MI2S block's point of view:
+ * - TX = transmit from SoC to external codec
+ * - RX = receive from external codec to SoC
+ */
+#define MI2S_RESET 0x00
+#define MI2S_MODE 0x04
+#define MI2S_TX_MODE 0x08
+#define MI2S_RX_MODE 0x0C
+
+#define MI2S_RESET_RESET 1
+
+#define MI2S_MODE_MASTER 0x1000
+#define MI2S_MODE_16BIT 0x0100
+#define MI2S_MODE_24BIT 0x0200
+#define MI2S_MODE_32BIT 0x0300
+#define MI2S_MODE_EN_3 0x0080
+#define MI2S_MODE_EN_2 0x0040
+#define MI2S_MODE_EN_1 0x0020
+#define MI2S_MODE_EN_0 0x0010
+#define MI2S_MODE_TX_3 0x0008
+#define MI2S_MODE_TX_2 0x0004
+#define MI2S_MODE_TX_1 0x0002
+#define MI2S_MODE_TX_0 0x0001
+
+#define MI2S_TX_MODE_2CH 0x0000
+#define MI2S_TX_MODE_4CH 0x0008
+#define MI2S_TX_MODE_6CH 0x0010
+#define MI2S_TX_MODE_8CH 0x0018
+#define MI2S_TX_MODE_STEREO 0x0004
+#define MI2S_TX_MODE_MONO_PACK 0x0002 /* 2 mono samples packed together */
+#define MI2S_TX_MODE_DMA_SYNC 0x0001 /* sync dma ack clocks */
+
+#define MI2S_RX_MODE_2CH 0x0000
+#define MI2S_RX_MODE_4CH 0x0008
+#define MI2S_RX_MODE_6CH 0x0010
+#define MI2S_RX_MODE_8CH 0x0018
+#define MI2S_RX_MODE_STEREO 0x0004
+#define MI2S_RX_MODE_MONO_PACK 0x0002 /* 2 mono samples packed together */
+#define MI2S_RX_MODE_DMA_SYNC 0x0001 /* sync dma ack clocks */
+
+static int mi2s_set_output(struct msm_codec *mc,
+ unsigned channels, unsigned bitdepth)
+{
+ unsigned mode = 0;
+ unsigned tx_mode = 0;
+
+ if (channels != 2 || bitdepth != 16)
+ return -EINVAL;
+
+ /* TODO: support non stereo-16 (does the DSP even do that?) */
+
+ mode |= MI2S_MODE_MASTER;
+ mode |= MI2S_MODE_16BIT;
+ mode |= MI2S_MODE_EN_0;
+ mode |= MI2S_MODE_TX_0;
+
+ tx_mode |= MI2S_TX_MODE_STEREO;
+ tx_mode |= MI2S_TX_MODE_2CH;
+ tx_mode |= MI2S_RX_MODE_DMA_SYNC;
+
+ writel(1, mc->tx_base + MI2S_RESET);
+ writel(mode, mc->tx_base + MI2S_MODE);
+ writel(tx_mode, mc->tx_base + MI2S_TX_MODE);
+ writel(0, mc->tx_base + MI2S_RESET);
+
+ return 0;
+}
+
+static int mi2s_set_input(struct msm_codec *mc,
+ unsigned channels, unsigned bitdepth)
+{
+ unsigned mode = 0;
+ unsigned rx_mode = 0;
+
+ if (channels != 2 || bitdepth != 16)
+ return -EINVAL;
+
+ /* TODO: support non stereo-16 */
+ /* TODO: packed mono mode? */
+
+ mode |= MI2S_MODE_MASTER;
+ mode |= MI2S_MODE_16BIT;
+ mode |= MI2S_MODE_EN_0;
+
+ rx_mode |= MI2S_RX_MODE_STEREO;
+ rx_mode |= MI2S_RX_MODE_2CH;
+ rx_mode |= MI2S_RX_MODE_DMA_SYNC;
+
+ writel(1, mc->rx_base + MI2S_RESET);
+ writel(mode, mc->rx_base + MI2S_MODE);
+ writel(rx_mode, mc->rx_base + MI2S_RX_MODE);
+ writel(0, mc->rx_base + MI2S_RESET);
+
+ return 0;
+}
+
+void lpa_enable(struct msm_codec *mc)
+{
+ unsigned val;
+
+ /* for "hardware reasons" we must ensure the
+ * adsp clock is on during this reset sequence.
+ */
+ clk_enable(mc->adsp_clk);
+
+ /* disable codec */
+ writel(LPA_CODEC_LOAD, mc->lpa_base + LPA_CODEC);
+
+ writel(LPA_RESETS_MISR | LPA_RESETS_OVERALL,
+ mc->lpa_base + LPA_RESETS);
+
+ while (!(readl(mc->lpa_base + LPA_STATUS) & LPA_STATUS_RESET_DONE))
+ ;
+
+ writel(LPA_ACK_RESET_DONE, mc->lpa_base + LPA_ACK);
+
+ clk_disable(mc->adsp_clk);
+
+ /* configure memory buffers */
+ writel(CFG_LLB_MIN_ADDR | LPA_LLB_MIN_ADDR_LOAD,
+ mc->lpa_base + LPA_LLB_MIN_ADDR);
+ writel(CFG_LLB_MAX_ADDR, mc->lpa_base + LPA_LLB_MAX_ADDR);
+
+ writel(CFG_SB_MIN_ADDR | LPA_SB_MIN_ADDR_LOAD,
+ mc->lpa_base + LPA_SB_MIN_ADDR);
+ writel(CFG_SB_MAX_ADDR, mc->lpa_base + LPA_SB_MAX_ADDR);
+
+ writel(CFG_HLB_MIN_ADDR | LPA_HLB_MIN_ADDR_LOAD,
+ mc->lpa_base + LPA_HLB_MIN_ADDR);
+ writel(CFG_HLB_MAX_ADDR, mc->lpa_base + LPA_HLB_MAX_ADDR);
+
+ writel(LPA_MEM_CTL_PWRUP, mc->lpa_base + LPA_MEMORY_CONTROL);
+
+
+ while (readl(mc->lpa_base + LPA_WMARK_ASSIGN) != LPA_WMARK_ASSIGN_DONE)
+ ;
+
+ /* setup watermark ownership */
+ writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_0_LLB);
+ writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_1_LLB);
+ writel(LPA_ID_APP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_2_LLB);
+ writel(LPA_ID_APP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_3_LLB);
+ writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_HLB);
+ writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT,
+ mc->lpa_base + LPA_WMARK_SB);
+ writel(0, mc->lpa_base + LPA_UTC_CONFIG);
+
+
+ val = readl(mc->lpa_base + LPA_CONTROL);
+ val |= LPA_CONTROL_LLB_EN;
+ val |= LPA_CONTROL_LLB_SAT_EN;
+ val |= LPA_CONTROL_SB_EN;
+ val |= LPA_CONTROL_SB_SAT_EN;
+ writel(val, mc->lpa_base + LPA_CONTROL);
+
+ writel(1 << LPA_ID_DSP, mc->lpa_base + LPA_INTR_ENABLE);
+}
+
+void lpa_start(struct msm_codec *mc)
+{
+ unsigned val, codec;
+
+ codec = LPA_CODEC_LOAD;
+ codec |= LPA_NUM_CHAN_STEREO;
+ codec |= LPA_SAMPLE_RATE_48KHZ;
+ codec |= LPA_BITS_PER_CHAN_16BITS;
+ codec |= LPA_INTF_WB_CODEC;
+ writel(codec, mc->lpa_base + LPA_CODEC);
+
+ /* clear LLB */
+ val = readl(mc->lpa_base + LPA_CONTROL);
+ writel(val | LPA_CONTROL_LLB_CLR_CMD, mc->lpa_base + LPA_CONTROL);
+
+ while (!(readl(mc->lpa_base + LPA_STATUS) & LPA_STATUS_LLB_CLR))
+ udelay(100);
+
+ /* enable codec */
+ codec |= LPA_CODEC_INTF_EN;
+ writel(codec, mc->lpa_base + LPA_CODEC);
+}
+
+void lpa_disable(struct msm_codec *mc)
+{
+ writel(LPA_CODEC_LOAD, mc->lpa_base + LPA_CODEC);
+}
+
+int msm_codec_output_enable(struct msm_codec *mc)
+{
+ unsigned rate, val;
+
+ pr_info("msm_codec_output_enable()\n");
+
+ /* yes rx clks for tx codec -- the clocks
+ * are named from the opposite POV of the
+ * codec for some reason...
+ */
+
+
+ /* bitrate * bits * channels * 8 */
+ rate = 48000 * 16 * 2 * 8;
+ clk_set_rate(mc->rx_mclk, rate);
+
+ printk("RATE %d\n", clk_get_rate(mc->rx_mclk));
+
+ clk_enable(mc->rx_mclk);
+ clk_enable(mc->rx_sclk);
+
+ clk_enable(mc->lpa_pclk);
+ clk_enable(mc->lpa_codec_clk);
+ clk_enable(mc->lpa_core_clk);
+ /* LPA init */
+
+ lpa_enable(mc);
+
+ /* interconnect reg -> LPA */
+ val = readl(mc->lpa_base + LPA_AICTL);
+ writel(val | 4, mc->lpa_base + LPA_AICTL);
+
+ /* fire up mi2s transport */
+ mi2s_set_output(mc, 2, 16);
+
+ lpa_start(mc);
+
+ /* AFE enable */
+
+ /* ADIE enable */
+
+ /* AMP enable */
+
+ return 0;
+}
+
+int msm_codec_output_disable(struct msm_codec *mc)
+{
+ pr_info("msm_codec_output_disable()\n");
+ /* AMP disable */
+ /* ADIE disable */
+ /* AFE disable */
+ /* LPA disable */
+
+ clk_disable(mc->lpa_core_clk);
+ clk_disable(mc->lpa_codec_clk);
+ clk_disable(mc->lpa_pclk);
+
+ clk_disable(mc->rx_sclk);
+ clk_disable(mc->rx_mclk);
+
+ return 0;
+}
+
+
+static struct msm_codec the_msm_codec;
+
+int msm_codec_output(int enable)
+{
+ struct msm_codec *mc = &the_msm_codec;
+ if (enable)
+ return msm_codec_output_enable(mc);
+ else
+ return msm_codec_output_disable(mc);
+}
+
+/* 7x30 memory map */
+
+#define PHYS_ADDR_LPA 0xA5000000
+#define PHYS_SIZE_LPA 0x00000800
+
+#define PHYS_ADDR_MI2S_HDMI 0xAC900000
+#define PHYS_ADDR_MI2S_CODEC_RX 0xAC940040
+#define PHYS_ADDR_MI2S_CODEC_TX 0xAC980080
+#define PHYS_SIZE_MI2S 0x00000040
+
+int msm_codec_init(void)
+{
+ struct msm_codec *mc = &the_msm_codec;
+
+ printk("msm_codec_init()\n");
+
+ mc->rx_mclk = clk_get(NULL, "mi2s_codec_rx_mclk");
+ if (IS_ERR(mc->rx_mclk))
+ return -ENODEV;
+ mc->rx_sclk = clk_get(NULL, "mi2s_codec_rx_sclk");
+ if (IS_ERR(mc->rx_sclk))
+ return -ENODEV;
+ mc->tx_mclk = clk_get(NULL, "mi2s_codec_tx_mclk");
+ if (IS_ERR(mc->tx_mclk))
+ return -ENODEV;
+ mc->tx_sclk = clk_get(NULL, "mi2s_codec_tx_sclk");
+ if (IS_ERR(mc->tx_sclk))
+ return -ENODEV;
+ mc->lpa_codec_clk = clk_get(NULL, "lpa_codec_clk");
+ if (IS_ERR(mc->lpa_codec_clk))
+ return -ENODEV;
+ mc->lpa_core_clk = clk_get(NULL, "lpa_core_clk");
+ if (IS_ERR(mc->lpa_core_clk))
+ return -ENODEV;
+ mc->lpa_pclk = clk_get(NULL, "lpa_pclk");
+ if (IS_ERR(mc->lpa_pclk))
+ return -ENODEV;
+ mc->adsp_clk = clk_get(NULL, "adsp_clk");
+ if (IS_ERR(mc->adsp_clk))
+ return -ENODEV;
+
+ mc->lpa_base = ioremap(PHYS_ADDR_LPA, PHYS_SIZE_LPA);
+ if (!mc->lpa_base)
+ return -ENODEV;
+ mc->rx_base = ioremap(PHYS_ADDR_MI2S_CODEC_RX, PHYS_SIZE_MI2S);
+ if (!mc->rx_base)
+ return -ENODEV;
+ mc->tx_base = ioremap(PHYS_ADDR_MI2S_CODEC_TX, PHYS_SIZE_MI2S);
+ if (!mc->tx_base)
+ return -ENODEV;
+
+ return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
new file mode 100644
index 0000000..4e63e60
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -0,0 +1,242 @@
+/* arch/arm/mach-msm/qdsp5v2/audio_out.c
+ *
+ * Copyright (C) 2010 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/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/delay.h>
+
+#include <linux/msm_audio.h>
+
+#include "adsp.h"
+#include "adsp_audio.h"
+
+#include "adsp_module_afe.h"
+
+
+void adie_enable(void);
+
+struct audio_buffer {
+ dma_addr_t phys;
+ void *data;
+ uint32_t size;
+ uint32_t used;
+};
+
+struct audio {
+ struct audio_buffer buf[2];
+
+ int cpu_buf;
+ int dsp_buf;
+ int running;
+ int session;
+
+ wait_queue_head_t wait;
+ struct audplay *audplay;
+ void *data;
+ dma_addr_t phys;
+};
+
+static void audio_send_data(void *cookie)
+{
+ struct audio *audio = cookie;
+ struct audio_buffer *ab = audio->buf + audio->dsp_buf;
+
+ if (ab->used) {
+ ab->used = 0;
+ audio->dsp_buf ^= 1;
+ wake_up(&audio->wait);
+ }
+}
+
+
+static int need_init = 1;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct audio *audio;
+
+ if (need_init) {
+ msm_codec_output(1);
+ afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2);
+ adie_enable();
+ need_init = 0;
+ }
+
+#if 0
+ msleep(5000);
+ afe_disable(AFE_DEVICE_MI2S_CODEC_RX);
+ msm_codec_output(0);
+
+ msleep(5000);
+ msm_codec_output(1);
+ afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2);
+ return 0;
+#endif
+
+ audio = kzalloc(sizeof(*audio), GFP_KERNEL);
+ if (!audio)
+ return -ENOMEM;
+
+ audio->data = dma_alloc_coherent(NULL, 8192, &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&audio->wait);
+
+ audio->buf[0].phys = audio->phys;
+ audio->buf[0].data = audio->data;
+ audio->buf[0].size = 4096;
+ audio->buf[0].used = 0;
+ audio->buf[1].phys = audio->phys + 4096;
+ audio->buf[1].data = audio->data + 4096;
+ audio->buf[1].size = 4096;
+ audio->buf[1].used = 0;
+
+ audio->audplay = audplay_get(audio_send_data, audio);
+ if (!audio->audplay) {
+ kfree(audio);
+ return -ENODEV;
+ }
+
+ audplay_dsp_config(audio->audplay, 1);
+
+ audplay_config_pcm(audio->audplay, 44100, 16, 2);
+
+ audplay_mix_select(audio->audplay, 1);
+ audplay_volume_pan(audio->audplay, 0x2000, 0);
+
+ file->private_data = audio;
+ return 0;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ while (count > 0) {
+ ab = audio->buf + audio->cpu_buf;
+
+ if (ab->used)
+ if (!wait_event_timeout(audio->wait,
+ (ab->used == 0), 5*HZ)) {
+ pr_err("audio_write: timeout. dsp dead?\n");
+ return -EIO;
+ }
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = xfer;
+ audplay_send_data(audio->audplay, ab->phys, ab->used);
+ audio->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+ pr_info("audio_release()\n");
+ audplay_dsp_config(audio->audplay, 0);
+ audplay_put(audio->audplay);
+ kfree(audio);
+ return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_SET_VOLUME: {
+ int vol;
+ if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_info("audio_out: volume %d\n", vol);
+ break;
+ }
+ case AUDIO_GET_STATS:
+ case AUDIO_START:
+ case AUDIO_STOP:
+ case AUDIO_FLUSH:
+ case AUDIO_SET_CONFIG:
+ /* implement me! */
+ break;
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = 4096;
+ config.buffer_count = 2;
+ config.sample_rate = 44100;
+ config.channel_count = 2;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ }
+ return rc;
+}
+
+
+static const struct file_operations audio_out_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_out_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &audio_out_fops,
+};
+
+static int __init audio_init(void)
+{
+ adsp_audio_init();
+ return misc_register(&audio_out_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/marimba.c b/arch/arm/mach-msm/qdsp5v2/marimba.c
new file mode 100644
index 0000000..fb1e18a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/marimba.c
@@ -0,0 +1,322 @@
+/* arch/arm/mach-msm/qdsp5v2/marimba.c
+ *
+ * Copyright (C) 2010 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.
+ *
+ */
+
+struct codec_reg {
+ unsigned char addr, mask, val;
+};
+
+static struct codec_reg init_rx[] = {
+ { 0x23, 0xF8, 0x00 },
+ { 0x24, 0x6F, 0x00 },
+ { 0x25, 0x7F, 0x00 },
+ { 0x26, 0xFC, 0x00 },
+ { 0x28, 0xFE, 0x00 },
+ { 0x29, 0xFE, 0x00 },
+ { 0x33, 0xFF, 0x00 },
+ { 0x34, 0xFF, 0x00 },
+ { 0x35, 0xFC, 0x00 },
+ { 0x36, 0xFE, 0x00 },
+ { 0x37, 0xFE, 0x00 },
+ { 0x38, 0xFE, 0x00 },
+ { 0x39, 0xF0, 0x00 },
+ { 0x3A, 0xFF, 0x0A },
+ { 0x3B, 0xFC, 0xAC },
+ { 0x3C, 0xFC, 0xAC },
+ { 0x3D, 0xFF, 0x55 },
+ { 0x3E, 0xFF, 0x55 },
+ { 0x3F, 0xCF, 0x00 },
+ { 0x40, 0x3F, 0x00 },
+ { 0x41, 0x3F, 0x00 },
+ { 0x42, 0xFF, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x44, 0xF7, 0x00 },
+ { 0x45, 0xFF, 0x00 },
+ { 0x46, 0xFF, 0x00 },
+ { 0x47, 0xF7, 0x00 },
+ { 0x48, 0xF7, 0x00 },
+ { 0x49, 0xFF, 0x00 },
+ { 0x4A, 0xFF, 0x00 },
+ { 0x80, 0x02, 0x00 },
+ { 0x81, 0xFF, 0x4C },
+ { 0x83, 0x23, 0x00 },
+ { 0x84, 0xFF, 0xAC },
+ { 0x85, 0xFF, 0xAC },
+ { 0x88, 0xFF, 0xFF },
+ { 0x8A, 0x0F, 0x03 },
+ { 0x8B, 0xFF, 0xAC },
+ { 0x8C, 0x03, 0x01 },
+ { 0x8D, 0xFF, 0x00 },
+ { 0x8E, 0xFF, 0x00 },
+
+/* lb regs */
+ { 0x2B, 0x8F, 0x02 },
+ { 0x2C, 0x8F, 0x02 },
+
+ { 0xFF, 0x00, 0x00 },
+};
+
+static struct codec_reg init_handset_48k_256_mono[] = {
+ { 0x80, 0x02, 0x02 },
+ { 0x80, 0x02, 0x00 },
+
+ { 0x24, 0x6F, 0x44 },
+ { 0x04, 0xFF, 0x8C },
+ { 0x81, 0xFF, 0x4e },
+ { 0x25, 0x0F, 0x0b },
+ { 0x26, 0xfc, 0xfc },
+ { 0x36, 0xc0, 0x80 },
+ { 0x3A, 0xFF, 0x2B },
+ { 0x23, 0xff, 0x20 },
+ { 0x3d, 0xFF, 0x55 },
+ { 0x83, 0x21, 0x21 },
+ { 0x33, 0x80, 0x80 },
+
+ { 0xFF, 0x00, 10 },
+
+ { 0x33, 0x40, 0x40 },
+ { 0x84, 0xff, 0x00 },
+ { 0x8A, 0x05, 0x04 },
+
+ { 0xFF, 0x00, 0x00 },
+};
+
+static struct codec_reg init_speaker_48k_256_stereo[] = {
+ { 0x80, 0x02, 0x02 },
+ { 0x80, 0x02, 0x00 },
+
+ { 0x24, 0x6F, 0x64 },
+ { 0x25, 0x0F, 0x0B },
+ { 0x26, 0xfc, 0xfc },
+ { 0x37, 0xe6, 0x80 },
+ { 0x3A, 0xFF, 0x2B },
+ { 0x3d, 0xFF, 0x55 },
+ { 0x83, 0x23, 0x23 },
+ { 0x23, 0xff, 0x20 },
+ { 0x33, 0x8a, 0x8a },
+ { 0x33, 0x05, 0x05 },
+
+ { 0xFF, 0x00, 30 },
+
+ { 0x84, 0xff, 0x03 },
+ { 0x85, 0xff, 0x03 },
+ { 0x8A, 0x0f, 0x0c },
+
+ { 0xFF, 0x00, 0x00 },
+};
+
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <mach/vreg.h>
+
+static struct vreg *vreg_marimba1;
+static struct vreg *vreg_marimba2;
+static struct vreg *vreg_marimba3;
+
+static int marimba_vreg_init(void)
+{
+ vreg_marimba1 = vreg_get(NULL, "s2");
+ if (IS_ERR(vreg_marimba1))
+ return PTR_ERR(vreg_marimba1);
+ vreg_marimba2 = vreg_get(NULL, "gp16");
+ if (IS_ERR(vreg_marimba2))
+ return PTR_ERR(vreg_marimba2);
+ /* codec vreg */
+ vreg_marimba3 = vreg_get(NULL, "s4");
+ if (IS_ERR(vreg_marimba3))
+ return PTR_ERR(vreg_marimba3);
+ return 0;
+}
+
+static void marimba_vreg_enable(void)
+{
+ vreg_enable(vreg_marimba1);
+ vreg_enable(vreg_marimba2);
+ vreg_enable(vreg_marimba3);
+}
+
+#define MARIMBA_ADDR_MARIMBA 0x0C
+#define MARIMBA_ADDR_FM 0x2A
+#define MARIMBA_ADDR_CDC 0x77
+#define MARIMBA_ADDR_QMEMBIST 0X66
+
+#define MARIMBA_REG_ID_FM 0x01
+#define MARIMBA_REG_ID_CDC 0x02
+#define MARIMBA_REG_ID_QMEMBIST 0x03
+#define MARIMBA_REG_ID_TSADC 0x04
+
+static int marimba_raw_write(struct i2c_client *client,
+ u8 addr, u8 reg, u8 value)
+{
+ struct i2c_msg msg;
+ u8 data[2];
+ int ret;
+
+ msg.addr = addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = data;
+ data[0] = reg;
+ data[1] = value;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ if (ret != 1)
+ pr_err("marimba_write: fail %d\n", ret);
+
+ return ret;
+}
+
+static int marimba_raw_read(struct i2c_client *client, u8 addr, u8 reg)
+{
+ struct i2c_msg msg[2];
+ u8 value;
+ int ret;
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = ®
+
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &value;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ if (ret != 2)
+ pr_err("marimba_read: fail %d\n", ret);
+
+ if (ret == 2)
+ return value;
+ return ret;
+}
+
+static u8 marimba_shadow[256];
+
+static int marimba_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ marimba_shadow[reg] = value;
+ return marimba_raw_write(client, client->addr, reg, value);
+}
+
+static int marimba_write_mask(struct i2c_client *client, u8 reg, u8 mask, u8 value)
+{
+ value = (marimba_shadow[reg] & (~mask)) | (value & mask);
+ marimba_shadow[reg] = value;
+ return marimba_raw_write(client, client->addr, reg, value);
+}
+
+static int marimba_read(struct i2c_client *client, u8 reg)
+{
+ return marimba_raw_read(client, client->addr, reg);
+}
+
+
+static struct i2c_client *marimba_client;
+
+void adie_load(struct i2c_client *client, struct codec_reg *regs)
+{
+ int n;
+ for (n = 0;; n++) {
+ if (regs[n].addr == 0xff) {
+ if (regs[n].val == 0)
+ return;
+ msleep(regs[n].val);
+ continue;
+ }
+ marimba_write_mask(client, regs[n].addr,
+ regs[n].mask, regs[n].val);
+ }
+}
+
+void adie_enable(void)
+{
+ struct i2c_client *client = marimba_client;
+
+ marimba_vreg_enable();
+
+ marimba_write(client, 0xff, 0x08); /* bring up codec */
+ marimba_write(client, 0xff, 0x0a); /* GDFS_EN_FEW=1 */
+ marimba_write(client, 0xff, 0x0e); /* GDFS_EN_REST=1 */
+ marimba_write(client, 0xff, 0x07); /* RESET_N=1 */
+ marimba_write(client, 0xff, 0x17); /* clock enable */
+ marimba_write(client, 0x03, 0x04); /* enable band gap */
+ marimba_write(client, 0x8F, 0x44); /* dither delay select, dmic gain bypass */
+
+ msleep(100);
+
+ adie_load(client, init_rx);
+ adie_load(client, init_speaker_48k_256_stereo);
+}
+
+static int marimba_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+
+ marimba_client = client;
+
+ printk("*** marimba probe %p '%s' @ 0x%x ** *\n",
+ client, client->name, client->addr);
+
+ /* 0x10 -> MARIMBA_MODE ?! */
+ marimba_raw_write(client, MARIMBA_ADDR_MARIMBA, 0x00, 0x10);
+
+ /* program address into marimba master device */
+ ret = marimba_raw_write(client, MARIMBA_ADDR_MARIMBA,
+ MARIMBA_REG_ID_CDC, client->addr);
+
+ if (ret != 1) {
+ pr_err("marimba_probe() cannot set address\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static const struct i2c_device_id marimba_id[] = {
+ { "marimba-codec", 0 },
+ { }
+};
+
+static struct i2c_driver marimba_driver = {
+ .probe = marimba_probe,
+ .id_table = marimba_id,
+ .driver = {
+ .name = "marimba",
+ },
+};
+
+
+static int marimba_init(void)
+{
+ int ret;
+ ret = marimba_vreg_init();
+ if (ret)
+ return ret;
+ return i2c_add_driver(&marimba_driver);
+}
+
+module_init(marimba_init);
diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile
new file mode 100644
index 0000000..05cb351
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/Makefile
@@ -0,0 +1,9 @@
+obj-y += q6audio.o
+obj-y += pcm_out.o
+obj-y += pcm_in.o
+obj-y += mp3.o
+obj-y += routing.o
+obj-y += audio_ctl.o
+obj-y += msm_q6vdec.o
+obj-y += msm_q6venc.o
+obj-y += dsp_debug.o
\ No newline at end of file
diff --git a/arch/arm/mach-msm/qdsp6/analog_audio.c b/arch/arm/mach-msm/qdsp6/analog_audio.c
new file mode 100644
index 0000000..3ec80d4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/analog_audio.c
@@ -0,0 +1,67 @@
+
+#include <linux/gpio.h>
+#include "../pmic.h"
+#include <mach/msm_qdsp6_audio.h>
+
+#define GPIO_HEADSET_AMP 157
+
+void analog_init(void)
+{
+ /* stereo pmic init */
+ pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB);
+ pmic_mic_set_volt(MIC_VOLT_1_80V);
+
+ gpio_direction_output(GPIO_HEADSET_AMP, 1);
+ gpio_set_value(GPIO_HEADSET_AMP, 0);
+}
+
+void analog_headset_enable(int en)
+{
+ /* enable audio amp */
+ gpio_set_value(GPIO_HEADSET_AMP, !!en);
+}
+
+void analog_speaker_enable(int en)
+{
+ struct spkr_config_mode scm;
+ memset(&scm, 0, sizeof(scm));
+
+ if (en) {
+ scm.is_right_chan_en = 1;
+ scm.is_left_chan_en = 1;
+ scm.is_stereo_en = 1;
+ scm.is_hpf_en = 1;
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+ pmic_set_spkr_configuration(&scm);
+ pmic_spkr_en(LEFT_SPKR, 1);
+ pmic_spkr_en(RIGHT_SPKR, 1);
+
+ /* unmute */
+ pmic_spkr_en_mute(LEFT_SPKR, 1);
+ pmic_spkr_en_mute(RIGHT_SPKR, 1);
+ } else {
+ pmic_spkr_en_mute(LEFT_SPKR, 0);
+ pmic_spkr_en_mute(RIGHT_SPKR, 0);
+
+ pmic_spkr_en(LEFT_SPKR, 0);
+ pmic_spkr_en(RIGHT_SPKR, 0);
+
+ pmic_set_spkr_configuration(&scm);
+ }
+}
+
+static struct q6audio_analog_ops ops = {
+ .init = analog_init,
+ .speaker_enable = analog_speaker_enable,
+ .headset_enable = analog_headset_enable,
+};
+
+static int __init init(void)
+{
+ q6audio_register_analog_ops(&ops);
+ return 0;
+}
+
+device_initcall(init);
diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c
new file mode 100644
index 0000000..170b3eb
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c
@@ -0,0 +1,176 @@
+/* arch/arm/mach-msm/qdsp6/audio_ctrl.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+
+#define BUFSZ (0)
+
+static DEFINE_MUTEX(voice_lock);
+static int voice_started;
+
+static struct audio_client *voc_tx_clnt;
+static struct audio_client *voc_rx_clnt;
+
+static int q6_voice_start(uint32_t rx_acdb_id, uint32_t tx_acdb_id)
+{
+ int rc = 0;
+
+ mutex_lock(&voice_lock);
+
+ if (voice_started) {
+ pr_err("voice: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ voc_rx_clnt = q6voice_open(AUDIO_FLAG_WRITE, rx_acdb_id);
+ if (!voc_rx_clnt) {
+ pr_err("voice: open voice rx failed.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ voc_tx_clnt = q6voice_open(AUDIO_FLAG_READ, tx_acdb_id);
+ if (!voc_tx_clnt) {
+ pr_err("voice: open voice tx failed.\n");
+ q6voice_close(voc_rx_clnt);
+ rc = -ENOMEM;
+ }
+
+ voice_started = 1;
+done:
+ mutex_unlock(&voice_lock);
+ return rc;
+}
+
+static int q6_voice_stop(void)
+{
+ mutex_lock(&voice_lock);
+ if (voice_started) {
+ q6voice_close(voc_tx_clnt);
+ q6voice_close(voc_rx_clnt);
+ voice_started = 0;
+ }
+ mutex_unlock(&voice_lock);
+ return 0;
+}
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int q6_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ uint32_t n;
+ uint32_t id[2];
+ char filename[64];
+
+ switch (cmd) {
+ case AUDIO_SWITCH_DEVICE:
+ rc = copy_from_user(&id, (void *)arg, sizeof(id));
+ if (rc) {
+ pr_err("%s: bad user address\n", __func__);
+ rc = -EFAULT;
+ } else
+ rc = q6audio_do_routing(id[0], id[1]);
+ break;
+ case AUDIO_SET_VOLUME:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (rc) {
+ pr_err("%s: bad user address\n", __func__);
+ rc = -EFAULT;
+ } else
+ rc = q6audio_set_rx_volume(n);
+ break;
+ case AUDIO_SET_MUTE:
+ rc = copy_from_user(&n, (void *)arg, sizeof(n));
+ if (rc) {
+ pr_err("%s: bad user address\n", __func__);
+ rc = -EFAULT;
+ } else
+ rc = q6audio_set_tx_mute(n);
+ break;
+ case AUDIO_UPDATE_ACDB:
+ rc = copy_from_user(&id, (void *)arg, sizeof(id));
+ if (rc) {
+ pr_err("%s: bad user address\n", __func__);
+ rc = -EFAULT;
+ } else
+ rc = q6audio_update_acdb(id[0], id[1]);
+ break;
+ case AUDIO_START_VOICE:
+ if (arg == 0)
+ id[0] = id[1] = 0;
+ else if (copy_from_user(&id, (void *)arg, sizeof(id))) {
+ pr_info("voice: copy acdb_id from user failed\n");
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6_voice_start(id[0], id[1]);
+ break;
+ case AUDIO_STOP_VOICE:
+ rc = q6_voice_stop();
+ break;
+ case AUDIO_REINIT_ACDB:
+ rc = copy_from_user(&filename, (void *)arg, sizeof(filename));
+ if (rc) {
+ pr_err("%s: bad user address\n", __func__);
+ rc = -EFAULT;
+ } else
+ rc = q6audio_reinit_acdb(filename);
+ break;
+ default:
+ pr_info("%s: unknown %d\n", __func__, cmd);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static struct file_operations q6_dev_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .ioctl = q6_ioctl,
+ .release = q6_release,
+};
+
+struct miscdevice q6_control_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_ctl",
+ .fops = &q6_dev_fops,
+};
+
+
+static int __init q6_audio_ctl_init(void) {
+ return misc_register(&q6_control_device);
+}
+
+device_initcall(q6_audio_ctl_init);
diff --git a/arch/arm/mach-msm/qdsp6/dal_acdb.h b/arch/arm/mach-msm/qdsp6/dal_acdb.h
new file mode 100644
index 0000000..0e95b3b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_acdb.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define ACDB_DAL_DEVICE 0x02000069
+#define ACDB_DAL_PORT "SMD_DAL_AM_AUD"
+
+#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API
+
+/* ioctls */
+#define ACDB_GET_DEVICE 0x0108bb92
+#define ACDB_SET_DEVICE 0x0108bb93
+#define ACDB_GET_STREAM 0x0108bb95
+#define ACDB_SET_STREAM 0x0108bb96
+#define ACDB_GET_DEVICE_TABLE 0x0108bb97
+#define ACDB_GET_STREAM_TABLE 0x0108bb98
+
+#define ACDB_RES_SUCCESS 0
+#define ACDB_RES_FAILURE -1
+#define ACDB_RES_BADPARM -2
+#define ACDB_RES_BADSTATE -3
+
+struct acdb_cmd_device {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+ uint32_t interface_id;
+ uint32_t algorithm_block_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+} __attribute__((packed));
+
+struct acdb_cmd_device_table {
+ uint32_t size;
+
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id;
+
+ /* physical page aligned buffer */
+ uint32_t total_bytes;
+ uint32_t unmapped_buf;
+
+ uint32_t res_size;
+} __attribute__((packed));
+
+struct acdb_result {
+ uint32_t dal_status;
+ uint32_t size;
+
+ uint32_t unmapped_buf;
+ uint32_t used_bytes;
+ uint32_t result;
+} __attribute__((packed));
diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h
new file mode 100644
index 0000000..99e3c63
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_adie.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MACH_MSM_QDSP6_ADIE_
+#define _MACH_MSM_QDSP6_ADIE_
+
+#include "../dal.h"
+
+#define ADIE_DAL_DEVICE 0x02000029
+#define ADIE_DAL_PORT "SMD_DAL_AM_AUD"
+
+enum {
+ ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API,
+ ADIE_OP_GET_ALL_PATH_IDS,
+ ADIE_OP_SET_PATH,
+ ADIE_OP_GET_NUM_PATH_FREQUENCY_PLANS,
+ ADIE_OP_GET_PATH_FREQUENCY_PLANS,
+ ADIE_OP_SET_PATH_FREQUENCY_PLAN,
+ ADIE_OP_PROCEED_TO_STAGE,
+ ADIE_OP_MUTE_PATH
+};
+
+/* Path IDs for normal operation. */
+#define ADIE_PATH_HANDSET_TX 0x010740f6
+#define ADIE_PATH_HANDSET_RX 0x010740f7
+#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8
+#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9
+#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa
+#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb
+#define ADIE_PATH_SPEAKER_TX 0x010740fc
+#define ADIE_PATH_SPEAKER_RX 0x010740fd
+#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101
+
+/* Path IDs used for TTY */
+#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe
+#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff
+
+/* Path IDs used by Factory Test Mode. */
+#define ADIE_PATH_FTM_MIC1_TX 0x01074108
+#define ADIE_PATH_FTM_MIC2_TX 0x01074107
+#define ADIE_PATH_FTM_HPH_L_RX 0x01074106
+#define ADIE_PATH_FTM_HPH_R_RX 0x01074104
+#define ADIE_PATH_FTM_EAR_RX 0x01074103
+#define ADIE_PATH_FTM_SPKR_RX 0x01074102
+
+/* Path IDs for Loopback */
+/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/
+#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100
+/* Line in -> AuxPGA -> LineOut Mono */
+#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82
+/* Line in -> AuxPGA -> Stereo Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109
+/* Line in -> AuxPGA -> Mono Headphone */
+#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85
+/* Line in -> AuxPGA -> Earpiece */
+#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81
+/* Line in -> AuxPGA -> AuxOut */
+#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86
+
+/* Concurrency Profiles */
+#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83
+#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84
+#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88
+#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89
+
+/* stages */
+#define ADIE_STAGE_PATH_OFF 0x0050
+#define ADIE_STAGE_DIGITAL_READY 0x0100
+#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000
+#define ADIE_STAGE_ANALOG_OFF 0x0750
+#define ADIE_STAGE_DIGITAL_OFF 0x0600
+
+/* path types */
+#define ADIE_PATH_RX 0
+#define ADIE_PATH_TX 1
+#define ADIE_PATH_LOOPBACK 2
+
+/* mute states */
+#define ADIE_MUTE_OFF 0
+#define ADIE_MUTE_ON 1
+
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/dal_audio.h b/arch/arm/mach-msm/qdsp6/dal_audio.h
new file mode 100644
index 0000000..b1ad07d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_audio.h
@@ -0,0 +1,565 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DAL_AUDIO_H__
+#define __DAL_AUDIO_H__
+
+#include "dal_audio_format.h"
+
+#define AUDIO_DAL_DEVICE 0x02000028
+#define AUDIO_DAL_PORT "DSP_DAL_AQ_AUD"
+
+enum {
+ AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API,
+ AUDIO_OP_DATA,
+ AUDIO_OP_INIT,
+};
+
+/* ---- common audio structures ---- */
+
+/* This flag, if set, indicates that the beginning of the data in the*/
+/* buffer is a synchronization point or key frame, meaning no data */
+/* before it in the stream is required in order to render the stream */
+/* from this point onward. */
+#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01
+
+/* This flag, if set, indicates that the buffer object is using valid */
+/* physical address used to store the media data */
+#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04
+
+/* This flag, if set, indicates that a media start timestamp has been */
+/* set for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08
+
+/* This flag, if set, indicates that a media stop timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10
+
+/* This flag, if set, indicates that a preroll timestamp has been set */
+/* for a buffer. */
+#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20
+
+/* This flag, if set, indicates that the data in the buffer is a fragment of */
+/* a larger block of data, and will be continued by the data in the next */
+/* buffer to be delivered. */
+#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40
+
+struct adsp_audio_buffer {
+ u32 addr; /* Physical Address of buffer */
+ u32 max_size; /* Maximum size of buffer */
+ u32 actual_size; /* Actual size of valid data in the buffer */
+ u32 offset; /* Offset to the first valid byte */
+ u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */
+ s64 start; /* Start timestamp, if any */
+ s64 stop; /* Stop timestamp, if any */
+ s64 preroll; /* Preroll timestamp, if any */
+} __attribute__ ((packed));
+
+
+
+/* ---- audio commands ---- */
+
+/* Command/event response types */
+#define ADSP_AUDIO_RESPONSE_COMMAND 0
+#define ADSP_AUDIO_RESPONSE_ASYNC 1
+
+struct adsp_command_hdr {
+ u32 size; /* sizeof(cmd) - sizeof(u32) */
+
+ u32 dst;
+ u32 src;
+
+ u32 opcode;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+
+ u32 padding;
+} __attribute__ ((packed));
+
+
+#define AUDIO_DOMAIN_APP 0
+#define AUDIO_DOMAIN_MODEM 1
+#define AUDIO_DOMAIN_DSP 2
+
+#define AUDIO_SERVICE_AUDIO 0
+#define AUDIO_SERVICE_VIDEO 1 /* really? */
+
+/* adsp audio addresses are (byte order) domain, service, major, minor */
+//#define AUDIO_ADDR(maj,min) ( (((maj) & 0xff) << 16) | (((min) & 0xff) << 24) | (1) )
+
+#define AUDIO_ADDR(maj,min,dom) ( (((min) & 0xff) << 24) | (((maj) & 0xff) << 16) | ((AUDIO_SERVICE_AUDIO) << 8) | (dom) )
+
+
+/* AAC Encoder modes */
+#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0
+#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1
+#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2
+
+struct adsp_audio_aac_enc_cfg {
+ u32 bit_rate; /* bits per second */
+ u32 encoder_mode; /* ADSP_AUDIO_ENC_* */
+} __attribute__ ((packed));
+
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0
+#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1
+
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8
+#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9
+
+struct adsp_audio_sbc_encoder_cfg {
+ u32 num_subbands;
+ u32 block_len;
+ u32 channel_mode;
+ u32 allocation_method;
+ u32 bit_rate;
+} __attribute__ ((packed));
+
+/* AMR NB encoder modes */
+#define ADSP_AUDIO_AMR_MR475 0
+#define ADSP_AUDIO_AMR_MR515 1
+#define ADSP_AUDIO_AMR_MMR59 2
+#define ADSP_AUDIO_AMR_MMR67 3
+#define ADSP_AUDIO_AMR_MMR74 4
+#define ADSP_AUDIO_AMR_MMR795 5
+#define ADSP_AUDIO_AMR_MMR102 6
+#define ADSP_AUDIO_AMR_MMR122 7
+
+/* The following are valid AMR NB DTX modes */
+#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2
+#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3
+
+/* AMR Encoder configuration */
+struct adsp_audio_amr_enc_cfg {
+ u32 mode; /* ADSP_AUDIO_AMR_MR* */
+ u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */
+ u32 enable; /* 1 = enable, 0 = disable */
+} __attribute__ ((packed));
+
+struct adsp_audio_qcelp13k_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+struct adsp_audio_evrc_enc_cfg {
+ u16 min_rate;
+ u16 max_rate;
+} __attribute__ ((packed));
+
+union adsp_audio_codec_config {
+ struct adsp_audio_amr_enc_cfg amr;
+ struct adsp_audio_aac_enc_cfg aac;
+ struct adsp_audio_qcelp13k_enc_cfg qcelp13k;
+ struct adsp_audio_evrc_enc_cfg evrc;
+ struct adsp_audio_sbc_encoder_cfg sbc;
+} __attribute__ ((packed));
+
+
+/* This is the default value. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000
+
+/* This bit, if set, indicates that the AVSync mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001
+
+/* This bit, if set, indicates that the Sample Rate/Channel Mode */
+/* Change Notification mode is activated. */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002
+
+/* This bit, if set, indicates that the sync clock is enabled */
+#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004
+
+struct adsp_open_command {
+ struct adsp_command_hdr hdr;
+
+ u32 device;
+ u32 endpoint; /* address */
+
+ u32 stream_context;
+ u32 mode;
+
+ u32 buf_max_size;
+
+ union adsp_audio_format format;
+ union adsp_audio_codec_config config;
+} __attribute__ ((packed));
+
+
+/* --- audio control and stream session ioctls ---- */
+
+/* Opcode to open a device stream session to capture audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79
+
+/* Opcode to open a device stream session to render audio */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a
+
+/* Opcode to open a device session, must open a device */
+#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b
+
+/* Close an existing stream or device */
+#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc
+
+
+
+/* A device switch requires three IOCTL */
+/* commands in the following sequence: PREPARE, STANDBY, COMMIT */
+
+/* adsp_audio_device_switch_command structure is needed for */
+/* DEVICE_SWITCH_PREPARE */
+
+/* Device switch protocol step #1. Pause old device and */
+/* generate silence for the old device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4
+
+/* Device switch protocol step #2. Release old device, */
+/* create new device and generate silence for the new device. */
+
+/* When client receives ack for this IOCTL, the client can */
+/* start sending IOCTL commands to configure, calibrate and */
+/* change filter settings on the new device. */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5
+
+/* Device switch protocol step #3. Start normal operations on new device */
+#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7
+
+struct adsp_device_switch_command {
+ struct adsp_command_hdr hdr;
+ u32 old_device;
+ u32 new_device;
+ u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */
+ u8 device_type; /* 0 = rx, 1 = tx, 2 = both */
+} __attribute__ ((packed));
+
+
+
+/* --- audio control session ioctls ---- */
+
+#define ADSP_PATH_RX 0
+#define ADSP_PATH_TX 1
+#define ADSP_PATH_BOTH 2
+
+/* These commands will affect a logical device and all its associated */
+/* streams. */
+
+
+/* Set device volume. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c
+
+struct adsp_set_dev_volume_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ s32 volume;
+} __attribute__ ((packed));
+
+/* Set Device stereo volume. This command has data payload, */
+/* struct adsp_audio_set_dev_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e
+
+/* Set L, R cross channel gain for a Device. This command has */
+/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40
+
+/* Set device mute state. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f
+
+struct adsp_set_dev_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 path; /* 0 = rx, 1 = tx, 2 = both */
+ u32 mute; /* 1 = mute */
+} __attribute__ ((packed));
+
+/* Configure Equalizer for a device. */
+/* This command has payload struct adsp_audio_set_dev_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e
+
+/* Set configuration data for an algorithm aspect of a device. */
+/* This command has payload struct adsp_audio_set_dev_cfg_command. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb
+
+struct adsp_set_dev_cfg_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 block_id;
+ u32 interface_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* Set configuration data for all interfaces of a device. */
+#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf
+
+struct adsp_set_dev_cfg_table_command {
+ struct adsp_command_hdr hdr;
+ u32 device_id;
+ u32 phys_addr;
+ u32 phys_size;
+ u32 phys_used;
+} __attribute__ ((packed));
+
+/* ---- audio stream data commands ---- */
+
+#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f
+#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80
+
+struct adsp_buffer_command {
+ struct adsp_command_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+
+/* ---- audio stream ioctls (only affect a single stream in a session) ---- */
+
+/* Stop stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54
+
+/* End of stream reached. Client will not send any more data. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150
+
+/* Do sample slipping/stuffing on AAC outputs. The payload of */
+/* this command is struct adsp_audio_slip_sample_command. */
+#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e
+
+/* Set stream volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de
+
+/* Set stream stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c
+
+/* Set L, R cross channel gain for a Stream. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d
+
+/* Set stream mute state. */
+/* This command has data payload, struct adsp_audio_set_stream_mute. */
+#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df
+
+/* Reconfigure bit rate information. This command has data */
+/* payload, struct adsp_audio_set_bit_rate_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1
+
+/* Set Channel Mapping. This command has data payload, struct */
+/* This command has data payload struct adsp_audio_set_channel_map_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a
+
+/* Enable/disable AACPlus SBR. */
+/* This command has data payload struct adsp_audio_set_sbr_command */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416
+
+/* Enable/disable WMA Pro Chex and Fex. This command has data payload */
+/* struct adsp_audio_stream_set_wma_command. */
+#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417
+
+
+/* ---- audio session ioctls (affect all streams in a session) --- */
+
+/* Start stream for audio device. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6
+
+/* Stop all stream(s) for audio session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e
+
+/* Pause the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8
+
+/* Resume the data flow for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9
+
+/* Drop any unprocessed data buffers for a session as indicated by major id. */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea
+
+/* Start Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd
+
+/* Stop Stream DTMF tone */
+#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554
+
+/* Set Session volume. */
+/* This command has data payload, struct adsp_audio_set_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd
+
+/* Set session stereo volume. This command has data payload, */
+/* struct adsp_audio_set_stereo_volume_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d
+
+/* Set L, R cross channel gain for a session. This command has */
+/* data payload, struct adsp_audio_set_x_chan_gain_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f
+
+/* Set Session mute state. */
+/* This command has data payload, struct adsp_audio_set_mute_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be
+
+/* Configure Equalizer for a stream. */
+/* This command has payload struct adsp_audio_set_equalizer_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0
+
+/* Set Audio Video sync information. */
+/* This command has data payload, struct adsp_audio_set_av_sync_command. */
+#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2
+
+/* Get Audio Media Session time. */
+/* This command returns the audioTime in adsp_audio_unsigned64_event */
+#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c
+
+
+/* these command structures are used for both STREAM and SESSION ioctls */
+
+struct adsp_set_volume_command {
+ struct adsp_command_hdr hdr;
+ s32 volume;
+} __attribute__ ((packed));
+
+struct adsp_set_mute_command {
+ struct adsp_command_hdr hdr;
+ u32 mute; /* 1 == mute */
+} __attribute__ ((packed));
+
+
+
+/* ---- audio events ---- */
+
+/* All IOCTL commands generate an event with the IOCTL opcode as the */
+/* event id after the IOCTL command has been executed. */
+
+/* This event is generated after a media stream session is opened. */
+#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6
+
+/* This event is generated after a media stream session is closed. */
+#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7
+
+/* Asyncronous buffer consumption. This event is generated after a */
+/* recived buffer is consumed during rendering or filled during */
+/* capture opeartion. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8
+
+/* This event is generated when rendering operation is starving for */
+/* data. In order to avoid audio loss at the end of a plauback, the */
+/* client should wait for this event before issuing the close command. */
+#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9
+
+/* This event is generated during capture operation when there are no */
+/* buffers available to copy the captured audio data */
+#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da
+
+/* This asynchronous event is generated as a result of an input */
+/* sample rate change and/or channel mode change detected by the */
+/* decoder. The event payload data is an array of 2 uint32 */
+/* values containing the sample rate in Hz and channel mode. */
+#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329
+
+struct adsp_event_hdr {
+ u32 evt_handle; /* DAL common header */
+ u32 evt_cookie;
+ u32 evt_length;
+
+ u32 src; /* "source" audio address */
+ u32 dst; /* "destination" audio address */
+
+ u32 event_id;
+ u32 response_type;
+ u32 seq_number;
+
+ u32 context; /* opaque to DSP */
+ u32 data;
+
+ u32 status;
+} __attribute__ ((packed));
+
+struct adsp_buffer_event {
+ struct adsp_event_hdr hdr;
+ struct adsp_audio_buffer buffer;
+} __attribute__ ((packed));
+
+
+/* ---- audio device IDs ---- */
+
+/* Device direction Rx/Tx flag */
+#define ADSP_AUDIO_RX_DEVICE 0x00
+#define ADSP_AUDIO_TX_DEVICE 0x01
+
+/* Default RX or TX device */
+#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679
+
+/* Source (TX) devices */
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b
+#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3
+
+/* Special loopback pseudo device to be paired with an RX device */
+/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */
+#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2
+
+/* Sink (RX) devices */
+#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511
+#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895
+#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509
+#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519
+#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c
+#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4
+#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512
+
+/* BT A2DP playback device. */
+/* This device must be paired with */
+/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */
+/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */
+#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a
+
+/* Voice Destination identifier - specifically used for */
+/* controlling Voice module from the Device Control Session */
+#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c
+
+/* Audio device usage types. */
+/* This is a bit mask to determine which topology to use in the */
+/* device session */
+#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01
+#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02
+#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10
+#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20
+#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/dal_audio_format.h
new file mode 100644
index 0000000..cdb2e1a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dal_audio_format.h
@@ -0,0 +1,285 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __ADSP_AUDIO_MEDIA_FORMAT_H
+#define __ADSP_AUDIO_MEDIA_FORMAT_H
+
+
+
+/* Supported audio media formats */
+
+/* format block in shmem */
+#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd
+/* adsp_audio_format_raw_pcm type */
+#define ADSP_AUDIO_FORMAT_DTMF 0x01087725
+/* adsp_audio_format_adpcm type */
+#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff
+/* Yamaha PCM format */
+#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07
+/* ISO/IEC 11172 */
+#define ADSP_AUDIO_FORMAT_MP3 0x0103d308
+/* ISO/IEC 14496 */
+#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1
+/* AMR-NB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c
+/* AMR-WB audio in FS format */
+#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e
+/* QCELP 13k, IS733 */
+#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a
+/* EVRC 8k, IS127 */
+#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89
+/* EVRC-B 8k, 4GV */
+#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3
+/* MIDI command stream */
+#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300
+/* A2DP SBC stream */
+#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8
+/* Version 10 Professional */
+#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92
+/* Version 9 Starndard */
+#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430
+/* AMR WideBand Plus */
+#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da
+/* AC3 Decoder */
+#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9
+
+
+/* Not yet supported audio media formats */
+
+
+
+/* ISO/IEC 13818 */
+#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309
+/* 3GPP TS 26.101 Sec 4.0 */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305
+/* 3GPP TS 26.101 Annex A */
+#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306
+/* 3GPP TS 26.201 */
+#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d
+/* G.711 */
+#define ADSP_AUDIO_FORMAT_G711 0x0106201d
+/* QCELP 8k, IS96A */
+#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29
+/* Version 1 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b
+/* Version 2, 7 & 8 codec */
+#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c
+/* Version 9 Professional codec */
+#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d
+/* Version 9 Voice codec */
+#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e
+/* Version 9 Lossless codec */
+#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f
+/* Real Media content, low-bitrate */
+#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f
+/* Real Media content */
+#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e
+
+
+/* For all of the audio formats, unless specified otherwise, */
+/* the following apply: */
+/* Format block bits are arranged in bytes and words in little-endian */
+/* order, i.e., least-significant bit first and least-significant */
+/* byte first. */
+
+
+
+/* AAC Format Block. */
+
+/* AAC format block consist of a format identifier followed by */
+/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */
+
+/* The following AAC format identifiers are supported */
+#define ADSP_AUDIO_AAC_ADTS 0x010619cf
+#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0
+#define ADSP_AUDIO_AAC_LOAS 0x010619d1
+#define ADSP_AUDIO_AAC_ADIF 0x010619d2
+#define ADSP_AUDIO_AAC_RAW 0x010619d3
+#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb
+
+
+#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd
+#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce
+
+/* Maxmum number of bytes allowed in a format block */
+#define ADSP_AUDIO_FORMAT_DATA_MAX 16
+
+
+struct adsp_audio_no_payload_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* no payload for this format type */
+} __attribute__ ((packed));
+
+
+/* For convenience, to be used as a standard format block */
+/* for various media types that don't need a unique format block */
+/* ie. PCM, DTMF, etc. */
+struct adsp_audio_standard_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+} __attribute__ ((packed));
+
+
+
+/* ADPCM format block */
+struct adsp_audio_adpcm_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 channels;
+ u16 bits_per_sample;
+ u32 sampling_rate;
+ u8 is_signed;
+ u8 is_interleaved;
+ u32 block_size;
+} __attribute__ ((packed));
+
+
+/* MIDI format block */
+struct adsp_audio_midi_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 sampling_rate;
+ u16 channels;
+ u16 mode;
+} __attribute__ ((packed));
+
+
+/* G711 format block */
+struct adsp_audio_g711_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 companding;
+} __attribute__ ((packed));
+
+
+struct adsp_audio_wma_pro_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u16 format_tag;
+ u16 channels;
+ u32 samples_per_sec;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 valid_bits_per_sample;
+ u32 channel_mask;
+ u16 encode_opt;
+ u16 advanced_encode_opt;
+ u32 advanced_encode_opt2;
+ u32 drc_peak_reference;
+ u32 drc_peak_target;
+ u32 drc_average_reference;
+ u32 drc_average_target;
+} __attribute__ ((packed));
+
+
+struct adsp_audio_amrwb_plus_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ u32 size;
+ u32 version;
+ u32 channels;
+ u32 amr_band_mode;
+ u32 amr_dtx_mode;
+ u32 amr_frame_format;
+ u32 amr_isf_index;
+} __attribute__ ((packed));
+
+
+/* Binary Byte Stream Format */
+/* Binary format type that defines a byte stream, */
+/* can be used to specify any format (ie. AAC) */
+struct adsp_audio_binary_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* payload */
+ /* number of bytes set in byte stream */
+ u32 num_bytes;
+ /* Byte stream binary data */
+ u8 data[ADSP_AUDIO_FORMAT_DATA_MAX];
+} __attribute__ ((packed));
+
+
+struct adsp_audio_shared_memory_format {
+ /* Media Format Code (must always be first element) */
+ u32 format;
+
+ /* Number of bytes in shared memory */
+ u32 len;
+ /* Phyisical address to data in shared memory */
+ u32 address;
+} __attribute__ ((packed));
+
+
+/* Union of all format types */
+union adsp_audio_format {
+ /* Basic format block with no payload */
+ struct adsp_audio_no_payload_format no_payload;
+ /* Generic format block PCM, DTMF */
+ struct adsp_audio_standard_format standard;
+ /* ADPCM format block */
+ struct adsp_audio_adpcm_format adpcm;
+ /* MIDI format block */
+ struct adsp_audio_midi_format midi;
+ /* G711 format block */
+ struct adsp_audio_g711_format g711;
+ /* WmaPro format block */
+ struct adsp_audio_wma_pro_format wma_pro;
+ /* WmaPro format block */
+ struct adsp_audio_amrwb_plus_format amrwb_plus;
+ /* binary (byte stream) format block, used for AAC */
+ struct adsp_audio_binary_format binary;
+ /* format block in shared memory */
+ struct adsp_audio_shared_memory_format shared_mem;
+};
+
+#endif
+
diff --git a/arch/arm/mach-msm/qdsp6/dsp_debug.c b/arch/arm/mach-msm/qdsp6/dsp_debug.c
new file mode 100644
index 0000000..71c10eb
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dsp_debug.c
@@ -0,0 +1,175 @@
+/* arch/arm/mach-msm/qdsp6/dsp_dump.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+
+#include "../proc_comm.h"
+
+static wait_queue_head_t dsp_wait;
+static int dsp_has_crashed;
+static int dsp_wait_count;
+
+static atomic_t dsp_crash_count = ATOMIC_INIT(0);
+
+void q6audio_dsp_not_responding(void)
+{
+
+ if (atomic_add_return(1, &dsp_crash_count) != 1) {
+ pr_err("q6audio_dsp_not_responding() - parking additional crasher...\n");
+ for (;;)
+ msleep(1000);
+ }
+ if (dsp_wait_count) {
+ dsp_has_crashed = 1;
+ wake_up(&dsp_wait);
+
+ while (dsp_has_crashed != 2)
+ wait_event(dsp_wait, dsp_has_crashed == 2);
+ } else {
+ pr_err("q6audio_dsp_not_responding() - no waiter?\n");
+ }
+ BUG();
+}
+
+static int dsp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t dsp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ if (!strcmp(cmd, "wait-for-crash")) {
+ while (!dsp_has_crashed) {
+ int res;
+ dsp_wait_count++;
+ res = wait_event_interruptible(dsp_wait, dsp_has_crashed);
+ if (res < 0) {
+ dsp_wait_count--;
+ return res;
+ }
+ }
+#if defined(CONFIG_MACH_MAHIMAHI)
+ /* assert DSP NMI */
+ msm_proc_comm(PCOM_CUSTOMER_CMD1, 0, 0);
+ msleep(250);
+#endif
+ } else if (!strcmp(cmd, "boom")) {
+ q6audio_dsp_not_responding();
+ } else if (!strcmp(cmd, "continue-crash")) {
+ dsp_has_crashed = 2;
+ wake_up(&dsp_wait);
+ } else {
+ pr_err("unknown dsp_debug command: %s\n", cmd);
+ }
+
+ return count;
+}
+
+#define DSP_RAM_BASE 0x2E800000
+#define DSP_RAM_SIZE 0x01800000
+
+static unsigned copy_ok_count;
+
+static ssize_t dsp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ size_t actual = 0;
+ size_t mapsize = PAGE_SIZE;
+ unsigned addr;
+ void __iomem *ptr;
+
+ if (*pos >= DSP_RAM_SIZE)
+ return 0;
+
+ if (*pos & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ addr = (*pos + DSP_RAM_BASE);
+
+ /* don't blow up if we're unaligned */
+ if (addr & (PAGE_SIZE - 1))
+ mapsize *= 2;
+
+ while (count >= PAGE_SIZE) {
+ ptr = ioremap(addr, mapsize);
+ if (!ptr) {
+ pr_err("dsp: map error @ %x\n", addr);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, ptr, PAGE_SIZE)) {
+ iounmap(ptr);
+ pr_err("dsp: copy error @ %p\n", buf);
+ return -EFAULT;
+ }
+ copy_ok_count += PAGE_SIZE;
+ iounmap(ptr);
+ addr += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ actual += PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+
+ *pos += actual;
+ return actual;
+}
+
+static int dsp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations dsp_fops = {
+ .owner = THIS_MODULE,
+ .open = dsp_open,
+ .read = dsp_read,
+ .write = dsp_write,
+ .release = dsp_release,
+};
+
+static struct miscdevice dsp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dsp_debug",
+ .fops = &dsp_fops,
+};
+
+
+static int __init dsp_init(void)
+{
+ init_waitqueue_head(&dsp_wait);
+ return misc_register(&dsp_misc);
+}
+
+device_initcall(dsp_init);
diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c
new file mode 100644
index 0000000..759afb1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/mp3.c
@@ -0,0 +1,220 @@
+/* arch/arm/mach-msm/qdsp6/mp3.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+
+#define BUFSZ (8192)
+#define DMASZ (BUFSZ * 2)
+
+struct mp3 {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+};
+
+static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct mp3 *mp3 = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&mp3->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME: {
+ int vol;
+ if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6audio_set_stream_volume(mp3->ac, vol);
+ break;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ pr_info("pcm_out: copy acdb_id from user failed\n");
+ rc = -EFAULT;
+ break;
+ }
+ if (mp3->ac) {
+ rc = -EBUSY;
+ } else {
+ mp3->ac = q6audio_open_mp3(BUFSZ,
+ mp3->sample_rate, mp3->channel_count, acdb_id);
+ if (!mp3->ac)
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (mp3->ac) {
+ rc = -EBUSY;
+ break;
+ }
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count < 1 || config.channel_count > 2) {
+ rc = -EINVAL;
+ break;
+ }
+ mp3->sample_rate = config.sample_rate;
+ mp3->channel_count = config.channel_count;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = mp3->sample_rate;
+ config.channel_count = mp3->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&mp3->lock);
+ return rc;
+}
+
+static int mp3_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct mp3 *mp3;
+ mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL);
+
+ if (!mp3)
+ return -ENOMEM;
+
+ mutex_init(&mp3->lock);
+ mp3->channel_count = 2;
+ mp3->sample_rate = 44100;
+
+ file->private_data = mp3;
+ return rc;
+}
+
+static ssize_t mp3_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mp3 *mp3 = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ if (!mp3->ac)
+ mp3_ioctl(file, AUDIO_START, 0);
+
+ ac = mp3->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ wait_event(ac->wait, (ab->used == 0));
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int mp3_fsync(struct file *f, struct dentry *dentry, int datasync)
+{
+ struct mp3 *mp3 = f->private_data;
+ if (mp3->ac)
+ return q6audio_async(mp3->ac);
+ return -ENODEV;
+}
+
+static int mp3_release(struct inode *inode, struct file *file)
+{
+ struct mp3 *mp3 = file->private_data;
+ if (mp3->ac)
+ q6audio_mp3_close(mp3->ac);
+ kfree(mp3);
+ return 0;
+}
+
+static struct file_operations mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = mp3_open,
+ .write = mp3_write,
+ .fsync = mp3_fsync,
+ .release = mp3_release,
+ .unlocked_ioctl = mp3_ioctl,
+};
+
+struct miscdevice mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &mp3_fops,
+};
+
+static int __init mp3_init(void) {
+ return misc_register(&mp3_misc);
+}
+
+device_initcall(mp3_init);
diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c
new file mode 100644
index 0000000..a719dbd
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c
@@ -0,0 +1,941 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+#define DEBUG_TRACE_VDEC
+#define DEBUG
+*/
+
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+#include <linux/android_pmem.h>
+#include <linux/msm_q6vdec.h>
+
+#include "../dal.h"
+
+#define DALDEVICEID_VDEC_DEVICE 0x02000026
+#define DALDEVICEID_VDEC_PORTNAME "DSP_DAL_AQ_VID"
+
+#define VDEC_INTERFACE_VERSION 0x00020000
+
+#define MAJOR_MASK 0xFFFF0000
+#define MINOR_MASK 0x0000FFFF
+
+#define VDEC_GET_MAJOR_VERSION(version) (((version)&MAJOR_MASK)>>16)
+
+#define VDEC_GET_MINOR_VERSION(version) ((version)&MINOR_MASK)
+
+#ifdef DEBUG_TRACE_VDEC
+#define TRACE(fmt,x...) \
+ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
+#else
+#define TRACE(fmt,x...) do { } while (0)
+#endif
+
+
+static DEFINE_MUTEX(idlecount_lock);
+static int idlecount;
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+
+static void prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&idlelock);
+ wake_lock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static void allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+
+enum {
+ VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API,
+ VDEC_DALRPC_SETBUFFERS,
+ VDEC_DALRPC_FREEBUFFERS,
+ VDEC_DALRPC_QUEUE,
+ VDEC_DALRPC_SIGEOFSTREAM,
+ VDEC_DALRPC_FLUSH,
+ VDEC_DALRPC_REUSEFRAMEBUFFER,
+ VDEC_DALRPC_GETDECATTRIBUTES,
+};
+
+enum {
+ VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00,
+ VDEC_ASYNCMSG_REUSE_FRAME,
+};
+
+struct vdec_init_cfg {
+ u32 decode_done_evt;
+ u32 reuse_frame_evt;
+ struct vdec_config cfg;
+};
+
+struct vdec_buffer_status {
+ u32 data;
+ u32 status;
+};
+
+#define VDEC_MSG_MAX 128
+
+struct vdec_msg_list {
+ struct list_head list;
+ struct vdec_msg vdec_msg;
+};
+
+struct vdec_mem_info {
+ u32 buf_type;
+ u32 id;
+ unsigned long phys_addr;
+ unsigned long len;
+ struct file *file;
+};
+
+struct vdec_mem_list {
+ struct list_head list;
+ struct vdec_mem_info mem;
+};
+
+struct vdec_data {
+ struct dal_client *vdec_handle;
+ struct list_head vdec_msg_list_head;
+ struct list_head vdec_msg_list_free;
+ wait_queue_head_t vdec_msg_evt;
+ spinlock_t vdec_list_lock;
+ struct list_head vdec_mem_list_head;
+ spinlock_t vdec_mem_list_lock;
+ int mem_initialized;
+ int running;
+ int close_decode;
+};
+
+static struct class *driver_class;
+static dev_t vdec_device_no;
+static struct cdev vdec_cdev;
+static int ref_cnt;
+static DEFINE_MUTEX(vdec_ref_lock);
+
+static inline int vdec_check_version(u32 client, u32 server)
+{
+ int ret = -EINVAL;
+ if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server))
+ && (VDEC_GET_MINOR_VERSION(client) <=
+ VDEC_GET_MINOR_VERSION(server)))
+ ret = 0;
+ return ret;
+}
+
+static int vdec_get_msg(struct vdec_data *vd, void *msg)
+{
+ struct vdec_msg_list *l;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!vd->running)
+ return -EPERM;
+
+ spin_lock_irqsave(&vd->vdec_list_lock, flags);
+ list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) {
+ if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg)))
+ pr_err("vdec_get_msg failed to copy_to_user!\n");
+ if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER)
+ TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id);
+ else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE)
+ TRACE("frame_done (stat=%d)\n",
+ l->vdec_msg.vfr_info.status);
+ else
+ TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id);
+ list_del(&l->list);
+ list_add(&l->list, &vd->vdec_msg_list_free);
+ ret = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
+
+ if (vd->close_decode)
+ ret = 1;
+
+ return ret;
+}
+
+static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg)
+{
+ struct vdec_msg_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&vd->vdec_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_msg_list_free, list) {
+ memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg));
+ list_del(&l->list);
+ list_add(&l->list, &vd->vdec_msg_list_head);
+ found = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&vd->vdec_list_lock, flags);
+
+ if (found)
+ wake_up(&vd->vdec_msg_evt);
+ else
+ pr_err("vdec_put_msg can't find free list!\n");
+}
+
+static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd,
+ u32 pmem_id, u32 buf_type)
+{
+ struct vdec_mem_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
+ if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+
+ if (found)
+ return l;
+ else
+ return NULL;
+
+}
+
+static int vdec_initialize(struct vdec_data *vd, void *argp)
+{
+ struct vdec_config_sps vdec_cfg_sps;
+ struct vdec_init_cfg vi_cfg;
+ struct vdec_buf_req vdec_buf_req;
+ struct u8 *header;
+ int ret = 0;
+
+ ret = copy_from_user(&vdec_cfg_sps,
+ &((struct vdec_init *)argp)->sps_cfg,
+ sizeof(vdec_cfg_sps));
+
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE;
+ vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME;
+ memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config));
+
+ header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL);
+ if (!header) {
+ pr_err("%s: kmalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = copy_from_user(header,
+ ((struct vdec_init *)argp)->sps_cfg.seq.header,
+ vdec_cfg_sps.seq.len);
+
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ kfree(header);
+ return ret;
+ }
+
+ TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d "
+ "vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n",
+ vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width,
+ vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable,
+ vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect,
+ vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag,
+ vi_cfg.cfg.fruc_enable);
+ ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE,
+ &vi_cfg, sizeof(vi_cfg),
+ header, vdec_cfg_sps.seq.len,
+ &vdec_buf_req, sizeof(vdec_buf_req));
+
+ kfree(header);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ else
+ ret = copy_to_user(((struct vdec_init *)argp)->buf_req,
+ &vdec_buf_req, sizeof(vdec_buf_req));
+
+ vd->close_decode = 0;
+ return ret;
+}
+
+static int vdec_setbuffers(struct vdec_data *vd, void *argp)
+{
+ struct vdec_buffer vmem;
+ struct vdec_mem_list *l;
+ unsigned long vstart;
+ unsigned long flags;
+ struct {
+ uint32_t size;
+ struct vdec_buf_info buf;
+ } rpc;
+ uint32_t res;
+
+ int ret = 0;
+
+ vd->mem_initialized = 0;
+
+ ret = copy_from_user(&vmem, argp, sizeof(vmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL);
+ if (!l) {
+ pr_err("%s: kzalloc failed!\n", __func__);
+ return -ENOMEM;
+ }
+
+ l->mem.id = vmem.pmem_id;
+ l->mem.buf_type = vmem.buf.buf_type;
+
+ ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart,
+ &l->mem.len, &l->mem.file);
+ if (ret) {
+ pr_err("%s: get_pmem_fd failed\n", __func__);
+ goto err_get_pmem_file;
+ }
+
+ TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d "
+ "islast=%d src_id=%d offset=0x%08x size=0x%x\n",
+ vmem.pmem_id, l->mem.phys_addr, l->mem.len,
+ vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast,
+ vmem.buf.region.src_id, vmem.buf.region.offset,
+ vmem.buf.region.size);
+
+ /* input buffers */
+ if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
+ pr_err("%s: invalid input buffer offset!\n", __func__);
+ ret = -EINVAL;
+ goto err_bad_offset;
+
+ }
+ vmem.buf.region.offset += l->mem.phys_addr;
+
+ rpc.size = sizeof(vmem.buf);
+ memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
+
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5,
+ &rpc, sizeof(rpc), &res, sizeof(res));
+
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ goto err_dal_call;
+ }
+
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_add(&l->list, &vd->vdec_mem_list_head);
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+
+ vd->mem_initialized = 1;
+ return ret;
+
+err_dal_call:
+err_bad_offset:
+ put_pmem_file(l->mem.file);
+err_get_pmem_file:
+ kfree(l);
+ return ret;
+}
+
+static int vdec_queue(struct vdec_data *vd, void *argp)
+{
+ struct {
+ uint32_t size;
+ struct vdec_input_buf_info buf_info;
+ uint32_t osize;
+ } rpc;
+ struct vdec_mem_list *l;
+ struct {
+ uint32_t result;
+ uint32_t size;
+ struct vdec_queue_status status;
+ } rpc_res;
+
+ u32 pmem_id;
+ int ret = 0;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s: memory is not being initialized!\n", __func__);
+ return -EPERM;
+ }
+
+ ret = copy_from_user(&rpc.buf_info,
+ &((struct vdec_input_buf *)argp)->buffer,
+ sizeof(rpc.buf_info));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ ret = copy_from_user(&pmem_id,
+ &((struct vdec_input_buf *)argp)->pmem_id,
+ sizeof(u32));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT);
+
+ if (NULL == l) {
+ pr_err("%s: not able to find the buffer from list\n", __func__);
+ return -EPERM;
+ }
+
+ if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) {
+ pr_err("%s: invalid queue buffer offset!\n", __func__);
+ return -EINVAL;
+ }
+
+ rpc.buf_info.offset += l->mem.phys_addr;
+ rpc.size = sizeof(struct vdec_input_buf_info);
+ rpc.osize = sizeof(struct vdec_queue_status);
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8,
+ &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res));
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp)
+{
+ u32 buf_id;
+ int ret = 0;
+
+ ret = copy_from_user(&buf_id, argp, sizeof(buf_id));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER,
+ buf_id);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static int vdec_flush(struct vdec_data *vd, void *argp)
+{
+ u32 flush_type;
+ int ret = 0;
+
+ ret = copy_from_user(&flush_type, argp, sizeof(flush_type));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ TRACE("flush_type=%d\n", flush_type);
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int vdec_close(struct vdec_data *vd, void *argp)
+{
+ struct vdec_mem_list *l;
+ int ret = 0;
+
+ pr_info("q6vdec_close()\n");
+ vd->close_decode = 1;
+ wake_up(&vd->vdec_msg_evt);
+ ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0);
+ if (ret)
+ pr_err("%s: failed to close daldevice (%d)\n", __func__, ret);
+
+ if (vd->mem_initialized) {
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list)
+ put_pmem_file(l->mem.file);
+ }
+
+ return ret;
+}
+static int vdec_getdecattributes(struct vdec_data *vd, void *argp)
+{
+ struct {
+ uint32_t status;
+ uint32_t size;
+ struct vdec_dec_attributes dec_attr;
+ } rpc;
+ uint32_t inp;
+ int ret = 0;
+ inp = sizeof(struct vdec_dec_attributes);
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9,
+ &inp, sizeof(inp), &rpc, sizeof(rpc));
+ if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ ret = -EIO;
+ } else
+ ret =
+ copy_to_user(((struct vdec_dec_attributes *)argp),
+ &rpc.dec_attr, sizeof(rpc.dec_attr));
+ return ret;
+}
+
+static int vdec_freebuffers(struct vdec_data *vd, void *argp)
+{
+ struct vdec_buffer vmem;
+ struct vdec_mem_list *l;
+ struct {
+ uint32_t size;
+ struct vdec_buf_info buf;
+ } rpc;
+ uint32_t res;
+
+ int ret = 0;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s: memory is not being initialized!\n", __func__);
+ return -EPERM;
+ }
+
+ ret = copy_from_user(&vmem, argp, sizeof(vmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type);
+
+ if (NULL == l) {
+ pr_err("%s: not able to find the buffer from list\n", __func__);
+ return -EPERM;
+ }
+
+ /* input buffers */
+ if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) {
+ pr_err("%s: invalid input buffer offset!\n", __func__);
+ return -EINVAL;
+
+ }
+ vmem.buf.region.offset += l->mem.phys_addr;
+
+ rpc.size = sizeof(vmem.buf);
+ memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info));
+
+ ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5,
+ &rpc, sizeof(rpc), &res, sizeof(res));
+ if (ret < 4) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ }
+
+ return ret;
+}
+static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct vdec_data *vd = file->private_data;
+ void __user *argp = (void __user *)arg;
+ int ret = 0;
+
+ if (!vd->running)
+ return -EPERM;
+
+ switch (cmd) {
+ case VDEC_IOCTL_INITIALIZE:
+ ret = vdec_initialize(vd, argp);
+ break;
+
+ case VDEC_IOCTL_SETBUFFERS:
+ ret = vdec_setbuffers(vd, argp);
+ break;
+
+ case VDEC_IOCTL_QUEUE:
+ TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_queue(vd, argp);
+ break;
+
+ case VDEC_IOCTL_REUSEFRAMEBUFFER:
+ TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_reuse_framebuffer(vd, argp);
+ break;
+
+ case VDEC_IOCTL_FLUSH:
+ ret = vdec_flush(vd, argp);
+ break;
+
+ case VDEC_IOCTL_EOS:
+ TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+
+ case VDEC_IOCTL_GETMSG:
+ TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ wait_event_interruptible(vd->vdec_msg_evt,
+ vdec_get_msg(vd, argp));
+
+ if (vd->close_decode)
+ ret = -EINTR;
+ break;
+
+ case VDEC_IOCTL_CLOSE:
+ ret = vdec_close(vd, argp);
+ break;
+
+ case VDEC_IOCTL_GETDECATTRIBUTES:
+ TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_getdecattributes(vd, argp);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+
+ case VDEC_IOCTL_FREEBUFFERS:
+ TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+ ret = vdec_freebuffers(vd, argp);
+
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n",
+ __func__, ret);
+ break;
+
+ default:
+ pr_err("%s: invalid ioctl!\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ TRACE("ioctl done (pid=%d tid=%d)\n",
+ current->group_leader->pid, current->pid);
+
+ return ret;
+}
+
+static void vdec_dcdone_handler(struct vdec_data *vd, void *frame,
+ uint32_t frame_size)
+{
+ struct vdec_msg msg;
+ struct vdec_mem_list *l;
+ unsigned long flags;
+ int found = 0;
+
+ if (frame_size < sizeof(struct vdec_frame_info)) {
+ pr_warning("%s: msg size mismatch %d != %d\n", __func__,
+ frame_size, sizeof(struct vdec_frame_info));
+ return;
+ }
+
+ memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame,
+ sizeof(struct vdec_frame_info));
+
+ if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) {
+ spin_lock_irqsave(&vd->vdec_mem_list_lock, flags);
+ list_for_each_entry(l, &vd->vdec_mem_list_head, list) {
+ if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) &&
+ (msg.vfr_info.offset >= l->mem.phys_addr) &&
+ (msg.vfr_info.offset <
+ (l->mem.phys_addr + l->mem.len))) {
+ found = 1;
+ msg.vfr_info.offset -= l->mem.phys_addr;
+ msg.vfr_info.data2 = l->mem.id;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags);
+ }
+
+ if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) {
+ msg.id = VDEC_MSG_FRAMEDONE;
+ vdec_put_msg(vd, &msg);
+ } else {
+ pr_err("%s: invalid phys addr = 0x%x\n",
+ __func__, msg.vfr_info.offset);
+ }
+
+}
+
+static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat,
+ uint32_t bufstat_size)
+{
+ struct vdec_buffer_status *vdec_bufstat;
+ struct vdec_msg msg;
+
+ /* TODO: how do we signal the client? If they are waiting on a
+ * message in an ioctl, they may block forever */
+ if (bufstat_size != sizeof(struct vdec_buffer_status)) {
+ pr_warning("%s: msg size mismatch %d != %d\n", __func__,
+ bufstat_size, sizeof(struct vdec_buffer_status));
+ return;
+ }
+ vdec_bufstat = (struct vdec_buffer_status *)bufstat;
+ msg.id = VDEC_MSG_REUSEINPUTBUFFER;
+ msg.buf_id = vdec_bufstat->data;
+ vdec_put_msg(vd, &msg);
+}
+
+static void callback(void *data, int len, void *cookie)
+{
+ struct vdec_data *vd = (struct vdec_data *)cookie;
+ uint32_t *tmp = (uint32_t *) data;
+
+ if (!vd->mem_initialized) {
+ pr_err("%s:memory not initialize but callback called!\n",
+ __func__);
+ return;
+ }
+
+ TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]);
+ switch (tmp[0]) {
+ case VDEC_ASYNCMSG_DECODE_DONE:
+ vdec_dcdone_handler(vd, &tmp[3], tmp[2]);
+ break;
+ case VDEC_ASYNCMSG_REUSE_FRAME:
+ vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]);
+ break;
+ default:
+ pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n",
+ __func__, tmp[0], tmp[2]);
+ }
+}
+static int vdec_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ int i;
+ struct vdec_msg_list *l;
+ struct vdec_data *vd;
+
+ pr_info("q6vdec_open()\n");
+ mutex_lock(&vdec_ref_lock);
+ if (ref_cnt > 0) {
+ pr_err("%s: Instance alredy running\n", __func__);
+ mutex_unlock(&vdec_ref_lock);
+ return -ENOMEM;
+ }
+ ref_cnt++;
+ mutex_unlock(&vdec_ref_lock);
+ vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL);
+ if (!vd) {
+ pr_err("%s: kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto vdec_open_err_handle_vd;
+ }
+ file->private_data = vd;
+
+ vd->mem_initialized = 0;
+ INIT_LIST_HEAD(&vd->vdec_msg_list_head);
+ INIT_LIST_HEAD(&vd->vdec_msg_list_free);
+ INIT_LIST_HEAD(&vd->vdec_mem_list_head);
+ init_waitqueue_head(&vd->vdec_msg_evt);
+
+ spin_lock_init(&vd->vdec_list_lock);
+ spin_lock_init(&vd->vdec_mem_list_lock);
+ for (i = 0; i < VDEC_MSG_MAX; i++) {
+ l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL);
+ if (!l) {
+ pr_err("%s: kzalloc failed!\n", __func__);
+ ret = -ENOMEM;
+ goto vdec_open_err_handle_list;
+ }
+ list_add(&l->list, &vd->vdec_msg_list_free);
+ }
+
+ vd->vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE,
+ DALDEVICEID_VDEC_PORTNAME,
+ callback, vd);
+
+ if (!vd->vdec_handle) {
+ pr_err("%s: failed to attach \n", __func__);
+ ret = -EIO;
+ goto vdec_open_err_handle_list;
+ }
+
+ vd->running = 1;
+ prevent_sleep();
+ return 0;
+
+vdec_open_err_handle_list:
+ {
+ struct vdec_msg_list *l, *n;
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+ }
+vdec_open_err_handle_vd:
+ kfree(vd);
+ return ret;
+}
+
+static int vdec_release(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct vdec_msg_list *l, *n;
+ struct vdec_mem_list *m, *k;
+ struct vdec_data *vd = file->private_data;
+
+ vd->running = 0;
+ wake_up_all(&vd->vdec_msg_evt);
+
+ if (!vd->close_decode)
+ vdec_close(vd, NULL);
+
+ ret = dal_detach(vd->vdec_handle);
+ if (ret)
+ printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret);
+
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+
+ list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) {
+ list_del(&l->list);
+ kfree(l);
+ }
+
+ list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) {
+ list_del(&m->list);
+ kfree(m);
+ }
+ mutex_lock(&vdec_ref_lock);
+ BUG_ON(ref_cnt <= 0);
+ ref_cnt--;
+ mutex_unlock(&vdec_ref_lock);
+ kfree(vd);
+ allow_sleep();
+ return 0;
+}
+
+static const struct file_operations vdec_fops = {
+ .owner = THIS_MODULE,
+ .open = vdec_open,
+ .release = vdec_release,
+ .unlocked_ioctl = vdec_ioctl,
+};
+
+static int __init vdec_init(void)
+{
+ struct device *class_dev;
+ int rc = 0;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vdec_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend");
+
+ rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec");
+ if (rc < 0) {
+ pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ driver_class = class_create(THIS_MODULE, "vdec");
+ if (IS_ERR(driver_class)) {
+ rc = -ENOMEM;
+ pr_err("%s: class_create failed %d\n", __func__, rc);
+ goto vdec_init_err_unregister_chrdev_region;
+ }
+ class_dev = device_create(driver_class, NULL,
+ vdec_device_no, NULL, "vdec");
+ if (!class_dev) {
+ pr_err("%s: class_device_create failed %d\n", __func__, rc);
+ rc = -ENOMEM;
+ goto vdec_init_err_class_destroy;
+ }
+
+ cdev_init(&vdec_cdev, &vdec_fops);
+ vdec_cdev.owner = THIS_MODULE;
+ rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1);
+
+ if (rc < 0) {
+ pr_err("%s: cdev_add failed %d\n", __func__, rc);
+ goto vdec_init_err_class_device_destroy;
+ }
+
+ return 0;
+
+vdec_init_err_class_device_destroy:
+ device_destroy(driver_class, vdec_device_no);
+vdec_init_err_class_destroy:
+ class_destroy(driver_class);
+vdec_init_err_unregister_chrdev_region:
+ unregister_chrdev_region(vdec_device_no, 1);
+ return rc;
+}
+
+static void __exit vdec_exit(void)
+{
+ device_destroy(driver_class, vdec_device_no);
+ class_destroy(driver_class);
+ unregister_chrdev_region(vdec_device_no, 1);
+}
+
+MODULE_DESCRIPTION("video decoder driver for QSD platform");
+MODULE_VERSION("2.00");
+
+module_init(vdec_init);
+module_exit(vdec_exit);
diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c
new file mode 100644
index 0000000..7b4eb85
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c
@@ -0,0 +1,1121 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2010, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Original driver and v2 protocol changes from Code Aurora.
+ * Heavily modified by Dima Zavin <dima@android.com>
+ * Further cleanup by Brian Swetland <swetland@google.com>
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_q6venc.h>
+#include <linux/wakelock.h>
+
+//#include <asm/cacheflush.h>
+#include "../dal.h"
+
+#define DALDEVICEID_VENC_DEVICE 0x0200002D
+#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID"
+
+#define VENC_NAME "q6venc"
+#define VENC_MSG_MAX 128
+
+#define VENC_INTERFACE_VERSION 0x00020000
+#define MAJOR_MASK 0xFFFF0000
+#define MINOR_MASK 0x0000FFFF
+#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16)
+#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK)
+
+#define VERSION_CHECK 0
+
+static DEFINE_MUTEX(idlecount_lock);
+static int idlecount;
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+
+static void prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&idlelock);
+ wake_lock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static void allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+enum {
+ VENC_BUFFER_TYPE_INPUT,
+ VENC_BUFFER_TYPE_OUTPUT,
+ VENC_BUFFER_TYPE_QDSP6,
+ VENC_BUFFER_TYPE_HDR
+};
+enum {
+ VENC_DALRPC_GET_SYNTAX_HEADER = DAL_OP_FIRST_DEVICE_API,
+ VENC_DALRPC_UPDATE_INTRA_REFRESH,
+ VENC_DALRPC_UPDATE_FRAME_RATE,
+ VENC_DALRPC_UPDATE_BITRATE,
+ VENC_DALRPC_UPDATE_QP_RANGE,
+ VENC_DALRPC_UPDATE_INTRA_PERIOD,
+ VENC_DALRPC_REQUEST_IFRAME,
+ VENC_DALRPC_START,
+ VENC_DALRPC_STOP,
+ VENC_DALRPC_SUSPEND,
+ VENC_DALRPC_RESUME,
+ VENC_DALRPC_FLUSH,
+ VENC_DALRPC_QUEUE_INPUT,
+ VENC_DALRPC_QUEUE_OUTPUT
+};
+struct venc_input_payload {
+ u32 data;
+};
+struct venc_output_payload {
+ u32 size;
+ long long time_stamp;
+ u32 flags;
+ u32 data;
+ u32 client_data_from_input;
+};
+union venc_payload {
+ struct venc_input_payload input_payload;
+ struct venc_output_payload output_payload;
+};
+struct venc_msg_type {
+ u32 event;
+ u32 status;
+ union venc_payload payload;
+};
+struct venc_input_buf {
+ struct venc_buf_type yuv_buf;
+ u32 data_size;
+ long long time_stamp;
+ u32 flags;
+ u32 dvs_offsetx;
+ u32 dvs_offsety;
+ u32 client_data;
+ u32 op_client_data;
+};
+struct venc_output_buf {
+ struct venc_buf_type bit_stream_buf;
+ u32 client_data;
+};
+struct venc_buf {
+ int fd;
+ u32 offset;
+ u32 size;
+ u32 btype;
+ unsigned long paddr;
+ struct file *file;
+};
+struct venc_pmem_list {
+ struct list_head list;
+ struct venc_buf buf;
+};
+struct venc_qmsg {
+ struct list_head list;
+ struct venc_msg msg;
+};
+struct venc_dev {
+ bool stop_called;
+ enum venc_state_type state;
+
+ struct list_head msg_pool;
+ struct list_head msg_queue;
+ spinlock_t msg_lock;
+
+ struct list_head venc_pmem_list_head;
+ spinlock_t venc_pmem_list_lock;
+ struct dal_client *q6_handle;
+ wait_queue_head_t venc_msg_evt;
+ struct device *class_devp;
+};
+
+#define DEBUG_VENC 0
+#if DEBUG_VENC
+#define TRACE(fmt, x...) \
+ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0)
+#else
+#define TRACE(fmt, x...) do { } while (0)
+#endif
+
+static struct cdev cdev;
+static dev_t venc_dev_num;
+static struct class *venc_class;
+static struct venc_dev *venc_device_p;
+static int venc_ref;
+
+static inline int venc_check_version(u32 client, u32 server)
+{
+ int ret = -EINVAL;
+
+ if ((VENC_GET_MAJOR_VERSION(client) == VENC_GET_MAJOR_VERSION(server))
+ && (VENC_GET_MINOR_VERSION(client) <=
+ VENC_GET_MINOR_VERSION(server)))
+ ret = 0;
+
+ return ret;
+}
+
+static struct venc_qmsg *__dequeue(spinlock_t *lock, struct list_head *queue)
+{
+ unsigned long flags;
+ struct venc_qmsg *msg;
+ spin_lock_irqsave(lock, flags);
+ if (list_empty(queue)) {
+ msg = NULL;
+ } else {
+ msg = list_first_entry(queue, struct venc_qmsg, list);
+ list_del(&msg->list);
+ }
+ spin_unlock_irqrestore(lock, flags);
+ return msg;
+}
+
+static inline struct venc_qmsg *venc_alloc_msg(struct venc_dev *dvenc)
+{
+ return __dequeue(&dvenc->msg_lock, &dvenc->msg_pool);
+}
+static inline struct venc_qmsg *venc_recv_msg(struct venc_dev *dvenc)
+{
+ return __dequeue(&dvenc->msg_lock, &dvenc->msg_queue);
+}
+
+static void venc_free_msg(struct venc_dev *dvenc, struct venc_qmsg *msg)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dvenc->msg_lock, flags);
+ list_add_tail(&msg->list, &dvenc->msg_pool);
+ spin_unlock_irqrestore(&dvenc->msg_lock, flags);
+}
+
+static void venc_post(struct venc_dev *dvenc,
+ unsigned code, unsigned status,
+ union venc_msg_data *data)
+{
+ unsigned long flags;
+ struct venc_qmsg *msg;
+ msg = venc_alloc_msg(dvenc);
+ if (msg == NULL) {
+ pr_err("%s cannot alloc message\n", __func__);
+ return;
+ }
+ msg->msg.msg_code = code;
+ msg->msg.status_code = status;
+ if (data) {
+ msg->msg.msg_data_size = sizeof(union venc_msg_data);
+ memcpy(&msg->msg.msg_data, data, sizeof(union venc_msg_data));
+ } else {
+ msg->msg.msg_data_size = 0;
+ }
+
+ spin_lock_irqsave(&dvenc->msg_lock, flags);
+ list_add_tail(&msg->list, &dvenc->msg_queue);
+ spin_unlock_irqrestore(&dvenc->msg_lock, flags);
+ wake_up(&dvenc->venc_msg_evt);
+}
+
+static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc,
+ struct venc_pmem *mptr,
+ u32 btype)
+{
+ int ret = 0;
+ unsigned long flags;
+ unsigned long len;
+ unsigned long vaddr;
+ struct venc_pmem_list *plist = NULL;
+
+ plist = kzalloc(sizeof(struct venc_pmem_list), GFP_KERNEL);
+ if (!plist) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ return NULL;
+ }
+
+ ret = get_pmem_file(mptr->fd, &(plist->buf.paddr),
+ &vaddr, &len, &(plist->buf.file));
+
+ /* xxx bounds checking insufficient here */
+ if (ret) {
+ pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n",
+ __func__, mptr->fd, mptr->offset);
+ goto err_venc_add_pmem;
+ } else if (mptr->offset >= len) {
+ pr_err("%s: invalid offset (%d > %ld) for fd=%d\n",
+ __func__, mptr->offset, len, mptr->fd);
+ ret = -EINVAL;
+ goto err_venc_get_pmem;
+ }
+
+ plist->buf.fd = mptr->fd;
+ plist->buf.paddr += mptr->offset;
+ plist->buf.size = mptr->size;
+ plist->buf.btype = btype;
+ plist->buf.offset = mptr->offset;
+
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ list_add(&plist->list, &dvenc->venc_pmem_list_head);
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+ return plist;
+
+err_venc_get_pmem:
+ put_pmem_file(plist->buf.file);
+err_venc_add_pmem:
+ kfree(plist);
+ return NULL;
+}
+
+static struct venc_pmem_list *venc_get_pmem_from_list(
+ struct venc_dev *dvenc, u32 pmem_fd,
+ u32 offset, u32 btype)
+{
+ struct venc_pmem_list *plist;
+ unsigned long flags;
+ struct file *file;
+ int found = 0;
+
+ file = fget(pmem_fd);
+ if (!file) {
+ pr_err("%s: invalid encoder buffer fd(%d)\n", __func__,
+ pmem_fd);
+ return NULL;
+ }
+ spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags);
+ list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) {
+ if (plist->buf.btype == btype && plist->buf.file == file &&
+ plist->buf.offset == offset) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags);
+ fput(file);
+ if (found)
+ return plist;
+
+ else
+ return NULL;
+}
+
+static int venc_set_buffer(struct venc_dev *dvenc, void *argp,
+ u32 btype)
+{
+ struct venc_pmem pmem;
+ struct venc_pmem_list *plist;
+ int ret = 0;
+
+ ret = copy_from_user(&pmem, argp, sizeof(pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ plist = venc_add_pmem_to_list(dvenc, &pmem, btype);
+ if (plist == NULL) {
+ pr_err("%s: buffer add_to_pmem_list failed\n",
+ __func__);
+ return -EPERM;
+ }
+ return ret;
+}
+
+static int venc_assign_q6_buffers(struct venc_dev *dvenc,
+ struct venc_buffers *pbufs,
+ struct venc_nonio_buf_config *pcfg)
+{
+ int ret = 0;
+ struct venc_pmem_list *plist;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[0]),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: recon_buf0 failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->recon_buf1.region = 0;
+ pcfg->recon_buf1.phys = plist->buf.paddr;
+ pcfg->recon_buf1.size = plist->buf.size;
+ pcfg->recon_buf1.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[1]),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: recons_buf1 failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->recon_buf2.region = 0;
+ pcfg->recon_buf2.phys = plist->buf.paddr;
+ pcfg->recon_buf2.size = plist->buf.size;
+ pcfg->recon_buf2.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->wb_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: wb_buf failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->wb_buf.region = 0;
+ pcfg->wb_buf.phys = plist->buf.paddr;
+ pcfg->wb_buf.size = plist->buf.size;
+ pcfg->wb_buf.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->cmd_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: cmd_buf failed to add_to_pmem_list\n",
+ __func__);
+ return -EPERM;
+ }
+ pcfg->cmd_buf.region = 0;
+ pcfg->cmd_buf.phys = plist->buf.paddr;
+ pcfg->cmd_buf.size = plist->buf.size;
+ pcfg->cmd_buf.offset = 0;
+
+ plist = venc_add_pmem_to_list(dvenc, &(pbufs->vlc_buf),
+ VENC_BUFFER_TYPE_QDSP6);
+ if (plist == NULL) {
+ pr_err("%s: vlc_buf failed to add_to_pmem_list"
+ " failed\n", __func__);
+ return -EPERM;
+ }
+ pcfg->vlc_buf.region = 0;
+ pcfg->vlc_buf.phys = plist->buf.paddr;
+ pcfg->vlc_buf.size = plist->buf.size;
+ pcfg->vlc_buf.offset = 0;
+
+ return ret;
+}
+
+static int venc_start(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_q6_config q6_config;
+ struct venc_init_config vconfig;
+
+ dvenc->state = VENC_STATE_START;
+ ret = copy_from_user(&vconfig, argp, sizeof(struct venc_init_config));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ memcpy(&q6_config, &(vconfig.q6_config), sizeof(q6_config));
+ ret = venc_assign_q6_buffers(dvenc, &(vconfig.q6_bufs),
+ &(q6_config.buf_params));
+ if (ret != 0) {
+ pr_err("%s: assign_q6_buffers failed\n", __func__);
+ return -EPERM;
+ }
+
+ q6_config.callback_event = dvenc->q6_handle;
+ TRACE("%s: parameters: handle:%p, config:%p, callback:%p \n", __func__,
+ dvenc->q6_handle, &q6_config, q6_config.callback_event);
+ TRACE("%s: parameters:recon1:0x%x, recon2:0x%x,"
+ " wb_buf:0x%x, cmd:0x%x, vlc:0x%x\n", __func__,
+ q6_config.buf_params.recon_buf1.phys,
+ q6_config.buf_params.recon_buf2.phys,
+ q6_config.buf_params.wb_buf.phys,
+ q6_config.buf_params.cmd_buf.phys,
+ q6_config.buf_params.vlc_buf.phys);
+ TRACE("%s: size of param:%d \n", __func__, sizeof(q6_config));
+ ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_START, &q6_config,
+ sizeof(q6_config));
+ if (ret != 0) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int venc_encode_frame(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_pmem buf;
+ struct venc_input_buf q6_input;
+ struct venc_pmem_list *plist;
+ struct venc_buffer input;
+
+ ret = copy_from_user(&input, argp, sizeof(struct venc_buffer));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ ret = copy_from_user(&buf,
+ ((struct venc_buffer *)argp)->ptr_buffer,
+ sizeof(struct venc_pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset,
+ VENC_BUFFER_TYPE_INPUT);
+ if (NULL == plist) {
+ plist = venc_add_pmem_to_list(dvenc, &buf,
+ VENC_BUFFER_TYPE_INPUT);
+ if (plist == NULL) {
+ pr_err("%s: buffer add_to_pmem_list failed\n",
+ __func__);
+ return -EPERM;
+ }
+ }
+
+ q6_input.flags = 0;
+ if (input.flags & VENC_FLAG_EOS)
+ q6_input.flags |= 0x00000001;
+ q6_input.yuv_buf.region = 0;
+ q6_input.yuv_buf.phys = plist->buf.paddr;
+ q6_input.yuv_buf.size = plist->buf.size;
+ q6_input.yuv_buf.offset = 0;
+ q6_input.data_size = plist->buf.size;
+ q6_input.client_data = (u32)input.client_data;
+ q6_input.time_stamp = input.time_stamp;
+ q6_input.dvs_offsetx = 0;
+ q6_input.dvs_offsety = 0;
+
+ TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x,"
+ " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd,
+ input.client_data, input.time_stamp);
+ ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_INPUT,
+ &q6_input, sizeof(q6_input));
+
+ if (ret != 0)
+ pr_err("%s: Q6 queue_input failed (%d)\n", __func__,
+ (int)ret);
+ return ret;
+}
+
+static int venc_fill_output(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_pmem buf;
+ struct venc_output_buf q6_output;
+ struct venc_pmem_list *plist;
+ struct venc_buffer output;
+
+ ret = copy_from_user(&output, argp, sizeof(struct venc_buffer));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ ret = copy_from_user(&buf,
+ ((struct venc_buffer *)argp)->ptr_buffer,
+ sizeof(struct venc_pmem));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset,
+ VENC_BUFFER_TYPE_OUTPUT);
+ if (NULL == plist) {
+ plist = venc_add_pmem_to_list(dvenc, &buf,
+ VENC_BUFFER_TYPE_OUTPUT);
+ if (NULL == plist) {
+ pr_err("%s: output buffer failed to add_to_pmem_list"
+ "\n", __func__);
+ return -EPERM;
+ }
+ }
+ q6_output.bit_stream_buf.region = 0;
+ q6_output.bit_stream_buf.phys = (u32)plist->buf.paddr;
+ q6_output.bit_stream_buf.size = plist->buf.size;
+ q6_output.bit_stream_buf.offset = 0;
+ q6_output.client_data = (u32)output.client_data;
+ ret =
+ dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_OUTPUT, &q6_output,
+ sizeof(q6_output));
+ if (ret != 0)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int venc_stop(struct venc_dev *dvenc)
+{
+ int ret = 0;
+
+ dvenc->stop_called = 1;
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1);
+ if (ret) {
+ pr_err("%s: remote runction failed (%d)\n", __func__, ret);
+ venc_post(dvenc, VENC_MSG_STOP, VENC_S_EFAIL, NULL);
+ }
+ return ret;
+}
+
+static int venc_pause(struct venc_dev *dvenc)
+{
+ int ret = 0;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ venc_post(dvenc, VENC_MSG_PAUSE, VENC_S_EFAIL, NULL);
+ }
+ return ret;
+}
+
+static int venc_resume(struct venc_dev *dvenc)
+{
+ int ret = 0;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1);
+ if (ret) {
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ venc_post(dvenc, VENC_MSG_RESUME, VENC_S_EFAIL, NULL);
+ }
+ return ret;
+}
+
+static int venc_flush(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ int status = VENC_S_SUCCESS;
+ union venc_msg_data data;
+
+ if (copy_from_user(&data.flush_ret, argp, sizeof(struct venc_buffer_flush)))
+ return -EFAULT;
+
+ if (data.flush_ret.flush_mode == VENC_FLUSH_ALL) {
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1);
+ if (ret)
+ status = VENC_S_EFAIL;
+ } else
+ status = VENC_S_ENOTSUPP;
+
+ if (status == VENC_S_SUCCESS)
+ return ret;
+
+ venc_post(dvenc, VENC_MSG_FLUSH, status, &data);
+ return -EIO;
+}
+
+static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp)
+{
+ pr_err("%s not supported\n", __func__);
+ return -EIO;
+}
+
+static int venc_set_qp_range(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_qp_range qp;
+
+ ret = copy_from_user(&qp, argp, sizeof(struct venc_qp_range));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret =
+ dal_call_f5(dvenc->q6_handle, VENC_DALRPC_UPDATE_QP_RANGE,
+ &qp, sizeof(struct venc_qp_range));
+ if (ret) {
+ pr_err("%s: remote function failed (%d) \n", __func__,
+ ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int venc_set_intra_period(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 pnum = 0;
+
+ ret = copy_from_user(&pnum, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_INTRA_PERIOD, pnum);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_intra_refresh(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 mb_num = 0;
+
+ ret = copy_from_user(&mb_num, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_INTRA_REFRESH, mb_num);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_frame_rate(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ struct venc_frame_rate pdata;
+ ret = copy_from_user(&pdata, argp, sizeof(struct venc_frame_rate));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f5(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_FRAME_RATE,
+ (void *)&(pdata),
+ sizeof(struct venc_frame_rate));
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_set_target_bitrate(struct venc_dev *dvenc, void *argp)
+{
+ int ret = 0;
+ u32 pdata = 0;
+
+ ret = copy_from_user(&pdata, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return ret;
+ }
+ if (dvenc->state == VENC_STATE_START ||
+ dvenc->state == VENC_STATE_PAUSE) {
+ ret = dal_call_f0(dvenc->q6_handle,
+ VENC_DALRPC_UPDATE_BITRATE, pdata);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__,
+ ret);
+ }
+ return ret;
+}
+
+static int venc_request_iframe(struct venc_dev *dvenc)
+{
+ int ret = 0;
+
+ if (dvenc->state != VENC_STATE_START)
+ return -EINVAL;
+
+ ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_REQUEST_IFRAME, 1);
+ if (ret)
+ pr_err("%s: remote function failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int venc_stop_read_msg(struct venc_dev *dvenc)
+{
+ venc_post(dvenc, VENC_MSG_STOP_READING_MSG, 0, NULL);
+ return 0;
+}
+
+static int venc_translate_error(enum venc_status_code q6_status)
+{
+ switch (q6_status) {
+ case VENC_STATUS_SUCCESS:
+ return VENC_S_SUCCESS;
+ case VENC_STATUS_ERROR:
+ return VENC_S_EFAIL;
+ case VENC_STATUS_INVALID_STATE:
+ return VENC_S_EINVALSTATE;
+ case VENC_STATUS_FLUSHING:
+ return VENC_S_EFLUSHED;
+ case VENC_STATUS_INVALID_PARAM:
+ return VENC_S_EBADPARAM;
+ case VENC_STATUS_CMD_QUEUE_FULL:
+ return VENC_S_ECMDQFULL;
+ case VENC_STATUS_CRITICAL:
+ return VENC_S_EFATAL;
+ case VENC_STATUS_INSUFFICIENT_RESOURCES:
+ return VENC_S_ENOHWRES;
+ case VENC_STATUS_TIMEOUT:
+ return VENC_S_ETIMEOUT;
+ default:
+ /* xxx probably shouldn't assume success */
+ return 0;
+ }
+}
+
+static void venc_q6_callback(void *_data, int len, void *cookie)
+{
+ int status = 0;
+ struct venc_dev *dvenc = (struct venc_dev *)cookie;
+ struct venc_msg_type *q6_msg = NULL;
+ struct venc_input_payload *pload1;
+ struct venc_output_payload *pload2;
+ union venc_msg_data data;
+ uint32_t *tmp = (uint32_t *) _data;
+
+ if (dvenc == NULL) {
+ pr_err("%s: empty driver parameter\n", __func__);
+ return;
+ }
+ if (tmp[2] == sizeof(struct venc_msg_type)) {
+ q6_msg = (struct venc_msg_type *)&tmp[3];
+ } else {
+ pr_err("%s: callback with empty message (%d, %d)\n",
+ __func__, tmp[2], sizeof(struct venc_msg_type));
+ return;
+ }
+
+ status = venc_translate_error(q6_msg->status);
+ if (status != VENC_STATUS_SUCCESS)
+ pr_err("%s: Q6 failed (%d)", __func__, (int)status);
+
+ switch ((enum venc_event_type_enum)q6_msg->event) {
+ case VENC_EVENT_START_STATUS:
+ dvenc->state = VENC_STATE_START;
+ venc_post(dvenc, VENC_MSG_START, status, NULL);
+ break;
+ case VENC_EVENT_STOP_STATUS:
+ dvenc->state = VENC_STATE_STOP;
+ venc_post(dvenc, VENC_MSG_STOP, status, NULL);
+ break;
+ case VENC_EVENT_SUSPEND_STATUS:
+ dvenc->state = VENC_STATE_PAUSE;
+ venc_post(dvenc, VENC_MSG_PAUSE, status, NULL);
+ break;
+ case VENC_EVENT_RESUME_STATUS:
+ dvenc->state = VENC_STATE_START;
+ venc_post(dvenc, VENC_MSG_RESUME, status, NULL);
+ break;
+ case VENC_EVENT_FLUSH_STATUS:
+ data.flush_ret.flush_mode = VENC_FLUSH_INPUT;
+ venc_post(dvenc, VENC_MSG_FLUSH, status, &data);
+ data.flush_ret.flush_mode = VENC_FLUSH_OUTPUT;
+ venc_post(dvenc, VENC_MSG_FLUSH, status, &data);
+ break;
+ case VENC_EVENT_RELEASE_INPUT:
+ pload1 = &((q6_msg->payload).input_payload);
+ TRACE("Release_input: data: 0x%x \n", pload1->data);
+ if (pload1 != NULL) {
+ /* xxx should we zero? */
+ data.buf.client_data = pload1->data;
+ venc_post(dvenc, VENC_MSG_INPUT_BUFFER_DONE, status, &data);
+ } else {
+ pr_err("%s no payload on buffer done?\n", __func__);
+ }
+ break;
+ case VENC_EVENT_DELIVER_OUTPUT:
+ pload2 = &((q6_msg->payload).output_payload);
+ data.buf.flags = 0;
+ if (pload2->flags & VENC_FLAG_SYNC_FRAME)
+ data.buf.flags |= VENC_FLAG_SYNC_FRAME;
+ if (pload2->flags & VENC_FLAG_CODEC_CONFIG)
+ data.buf.flags |= VENC_FLAG_CODEC_CONFIG;
+ if (pload2->flags & VENC_FLAG_END_OF_FRAME)
+ data.buf.flags |= VENC_FLAG_END_OF_FRAME;
+ if (pload2->flags & VENC_FLAG_EOS)
+ data.buf.flags |= VENC_FLAG_EOS;
+ data.buf.len = pload2->size;
+ data.buf.offset = 0;
+ data.buf.time_stamp = pload2->time_stamp;
+ data.buf.client_data = pload2->data;
+ venc_post(dvenc, VENC_MSG_OUTPUT_BUFFER_DONE, status, &data);
+ break;
+ default:
+ pr_err("%s: invalid response from Q6 (%d)\n", __func__,
+ (int)q6_msg->event);
+ break;
+ }
+}
+
+static int venc_read_next_msg(struct venc_dev *dvenc, void __user *argp)
+{
+ int res;
+ struct venc_qmsg *msg;
+
+ res = wait_event_interruptible(dvenc->venc_msg_evt,
+ (msg = venc_recv_msg(dvenc)) != NULL);
+ if (res < 0)
+ return res;
+ res = copy_to_user(argp, &msg->msg, sizeof(struct venc_msg));
+ venc_free_msg(dvenc, msg);
+ if (res)
+ return -EFAULT;
+ return 0;
+}
+
+static long q6venc_ioctl(struct file *file, u32 cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct venc_dev *dvenc = file->private_data;
+
+ switch (cmd) {
+ case VENC_IOCTL_SET_INPUT_BUFFER:
+ return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT);
+ case VENC_IOCTL_SET_OUTPUT_BUFFER:
+ return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT);
+ case VENC_IOCTL_GET_SEQUENCE_HDR:
+ return venc_get_sequence_hdr(dvenc, argp);
+ case VENC_IOCTL_SET_QP_RANGE:
+ return venc_set_qp_range(dvenc, argp);
+ case VENC_IOCTL_SET_INTRA_PERIOD:
+ return venc_set_intra_period(dvenc, argp);
+ case VENC_IOCTL_SET_INTRA_REFRESH:
+ return venc_set_intra_refresh(dvenc, argp);
+ case VENC_IOCTL_SET_FRAME_RATE:
+ return venc_set_frame_rate(dvenc, argp);
+ case VENC_IOCTL_SET_TARGET_BITRATE:
+ return venc_set_target_bitrate(dvenc, argp);
+ case VENC_IOCTL_CMD_REQUEST_IFRAME:
+ if (dvenc->state == VENC_STATE_START)
+ return venc_request_iframe(dvenc);
+ else
+ return 0;
+ case VENC_IOCTL_CMD_START:
+ return venc_start(dvenc, argp);
+ case VENC_IOCTL_CMD_STOP:
+ return venc_stop(dvenc);
+ case VENC_IOCTL_CMD_PAUSE:
+ return venc_pause(dvenc);
+ case VENC_IOCTL_CMD_RESUME:
+ return venc_resume(dvenc);
+ case VENC_IOCTL_CMD_ENCODE_FRAME:
+ return venc_encode_frame(dvenc, argp);
+ case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER:
+ return venc_fill_output(dvenc, argp);
+ case VENC_IOCTL_CMD_FLUSH:
+ return venc_flush(dvenc, argp);
+ case VENC_IOCTL_CMD_READ_NEXT_MSG:
+ return venc_read_next_msg(dvenc, argp);
+ case VENC_IOCTL_CMD_STOP_READ_MSG:
+ return venc_stop_read_msg(dvenc);
+ default:
+ pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd);
+ return -EINVAL;
+ }
+}
+
+static int q6venc_open(struct inode *inode, struct file *file)
+{
+ int i;
+ int ret = 0;
+ struct venc_dev *dvenc;
+ struct venc_qmsg *msg;
+#if VERSION_CHECK
+ struct dal_info version_info;
+#endif
+
+ dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL);
+ if (!dvenc)
+ return -ENOMEM;
+
+ file->private_data = dvenc;
+ INIT_LIST_HEAD(&dvenc->msg_pool);
+ INIT_LIST_HEAD(&dvenc->msg_queue);
+ INIT_LIST_HEAD(&dvenc->venc_pmem_list_head);
+ init_waitqueue_head(&dvenc->venc_msg_evt);
+ spin_lock_init(&dvenc->msg_lock);
+ spin_lock_init(&dvenc->venc_pmem_list_lock);
+ venc_ref++;
+
+ for (i = 0; i < VENC_MSG_MAX; i++) {
+ msg = kzalloc(sizeof(struct venc_qmsg), GFP_KERNEL);
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto fail_list_alloc;
+ }
+ venc_free_msg(dvenc, msg);
+ }
+
+ dvenc->q6_handle = dal_attach(DALDEVICEID_VENC_DEVICE,
+ DALDEVICEID_VENC_PORTNAME,
+ venc_q6_callback, (void *)dvenc);
+
+ if (!(dvenc->q6_handle)) {
+ pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret);
+ goto fail_list_alloc;
+ }
+
+#if VERSION_CHECK
+ ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info,
+ sizeof(struct dal_info));
+ if (ret) {
+ pr_err("%s: failed to get version\n", __func__);
+ ret = -EINVAL;
+ goto fail_open;
+ }
+ if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) {
+ pr_err("%s: driver version mismatch\n", __func__);
+ ret = -EINVAL;
+ goto fail_open;
+ }
+#endif
+ ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1);
+ if (ret) {
+ pr_err("%s: dal_call_open failed (%d)\n", __func__, ret);
+ goto fail_open;
+ }
+ dvenc->state = VENC_STATE_STOP;
+ prevent_sleep();
+ return ret;
+
+fail_open:
+ dal_detach(dvenc->q6_handle);
+
+fail_list_alloc:
+ while ((msg = venc_alloc_msg(dvenc)))
+ kfree(msg);
+
+ kfree(dvenc);
+ venc_ref--;
+ return ret;
+}
+
+static int q6venc_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct venc_pmem_list *plist, *m;
+ struct venc_dev *dvenc;
+ struct venc_qmsg *msg;
+
+ venc_ref--;
+
+ dvenc = file->private_data;
+ wake_up_all(&dvenc->venc_msg_evt);
+ if (!dvenc->stop_called)
+ dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1);
+ dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1);
+ dal_detach(dvenc->q6_handle);
+
+
+ /* free all messages in the pool */
+ while ((msg = venc_alloc_msg(dvenc)))
+ kfree(msg);
+
+ /* free all messages sitting in the queue */
+ while ((msg = venc_recv_msg(dvenc)))
+ kfree(msg);
+
+ list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) {
+ put_pmem_file(plist->buf.file);
+ list_del(&plist->list);
+ kfree(plist);
+ }
+ kfree(dvenc);
+ allow_sleep();
+ return ret;
+}
+
+const struct file_operations q6venc_fops = {
+ .owner = THIS_MODULE,
+ .open = q6venc_open,
+ .release = q6venc_release,
+ .unlocked_ioctl = q6venc_ioctl,
+};
+
+static int __init q6venc_init(void)
+{
+ int ret = 0;
+
+ venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL);
+ if (!venc_device_p) {
+ pr_err("%s: unable to allocate memory for venc_device_p\n",
+ __func__);
+ return -ENOMEM;
+ }
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend");
+
+ ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME);
+ if (ret < 0) {
+ pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__,
+ ret);
+ return ret;
+ }
+ venc_class = class_create(THIS_MODULE, VENC_NAME);
+ if (IS_ERR(venc_class)) {
+ ret = PTR_ERR(venc_class);
+ pr_err("%s: failed to create venc_class (%d)\n",
+ __func__, ret);
+ goto err_venc_class_create;
+ }
+ venc_device_p->class_devp =
+ device_create(venc_class, NULL, venc_dev_num, NULL,
+ VENC_NAME);
+ if (IS_ERR(venc_device_p->class_devp)) {
+ ret = PTR_ERR(venc_device_p->class_devp);
+ pr_err("%s: failed to create class_device (%d)\n", __func__,
+ ret);
+ goto err_venc_class_device_create;
+ }
+ cdev_init(&cdev, &q6venc_fops);
+ cdev.owner = THIS_MODULE;
+ ret = cdev_add(&cdev, venc_dev_num, 1);
+ if (ret < 0) {
+ pr_err("%s: cdev_add failed (%d)\n", __func__, ret);
+ goto err_venc_cdev_add;
+ }
+ init_waitqueue_head(&venc_device_p->venc_msg_evt);
+ return ret;
+
+err_venc_cdev_add:
+ device_destroy(venc_class, venc_dev_num);
+err_venc_class_device_create:
+ class_destroy(venc_class);
+err_venc_class_create:
+ unregister_chrdev_region(venc_dev_num, 1);
+ return ret;
+}
+
+static void __exit q6venc_exit(void)
+{
+ cdev_del(&(cdev));
+ device_destroy(venc_class, venc_dev_num);
+ class_destroy(venc_class);
+ unregister_chrdev_region(venc_dev_num, 1);
+}
+
+MODULE_DESCRIPTION("Video encoder driver for QDSP6");
+MODULE_VERSION("2.0");
+module_init(q6venc_init);
+module_exit(q6venc_exit);
diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c
new file mode 100644
index 0000000..d5a8f77
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/pcm_in.c
@@ -0,0 +1,219 @@
+/* arch/arm/mach-msm/qdsp6/pcm_in.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 HTC 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+
+#define BUFSZ (256)
+
+static DEFINE_MUTEX(pcm_in_lock);
+static uint32_t sample_rate = 8000;
+static uint32_t channel_count = 1;
+static uint32_t buffer_size = BUFSZ;
+static int pcm_in_opened = 0;
+
+void audio_client_dump(struct audio_client *ac);
+
+static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_SET_VOLUME:
+ break;
+ case AUDIO_GET_STATS: {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ rc = 0;
+
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_lock(&pcm_in_lock);
+ if (file->private_data) {
+ rc = -EBUSY;
+ } else {
+ file->private_data = q6audio_open_pcm(
+ buffer_size, sample_rate, channel_count,
+ AUDIO_FLAG_READ, acdb_id);
+ if (!file->private_data)
+ rc = -ENOMEM;
+ }
+ mutex_unlock(&pcm_in_lock);
+ break;
+ }
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (!config.channel_count || config.channel_count > 2) {
+ rc = -EINVAL;
+ break;
+ }
+ if (config.sample_rate < 8000 || config.sample_rate > 48000) {
+ rc = -EINVAL;
+ break;
+ }
+ if (config.buffer_size < 128 || config.buffer_size > 8192) {
+ rc = -EINVAL;
+ break;
+ }
+ sample_rate = config.sample_rate;
+ channel_count = config.channel_count;
+ buffer_size = config.buffer_size;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = buffer_size;
+ config.buffer_count = 2;
+ config.sample_rate = sample_rate;
+ config.channel_count = channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int q6_in_open(struct inode *inode, struct file *file)
+{
+ int rc;
+
+ pr_info("pcm_in: open\n");
+ mutex_lock(&pcm_in_lock);
+ if (pcm_in_opened) {
+ pr_err("pcm_in: busy\n");
+ rc = -EBUSY;
+ } else {
+ pcm_in_opened = 1;
+ rc = 0;
+ }
+ mutex_unlock(&pcm_in_lock);
+ return rc;
+}
+
+static ssize_t q6_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+ int res;
+
+ mutex_lock(&pcm_in_lock);
+ ac = file->private_data;
+ if (!ac) {
+ res = -ENODEV;
+ goto fail;
+ }
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
+ audio_client_dump(ac);
+ pr_err("pcm_read: timeout. dsp dead?\n");
+ q6audio_dsp_not_responding();
+ }
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_to_user(buf, ab->data, xfer)) {
+ res = -EFAULT;
+ goto fail;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = 1;
+ q6audio_read(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+fail:
+ res = buf - start;
+ mutex_unlock(&pcm_in_lock);
+
+ return res;
+}
+
+static int q6_in_release(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ mutex_lock(&pcm_in_lock);
+ if (file->private_data)
+ rc = q6audio_close(file->private_data);
+ pcm_in_opened = 0;
+ mutex_unlock(&pcm_in_lock);
+ pr_info("pcm_in: release\n");
+ return rc;
+}
+
+static struct file_operations q6_in_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_in_open,
+ .read = q6_in_read,
+ .release = q6_in_release,
+ .unlocked_ioctl = q6_in_ioctl,
+};
+
+struct miscdevice q6_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &q6_in_fops,
+};
+
+static int __init q6_in_init(void) {
+ return misc_register(&q6_in_misc);
+}
+
+device_initcall(q6_in_init);
diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c
new file mode 100644
index 0000000..6d041d8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/pcm_out.c
@@ -0,0 +1,235 @@
+/* arch/arm/mach-msm/qdsp6/pcm_out.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+
+void audio_client_dump(struct audio_client *ac);
+
+#define BUFSZ (3072)
+
+struct pcm {
+ struct mutex lock;
+ struct audio_client *ac;
+ uint32_t sample_rate;
+ uint32_t channel_count;
+ size_t buffer_size;
+};
+
+static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pcm *pcm = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&pcm->lock);
+ switch (cmd) {
+ case AUDIO_SET_VOLUME: {
+ int vol;
+ if (!pcm->ac) {
+ pr_err("%s: cannot set volume before AUDIO_START!\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&vol, (void*) arg, sizeof(vol))) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6audio_set_stream_volume(pcm->ac, vol);
+ break;
+ }
+ case AUDIO_START: {
+ uint32_t acdb_id;
+ if (arg == 0) {
+ acdb_id = 0;
+ } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) {
+ pr_info("pcm_out: copy acdb_id from user failed\n");
+ rc = -EFAULT;
+ break;
+ }
+ if (pcm->ac) {
+ rc = -EBUSY;
+ } else {
+ pcm->ac = q6audio_open_pcm(pcm->buffer_size, pcm->sample_rate,
+ pcm->channel_count,
+ AUDIO_FLAG_WRITE, acdb_id);
+ if (!pcm->ac)
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case AUDIO_STOP:
+ break;
+ case AUDIO_FLUSH:
+ break;
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (pcm->ac) {
+ rc = -EBUSY;
+ break;
+ }
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count < 1 || config.channel_count > 2) {
+ rc = -EINVAL;
+ break;
+ }
+ if (config.sample_rate < 8000 || config.sample_rate > 48000) {
+ rc = -EINVAL;
+ break;
+ }
+ if (config.buffer_size < 128 || config.buffer_size > 8192) {
+ rc = -EINVAL;
+ break;
+ }
+ pcm->sample_rate = config.sample_rate;
+ pcm->channel_count = config.channel_count;
+ pcm->buffer_size = config.buffer_size;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = pcm->buffer_size;
+ config.buffer_count = 2;
+ config.sample_rate = pcm->sample_rate;
+ config.channel_count = pcm->channel_count;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&pcm->lock);
+ return rc;
+}
+
+static int pcm_open(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm;
+
+ pr_info("pcm_out: open\n");
+ pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
+
+ if (!pcm)
+ return -ENOMEM;
+
+ mutex_init(&pcm->lock);
+ pcm->channel_count = 2;
+ pcm->sample_rate = 44100;
+ pcm->buffer_size = BUFSZ;
+
+ file->private_data = pcm;
+ return 0;
+}
+
+static ssize_t pcm_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct pcm *pcm = file->private_data;
+ struct audio_client *ac;
+ struct audio_buffer *ab;
+ const char __user *start = buf;
+ int xfer;
+
+ if (!pcm->ac)
+ pcm_ioctl(file, AUDIO_START, 0);
+
+ ac = pcm->ac;
+ if (!ac)
+ return -ENODEV;
+
+ while (count > 0) {
+ ab = ac->buf + ac->cpu_buf;
+
+ if (ab->used)
+ if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) {
+ audio_client_dump(ac);
+ pr_err("pcm_write: timeout. dsp dead?\n");
+ q6audio_dsp_not_responding();
+ }
+
+ xfer = count;
+ if (xfer > ab->size)
+ xfer = ab->size;
+
+ if (copy_from_user(ab->data, buf, xfer))
+ return -EFAULT;
+
+ buf += xfer;
+ count -= xfer;
+
+ ab->used = xfer;
+ q6audio_write(ac, ab);
+ ac->cpu_buf ^= 1;
+ }
+
+ return buf - start;
+}
+
+static int pcm_release(struct inode *inode, struct file *file)
+{
+ struct pcm *pcm = file->private_data;
+ if (pcm->ac)
+ q6audio_close(pcm->ac);
+ kfree(pcm);
+ pr_info("pcm_out: release\n");
+ return 0;
+}
+
+static struct file_operations pcm_fops = {
+ .owner = THIS_MODULE,
+ .open = pcm_open,
+ .write = pcm_write,
+ .release = pcm_release,
+ .unlocked_ioctl = pcm_ioctl,
+};
+
+struct miscdevice pcm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &pcm_fops,
+};
+
+static int __init pcm_init(void) {
+ return misc_register(&pcm_misc);
+}
+
+device_initcall(pcm_init);
diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c
new file mode 100644
index 0000000..1b053e7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/q6audio.c
@@ -0,0 +1,1574 @@
+/* arch/arm/mach-msm/qdsp6/q6audio.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/firmware.h>
+#include <linux/miscdevice.h>
+
+#include "../dal.h"
+#include "dal_audio.h"
+#include "dal_audio_format.h"
+#include "dal_acdb.h"
+#include "dal_adie.h"
+#include <mach/msm_qdsp6_audio.h>
+
+#include <linux/gpio.h>
+
+#include "q6audio_devices.h"
+
+#if 0
+#define TRACE(x...) pr_info("Q6: "x)
+#else
+#define TRACE(x...) do{}while(0)
+#endif
+
+static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = {
+ [Q6_HW_HANDSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_HEADSET] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_SPEAKER] = {
+ .min_gain = -1500,
+ .max_gain = 0,
+ },
+ [Q6_HW_TTY] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_SCO] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+ [Q6_HW_BT_A2DP] = {
+ .min_gain = -2000,
+ .max_gain = 0,
+ },
+};
+
+static struct wake_lock wakelock;
+static struct wake_lock idlelock;
+static int idlecount;
+static DEFINE_MUTEX(idlecount_lock);
+
+void audio_prevent_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (++idlecount == 1) {
+ wake_lock(&wakelock);
+ wake_lock(&idlelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+void audio_allow_sleep(void)
+{
+ mutex_lock(&idlecount_lock);
+ if (--idlecount == 0) {
+ wake_unlock(&idlelock);
+ wake_unlock(&wakelock);
+ }
+ mutex_unlock(&idlecount_lock);
+}
+
+static struct clk *icodec_rx_clk;
+static struct clk *icodec_tx_clk;
+static struct clk *ecodec_clk;
+static struct clk *sdac_clk;
+
+static struct q6audio_analog_ops default_analog_ops;
+static struct q6audio_analog_ops *analog_ops = &default_analog_ops;
+static uint32_t tx_clk_freq = 8000;
+static int tx_mute_status = 0;
+static int rx_vol_level = 100;
+static char acdb_file[64] = "default.acdb";
+static uint32_t tx_acdb = 0;
+static uint32_t rx_acdb = 0;
+
+void q6audio_register_analog_ops(struct q6audio_analog_ops *ops)
+{
+ analog_ops = ops;
+}
+
+void q6audio_set_acdb_file(char* filename)
+{
+ if (filename)
+ strncpy(acdb_file, filename, sizeof(acdb_file)-1);
+}
+
+static struct q6_device_info *q6_lookup_device(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_audio_devices;
+ for (;;) {
+ if (di->id == device_id)
+ return di;
+ if (di->id == 0) {
+ pr_err("q6_lookup_device: bogus id 0x%08x\n",
+ device_id);
+ return di;
+ }
+ di++;
+ }
+}
+
+static uint32_t q6_device_to_codec(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->codec;
+}
+
+static uint32_t q6_device_to_dir(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->dir;
+}
+
+static uint32_t q6_device_to_cad_id(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->cad_id;
+}
+
+static uint32_t q6_device_to_path(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->path;
+}
+
+static uint32_t q6_device_to_rate(uint32_t device_id)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ return di->rate;
+}
+
+int q6_device_volume(uint32_t device_id, int level)
+{
+ struct q6_device_info *di = q6_lookup_device(device_id);
+ if (analog_ops->get_rx_vol)
+ return analog_ops->get_rx_vol(di->hw, level);
+ else {
+ struct q6_hw_info *hw;
+ hw = &q6_audio_hw[di->hw];
+ return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100;
+ }
+}
+
+static inline int adie_open(struct dal_client *client)
+{
+ return dal_call_f0(client, DAL_OP_OPEN, 0);
+}
+
+static inline int adie_close(struct dal_client *client)
+{
+ return dal_call_f0(client, DAL_OP_CLOSE, 0);
+}
+
+static inline int adie_set_path(struct dal_client *client,
+ uint32_t id, uint32_t path_type)
+{
+ return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type);
+}
+
+static inline int adie_set_path_freq_plan(struct dal_client *client,
+ uint32_t path_type, uint32_t plan)
+{
+ return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN,
+ path_type, plan);
+}
+
+static inline int adie_proceed_to_stage(struct dal_client *client,
+ uint32_t path_type, uint32_t stage)
+{
+ return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE,
+ path_type, stage);
+}
+
+static inline int adie_mute_path(struct dal_client *client,
+ uint32_t path_type, uint32_t mute_state)
+{
+ return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state);
+}
+
+static int adie_refcount;
+
+static struct dal_client *adie;
+static struct dal_client *adsp;
+static struct dal_client *acdb;
+
+static int adie_enable(void)
+{
+ adie_refcount++;
+ if (adie_refcount == 1)
+ adie_open(adie);
+ return 0;
+}
+
+static int adie_disable(void)
+{
+ adie_refcount--;
+ if (adie_refcount == 0)
+ adie_close(adie);
+ return 0;
+}
+
+/* 4k DMA scratch page used for exchanging acdb device config tables
+ * and stream format descriptions with the DSP.
+ */
+static void *audio_data;
+static dma_addr_t audio_phys;
+
+#define SESSION_MIN 0
+#define SESSION_MAX 64
+
+static DEFINE_MUTEX(session_lock);
+static DEFINE_MUTEX(audio_lock);
+
+static struct audio_client *session[SESSION_MAX];
+
+static int session_alloc(struct audio_client *ac)
+{
+ int n;
+
+ mutex_lock(&session_lock);
+ for (n = SESSION_MIN; n < SESSION_MAX; n++) {
+ if (!session[n]) {
+ session[n] = ac;
+ mutex_unlock(&session_lock);
+ return n;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void session_free(int n, struct audio_client *ac)
+{
+ mutex_lock(&session_lock);
+ if (session[n] == ac)
+ session[n] = 0;
+ mutex_unlock(&session_lock);
+}
+
+static void audio_client_free(struct audio_client *ac)
+{
+ session_free(ac->session, ac);
+
+ if (ac->buf[0].data)
+ dma_free_coherent(NULL, ac->buf[0].size,
+ ac->buf[0].data, ac->buf[0].phys);
+ if (ac->buf[1].data)
+ dma_free_coherent(NULL, ac->buf[1].size,
+ ac->buf[1].data, ac->buf[1].phys);
+ kfree(ac);
+}
+
+static struct audio_client *audio_client_alloc(unsigned bufsz)
+{
+ struct audio_client *ac;
+ int n;
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return 0;
+
+ n = session_alloc(ac);
+ if (n < 0)
+ goto fail_session;
+ ac->session = n;
+
+ if (bufsz > 0) {
+ ac->buf[0].data = dma_alloc_coherent(NULL, bufsz,
+ &ac->buf[0].phys, GFP_KERNEL);
+ if (!ac->buf[0].data)
+ goto fail;
+ ac->buf[1].data = dma_alloc_coherent(NULL, bufsz,
+ &ac->buf[1].phys, GFP_KERNEL);
+ if (!ac->buf[1].data)
+ goto fail;
+
+ ac->buf[0].size = bufsz;
+ ac->buf[1].size = bufsz;
+ }
+
+ init_waitqueue_head(&ac->wait);
+ ac->client = adsp;
+
+ return ac;
+
+fail:
+ session_free(n, ac);
+fail_session:
+ audio_client_free(ac);
+ return 0;
+}
+
+void audio_client_dump(struct audio_client *ac)
+{
+ dal_trace_dump(ac->client);
+}
+
+static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len)
+{
+ struct adsp_command_hdr *hdr = ptr;
+ uint32_t tmp;
+ int r;
+
+ hdr->size = len - sizeof(u32);
+ hdr->dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ hdr->src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM);
+ hdr->context = ac->session;
+ ac->cb_status = -EBUSY;
+ r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len, &tmp, sizeof(tmp));
+ if (r != 4)
+ return -EIO;
+ if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) {
+ dal_trace_dump(ac->client);
+ pr_err("audio_ioctl: timeout. dsp dead?\n");
+ q6audio_dsp_not_responding();
+ }
+ return ac->cb_status;
+}
+
+static int audio_command(struct audio_client *ac, uint32_t cmd)
+{
+ struct adsp_command_hdr rpc;
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = cmd;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_open_control(struct audio_client *ac)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_out_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.buf_max_size = bufsz;
+
+ TRACE("open out %p\n", ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_in_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 1;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD;
+ rpc.buf_max_size = bufsz;
+
+ TRACE("%p: open in\n", ac);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz,
+ uint32_t rate, uint32_t channels)
+{
+ struct adsp_open_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+
+ rpc.format.standard.format = ADSP_AUDIO_FORMAT_MP3;
+ rpc.format.standard.channels = channels;
+ rpc.format.standard.bits_per_sample = 16;
+ rpc.format.standard.sampling_rate = rate;
+ rpc.format.standard.is_signed = 1;
+ rpc.format.standard.is_interleaved = 0;
+
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE;
+ rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT;
+ rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK;
+ rpc.buf_max_size = bufsz;
+
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_close(struct audio_client *ac)
+{
+ TRACE("%p: close\n", ac);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE);
+ return 0;
+}
+
+static int audio_set_table(struct audio_client *ac,
+ uint32_t device_id, int size)
+{
+ struct adsp_set_dev_cfg_table_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE;
+ if (q6_device_to_dir(device_id) == Q6_TX)
+ rpc.hdr.data = tx_clk_freq;
+ rpc.device_id = device_id;
+ rpc.phys_addr = audio_phys;
+ rpc.phys_size = size;
+ rpc.phys_used = size;
+
+ TRACE("control: set table %x\n", device_id);
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+int q6audio_read(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->used;
+
+ TRACE("%p: read\n", ac);
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+ return 0;
+}
+
+int q6audio_write(struct audio_client *ac, struct audio_buffer *ab)
+{
+ struct adsp_buffer_command rpc;
+ uint32_t res;
+ int r;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.size = sizeof(rpc) - sizeof(u32);
+ rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP);
+ rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM);
+ rpc.hdr.context = ac->session;
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX;
+ rpc.buffer.addr = ab->phys;
+ rpc.buffer.max_size = ab->size;
+ rpc.buffer.actual_size = ab->used;
+
+ TRACE("%p: write\n", ac);
+ r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc),
+ &res, sizeof(res));
+ return 0;
+}
+
+static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume)
+{
+ struct adsp_set_dev_volume_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.volume = volume;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_RX;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume)
+{
+ struct adsp_set_dev_volume_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_TX;
+ rpc.volume = volume;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute)
+{
+ struct adsp_set_dev_mute_command rpc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE;
+ rpc.device_id = dev_id;
+ rpc.path = ADSP_PATH_TX;
+ rpc.mute = !!mute;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int audio_stream_volume(struct audio_client *ac, int volume)
+{
+ struct adsp_set_volume_command rpc;
+ int rc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL;
+ rpc.volume = volume;
+ rc = audio_ioctl(ac, &rpc, sizeof(rpc));
+ return rc;
+}
+
+static int audio_stream_mute(struct audio_client *ac, int mute)
+{
+ struct adsp_set_mute_command rpc;
+ int rc;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE;
+ rpc.mute = mute;
+ rc = audio_ioctl(ac, &rpc, sizeof(rpc));
+ return rc;
+}
+
+static void callback(void *data, int len, void *cookie)
+{
+ struct adsp_event_hdr *e = data;
+ struct audio_client *ac;
+
+
+ if (e->context >= SESSION_MAX) {
+ pr_err("audio callback: bogus session %d\n",
+ e->context);
+ return;
+ }
+ ac = session[e->context];
+ if (!ac) {
+ pr_err("audio callback: unknown session %d\n",
+ e->context);
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) {
+ TRACE("%p: CB stream eos\n", ac);
+ if (e->status)
+ pr_err("playback status %d\n", e->status);
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+ return;
+ }
+
+ if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) {
+ TRACE("%p: CB done (%d)\n", ac, e->status);
+ if (e->status)
+ pr_err("buffer status %d\n", e->status);
+ ac->buf[ac->dsp_buf].used = 0;
+ ac->dsp_buf ^= 1;
+ wake_up(&ac->wait);
+ return;
+ }
+
+ TRACE("%p: CB %08x status %d\n", ac, e->event_id, e->status);
+ if (e->status)
+ pr_warning("audio_cb: s=%d e=%08x status=%d\n",
+ e->context, e->event_id, e->status);
+ if (ac->cb_status == -EBUSY) {
+ ac->cb_status = e->status;
+ wake_up(&ac->wait);
+ }
+}
+
+static void audio_init(struct dal_client *client)
+{
+ u32 tmp[3];
+
+ tmp[0] = 2 * sizeof(u32);
+ tmp[1] = 1;
+ tmp[2] = 0;
+ dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp),
+ tmp, sizeof(u32));
+}
+
+static struct audio_client *ac_control;
+
+static int q6audio_init(void)
+{
+ struct audio_client *ac = 0;
+ int res;
+
+ mutex_lock(&audio_lock);
+ if (ac_control) {
+ res = 0;
+ goto done;
+ }
+
+ pr_info("audio: init: codecs\n");
+ icodec_rx_clk = clk_get(0, "icodec_rx_clk");
+ icodec_tx_clk = clk_get(0, "icodec_tx_clk");
+ ecodec_clk = clk_get(0, "ecodec_clk");
+ sdac_clk = clk_get(0, "sdac_clk");
+ audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL);
+
+ adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT,
+ callback, 0);
+ if (!adsp) {
+ pr_err("audio_init: cannot attach to adsp\n");
+ res = -ENODEV;
+ goto done;
+ }
+ pr_info("audio: init: INIT\n");
+ audio_init(adsp);
+ dal_trace(adsp);
+
+ ac = audio_client_alloc(0);
+ if (!ac) {
+ pr_err("audio_init: cannot allocate client\n");
+ res = -ENOMEM;
+ goto done;
+ }
+
+ pr_info("audio: init: OPEN control\n");
+ if (audio_open_control(ac)) {
+ pr_err("audio_init: cannot open control channel\n");
+ res = -ENODEV;
+ goto done;
+ }
+
+ pr_info("audio: init: attach ACDB\n");
+ acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0);
+ if (!acdb) {
+ pr_err("audio_init: cannot attach to acdb channel\n");
+ res = -ENODEV;
+ goto done;
+ }
+
+ pr_info("audio: init: attach ADIE\n");
+ adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0);
+ if (!adie) {
+ pr_err("audio_init: cannot attach to adie\n");
+ res = -ENODEV;
+ goto done;
+ }
+ if (analog_ops->init)
+ analog_ops->init();
+
+ res = 0;
+ ac_control = ac;
+
+ wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+ wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend");
+done:
+ if ((res < 0) && ac)
+ audio_client_free(ac);
+ mutex_unlock(&audio_lock);
+
+ return res;
+}
+
+struct audio_config_data {
+ uint32_t device_id;
+ uint32_t sample_rate;
+ uint32_t offset;
+ uint32_t length;
+};
+
+struct audio_config_database {
+ uint8_t magic[8];
+ uint32_t entry_count;
+ uint32_t unused;
+ struct audio_config_data entry[0];
+};
+
+void *acdb_data;
+const struct firmware *acdb_fw;
+extern struct miscdevice q6_control_device;
+
+static int acdb_init(char *filename)
+{
+ const struct audio_config_database *db;
+ const struct firmware *fw;
+ int n;
+
+ pr_info("acdb: load '%s'\n", filename);
+ if (request_firmware(&fw, filename, q6_control_device.this_device) < 0) {
+ pr_err("acdb: load 'default.acdb' failed...\n");
+ return -ENODEV;
+ }
+ db = (void*) fw->data;
+
+ if (fw->size < sizeof(struct audio_config_database)) {
+ pr_err("acdb: undersized database\n");
+ goto fail;
+ }
+ if (strcmp(db->magic, "ACDB1.0")) {
+ pr_err("acdb: invalid magic\n");
+ goto fail;
+ }
+ if (db->entry_count > 1024) {
+ pr_err("acdb: too many entries\n");
+ goto fail;
+ }
+ if (fw->size < (sizeof(struct audio_config_database) +
+ db->entry_count * sizeof(struct audio_config_data))) {
+ pr_err("acdb: undersized TOC\n");
+ goto fail;
+ }
+ for (n = 0; n < db->entry_count; n++) {
+ if (db->entry[n].length > 4096) {
+ pr_err("acdb: entry %d too large (%d)\n",
+ n, db->entry[n].length);
+ goto fail;
+ }
+ if ((db->entry[n].offset + db->entry[n].length) > fw->size) {
+ pr_err("acdb: entry %d outside of data\n", n);
+ goto fail;
+ }
+ }
+ if (acdb_data)
+ release_firmware(acdb_fw);
+ acdb_data = (void*) fw->data;
+ acdb_fw = fw;
+ return 0;
+fail:
+ release_firmware(fw);
+ return -ENODEV;
+}
+
+static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate)
+{
+ struct audio_config_database *db;
+ int n, res;
+
+ if (q6audio_init())
+ return 0;
+
+ if (!acdb_data) {
+ res = acdb_init(acdb_file);
+ if (res)
+ return res;
+ }
+
+ db = acdb_data;
+ for (n = 0; n < db->entry_count; n++) {
+ if (db->entry[n].device_id != device_id)
+ continue;
+ if (db->entry[n].sample_rate != sample_rate)
+ continue;
+ break;
+ }
+
+ if (n == db->entry_count) {
+ pr_err("acdb: no entry for device %d, rate %d.\n",
+ device_id, sample_rate);
+ return 0;
+ }
+
+ pr_info("acdb: %d bytes for device %d, rate %d.\n",
+ db->entry[n].length, device_id, sample_rate);
+
+ memcpy(audio_data, acdb_data + db->entry[n].offset, db->entry[n].length);
+ return db->entry[n].length;
+}
+
+static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX;
+static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR;
+static uint32_t audio_rx_device_group = -1;
+static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX;
+static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC;
+static uint32_t audio_tx_device_group = -1;
+
+static int qdsp6_devchg_notify(struct audio_client *ac,
+ uint32_t dev_type, uint32_t dev_id)
+{
+ struct adsp_device_switch_command rpc;
+
+ if (dev_type != ADSP_AUDIO_RX_DEVICE &&
+ dev_type != ADSP_AUDIO_TX_DEVICE)
+ return -EINVAL;
+
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE;
+ if (dev_type == ADSP_AUDIO_RX_DEVICE) {
+ rpc.old_device = audio_rx_device_id;
+ rpc.new_device = dev_id;
+ } else {
+ rpc.old_device = audio_tx_device_id;
+ rpc.new_device = dev_id;
+ }
+ rpc.device_class = 0;
+ rpc.device_type = dev_type;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
+
+static int qdsp6_standby(struct audio_client *ac)
+{
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY);
+}
+
+static int qdsp6_start(struct audio_client *ac)
+{
+ return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT);
+}
+
+static void audio_rx_analog_enable(int en)
+{
+ switch (audio_rx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO:
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET:
+ if (analog_ops->headset_enable)
+ analog_ops->headset_enable(en);
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO:
+ if (analog_ops->speaker_enable)
+ analog_ops->speaker_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR:
+ if (analog_ops->receiver_enable)
+ analog_ops->receiver_enable(en);
+ break;
+ }
+}
+
+static void audio_tx_analog_enable(int en)
+{
+ switch (audio_tx_device_id) {
+ case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC:
+ if (analog_ops->int_mic_enable)
+ analog_ops->int_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC:
+ case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC:
+ if (analog_ops->ext_mic_enable)
+ analog_ops->ext_mic_enable(en);
+ break;
+ case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC:
+ if (analog_ops->bt_sco_enable)
+ analog_ops->bt_sco_enable(en);
+ break;
+ }
+}
+
+static int audio_update_acdb(uint32_t adev, uint32_t acdb_id)
+{
+ uint32_t sample_rate;
+ int sz = -1;
+
+ sample_rate = q6_device_to_rate(adev);
+
+ if (q6_device_to_dir(adev) == Q6_RX)
+ rx_acdb = acdb_id;
+ else
+ tx_acdb = acdb_id;
+
+ if (acdb_id != 0)
+ sz = acdb_get_config_table(acdb_id, sample_rate);
+
+ if (sz <= 0) {
+ acdb_id = q6_device_to_cad_id(adev);
+ sz = acdb_get_config_table(acdb_id, sample_rate);
+ if (sz <= 0)
+ return -EINVAL;
+ }
+
+ audio_set_table(ac_control, adev, sz);
+ return 0;
+}
+
+static void _audio_rx_path_enable(int reconf, uint32_t acdb_id)
+{
+ adie_enable();
+ adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX);
+ adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY);
+
+ audio_update_acdb(audio_rx_device_id, acdb_id);
+ if (!reconf)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+
+ audio_rx_analog_enable(1);
+}
+
+static void _audio_tx_path_enable(int reconf, uint32_t acdb_id)
+{
+ audio_tx_analog_enable(1);
+
+ adie_enable();
+ adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX);
+
+ if (tx_clk_freq > 8000)
+ adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000);
+ else
+ adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_ANALOG_READY);
+
+ audio_update_acdb(audio_tx_device_id, acdb_id);
+
+ if (!reconf)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+
+ audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status);
+}
+
+static void _audio_rx_path_disable(void)
+{
+ audio_rx_analog_enable(0);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF);
+ adie_disable();
+}
+
+static void _audio_tx_path_disable(void)
+{
+ audio_tx_analog_enable(0);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF);
+ adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF);
+ adie_disable();
+}
+
+static int icodec_rx_clk_refcount;
+static int icodec_tx_clk_refcount;
+static int ecodec_clk_refcount;
+static int sdac_clk_refcount;
+
+static void _audio_rx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_rx_device_id);
+
+ switch(device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount++;
+ if (icodec_rx_clk_refcount == 1) {
+ clk_set_rate(icodec_rx_clk, 12288000);
+ clk_enable(icodec_rx_clk);
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_refcount++;
+ if (ecodec_clk_refcount == 1) {
+ clk_set_rate(ecodec_clk, 2048000);
+ clk_enable(ecodec_clk);
+ }
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_rx_device_group = device_group;
+}
+
+static void _audio_tx_clk_enable(void)
+{
+ uint32_t device_group = q6_device_to_codec(audio_tx_device_id);
+
+ switch (device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount++;
+ if (icodec_tx_clk_refcount == 1) {
+ clk_set_rate(icodec_tx_clk, tx_clk_freq * 256);
+ clk_enable(icodec_tx_clk);
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_refcount++;
+ if (ecodec_clk_refcount == 1) {
+ clk_set_rate(ecodec_clk, 2048000);
+ clk_enable(ecodec_clk);
+ }
+ break;
+ case Q6_SDAC_TX:
+ /* TODO: In QCT BSP, clk rate was set to 20480000 */
+ sdac_clk_refcount++;
+ if (sdac_clk_refcount == 1) {
+ clk_set_rate(sdac_clk, 12288000);
+ clk_enable(sdac_clk);
+ }
+ break;
+ default:
+ return;
+ }
+ audio_tx_device_group = device_group;
+}
+
+static void _audio_rx_clk_disable(void)
+{
+ switch (audio_rx_device_group) {
+ case Q6_ICODEC_RX:
+ icodec_rx_clk_refcount--;
+ if (icodec_rx_clk_refcount == 0) {
+ clk_disable(icodec_rx_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_RX:
+ ecodec_clk_refcount--;
+ if (ecodec_clk_refcount == 0) {
+ clk_disable(ecodec_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ case Q6_SDAC_RX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_rx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("audiolib: invalid rx device group %d\n",
+ audio_rx_device_group);
+ break;
+ }
+}
+
+static void _audio_tx_clk_disable(void)
+{
+ switch (audio_tx_device_group) {
+ case Q6_ICODEC_TX:
+ icodec_tx_clk_refcount--;
+ if (icodec_tx_clk_refcount == 0) {
+ clk_disable(icodec_tx_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ case Q6_ECODEC_TX:
+ ecodec_clk_refcount--;
+ if (ecodec_clk_refcount == 0) {
+ clk_disable(ecodec_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ case Q6_SDAC_TX:
+ sdac_clk_refcount--;
+ if (sdac_clk_refcount == 0) {
+ clk_disable(sdac_clk);
+ audio_tx_device_group = -1;
+ }
+ break;
+ default:
+ pr_err("audiolib: invalid tx device group %d\n",
+ audio_tx_device_group);
+ break;
+ }
+}
+
+static void _audio_rx_clk_reinit(uint32_t rx_device)
+{
+ uint32_t device_group = q6_device_to_codec(rx_device);
+
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_disable();
+
+ audio_rx_device_id = rx_device;
+ audio_rx_path_id = q6_device_to_path(rx_device);
+
+ if (device_group != audio_rx_device_group)
+ _audio_rx_clk_enable();
+
+}
+
+static void _audio_tx_clk_reinit(uint32_t tx_device)
+{
+ uint32_t device_group = q6_device_to_codec(tx_device);
+
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_disable();
+
+ audio_tx_device_id = tx_device;
+ audio_tx_path_id = q6_device_to_path(tx_device);
+
+ if (device_group != audio_tx_device_group)
+ _audio_tx_clk_enable();
+}
+
+static DEFINE_MUTEX(audio_path_lock);
+static int audio_rx_path_refcount;
+static int audio_tx_path_refcount;
+
+static int audio_rx_path_enable(int en, uint32_t acdb_id)
+{
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ _audio_rx_clk_enable();
+ _audio_rx_path_enable(0, acdb_id);
+ }
+ } else {
+ audio_rx_path_refcount--;
+ if (audio_rx_path_refcount == 0) {
+ _audio_rx_path_disable();
+ _audio_rx_clk_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static int audio_tx_path_enable(int en, uint32_t acdb_id)
+{
+ mutex_lock(&audio_path_lock);
+ if (en) {
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable(0, acdb_id);
+ }
+ } else {
+ audio_tx_path_refcount--;
+ if (audio_tx_path_refcount == 0) {
+ _audio_tx_path_disable();
+ _audio_tx_clk_disable();
+ }
+ }
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_reinit_acdb(char* filename) {
+ int res;
+
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+ if (strlen(filename) < 0 || !strcmp(filename, acdb_file)) {
+ res = -EINVAL;
+ goto done;
+ }
+ res = acdb_init(filename);
+ if (!res)
+ strcpy(acdb_file, filename);
+done:
+ mutex_unlock(&audio_path_lock);
+ return res;
+
+}
+
+int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst)
+{
+ int res;
+
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+ res = audio_update_acdb(id_dst, id_src);
+ if (res)
+ goto done;
+
+ if (q6_device_to_dir(id_dst) == Q6_RX)
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst);
+ else
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+done:
+ mutex_unlock(&audio_path_lock);
+ return res;
+}
+
+int q6audio_set_tx_mute(int mute)
+{
+ uint32_t adev;
+
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ if (mute == tx_mute_status) {
+ mutex_unlock(&audio_path_lock);
+ return 0;
+ }
+
+ adev = audio_tx_device_id;
+ audio_tx_mute(ac_control, adev, mute);
+ tx_mute_status = mute;
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_stream_volume(struct audio_client *ac, int vol)
+{
+ if (vol > 1200 || vol < -4000) {
+ pr_err("unsupported volume level %d\n", vol);
+ return -EINVAL;
+ }
+ mutex_lock(&audio_path_lock);
+ audio_stream_mute(ac, 0);
+ audio_stream_volume(ac, vol);
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_rx_volume(int level)
+{
+ uint32_t adev;
+ int vol;
+
+ if (q6audio_init())
+ return 0;
+
+ if (level < 0 || level > 100)
+ return -EINVAL;
+
+ mutex_lock(&audio_path_lock);
+ adev = ADSP_AUDIO_DEVICE_ID_VOICE;
+ vol = q6_device_volume(audio_rx_device_id, level);
+ audio_rx_mute(ac_control, adev, 0);
+ audio_rx_volume(ac_control, adev, vol);
+ rx_vol_level = level;
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+static void do_rx_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ if (device_id == audio_rx_device_id) {
+ if (acdb_id != rx_acdb) {
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ }
+ return;
+ }
+
+ if (audio_rx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id);
+ _audio_rx_path_disable();
+ _audio_rx_clk_reinit(device_id);
+ _audio_rx_path_enable(1, acdb_id);
+ } else {
+ audio_rx_device_id = device_id;
+ audio_rx_path_id = q6_device_to_path(device_id);
+ }
+}
+
+static void do_tx_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ if (device_id == audio_tx_device_id) {
+ if (acdb_id != tx_acdb) {
+ audio_update_acdb(device_id, acdb_id);
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ }
+ return;
+ }
+
+ if (audio_tx_path_refcount > 0) {
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id);
+ _audio_tx_path_disable();
+ _audio_tx_clk_reinit(device_id);
+ _audio_tx_path_enable(1, acdb_id);
+ } else {
+ audio_tx_device_id = device_id;
+ audio_tx_path_id = q6_device_to_path(device_id);
+ }
+}
+
+int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id)
+{
+ if (q6audio_init())
+ return 0;
+
+ mutex_lock(&audio_path_lock);
+
+ switch(q6_device_to_dir(device_id)) {
+ case Q6_RX:
+ do_rx_routing(device_id, acdb_id);
+ break;
+ case Q6_TX:
+ do_tx_routing(device_id, acdb_id);
+ break;
+ }
+
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+int q6audio_set_route(const char *name)
+{
+ uint32_t route;
+ if (!strcmp(name, "speaker")) {
+ route = ADIE_PATH_SPEAKER_STEREO_RX;
+ } else if (!strcmp(name, "headphones")) {
+ route = ADIE_PATH_HEADSET_STEREO_RX;
+ } else if (!strcmp(name, "handset")) {
+ route = ADIE_PATH_HANDSET_RX;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&audio_path_lock);
+ if (route == audio_rx_path_id)
+ goto done;
+
+ audio_rx_path_id = route;
+
+ if (audio_rx_path_refcount > 0) {
+ _audio_rx_path_disable();
+ _audio_rx_path_enable(1, 0);
+ }
+ if (audio_tx_path_refcount > 0) {
+ _audio_tx_path_disable();
+ _audio_tx_path_enable(1, 0);
+ }
+done:
+ mutex_unlock(&audio_path_lock);
+ return 0;
+}
+
+struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t flags, uint32_t acdb_id)
+{
+ int rc, retry = 5;
+ struct audio_client *ac;
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+
+ mutex_lock(&audio_path_lock);
+
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ audio_rx_path_refcount++;
+ if (audio_rx_path_refcount == 1) {
+ _audio_rx_clk_enable();
+ audio_update_acdb(audio_rx_device_id, acdb_id);
+ qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id);
+ qdsp6_standby(ac_control);
+ qdsp6_start(ac_control);
+ }
+ } else {
+ /* TODO: consider concurrency with voice call */
+ tx_clk_freq = rate;
+ audio_tx_path_refcount++;
+ if (audio_tx_path_refcount == 1) {
+ _audio_tx_clk_enable();
+ _audio_tx_path_enable(0, acdb_id);
+ }
+ }
+
+ for (retry = 5;;retry--) {
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ rc = audio_out_open(ac, bufsz, rate, channels);
+ else
+ rc = audio_in_open(ac, bufsz, rate, channels);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+ pr_err("q6audio: open pcm error %d, retrying\n", rc);
+ msleep(1);
+ }
+
+ if (ac->flags & AUDIO_FLAG_WRITE) {
+ if (audio_rx_path_refcount == 1) {
+ adie_enable();
+ adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX);
+ adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000);
+
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY);
+ adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY);
+
+ audio_rx_analog_enable(1);
+ }
+ }
+
+ mutex_unlock(&audio_path_lock);
+
+ for (retry = 5;;retry--) {
+ rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+ if (rc == 0)
+ break;
+ if (retry == 0)
+ q6audio_dsp_not_responding();
+ pr_err("q6audio: stream start error %d, retrying\n", rc);
+ }
+
+ if (!(ac->flags & AUDIO_FLAG_WRITE)) {
+ ac->buf[0].used = 1;
+ ac->buf[1].used = 1;
+ q6audio_read(ac, &ac->buf[0]);
+ q6audio_read(ac, &ac->buf[1]);
+ }
+
+ audio_prevent_sleep();
+ return ac;
+}
+
+int q6audio_close(struct audio_client *ac)
+{
+ audio_close(ac);
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(0, 0);
+ else
+ audio_tx_path_enable(0, 0);
+
+ audio_client_free(ac);
+ audio_allow_sleep();
+ return 0;
+}
+
+struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(0);
+ if (!ac)
+ return 0;
+
+ ac->flags = flags;
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(1, acdb_id);
+ else {
+ tx_clk_freq = 8000;
+ audio_tx_path_enable(1, acdb_id);
+ }
+
+ return ac;
+}
+
+int q6voice_close(struct audio_client *ac)
+{
+ if (ac->flags & AUDIO_FLAG_WRITE)
+ audio_rx_path_enable(0, 0);
+ else
+ audio_tx_path_enable(0, 0);
+
+ audio_client_free(ac);
+ return 0;
+}
+
+struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate,
+ uint32_t channels, uint32_t acdb_id)
+{
+ struct audio_client *ac;
+
+ printk("q6audio_open_mp3()\n");
+
+ if (q6audio_init())
+ return 0;
+
+ ac = audio_client_alloc(bufsz);
+ if (!ac)
+ return 0;
+
+ ac->flags = AUDIO_FLAG_WRITE;
+ audio_rx_path_enable(1, acdb_id);
+
+ audio_mp3_open(ac, bufsz, rate, channels);
+ audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START);
+
+ return ac;
+}
+
+int q6audio_mp3_close(struct audio_client *ac)
+{
+ audio_close(ac);
+ audio_rx_path_enable(0, 0);
+ audio_client_free(ac);
+ return 0;
+}
+
+int q6audio_async(struct audio_client *ac)
+{
+ struct adsp_command_hdr rpc;
+ memset(&rpc, 0, sizeof(rpc));
+ rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS;
+ rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC;
+ return audio_ioctl(ac, &rpc, sizeof(rpc));
+}
diff --git a/arch/arm/mach-msm/qdsp6/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/q6audio_devices.h
new file mode 100644
index 0000000..d4d30b5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/q6audio_devices.h
@@ -0,0 +1,265 @@
+/* arch/arm/mach-msm/qdsp6/q6audio_devices.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+struct q6_device_info {
+ uint32_t id;
+ uint32_t cad_id;
+ uint32_t path;
+ uint32_t rate;
+ uint8_t dir;
+ uint8_t codec;
+ uint8_t hw;
+};
+
+#define Q6_ICODEC_RX 0
+#define Q6_ICODEC_TX 1
+#define Q6_ECODEC_RX 2
+#define Q6_ECODEC_TX 3
+#define Q6_SDAC_RX 6
+#define Q6_SDAC_TX 7
+#define Q6_CODEC_NONE 255
+
+#define Q6_TX 1
+#define Q6_RX 2
+#define Q6_TX_RX 3
+
+#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01
+#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02
+#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07
+#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08
+#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09
+#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A
+#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C
+#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D
+
+#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E
+#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F
+
+/* Logical Device to indicate A2DP routing */
+#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11
+#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13
+#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14
+
+#define CAD_HW_DEVICE_ID_VOICE 0x15
+
+#define CAD_HW_DEVICE_ID_I2S_RX 0x20
+#define CAD_HW_DEVICE_ID_I2S_TX 0x21
+
+/* AUXPGA */
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22
+#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24
+#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25
+
+#define CAD_HW_DEVICE_ID_NULL_RX 0x2A
+
+#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F
+
+#define CAD_HW_DEVICE_ID_INVALID 0xFF
+
+#define CAD_RX_DEVICE 0x00
+#define CAD_TX_DEVICE 0x01
+
+static struct q6_device_info q6_audio_devices[] = {
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR,
+ .path = ADIE_PATH_HANDSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO,
+ .path = ADIE_PATH_HEADSET_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO,
+ .path = ADIE_PATH_HEADSET_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO,
+ .path = ADIE_PATH_SPEAKER_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO,
+ .path = ADIE_PATH_SPEAKER_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX,
+ .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+ .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR,
+ .path = ADIE_PATH_TTY_HEADSET_RX,
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ICODEC_RX,
+ .hw = Q6_HW_TTY,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC,
+ .path = ADIE_PATH_HANDSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HANDSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC,
+ .path = ADIE_PATH_HEADSET_MONO_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC,
+ .path = ADIE_PATH_SPEAKER_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC,
+ .path = ADIE_PATH_TTY_HEADSET_TX,
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ICODEC_TX,
+ .hw = Q6_HW_HEADSET,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_ECODEC_RX,
+ .hw = Q6_HW_BT_A2DP,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC,
+ .path = 0, /* XXX */
+ .rate = 8000,
+ .dir = Q6_TX,
+ .codec = Q6_ECODEC_TX,
+ .hw = Q6_HW_BT_SCO,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_RX,
+ .path = 0, /* XXX */
+ .rate = 48000,
+ .dir = Q6_RX,
+ .codec = Q6_SDAC_RX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC,
+ .cad_id = CAD_HW_DEVICE_ID_I2S_TX,
+ .path = 0, /* XXX */
+ .rate = 16000,
+ .dir = Q6_TX,
+ .codec = Q6_SDAC_TX,
+ .hw = Q6_HW_SPEAKER,
+ },
+ {
+ .id = 0,
+ .cad_id = 0,
+ .path = 0,
+ .rate = 8000,
+ .dir = 0,
+ .codec = Q6_CODEC_NONE,
+ .hw = 0,
+ },
+};
+
diff --git a/arch/arm/mach-msm/qdsp6/routing.c b/arch/arm/mach-msm/qdsp6/routing.c
new file mode 100644
index 0000000..a851896
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/routing.c
@@ -0,0 +1,71 @@
+/* arch/arm/mach-msm/qdsp6/routing.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+extern int q6audio_set_route(const char *name);
+
+static int q6_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t q6_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ q6audio_set_route(cmd);
+
+ return count;
+}
+
+static int q6_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static struct file_operations q6_fops = {
+ .owner = THIS_MODULE,
+ .open = q6_open,
+ .write = q6_write,
+ .release = q6_release,
+};
+
+static struct miscdevice q6_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_route",
+ .fops = &q6_fops,
+};
+
+
+static int __init q6_init(void) {
+ return misc_register(&q6_misc);
+}
+
+device_initcall(q6_init);
diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c
new file mode 100644
index 0000000..75f6140
--- /dev/null
+++ b/arch/arm/mach-msm/remote_spinlock.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <mach/remote_spinlock.h>
+#include "smd_private.h"
+
+#define SMEM_SPINLOCK_COUNT 8
+#define SMEM_SPINLOCK_ARRAY_SIZE (SMEM_SPINLOCK_COUNT * sizeof(uint32_t))
+
+struct raw_remote_spinlock {
+ union {
+ volatile u32 lock;
+ struct {
+ volatile u8 self_lock;
+ volatile u8 other_lock;
+ volatile u8 next_yield;
+ u8 pad;
+ } dek;
+ };
+};
+
+static inline void __raw_remote_ex_spin_lock(struct raw_remote_spinlock *lock)
+{
+ unsigned long tmp;
+
+ asm volatile (
+ "1: ldrex %0, [%1]\n"
+ " teq %0, #0\n"
+ " strexeq %0, %2, [%1]\n"
+ " teqeq %0, #0\n"
+ " bne 1b"
+ : "=&r" (tmp)
+ : "r" (&lock->lock), "r" (1)
+ : "cc");
+
+ smp_mb();
+}
+
+static inline void __raw_remote_ex_spin_unlock(struct raw_remote_spinlock *lock)
+{
+ smp_mb();
+
+ asm volatile (
+ " str %1, [%0]\n"
+ :
+ : "r" (&lock->lock), "r" (0)
+ : "cc");
+}
+
+static inline void __raw_remote_swp_spin_lock(struct raw_remote_spinlock *lock)
+{
+ unsigned long tmp;
+
+ asm volatile (
+ "1: swp %0, %2, [%1]\n"
+ " teq %0, #0\n"
+ " bne 1b"
+ : "=&r" (tmp)
+ : "r" (&lock->lock), "r" (1)
+ : "cc");
+
+ smp_mb();
+}
+
+static inline void __raw_remote_swp_spin_unlock(struct raw_remote_spinlock *lock)
+{
+ smp_mb();
+
+ asm volatile (
+ " str %1, [%0]"
+ :
+ : "r" (&lock->lock), "r" (0)
+ : "cc");
+}
+
+#define DEK_LOCK_REQUEST 1
+#define DEK_LOCK_YIELD (!DEK_LOCK_REQUEST)
+#define DEK_YIELD_TURN_SELF 0
+static void __raw_remote_dek_spin_lock(struct raw_remote_spinlock *lock)
+{
+ lock->dek.self_lock = DEK_LOCK_REQUEST;
+
+ while (lock->dek.other_lock) {
+
+ if (lock->dek.next_yield == DEK_YIELD_TURN_SELF)
+ lock->dek.self_lock = DEK_LOCK_YIELD;
+
+ while (lock->dek.other_lock)
+ ;
+
+ lock->dek.self_lock = DEK_LOCK_REQUEST;
+ }
+ lock->dek.next_yield = DEK_YIELD_TURN_SELF;
+
+ smp_mb();
+}
+
+static void __raw_remote_dek_spin_unlock(struct raw_remote_spinlock *lock)
+{
+ smp_mb();
+
+ lock->dek.self_lock = DEK_LOCK_YIELD;
+}
+
+#if defined(CONFIG_MSM_REMOTE_SPINLOCK_DEKKERS)
+/* Use Dekker's algorithm when LDREX/STREX and SWP are unavailable for
+ * shared memory */
+#define _raw_remote_spin_lock(lock) __raw_remote_dek_spin_lock(lock)
+#define _raw_remote_spin_unlock(lock) __raw_remote_dek_spin_unlock(lock)
+#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SWP)
+/* Use SWP-based locks when LDREX/STREX are unavailable for shared memory. */
+#define _raw_remote_spin_lock(lock) __raw_remote_swp_spin_lock(lock)
+#define _raw_remote_spin_unlock(lock) __raw_remote_swp_spin_unlock(lock)
+#else
+/* Use LDREX/STREX for shared memory locking, when available */
+#define _raw_remote_spin_lock(lock) __raw_remote_ex_spin_lock(lock)
+#define _raw_remote_spin_unlock(lock) __raw_remote_ex_spin_unlock(lock)
+#endif
+
+void _remote_spin_lock(remote_spinlock_t *lock)
+{
+ _raw_remote_spin_lock(lock->remote);
+}
+EXPORT_SYMBOL(_remote_spin_lock);
+
+void _remote_spin_unlock(remote_spinlock_t *lock)
+{
+ _raw_remote_spin_unlock(lock->remote);
+}
+EXPORT_SYMBOL(_remote_spin_unlock);
+
+static int remote_spin_lock_smem_init(remote_spinlock_t *lock, int id)
+{
+ void *start;
+
+ if (id >= SMEM_SPINLOCK_COUNT)
+ return -EINVAL;
+
+ start = smem_alloc(SMEM_SPINLOCK_ARRAY, SMEM_SPINLOCK_ARRAY_SIZE);
+ if (start == NULL)
+ return -ENXIO;
+
+ lock->remote =
+ (struct raw_remote_spinlock *)(start + id * sizeof(uint32_t));
+ return 0;
+}
+
+#define DAL_CHUNK_NAME_LENGTH 12
+struct dal_chunk_header {
+ uint32_t size;
+ char name[DAL_CHUNK_NAME_LENGTH];
+ uint32_t lock;
+ uint32_t reserved;
+ uint32_t type;
+ uint32_t version;
+};
+
+static int remote_spin_lock_dal_init(remote_spinlock_t *lock, const char *name)
+{
+ unsigned long start;
+ unsigned long end;
+ unsigned size;
+ struct dal_chunk_header *cur_hdr;
+
+ if (!name)
+ return -EINVAL;
+
+ start = (unsigned long)smem_item(SMEM_DAL_AREA, &size);
+ if (!start)
+ return -ENXIO;
+
+ end = start + size;
+
+ /* Find first chunk header */
+ cur_hdr = (struct dal_chunk_header *)ALIGN(start, 4096);
+ lock->remote = NULL;
+ while (((unsigned long)(cur_hdr + 1) <= end) && (cur_hdr->size != 0)) {
+ if (!strncmp(cur_hdr->name, name, DAL_CHUNK_NAME_LENGTH)) {
+ lock->remote =
+ (struct raw_remote_spinlock *)&cur_hdr->lock;
+ return 0;
+ }
+ cur_hdr = (void *)cur_hdr + cur_hdr->size;
+ }
+
+ pr_err("%s: DAL remote spin lock '%s' not found.\n", __func__, name);
+ return -EINVAL;
+}
+
+int _remote_spin_lock_init(remote_spinlock_t *lock, const char *name)
+{
+ BUG_ON(name == NULL);
+
+ /* remote spinlocks can be one of two formats:
+ * D:<dal chunk name>
+ * S:<single digit smem lock id>
+ */
+ if (!strncmp(name, "D:", 2)) {
+ return remote_spin_lock_dal_init(lock, &name[2]);
+ } else if (!strncmp(name, "S:", 2)) {
+ BUG_ON(name[3] != '\0');
+ return remote_spin_lock_smem_init(lock, (uint8_t)(name[2]-'0'));
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(_remote_spin_lock_init);
diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c
new file mode 100644
index 0000000..b23fccf
--- /dev/null
+++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c
@@ -0,0 +1,68 @@
+/* arch/arm/mach-msm/rpc_server_dog_keepalive.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* dog_keepalive server definitions */
+
+#define DOG_KEEPALIVE_PROG 0x30000015
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define DOG_KEEPALIVE_VERS 0
+#define RPC_DOG_KEEPALIVE_BEACON 1
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define DOG_KEEPALIVE_VERS 0x731fa727
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define DOG_KEEPALIVE_VERS 0x00010000
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#else
+#error "Unsupported AMSS version"
+#endif
+#define RPC_DOG_KEEPALIVE_NULL 0
+
+
+/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_DOG_KEEPALIVE_NULL:
+ return 0;
+ case RPC_DOG_KEEPALIVE_BEACON:
+ printk(KERN_INFO "DOG KEEPALIVE PING\n");
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server rpc_server = {
+ .prog = DOG_KEEPALIVE_PROG,
+ .vers = DOG_KEEPALIVE_VERS,
+ .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+ /* Dual server registration to support backwards compatibility vers */
+ return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c
new file mode 100644
index 0000000..2f90fc88
--- /dev/null
+++ b/arch/arm/mach-msm/rpc_server_time_remote.c
@@ -0,0 +1,77 @@
+/* arch/arm/mach-msm/rpc_server_time_remote.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* time_remote_mtoa server definitions. */
+
+#define TIME_REMOTE_MTOA_PROG 0x3000005d
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define TIME_REMOTE_MTOA_VERS 0
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define TIME_REMOTE_MTOA_VERS 0x9202a8e4
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define TIME_REMOTE_MTOA_VERS 0x00010000
+#else
+#error "Unknown AMSS version"
+#endif
+#define RPC_TIME_REMOTE_MTOA_NULL 0
+#define RPC_TIME_TOD_SET_APPS_BASES 2
+
+struct rpc_time_tod_set_apps_bases_args {
+ uint32_t tick;
+ uint64_t stamp;
+};
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_TIME_REMOTE_MTOA_NULL:
+ return 0;
+
+ case RPC_TIME_TOD_SET_APPS_BASES: {
+ struct rpc_time_tod_set_apps_bases_args *args;
+ args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1);
+ args->tick = be32_to_cpu(args->tick);
+ args->stamp = be64_to_cpu(args->stamp);
+ printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n"
+ "\ttick = %d\n"
+ "\tstamp = %lld\n",
+ args->tick, args->stamp);
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server rpc_server = {
+ .prog = TIME_REMOTE_MTOA_PROG,
+ .vers = TIME_REMOTE_MTOA_VERS,
+ .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+ /* Dual server registration to support backwards compatibility vers */
+ return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/arch/arm/mach-msm/sirc.c b/arch/arm/mach-msm/sirc.c
index b079452..74e0686 100644
--- a/arch/arm/mach-msm/sirc.c
+++ b/arch/arm/mach-msm/sirc.c
@@ -1,25 +1,27 @@
-/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+/* linux/arch/arm/mach-msm/irq.c
*
- * 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.
+ * Copyright (c) 2009 QUALCOMM Incorporated.
+ * Copyright (C) 2009 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.
*
- * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
*/
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
+#include <mach/fiq.h>
+#include <mach/msm_iomap.h>
+
+#include "sirc.h"
static unsigned int int_enable;
static unsigned int wake_enable;
@@ -37,6 +39,7 @@
{
.int_status = SPSS_SIRC_IRQ_STATUS,
.cascade_irq = INT_SIRC_0,
+ .cascade_fiq = INT_SIRC_1,
}
};
@@ -120,6 +123,24 @@
return 0;
}
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+void sirc_fiq_select(int irq, bool enable)
+{
+ uint32_t mask = 1 << (irq - FIRST_SIRC_IRQ);
+ uint32_t val;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ val = readl(SPSS_SIRC_INT_SELECT);
+ if (enable)
+ val |= mask;
+ else
+ val &= ~mask;
+ writel(val, SPSS_SIRC_INT_SELECT);
+ local_irq_restore(flags);
+}
+#endif
+
/* Finds the pending interrupt on the passed cascade irq and redrives it */
static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc)
{
@@ -145,6 +166,22 @@
desc->chip->ack(irq);
}
+void msm_sirc_enter_sleep(void)
+{
+ save_type = readl(sirc_regs.int_type);
+ save_polarity = readl(sirc_regs.int_polarity);
+ writel(wake_enable, sirc_regs.int_enable);
+ return;
+}
+
+void msm_sirc_exit_sleep(void)
+{
+ writel(save_type, sirc_regs.int_type);
+ writel(save_polarity, sirc_regs.int_polarity);
+ writel(int_enable, sirc_regs.int_enable);
+ return;
+}
+
static struct irq_chip sirc_irq_chip = {
.name = "sirc",
.ack = sirc_irq_ack,
@@ -161,7 +198,7 @@
int_enable = 0;
wake_enable = 0;
- for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) {
+ for (i = FIRST_SIRC_IRQ; i < FIRST_SIRC_IRQ + NR_SIRC_IRQS; i++) {
set_irq_chip(i, &sirc_irq_chip);
set_irq_handler(i, handle_edge_irq);
set_irq_flags(i, IRQF_VALID);
@@ -171,6 +208,10 @@
set_irq_chained_handler(sirc_reg_table[i].cascade_irq,
sirc_irq_handler);
set_irq_wake(sirc_reg_table[i].cascade_irq, 1);
+#if defined(CONFIG_MSM_FIQ_SUPPORT)
+ msm_fiq_select(sirc_reg_table[i].cascade_fiq);
+ msm_fiq_enable(sirc_reg_table[i].cascade_fiq);
+#endif
}
return;
}
diff --git a/arch/arm/mach-msm/sirc.h b/arch/arm/mach-msm/sirc.h
new file mode 100644
index 0000000..8e1399f
--- /dev/null
+++ b/arch/arm/mach-msm/sirc.h
@@ -0,0 +1,27 @@
+/* arch/arm/mach-msm/pm.h
+ *
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SIRC_H
+#define _ARCH_ARM_MACH_MSM_SIRC_H
+
+#ifdef CONFIG_ARCH_QSD8X50
+void sirc_fiq_select(int irq, bool enable);
+void __init msm_init_sirc(void);
+#else
+static inline void sirc_fiq_select(int irq, bool enable) {}
+static inline void __init msm_init_sirc(void) {}
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index cf11d41..d613d5c 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -26,8 +26,10 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <mach/msm_smd.h>
+#include <mach/msm_iomap.h>
#include <mach/system.h>
#include "smd_private.h"
@@ -62,8 +64,21 @@
module_param_named(debug_mask, msm_smd_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
+void *smem_item(unsigned id, unsigned *size);
+static void smd_diag(void);
+
static unsigned last_heap_free = 0xffffffff;
+static inline void msm_a2m_int(uint32_t irq)
+{
+#if defined(CONFIG_ARCH_MSM7X30)
+ writel(1 << irq, MSM_GCC_BASE + 0x8);
+#else
+ writel(1, MSM_CSR_BASE + 0x400 + (irq * 4));
+#endif
+}
+
+
static inline void notify_other_smsm(void)
{
msm_a2m_int(5);
@@ -93,11 +108,14 @@
}
}
+void msm_pm_flush_console(void);
+
/* call when SMSM_RESET flag is set in the A9's smsm_state */
static void handle_modem_crash(void)
{
pr_err("ARM9 has CRASHED\n");
smd_diag();
+ msm_pm_flush_console();
/* hard reboot if possible */
if (msm_hw_reset_hook)
@@ -108,6 +126,8 @@
;
}
+extern int (*msm_check_for_modem_crash)(void);
+
uint32_t raw_smsm_get_state(enum smsm_state_item item)
{
return readl(smd_info.state + item * 4);
@@ -144,6 +164,44 @@
static unsigned char smd_ch_allocated[64];
static struct work_struct probe_work;
+static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type);
+
+static void smd_channel_probe_worker(struct work_struct *work)
+{
+ struct smd_alloc_elm *shared;
+ unsigned ctype;
+ unsigned type;
+ unsigned n;
+
+ shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
+ if (!shared) {
+ pr_err("smd: cannot find allocation table\n");
+ return;
+ }
+ for (n = 0; n < 64; n++) {
+ if (smd_ch_allocated[n])
+ continue;
+ if (!shared[n].ref_count)
+ continue;
+ if (!shared[n].name[0])
+ continue;
+ ctype = shared[n].ctype;
+ type = ctype & SMD_TYPE_MASK;
+
+ /* DAL channels are stream but neither the modem,
+ * nor the DSP correctly indicate this. Fixup manually.
+ */
+ if (!memcmp(shared[n].name, "DAL", 3))
+ ctype = (ctype & (~SMD_KIND_MASK)) | SMD_KIND_STREAM;
+
+ type = shared[n].ctype & SMD_TYPE_MASK;
+ if ((type == SMD_TYPE_APPS_MODEM) ||
+ (type == SMD_TYPE_APPS_DSP))
+ if (!smd_alloc_channel(shared[n].name, shared[n].cid, ctype))
+ smd_ch_allocated[n] = 1;
+ }
+}
+
/* how many bytes are available for reading */
static int smd_stream_read_avail(struct smd_channel *ch)
{
@@ -378,13 +436,11 @@
return IRQ_HANDLED;
}
-#if defined(CONFIG_QDSP6)
static irqreturn_t smd_dsp_irq_handler(int irq, void *data)
{
handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
return IRQ_HANDLED;
}
-#endif
static void smd_fake_irq_handler(unsigned long arg)
{
@@ -554,6 +610,48 @@
return r;
}
+static int smd_alloc_v2(struct smd_channel *ch)
+{
+ struct smd_shared_v2 *shared2;
+ void *buffer;
+ unsigned buffer_sz;
+
+ shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, sizeof(*shared2));
+ buffer = smem_item(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz);
+
+ if (!buffer)
+ return -1;
+
+ /* buffer must be a power-of-two size */
+ if (buffer_sz & (buffer_sz - 1))
+ return -1;
+
+ buffer_sz /= 2;
+ ch->send = &shared2->ch0;
+ ch->recv = &shared2->ch1;
+ ch->send_data = buffer;
+ ch->recv_data = buffer + buffer_sz;
+ ch->fifo_size = buffer_sz;
+ return 0;
+}
+
+static int smd_alloc_v1(struct smd_channel *ch)
+{
+ struct smd_shared_v1 *shared1;
+ shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1));
+ if (!shared1) {
+ pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n);
+ return -1;
+ }
+ ch->send = &shared1->ch0;
+ ch->recv = &shared1->ch1;
+ ch->send_data = shared1->data0;
+ ch->recv_data = shared1->data1;
+ ch->fifo_size = SMD_BUF_SIZE;
+ return 0;
+}
+
+
static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type)
{
struct smd_channel *ch;
@@ -565,7 +663,7 @@
}
ch->n = cid;
- if (_smd_alloc_channel(ch)) {
+ if (smd_alloc_v2(ch) && smd_alloc_v1(ch)) {
kfree(ch);
return -1;
}
@@ -612,42 +710,6 @@
return 0;
}
-static void smd_channel_probe_worker(struct work_struct *work)
-{
- struct smd_alloc_elm *shared;
- unsigned ctype;
- unsigned type;
- unsigned n;
-
- shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
- if (!shared) {
- pr_err("smd: cannot find allocation table\n");
- return;
- }
- for (n = 0; n < 64; n++) {
- if (smd_ch_allocated[n])
- continue;
- if (!shared[n].ref_count)
- continue;
- if (!shared[n].name[0])
- continue;
- ctype = shared[n].ctype;
- type = ctype & SMD_TYPE_MASK;
-
- /* DAL channels are stream but neither the modem,
- * nor the DSP correctly indicate this. Fixup manually.
- */
- if (!memcmp(shared[n].name, "DAL", 3))
- ctype = (ctype & (~SMD_KIND_MASK)) | SMD_KIND_STREAM;
-
- type = shared[n].ctype & SMD_TYPE_MASK;
- if ((type == SMD_TYPE_APPS_MODEM) ||
- (type == SMD_TYPE_APPS_DSP))
- if (!smd_alloc_channel(shared[n].name, shared[n].cid, ctype))
- smd_ch_allocated[n] = 1;
- }
-}
-
static void do_nothing_notify(void *priv, unsigned flags)
{
}
@@ -845,9 +907,9 @@
if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
pr_info("<SM %08x %08x>\n", apps, modm);
- if (modm & SMSM_RESET)
+ if (modm & SMSM_RESET) {
handle_modem_crash();
-
+ }
do_smd_probe();
spin_unlock_irqrestore(&smem_lock, flags);
@@ -857,9 +919,9 @@
int smsm_change_state(enum smsm_state_item item,
uint32_t clear_mask, uint32_t set_mask)
{
- unsigned long addr = smd_info.state + item * 4;
unsigned long flags;
unsigned state;
+ unsigned addr = smd_info.state + item * 4;
if (!smd_info.ready)
return -EIO;
@@ -997,17 +1059,12 @@
return 0;
}
-static int __init msm_smd_probe(struct platform_device *pdev)
+extern void msm_init_last_radio_log(struct module *);
+
+static int msm_smd_probe(struct platform_device *pdev)
{
pr_info("smd_init()\n");
- /*
- * If we haven't waited for the ARM9 to boot up till now,
- * then we need to wait here. Otherwise this should just
- * return immediately.
- */
- proc_comm_boot_wait();
-
INIT_WORK(&probe_work, smd_channel_probe_worker);
if (smd_core_init()) {
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index 3b2dd71..6951cc7 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -154,20 +154,31 @@
return sprintf(buf, "%d.%d\n", version >> 16, version & 0xffff);
}
+struct smem_msm_id {
+ uint32_t format;
+ uint32_t msm_id;
+ uint32_t msm_ver;
+ char build_id[32];
+};
+
static int debug_read_build_id(char *buf, int max)
{
unsigned size;
void *data;
+ struct smem_msm_id *msm_id;
- data = smem_item(SMEM_HW_SW_BUILD_ID, &size);
- if (!data)
+ msm_id = smem_item(SMEM_HW_SW_BUILD_ID, &size);
+ if (!msm_id || (size < sizeof(struct smem_msm_id)))
return 0;
if (size >= max)
size = max;
- memcpy(buf, data, size);
- return size;
+ return scnprintf(buf, size, "fmt=%d id=%d vers=%d.%d build_id='%s'\n",
+ msm_id->format,msm_id->msm_id,
+ (msm_id->msm_ver >> 16) & 0xffff,
+ msm_id->msm_ver & 0xffff,
+ msm_id->build_id);
}
static int debug_read_alloc_tbl(char *buf, int max)
@@ -227,7 +238,7 @@
dent = debugfs_create_dir("smd", 0);
if (IS_ERR(dent))
- return 1;
+ return PTR_ERR(dent);
debug_create("ch", 0444, dent, debug_read_ch);
debug_create("stat", 0444, dent, debug_read_stat);
@@ -269,9 +280,10 @@
{
unsigned long flags;
uint32_t *ptr;
+#ifndef CONFIG_ARCH_MSM_SCORPION
struct tramp_gpio_smem *gpio;
struct smsm_interrupt_info *int_info;
-
+#endif
spin_lock_irqsave(&smem_lock, flags);
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 727bfe6..2da758e 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -19,25 +19,25 @@
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/list.h>
-#include <linux/io.h>
-#include <mach/msm_iomap.h>
-
-struct smem_heap_info {
+struct smem_heap_info
+{
unsigned initialized;
unsigned free_offset;
unsigned heap_remaining;
unsigned reserved;
};
-struct smem_heap_entry {
+struct smem_heap_entry
+{
unsigned allocated;
unsigned offset;
unsigned size;
unsigned reserved;
};
-struct smem_proc_comm {
+struct smem_proc_comm
+{
unsigned command;
unsigned status;
unsigned data1;
@@ -54,7 +54,8 @@
#define VERSION_APPS 8
#define VERSION_MODEM 9
-struct smem_shared {
+struct smem_shared
+{
struct smem_proc_comm proc_comm[4];
unsigned version[32];
struct smem_heap_info heap_info;
@@ -64,8 +65,9 @@
#define SMSM_V1_SIZE (sizeof(unsigned) * 8)
#define SMSM_V2_SIZE (sizeof(unsigned) * 4)
-#ifdef CONFIG_MSM_SMD_PKG3
-struct smsm_interrupt_info {
+#ifndef CONFIG_ARCH_MSM_SCORPION
+struct smsm_interrupt_info
+{
uint32_t interrupt_mask;
uint32_t pending_interrupts;
uint32_t wakeup_reason;
@@ -126,7 +128,7 @@
#define SMSM_WKUP_REASON_ALARM 0x00000010
#define SMSM_WKUP_REASON_RESET 0x00000020
-#ifdef CONFIG_ARCH_MSM7X00A
+#ifndef CONFIG_ARCH_MSM_SCORPION
enum smsm_state_item {
SMSM_STATE_APPS = 1,
SMSM_STATE_MODEM = 3,
@@ -154,7 +156,8 @@
#define SMEM_NUM_SMD_CHANNELS 64
-typedef enum {
+typedef enum
+{
/* fixed items */
SMEM_PROC_COMM = 0,
SMEM_HEAP_INFO,
@@ -229,7 +232,7 @@
SMEM_SMEM_LOG_POWER_WRAP,
SMEM_SMEM_LOG_POWER_EVENTS,
SMEM_ERR_CRASH_LOG,
- SMEM_ERR_F3_TRACE_LOG,
+ SMEM_ERR_F3_TRACE_LOG,
SMEM_NUM_ITEMS,
} smem_mem_type;
@@ -268,7 +271,6 @@
unsigned head;
} __attribute__(( aligned(4), packed ));
-/* Only used on SMD package v3 on msm7201a */
struct smd_shared_v1 {
struct smd_half_channel ch0;
unsigned char data0[SMD_BUF_SIZE];
@@ -276,11 +278,10 @@
unsigned char data1[SMD_BUF_SIZE];
};
-/* Used on SMD package v4 */
struct smd_shared_v2 {
struct smd_half_channel ch0;
struct smd_half_channel ch1;
-};
+};
struct smd_channel {
volatile struct smd_half_channel *send;
@@ -333,71 +334,4 @@
void *smem_item(unsigned id, unsigned *size);
uint32_t raw_smsm_get_state(enum smsm_state_item item);
-extern void msm_init_last_radio_log(struct module *);
-
-#ifdef CONFIG_MSM_SMD_PKG3
-/*
- * This allocator assumes an SMD Package v3 which only exists on
- * MSM7x00 SoC's.
- */
-static inline int _smd_alloc_channel(struct smd_channel *ch)
-{
- struct smd_shared_v1 *shared1;
-
- shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1));
- if (!shared1) {
- pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n);
- return -1;
- }
- ch->send = &shared1->ch0;
- ch->recv = &shared1->ch1;
- ch->send_data = shared1->data0;
- ch->recv_data = shared1->data1;
- ch->fifo_size = SMD_BUF_SIZE;
- return 0;
-}
-#else
-/*
- * This allocator assumes an SMD Package v4, the most common
- * and the default.
- */
-static inline int _smd_alloc_channel(struct smd_channel *ch)
-{
- struct smd_shared_v2 *shared2;
- void *buffer;
- unsigned buffer_sz;
-
- shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, sizeof(*shared2));
- buffer = smem_item(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz);
-
- if (!buffer)
- return -1;
-
- /* buffer must be a power-of-two size */
- if (buffer_sz & (buffer_sz - 1))
- return -1;
-
- buffer_sz /= 2;
- ch->send = &shared2->ch0;
- ch->recv = &shared2->ch1;
- ch->send_data = buffer;
- ch->recv_data = buffer + buffer_sz;
- ch->fifo_size = buffer_sz;
- return 0;
-}
-#endif /* CONFIG_MSM_SMD_PKG3 */
-
-#if defined(CONFIG_ARCH_MSM7X30)
-static inline void msm_a2m_int(uint32_t irq)
-{
- writel(1 << irq, MSM_GCC_BASE + 0x8);
-}
-#else
-static inline void msm_a2m_int(uint32_t irq)
-{
- writel(1, MSM_CSR_BASE + 0x400 + (irq * 4));
-}
-#endif /* CONFIG_ARCH_MSM7X30 */
-
-
#endif
diff --git a/arch/arm/mach-msm/smd_qmi.c b/arch/arm/mach-msm/smd_qmi.c
new file mode 100644
index 0000000..50411df
--- /dev/null
+++ b/arch/arm/mach-msm/smd_qmi.c
@@ -0,0 +1,860 @@
+/* arch/arm/mach-msm/smd_qmi.c
+ *
+ * QMI Control Driver -- Manages network data connections.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#include <asm/uaccess.h>
+#include <mach/msm_smd.h>
+
+#define QMI_CTL 0x00
+#define QMI_WDS 0x01
+#define QMI_DMS 0x02
+#define QMI_NAS 0x03
+
+#define QMI_RESULT_SUCCESS 0x0000
+#define QMI_RESULT_FAILURE 0x0001
+
+struct qmi_msg {
+ unsigned char service;
+ unsigned char client_id;
+ unsigned short txn_id;
+ unsigned short type;
+ unsigned short size;
+ unsigned char *tlv;
+};
+
+#define qmi_ctl_client_id 0
+
+#define STATE_OFFLINE 0
+#define STATE_QUERYING 1
+#define STATE_ONLINE 2
+
+struct qmi_ctxt {
+ struct miscdevice misc;
+
+ struct mutex lock;
+
+ unsigned char ctl_txn_id;
+ unsigned char wds_client_id;
+ unsigned short wds_txn_id;
+
+ unsigned wds_busy;
+ unsigned wds_handle;
+ unsigned state_dirty;
+ unsigned state;
+
+ unsigned char addr[4];
+ unsigned char mask[4];
+ unsigned char gateway[4];
+ unsigned char dns1[4];
+ unsigned char dns2[4];
+
+ smd_channel_t *ch;
+ const char *ch_name;
+ struct wake_lock wake_lock;
+
+ struct work_struct open_work;
+ struct work_struct read_work;
+};
+
+static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n);
+
+static void qmi_read_work(struct work_struct *ws);
+static void qmi_open_work(struct work_struct *work);
+
+void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n)
+{
+ mutex_init(&ctxt->lock);
+ INIT_WORK(&ctxt->read_work, qmi_read_work);
+ INIT_WORK(&ctxt->open_work, qmi_open_work);
+ wake_lock_init(&ctxt->wake_lock, WAKE_LOCK_SUSPEND, ctxt->misc.name);
+ ctxt->ctl_txn_id = 1;
+ ctxt->wds_txn_id = 1;
+ ctxt->wds_busy = 1;
+ ctxt->state = STATE_OFFLINE;
+
+}
+
+static struct workqueue_struct *qmi_wq;
+
+static int verbose = 0;
+
+/* anyone waiting for a state change waits here */
+static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue);
+
+
+static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix)
+{
+ unsigned sz, n;
+ unsigned char *x;
+
+ if (!verbose)
+ return;
+
+ printk(KERN_INFO
+ "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n",
+ prefix, msg->service, msg->client_id,
+ msg->txn_id, msg->type, msg->size);
+
+ x = msg->tlv;
+ sz = msg->size;
+
+ while (sz >= 3) {
+ sz -= 3;
+
+ n = x[1] | (x[2] << 8);
+ if (n > sz)
+ break;
+
+ printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ",
+ prefix, x[0], n);
+ x += 3;
+ sz -= n;
+ while (n-- > 0)
+ printk("%02x ", *x++);
+ printk("}\n");
+ }
+}
+
+int qmi_add_tlv(struct qmi_msg *msg,
+ unsigned type, unsigned size, const void *data)
+{
+ unsigned char *x = msg->tlv + msg->size;
+
+ x[0] = type;
+ x[1] = size;
+ x[2] = size >> 8;
+
+ memcpy(x + 3, data, size);
+
+ msg->size += (size + 3);
+
+ return 0;
+}
+
+/* Extract a tagged item from a qmi message buffer,
+** taking care not to overrun the buffer.
+*/
+static int qmi_get_tlv(struct qmi_msg *msg,
+ unsigned type, unsigned size, void *data)
+{
+ unsigned char *x = msg->tlv;
+ unsigned len = msg->size;
+ unsigned n;
+
+ while (len >= 3) {
+ len -= 3;
+
+ /* size of this item */
+ n = x[1] | (x[2] << 8);
+ if (n > len)
+ break;
+
+ if (x[0] == type) {
+ if (n != size)
+ return -1;
+ memcpy(data, x + 3, size);
+ return 0;
+ }
+
+ x += (n + 3);
+ len -= n;
+ }
+
+ return -1;
+}
+
+static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error)
+{
+ unsigned short status[2];
+ if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) {
+ *error = 0;
+ return QMI_RESULT_FAILURE;
+ } else {
+ *error = status[1];
+ return status[0];
+ }
+}
+
+/* 0x01 <qmux-header> <payload> */
+#define QMUX_HEADER 13
+
+/* should be >= HEADER + FOOTER */
+#define QMUX_OVERHEAD 16
+
+static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned char *data;
+ unsigned hlen;
+ unsigned len;
+ int r;
+
+ qmi_dump_msg(msg, "send");
+
+ if (msg->service == QMI_CTL) {
+ hlen = QMUX_HEADER - 1;
+ } else {
+ hlen = QMUX_HEADER;
+ }
+
+ /* QMUX length is total header + total payload - IFC selector */
+ len = hlen + msg->size - 1;
+ if (len > 0xffff)
+ return -1;
+
+ data = msg->tlv - hlen;
+
+ /* prepend encap and qmux header */
+ *data++ = 0x01; /* ifc selector */
+
+ /* qmux header */
+ *data++ = len;
+ *data++ = len >> 8;
+ *data++ = 0x00; /* flags: client */
+ *data++ = msg->service;
+ *data++ = msg->client_id;
+
+ /* qmi header */
+ *data++ = 0x00; /* flags: send */
+ *data++ = msg->txn_id;
+ if (msg->service != QMI_CTL)
+ *data++ = msg->txn_id >> 8;
+
+ *data++ = msg->type;
+ *data++ = msg->type >> 8;
+ *data++ = msg->size;
+ *data++ = msg->size >> 8;
+
+ /* len + 1 takes the interface selector into account */
+ r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1);
+
+ if (r != len) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned err;
+ if (msg->type == 0x0022) {
+ unsigned char n[2];
+ if (qmi_get_status(msg, &err))
+ return;
+ if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
+ return;
+ if (n[0] == QMI_WDS) {
+ printk(KERN_INFO
+ "qmi: ctl: wds use client_id 0x%02x\n", n[1]);
+ ctxt->wds_client_id = n[1];
+ ctxt->wds_busy = 0;
+ }
+ }
+}
+
+static int qmi_network_get_profile(struct qmi_ctxt *ctxt);
+
+static void swapaddr(unsigned char *src, unsigned char *dst)
+{
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+}
+
+static unsigned char zero[4];
+static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned char tmp[4];
+ unsigned r;
+
+ r = qmi_get_tlv(msg, 0x1e, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->addr);
+ r = qmi_get_tlv(msg, 0x21, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->mask);
+ r = qmi_get_tlv(msg, 0x20, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->gateway);
+ r = qmi_get_tlv(msg, 0x15, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->dns1);
+ r = qmi_get_tlv(msg, 0x16, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->dns2);
+}
+
+static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ unsigned err;
+ switch (msg->type) {
+ case 0x0021:
+ if (qmi_get_status(msg, &err)) {
+ printk(KERN_ERR
+ "qmi: wds: network stop failed (%04x)\n", err);
+ } else {
+ printk(KERN_INFO
+ "qmi: wds: network stopped\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ }
+ break;
+ case 0x0020:
+ if (qmi_get_status(msg, &err)) {
+ printk(KERN_ERR
+ "qmi: wds: network start failed (%04x)\n", err);
+ } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) {
+ printk(KERN_INFO
+ "qmi: wds no handle?\n");
+ } else {
+ printk(KERN_INFO
+ "qmi: wds: got handle 0x%08x\n",
+ ctxt->wds_handle);
+ }
+ break;
+ case 0x002D:
+ printk("qmi: got network profile\n");
+ if (ctxt->state == STATE_QUERYING) {
+ qmi_read_runtime_profile(ctxt, msg);
+ ctxt->state = STATE_ONLINE;
+ ctxt->state_dirty = 1;
+ }
+ break;
+ default:
+ printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type);
+ }
+ ctxt->wds_busy = 0;
+}
+
+static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ if (msg->type == 0x0022) {
+ unsigned char n[2];
+ if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
+ return;
+ switch (n[0]) {
+ case 1:
+ printk(KERN_INFO "qmi: wds: DISCONNECTED\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ break;
+ case 2:
+ printk(KERN_INFO "qmi: wds: CONNECTED\n");
+ ctxt->state = STATE_QUERYING;
+ ctxt->state_dirty = 1;
+ qmi_network_get_profile(ctxt);
+ break;
+ case 3:
+ printk(KERN_INFO "qmi: wds: SUSPENDED\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ }
+ } else {
+ printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type);
+ }
+}
+
+static void qmi_process_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ printk("wds: %04x @ %02x\n", msg->type, msg->client_id);
+ if (msg->client_id == ctxt->wds_client_id) {
+ qmi_process_unicast_wds_msg(ctxt, msg);
+ } else if (msg->client_id == 0xff) {
+ qmi_process_broadcast_wds_msg(ctxt, msg);
+ } else {
+ printk(KERN_ERR
+ "qmi_process_wds_msg client id 0x%02x unknown\n",
+ msg->client_id);
+ }
+}
+
+static void qmi_process_qmux(struct qmi_ctxt *ctxt,
+ unsigned char *buf, unsigned sz)
+{
+ struct qmi_msg msg;
+
+ /* require a full header */
+ if (sz < 5)
+ return;
+
+ /* require a size that matches the buffer size */
+ if (sz != (buf[0] | (buf[1] << 8)))
+ return;
+
+ /* only messages from a service (bit7=1) are allowed */
+ if (buf[2] != 0x80)
+ return;
+
+ msg.service = buf[3];
+ msg.client_id = buf[4];
+
+ /* annoyingly, CTL messages have a shorter TID */
+ if (buf[3] == 0) {
+ if (sz < 7)
+ return;
+ msg.txn_id = buf[6];
+ buf += 7;
+ sz -= 7;
+ } else {
+ if (sz < 8)
+ return;
+ msg.txn_id = buf[6] | (buf[7] << 8);
+ buf += 8;
+ sz -= 8;
+ }
+
+ /* no type and size!? */
+ if (sz < 4)
+ return;
+ sz -= 4;
+
+ msg.type = buf[0] | (buf[1] << 8);
+ msg.size = buf[2] | (buf[3] << 8);
+ msg.tlv = buf + 4;
+
+ if (sz != msg.size)
+ return;
+
+ qmi_dump_msg(&msg, "recv");
+
+ mutex_lock(&ctxt->lock);
+ switch (msg.service) {
+ case QMI_CTL:
+ qmi_process_ctl_msg(ctxt, &msg);
+ break;
+ case QMI_WDS:
+ qmi_process_wds_msg(ctxt, &msg);
+ break;
+ default:
+ printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n",
+ msg.service);
+ break;
+ }
+ mutex_unlock(&ctxt->lock);
+
+ wake_up(&qmi_wait_queue);
+}
+
+#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD)
+
+static void qmi_read_work(struct work_struct *ws)
+{
+ struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work);
+ struct smd_channel *ch = ctxt->ch;
+ unsigned char buf[QMI_MAX_PACKET];
+ int sz;
+
+ for (;;) {
+ sz = smd_cur_packet_size(ch);
+ if (sz == 0)
+ break;
+ if (sz < smd_read_avail(ch))
+ break;
+ if (sz > QMI_MAX_PACKET) {
+ smd_read(ch, 0, sz);
+ continue;
+ }
+ if (smd_read(ch, buf, sz) != sz) {
+ printk(KERN_ERR "qmi: not enough data?!\n");
+ continue;
+ }
+
+ /* interface selector must be 1 */
+ if (buf[0] != 0x01)
+ continue;
+
+ qmi_process_qmux(ctxt, buf + 1, sz - 1);
+ }
+}
+
+static int qmi_request_wds_cid(struct qmi_ctxt *ctxt);
+
+static void qmi_open_work(struct work_struct *ws)
+{
+ struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work);
+ mutex_lock(&ctxt->lock);
+ qmi_request_wds_cid(ctxt);
+ mutex_unlock(&ctxt->lock);
+}
+
+static void qmi_notify(void *priv, unsigned event)
+{
+ struct qmi_ctxt *ctxt = priv;
+
+ switch (event) {
+ case SMD_EVENT_DATA: {
+ int sz;
+ sz = smd_cur_packet_size(ctxt->ch);
+ if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) {
+ wake_lock_timeout(&ctxt->wake_lock, HZ / 2);
+ queue_work(qmi_wq, &ctxt->read_work);
+ }
+ break;
+ }
+ case SMD_EVENT_OPEN:
+ printk(KERN_INFO "qmi: smd opened\n");
+ queue_work(qmi_wq, &ctxt->open_work);
+ break;
+ case SMD_EVENT_CLOSE:
+ printk(KERN_INFO "qmi: smd closed\n");
+ break;
+ }
+}
+
+static int qmi_request_wds_cid(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[64 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+ unsigned char n;
+
+ msg.service = QMI_CTL;
+ msg.client_id = qmi_ctl_client_id;
+ msg.txn_id = ctxt->ctl_txn_id;
+ msg.type = 0x0022;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->ctl_txn_id += 2;
+
+ n = QMI_WDS;
+ qmi_add_tlv(&msg, 0x01, 0x01, &n);
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_get_profile(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[96 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x002D;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn)
+{
+ unsigned char data[96 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+ char *auth_type;
+ char *user;
+ char *pass;
+
+ for (user = apn; *user; user++) {
+ if (*user == ' ') {
+ *user++ = 0;
+ break;
+ }
+ }
+ for (pass = user; *pass; pass++) {
+ if (*pass == ' ') {
+ *pass++ = 0;
+ break;
+ }
+ }
+
+ for (auth_type = pass; *auth_type; auth_type++) {
+ if (*auth_type == ' ') {
+ *auth_type++ = 0;
+ break;
+ }
+ }
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x0020;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ qmi_add_tlv(&msg, 0x14, strlen(apn), apn);
+ if (*auth_type)
+ qmi_add_tlv(&msg, 0x16, strlen(auth_type), auth_type);
+ if (*user) {
+ if (!*auth_type) {
+ unsigned char x;
+ x = 3;
+ qmi_add_tlv(&msg, 0x16, 1, &x);
+ }
+ qmi_add_tlv(&msg, 0x17, strlen(user), user);
+ if (*pass)
+ qmi_add_tlv(&msg, 0x18, strlen(pass), pass);
+ }
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_down(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[16 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x0021;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle);
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max)
+{
+ int i;
+ char *statename;
+
+ if (ctxt->state == STATE_ONLINE) {
+ statename = "up";
+ } else if (ctxt->state == STATE_OFFLINE) {
+ statename = "down";
+ } else {
+ statename = "busy";
+ }
+
+ i = scnprintf(buf, max, "STATE=%s\n", statename);
+ i += scnprintf(buf + i, max - i, "CID=%d\n",ctxt->wds_client_id);
+
+ if (ctxt->state != STATE_ONLINE){
+ return i;
+ }
+
+ i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n",
+ ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]);
+ i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n",
+ ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]);
+ i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n",
+ ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2],
+ ctxt->gateway[3]);
+ i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n",
+ ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]);
+ i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n",
+ ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]);
+
+ return i;
+}
+
+static ssize_t qmi_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct qmi_ctxt *ctxt = fp->private_data;
+ char msg[256];
+ int len;
+ int r;
+
+ mutex_lock(&ctxt->lock);
+ for (;;) {
+ if (ctxt->state_dirty) {
+ ctxt->state_dirty = 0;
+ len = qmi_print_state(ctxt, msg, 256);
+ break;
+ }
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty);
+ if (r < 0)
+ return r;
+ mutex_lock(&ctxt->lock);
+ }
+ mutex_unlock(&ctxt->lock);
+
+ if (len > count)
+ len = count;
+
+ if (copy_to_user(buf, msg, len))
+ return -EFAULT;
+
+ return len;
+}
+
+
+static ssize_t qmi_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct qmi_ctxt *ctxt = fp->private_data;
+ unsigned char cmd[64];
+ int len;
+ int r;
+
+ if (count < 1)
+ return 0;
+
+ len = count > 63 ? 63 : count;
+
+ if (copy_from_user(cmd, buf, len))
+ return -EFAULT;
+
+ cmd[len] = 0;
+
+ /* lazy */
+ if (cmd[len-1] == '\n') {
+ cmd[len-1] = 0;
+ len--;
+ }
+
+ if (!strncmp(cmd, "verbose", 7)) {
+ verbose = 1;
+ } else if (!strncmp(cmd, "terse", 5)) {
+ verbose = 0;
+ } else if (!strncmp(cmd, "poll", 4)) {
+ ctxt->state_dirty = 1;
+ wake_up(&qmi_wait_queue);
+ } else if (!strncmp(cmd, "down", 4)) {
+retry_down:
+ mutex_lock(&ctxt->lock);
+ if (ctxt->wds_busy) {
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
+ if (r < 0)
+ return r;
+ goto retry_down;
+ }
+ ctxt->wds_busy = 1;
+ qmi_network_down(ctxt);
+ mutex_unlock(&ctxt->lock);
+ } else if (!strncmp(cmd, "up:", 3)) {
+retry_up:
+ mutex_lock(&ctxt->lock);
+ if (ctxt->wds_busy) {
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
+ if (r < 0)
+ return r;
+ goto retry_up;
+ }
+ ctxt->wds_busy = 1;
+ qmi_network_up(ctxt, cmd+3);
+ mutex_unlock(&ctxt->lock);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static int qmi_open(struct inode *ip, struct file *fp)
+{
+ struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev));
+ int r = 0;
+
+ if (!ctxt) {
+ printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev));
+ return -ENODEV;
+ }
+
+ fp->private_data = ctxt;
+
+ mutex_lock(&ctxt->lock);
+ if (ctxt->ch == 0)
+ r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify);
+ if (r == 0)
+ wake_up(&qmi_wait_queue);
+ mutex_unlock(&ctxt->lock);
+
+ return r;
+}
+
+static int qmi_release(struct inode *ip, struct file *fp)
+{
+ return 0;
+}
+
+static struct file_operations qmi_fops = {
+ .owner = THIS_MODULE,
+ .read = qmi_read,
+ .write = qmi_write,
+ .open = qmi_open,
+ .release = qmi_release,
+};
+
+static struct qmi_ctxt qmi_device0 = {
+ .ch_name = "SMD_DATA5_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi0",
+ .fops = &qmi_fops,
+ }
+};
+static struct qmi_ctxt qmi_device1 = {
+ .ch_name = "SMD_DATA6_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi1",
+ .fops = &qmi_fops,
+ }
+};
+static struct qmi_ctxt qmi_device2 = {
+ .ch_name = "SMD_DATA7_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi2",
+ .fops = &qmi_fops,
+ }
+};
+
+static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n)
+{
+ if (n == qmi_device0.misc.minor)
+ return &qmi_device0;
+ if (n == qmi_device1.misc.minor)
+ return &qmi_device1;
+ if (n == qmi_device2.misc.minor)
+ return &qmi_device2;
+ return 0;
+}
+
+static int __init qmi_init(void)
+{
+ int ret;
+
+ qmi_wq = create_singlethread_workqueue("qmi");
+ if (qmi_wq == 0)
+ return -ENOMEM;
+
+ qmi_ctxt_init(&qmi_device0, 0);
+ qmi_ctxt_init(&qmi_device1, 1);
+ qmi_ctxt_init(&qmi_device2, 2);
+
+ ret = misc_register(&qmi_device0.misc);
+ if (ret == 0)
+ ret = misc_register(&qmi_device1.misc);
+ if (ret == 0)
+ ret = misc_register(&qmi_device2.misc);
+ return ret;
+}
+
+module_init(qmi_init);
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
new file mode 100644
index 0000000..ed9745a
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -0,0 +1,1344 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.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.
+ *
+ */
+
+/* TODO: handle cases where smd_write() will tempfail due to full fifo */
+/* TODO: thread priority? schedule a work to bump it? */
+/* TODO: maybe make server_list_lock a mutex */
+/* TODO: pool fragments to avoid kmalloc/kfree churn */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/wakelock.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/byteorder.h>
+
+#include <mach/msm_smd.h>
+#include "smd_rpcrouter.h"
+
+#define TRACE_R2R_MSG 0
+#define TRACE_R2R_RAW 0
+#define TRACE_RPC_MSG 0
+#define TRACE_NOTIFY_MSG 0
+
+#define MSM_RPCROUTER_DEBUG 0
+#define MSM_RPCROUTER_DEBUG_PKT 0
+#define MSM_RPCROUTER_R2R_DEBUG 0
+#define DUMP_ALL_RECEIVED_HEADERS 0
+
+#define DIAG(x...) printk("[RR] ERROR " x)
+
+#if MSM_RPCROUTER_DEBUG
+#define D(x...) printk(x)
+#else
+#define D(x...) do {} while (0)
+#endif
+
+#if TRACE_R2R_MSG
+#define RR(x...) printk("[RR] "x)
+#else
+#define RR(x...) do {} while (0)
+#endif
+
+#if TRACE_RPC_MSG
+#define IO(x...) printk("[RPC] "x)
+#else
+#define IO(x...) do {} while (0)
+#endif
+
+#if TRACE_NOTIFY_MSG
+#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x)
+#else
+#define NTFY(x...) do {} while (0)
+#endif
+
+static LIST_HEAD(local_endpoints);
+static LIST_HEAD(remote_endpoints);
+
+static LIST_HEAD(server_list);
+
+static smd_channel_t *smd_channel;
+static int initialized;
+static wait_queue_head_t newserver_wait;
+static wait_queue_head_t smd_wait;
+static int smd_wait_count; /* odd while waiting */
+
+static DEFINE_SPINLOCK(local_endpoints_lock);
+static DEFINE_SPINLOCK(remote_endpoints_lock);
+static DEFINE_SPINLOCK(server_list_lock);
+static DEFINE_SPINLOCK(smd_lock);
+
+static struct workqueue_struct *rpcrouter_workqueue;
+static struct wake_lock rpcrouter_wake_lock;
+static int rpcrouter_need_len;
+
+static atomic_t next_xid = ATOMIC_INIT(1);
+static atomic_t next_mid = ATOMIC_INIT(0);
+
+static void do_read_data(struct work_struct *work);
+static void do_create_pdevs(struct work_struct *work);
+static void do_create_rpcrouter_pdev(struct work_struct *work);
+
+static DECLARE_WORK(work_read_data, do_read_data);
+static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
+static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
+static atomic_t rpcrouter_pdev_created = ATOMIC_INIT(0);
+
+#define RR_STATE_IDLE 0
+#define RR_STATE_HEADER 1
+#define RR_STATE_BODY 2
+#define RR_STATE_ERROR 3
+
+struct rr_context {
+ struct rr_packet *pkt;
+ uint8_t *ptr;
+ uint32_t state; /* current assembly state */
+ uint32_t count; /* bytes needed in this state */
+};
+
+struct rr_context the_rr_context;
+
+static struct platform_device rpcrouter_pdev = {
+ .name = "oncrpc_router",
+ .id = -1,
+};
+
+
+static int rpcrouter_send_control_msg(union rr_control_msg *msg)
+{
+ struct rr_header hdr;
+ unsigned long flags;
+ int need;
+
+ if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) {
+ printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, "
+ "router not initialized\n");
+ return -EINVAL;
+ }
+
+ hdr.version = RPCROUTER_VERSION;
+ hdr.type = msg->cmd;
+ hdr.src_pid = RPCROUTER_PID_LOCAL;
+ hdr.src_cid = RPCROUTER_ROUTER_ADDRESS;
+ hdr.confirm_rx = 0;
+ hdr.size = sizeof(*msg);
+ hdr.dst_pid = 0;
+ hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
+
+ /* TODO: what if channel is full? */
+
+ need = sizeof(hdr) + hdr.size;
+ spin_lock_irqsave(&smd_lock, flags);
+ while (smd_write_avail(smd_channel) < need) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+ smd_write(smd_channel, &hdr, sizeof(hdr));
+ smd_write(smd_channel, msg, hdr.size);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ return 0;
+}
+
+static struct rr_server *rpcrouter_create_server(uint32_t pid,
+ uint32_t cid,
+ uint32_t prog,
+ uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ int rc;
+
+ server = kmalloc(sizeof(struct rr_server), GFP_KERNEL);
+ if (!server)
+ return ERR_PTR(-ENOMEM);
+
+ memset(server, 0, sizeof(struct rr_server));
+ server->pid = pid;
+ server->cid = cid;
+ server->prog = prog;
+ server->vers = ver;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_add_tail(&server->list, &server_list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ if (pid == RPCROUTER_PID_REMOTE) {
+ rc = msm_rpcrouter_create_server_cdev(server);
+ if (rc < 0)
+ goto out_fail;
+ }
+ return server;
+out_fail:
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ kfree(server);
+ return ERR_PTR(rc);
+}
+
+static void rpcrouter_destroy_server(struct rr_server *server)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ kfree(server);
+}
+
+static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->prog == prog
+ && server->vers == ver) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->device_number == dev) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+ int i;
+
+ ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL);
+ if (!ept)
+ return NULL;
+ memset(ept, 0, sizeof(struct msm_rpc_endpoint));
+
+ /* mark no reply outstanding */
+ ept->next_rroute = 0;
+ for (i = 0; i < MAX_REPLY_ROUTE; i++)
+ ept->rroute[i].pid = 0xffffffff;
+
+ ept->cid = (uint32_t) ept;
+ ept->pid = RPCROUTER_PID_LOCAL;
+ ept->dev = dev;
+
+ if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) {
+ struct rr_server *srv;
+ /*
+ * This is a userspace client which opened
+ * a program/ver devicenode. Bind the client
+ * to that destination
+ */
+ srv = rpcrouter_lookup_server_by_dev(dev);
+ /* TODO: bug? really? */
+ BUG_ON(!srv);
+
+ ept->dst_pid = srv->pid;
+ ept->dst_cid = srv->cid;
+ ept->dst_prog = cpu_to_be32(srv->prog);
+ ept->dst_vers = cpu_to_be32(srv->vers);
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+
+ D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers);
+ } else {
+ /* mark not connected */
+ ept->dst_pid = 0xffffffff;
+ D("Creating a master local ept %p\n", ept);
+ }
+
+ init_waitqueue_head(&ept->wait_q);
+ INIT_LIST_HEAD(&ept->read_q);
+ spin_lock_init(&ept->read_q_lock);
+ wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
+ INIT_LIST_HEAD(&ept->incomplete);
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_add_tail(&ept->list, &local_endpoints);
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+}
+
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept)
+{
+ int rc;
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.pid = ept->pid;
+ msg.cli.cid = ept->cid;
+
+ RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid);
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ wake_lock_destroy(&ept->read_q_wake_lock);
+ list_del(&ept->list);
+ kfree(ept);
+ return 0;
+}
+
+static int rpcrouter_create_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *new_c;
+ unsigned long flags;
+
+ new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL);
+ if (!new_c)
+ return -ENOMEM;
+ memset(new_c, 0, sizeof(struct rr_remote_endpoint));
+
+ new_c->cid = cid;
+ new_c->pid = RPCROUTER_PID_REMOTE;
+ init_waitqueue_head(&new_c->quota_wait);
+ spin_lock_init(&new_c->quota_lock);
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_add_tail(&new_c->list, &remote_endpoints);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return 0;
+}
+
+static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_for_each_entry(ept, &local_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return NULL;
+}
+
+static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_for_each_entry(ept, &remote_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return NULL;
+}
+
+static int process_control_msg(union rr_control_msg *msg, int len)
+{
+ union rr_control_msg ctl;
+ struct rr_server *server;
+ struct rr_remote_endpoint *r_ept;
+ int rc = 0;
+ unsigned long flags;
+
+ if (len != sizeof(*msg)) {
+ printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n",
+ len, sizeof(*msg));
+ return -EINVAL;
+ }
+
+ switch (msg->cmd) {
+ case RPCROUTER_CTRL_CMD_HELLO:
+ RR("o HELLO\n");
+
+ RR("x HELLO\n");
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
+ rpcrouter_send_control_msg(&ctl);
+
+ initialized = 1;
+
+ /* Send list of servers one at a time */
+ ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+
+ /* TODO: long time to hold a spinlock... */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ ctl.srv.pid = server->pid;
+ ctl.srv.cid = server->cid;
+ ctl.srv.prog = server->prog;
+ ctl.srv.vers = server->vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ server->pid, server->cid,
+ server->prog, server->vers);
+
+ rpcrouter_send_control_msg(&ctl);
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev);
+ break;
+
+ case RPCROUTER_CTRL_CMD_RESUME_TX:
+ RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (!r_ept) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to resume client\n");
+ break;
+ }
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ r_ept->tx_quota_cntr = 0;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ wake_up(&r_ept->quota_wait);
+ break;
+
+ case RPCROUTER_CTRL_CMD_NEW_SERVER:
+ RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers);
+
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+
+ if (!server) {
+ server = rpcrouter_create_server(
+ msg->srv.pid, msg->srv.cid,
+ msg->srv.prog, msg->srv.vers);
+ if (!server)
+ return -ENOMEM;
+ /*
+ * XXX: Verify that its okay to add the
+ * client to our remote client list
+ * if we get a NEW_SERVER notification
+ */
+ if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) {
+ rc = rpcrouter_create_remote_endpoint(
+ msg->srv.cid);
+ if (rc < 0)
+ printk(KERN_ERR
+ "rpcrouter:Client create"
+ "error (%d)\n", rc);
+ }
+ schedule_work(&work_create_pdevs);
+ wake_up(&newserver_wait);
+ } else {
+ if ((server->pid == msg->srv.pid) &&
+ (server->cid == msg->srv.cid)) {
+ printk(KERN_ERR "rpcrouter: Duplicate svr\n");
+ } else {
+ server->pid = msg->srv.pid;
+ server->cid = msg->srv.cid;
+ }
+ }
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
+ RR("o REMOVE_SERVER prog=%08x:%d\n",
+ msg->srv.prog, msg->srv.vers);
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+ if (server)
+ rpcrouter_destroy_server(server);
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
+ RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+ if (msg->cli.pid != RPCROUTER_PID_REMOTE) {
+ printk(KERN_ERR
+ "rpcrouter: Denying remote removal of "
+ "local client\n");
+ break;
+ }
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (r_ept) {
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_del(&r_ept->list);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ kfree(r_ept);
+ }
+
+ /* Notify local clients of this event */
+ printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n");
+ rc = -ENOSYS;
+
+ break;
+ default:
+ RR("o UNKNOWN(%08x)\n", msg->cmd);
+ rc = -ENOSYS;
+ }
+
+ return rc;
+}
+
+static void do_create_rpcrouter_pdev(struct work_struct *work)
+{
+ if (atomic_cmpxchg(&rpcrouter_pdev_created, 0, 1) == 0)
+ platform_device_register(&rpcrouter_pdev);
+}
+
+static void do_create_pdevs(struct work_struct *work)
+{
+ unsigned long flags;
+ struct rr_server *server;
+
+ /* TODO: race if destroyed while being registered */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->pid == RPCROUTER_PID_REMOTE) {
+ if (server->pdev_name[0] == 0) {
+ spin_unlock_irqrestore(&server_list_lock,
+ flags);
+ msm_rpcrouter_create_server_pdev(server);
+ schedule_work(&work_create_pdevs);
+ return;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+}
+
+static void rpcrouter_smdnotify(void *_dev, unsigned event)
+{
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ if (smd_read_avail(smd_channel) >= rpcrouter_need_len)
+ wake_lock(&rpcrouter_wake_lock);
+ wake_up(&smd_wait);
+}
+
+static void *rr_malloc(unsigned sz)
+{
+ void *ptr = kmalloc(sz, GFP_KERNEL);
+ if (ptr)
+ return ptr;
+
+ printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz);
+ do {
+ ptr = kmalloc(sz, GFP_KERNEL);
+ } while (!ptr);
+
+ return ptr;
+}
+
+/* TODO: deal with channel teardown / restore */
+static int rr_read(void *data, int len)
+{
+ int rc;
+ unsigned long flags;
+// printk("rr_read() %d\n", len);
+ for(;;) {
+ spin_lock_irqsave(&smd_lock, flags);
+ if (smd_read_avail(smd_channel) >= len) {
+ rc = smd_read(smd_channel, data, len);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ if (rc == len)
+ return 0;
+ else
+ return -EIO;
+ }
+ rpcrouter_need_len = len;
+ wake_unlock(&rpcrouter_wake_lock);
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+// printk("rr_read: waiting (%d)\n", len);
+ smd_wait_count++;
+ wake_up(&smd_wait);
+ wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
+ smd_wait_count++;
+ }
+ return 0;
+}
+
+static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX];
+
+static void do_read_data(struct work_struct *work)
+{
+ struct rr_header hdr;
+ struct rr_packet *pkt;
+ struct rr_fragment *frag;
+ struct msm_rpc_endpoint *ept;
+ uint32_t pm, mid;
+ unsigned long flags;
+
+ if (rr_read(&hdr, sizeof(hdr)))
+ goto fail_io;
+
+#if TRACE_R2R_RAW
+ RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
+ hdr.version, hdr.type, hdr.src_pid, hdr.src_cid,
+ hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
+#endif
+
+ if (hdr.version != RPCROUTER_VERSION) {
+ DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION);
+ goto fail_data;
+ }
+ if (hdr.size > RPCROUTER_MSGSIZE_MAX) {
+ DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX);
+ goto fail_data;
+ }
+
+ if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) {
+ if (rr_read(r2r_buf, hdr.size))
+ goto fail_io;
+ process_control_msg((void*) r2r_buf, hdr.size);
+ goto done;
+ }
+
+ if (hdr.size < sizeof(pm)) {
+ DIAG("runt packet (no pacmark)\n");
+ goto fail_data;
+ }
+ if (rr_read(&pm, sizeof(pm)))
+ goto fail_io;
+
+ hdr.size -= sizeof(pm);
+
+ frag = rr_malloc(hdr.size + sizeof(*frag));
+ frag->next = NULL;
+ frag->length = hdr.size;
+ if (rr_read(frag->data, hdr.size))
+ goto fail_io;
+
+ ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid);
+ if (!ept) {
+ DIAG("no local ept for cid %08x\n", hdr.dst_cid);
+ kfree(frag);
+ goto done;
+ }
+
+ /* See if there is already a partial packet that matches our mid
+ * and if so, append this fragment to that packet.
+ */
+ mid = PACMARK_MID(pm);
+ list_for_each_entry(pkt, &ept->incomplete, list) {
+ if (pkt->mid == mid) {
+ pkt->last->next = frag;
+ pkt->last = frag;
+ pkt->length += frag->length;
+ if (PACMARK_LAST(pm)) {
+ list_del(&pkt->list);
+ goto packet_complete;
+ }
+ goto done;
+ }
+ }
+ /* This mid is new -- create a packet for it, and put it on
+ * the incomplete list if this fragment is not a last fragment,
+ * otherwise put it on the read queue.
+ */
+ pkt = rr_malloc(sizeof(struct rr_packet));
+ pkt->first = frag;
+ pkt->last = frag;
+ memcpy(&pkt->hdr, &hdr, sizeof(hdr));
+ pkt->mid = mid;
+ pkt->length = frag->length;
+ if (!PACMARK_LAST(pm)) {
+ list_add_tail(&pkt->list, &ept->incomplete);
+ goto done;
+ }
+
+packet_complete:
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ if (ept->flags & MSM_RPC_ENABLE_RECEIVE) {
+ wake_lock(&ept->read_q_wake_lock);
+ list_add_tail(&pkt->list, &ept->read_q);
+ wake_up(&ept->wait_q);
+ } else {
+ pr_warning("smd_rpcrouter: Unexpected incoming data on %08x:%08x\n",
+ be32_to_cpu(ept->dst_prog),
+ be32_to_cpu(ept->dst_vers));
+ }
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+done:
+
+ if (hdr.confirm_rx) {
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX;
+ msg.cli.pid = hdr.dst_pid;
+ msg.cli.cid = hdr.dst_cid;
+
+ RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid);
+ rpcrouter_send_control_msg(&msg);
+ }
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return;
+
+fail_io:
+fail_data:
+ printk(KERN_ERR "rpc_router has died\n");
+ wake_unlock(&rpcrouter_wake_lock);
+}
+
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog,
+ uint32_t vers, uint32_t proc)
+{
+ memset(hdr, 0, sizeof(struct rpc_request_hdr));
+ hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ hdr->rpc_vers = cpu_to_be32(2);
+ hdr->prog = cpu_to_be32(prog);
+ hdr->vers = cpu_to_be32(vers);
+ hdr->procedure = cpu_to_be32(proc);
+}
+
+struct msm_rpc_endpoint *msm_rpc_open(void)
+{
+ struct msm_rpc_endpoint *ept;
+
+ ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0));
+ if (ept == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ return ept;
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+EXPORT_SYMBOL(msm_rpc_close);
+
+static int msm_rpc_write_pkt(struct msm_rpc_endpoint *ept,
+ struct rr_remote_endpoint *r_ept,
+ struct rr_header *hdr,
+ uint32_t pacmark,
+ void *buffer, int count)
+{
+ DEFINE_WAIT(__wait);
+ unsigned long flags;
+ int needed;
+
+ for (;;) {
+ prepare_to_wait(&r_ept->quota_wait, &__wait,
+ TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA)
+ break;
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))
+ break;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ schedule();
+ }
+ finish_wait(&r_ept->quota_wait, &__wait);
+
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ return -ERESTARTSYS;
+ }
+ r_ept->tx_quota_cntr++;
+ if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA)
+ hdr->confirm_rx = 1;
+
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+ spin_lock_irqsave(&smd_lock, flags);
+
+ needed = sizeof(*hdr) + hdr->size;
+ while (smd_write_avail(smd_channel) < needed) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+
+ /* TODO: deal with full fifo */
+ smd_write(smd_channel, hdr, sizeof(*hdr));
+ smd_write(smd_channel, &pacmark, sizeof(pacmark));
+ smd_write(smd_channel, buffer, count);
+
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+ return 0;
+}
+
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
+{
+ struct rr_header hdr;
+ uint32_t pacmark;
+ uint32_t mid;
+ struct rpc_request_hdr *rq = buffer;
+ struct rr_remote_endpoint *r_ept;
+ int ret;
+ int total;
+
+ /* snoop the RPC packet and enforce permissions */
+
+ /* has to have at least the xid and type fields */
+ if (count < (sizeof(uint32_t) * 2)) {
+ printk(KERN_ERR "rr_write: rejecting runt packet\n");
+ return -EINVAL;
+ }
+
+ if (rq->type == 0) {
+ /* RPC CALL */
+ if (count < (sizeof(uint32_t) * 6)) {
+ printk(KERN_ERR
+ "rr_write: rejecting runt call packet\n");
+ return -EINVAL;
+ }
+ if (ept->dst_pid == 0xffffffff) {
+ printk(KERN_ERR "rr_write: not connected\n");
+ return -ENOTCONN;
+ }
+
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ if ((ept->dst_prog != rq->prog) ||
+ !msm_rpc_is_compatible_version(
+ be32_to_cpu(ept->dst_vers),
+ be32_to_cpu(rq->vers))) {
+#else
+ if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) {
+#endif
+ printk(KERN_ERR
+ "rr_write: cannot write to %08x:%d "
+ "(bound to %08x:%d)\n",
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(ept->dst_prog),
+ be32_to_cpu(ept->dst_vers));
+ return -EINVAL;
+ }
+ hdr.dst_pid = ept->dst_pid;
+ hdr.dst_cid = ept->dst_cid;
+ IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n",
+ ept,
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ ept->dst_pid, ept->dst_cid, count,
+ be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure));
+ } else {
+ /* RPC REPLY */
+ /* TODO: locking */
+ for (ret = 0; ret < MAX_REPLY_ROUTE; ret++)
+ if (ept->rroute[ret].xid == rq->xid) {
+ if (ept->rroute[ret].pid == 0xffffffff)
+ continue;
+ hdr.dst_pid = ept->rroute[ret].pid;
+ hdr.dst_cid = ept->rroute[ret].cid;
+ /* consume this reply */
+ ept->rroute[ret].pid = 0xffffffff;
+ goto found_rroute;
+ }
+
+ printk(KERN_ERR "rr_write: rejecting packet w/ bad xid\n");
+ return -EINVAL;
+
+found_rroute:
+ IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n",
+ ept,
+ be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
+ }
+
+ r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid);
+
+ if (!r_ept) {
+ printk(KERN_ERR
+ "msm_rpc_write(): No route to ept "
+ "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
+ return -EHOSTUNREACH;
+ }
+
+ /* Create routing header */
+ hdr.type = RPCROUTER_CTRL_CMD_DATA;
+ hdr.version = RPCROUTER_VERSION;
+ hdr.src_pid = ept->pid;
+ hdr.src_cid = ept->cid;
+
+ total = count;
+
+ mid = atomic_add_return(1, &next_mid) & 0xFF;
+
+ while (count > 0) {
+ unsigned xfer;
+
+ if (count > RPCROUTER_DATASIZE_MAX)
+ xfer = RPCROUTER_DATASIZE_MAX;
+ else
+ xfer = count;
+
+ hdr.confirm_rx = 0;
+ hdr.size = xfer + sizeof(uint32_t);
+
+ /* total == count -> must be first packet
+ * xfer == count -> must be last packet
+ */
+ pacmark = PACMARK(xfer, mid, (total == count), (xfer == count));
+
+ ret = msm_rpc_write_pkt(ept, r_ept, &hdr, pacmark, buffer, xfer);
+ if (ret < 0)
+ return ret;
+
+ buffer += xfer;
+ count -= xfer;
+ }
+
+ return total;
+}
+EXPORT_SYMBOL(msm_rpc_write);
+
+/*
+ * NOTE: It is the responsibility of the caller to kfree buffer
+ */
+int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer,
+ unsigned user_len, long timeout)
+{
+ struct rr_fragment *frag, *next;
+ char *buf;
+ int rc;
+
+ rc = __msm_rpc_read(ept, &frag, user_len, timeout);
+ if (rc <= 0)
+ return rc;
+
+ /* single-fragment messages conveniently can be
+ * returned as-is (the buffer is at the front)
+ */
+ if (frag->next == 0) {
+ *buffer = (void*) frag;
+ return rc;
+ }
+
+ /* multi-fragment messages, we have to do it the
+ * hard way, which is rather disgusting right now
+ */
+ buf = rr_malloc(rc);
+ *buffer = buf;
+
+ while (frag != NULL) {
+ memcpy(buf, frag->data, frag->length);
+ next = frag->next;
+ buf += frag->length;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ long timeout)
+{
+ return msm_rpc_call_reply(ept, proc,
+ _request, request_size,
+ NULL, 0, timeout);
+}
+EXPORT_SYMBOL(msm_rpc_call);
+
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ void *_reply, int reply_size,
+ long timeout)
+{
+ struct rpc_request_hdr *req = _request;
+ struct rpc_reply_hdr *reply;
+ int rc;
+
+ if (request_size < sizeof(*req))
+ return -ETOOSMALL;
+
+ if (ept->dst_pid == 0xffffffff)
+ return -ENOTCONN;
+
+ /* We can't use msm_rpc_setup_req() here, because dst_prog and
+ * dst_vers here are already in BE.
+ */
+ memset(req, 0, sizeof(*req));
+ req->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ req->rpc_vers = cpu_to_be32(2);
+ req->prog = ept->dst_prog;
+ req->vers = ept->dst_vers;
+ req->procedure = cpu_to_be32(proc);
+
+ /* Allow replys to be added to the queue */
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+
+ rc = msm_rpc_write(ept, req, request_size);
+ if (rc < 0)
+ goto error;
+
+ for (;;) {
+ rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
+ if (rc < 0)
+ goto error;
+ if (rc < (3 * sizeof(uint32_t))) {
+ rc = -EIO;
+ break;
+ }
+ /* we should not get CALL packets -- ignore them */
+ if (reply->type == 0) {
+ kfree(reply);
+ continue;
+ }
+ /* If an earlier call timed out, we could get the (no
+ * longer wanted) reply for it. Ignore replies that
+ * we don't expect.
+ */
+ if (reply->xid != req->xid) {
+ kfree(reply);
+ continue;
+ }
+ if (reply->reply_stat != 0) {
+ rc = -EPERM;
+ break;
+ }
+ if (reply->data.acc_hdr.accept_stat != 0) {
+ rc = -EINVAL;
+ break;
+ }
+ if (_reply == NULL) {
+ rc = 0;
+ break;
+ }
+ if (rc > reply_size) {
+ rc = -ENOMEM;
+ } else {
+ memcpy(_reply, reply, rc);
+ }
+ break;
+ }
+ kfree(reply);
+error:
+ ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
+ wake_unlock(&ept->read_q_wake_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_rpc_call_reply);
+
+
+static inline int ept_packet_available(struct msm_rpc_endpoint *ept)
+{
+ unsigned long flags;
+ int ret;
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ ret = !list_empty(&ept->read_q);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return ret;
+}
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag_ret,
+ unsigned len, long timeout)
+{
+ struct rr_packet *pkt;
+ struct rpc_request_hdr *rq;
+ DEFINE_WAIT(__wait);
+ unsigned long flags;
+ int rc;
+
+ IO("READ on ept %p\n", ept);
+
+ if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
+ if (timeout < 0) {
+ wait_event(ept->wait_q, ept_packet_available(ept));
+ } else {
+ rc = wait_event_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ } else {
+ if (timeout < 0) {
+ rc = wait_event_interruptible(
+ ept->wait_q, ept_packet_available(ept));
+ if (rc < 0)
+ return rc;
+ } else {
+ rc = wait_event_interruptible_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ }
+
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ if (list_empty(&ept->read_q)) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -EAGAIN;
+ }
+ pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
+ if (pkt->length > len) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -ETOOSMALL;
+ }
+ list_del(&pkt->list);
+ if (list_empty(&ept->read_q))
+ wake_unlock(&ept->read_q_wake_lock);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+ rc = pkt->length;
+
+ *frag_ret = pkt->first;
+ rq = (void*) pkt->first->data;
+ if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) {
+ IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n",
+ ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(rq->procedure),
+ be32_to_cpu(rq->xid));
+ /* RPC CALL */
+ if (ept->rroute[ept->next_rroute].pid != 0xffffffff) {
+ printk(KERN_WARNING
+ "rr_read: lost previous reply xid...\n");
+ }
+ /* TODO: locking? */
+ ept->rroute[ept->next_rroute].pid = pkt->hdr.src_pid;
+ ept->rroute[ept->next_rroute].cid = pkt->hdr.src_cid;
+ ept->rroute[ept->next_rroute].xid = rq->xid;
+ ept->next_rroute = (ept->next_rroute + 1) & (MAX_REPLY_ROUTE - 1);
+ }
+#if TRACE_RPC_MSG
+ else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1))
+ IO("READ on ept %p is a REPLY\n", ept);
+ else IO("READ on ept %p (%d bytes)\n", ept, rc);
+#endif
+
+ kfree(pkt);
+ return rc;
+}
+
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+int msm_rpc_is_compatible_version(uint32_t server_version,
+ uint32_t client_version)
+{
+ if ((server_version & RPC_VERSION_MODE_MASK) !=
+ (client_version & RPC_VERSION_MODE_MASK))
+ return 0;
+
+ if (server_version & RPC_VERSION_MODE_MASK)
+ return server_version == client_version;
+
+ return ((server_version & RPC_VERSION_MAJOR_MASK) ==
+ (client_version & RPC_VERSION_MAJOR_MASK)) &&
+ ((server_version & RPC_VERSION_MINOR_MASK) >=
+ (client_version & RPC_VERSION_MINOR_MASK));
+}
+EXPORT_SYMBOL(msm_rpc_is_compatible_version);
+
+static int msm_rpc_get_compatible_server(uint32_t prog,
+ uint32_t ver,
+ uint32_t *found_vers)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ if (found_vers == NULL)
+ return 0;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if ((server->prog == prog) &&
+ msm_rpc_is_compatible_version(server->vers, ver)) {
+ *found_vers = server->vers;
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return -1;
+}
+#endif
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_server *server;
+
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ if (!(vers & RPC_VERSION_MODE_MASK)) {
+ uint32_t found_vers;
+ if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0)
+ return ERR_PTR(-EHOSTUNREACH);
+ if (found_vers != vers) {
+ D("RPC using new version %08x:{%08x --> %08x}\n",
+ prog, vers, found_vers);
+ vers = found_vers;
+ }
+ }
+#endif
+
+ server = rpcrouter_lookup_server(prog, vers);
+ if (!server)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ ept = msm_rpc_open();
+ if (IS_ERR(ept))
+ return ept;
+
+ ept->flags = flags;
+ ept->dst_pid = server->pid;
+ ept->dst_cid = server->cid;
+ ept->dst_prog = cpu_to_be32(prog);
+ ept->dst_vers = cpu_to_be32(vers);
+
+ return ept;
+}
+EXPORT_SYMBOL(msm_rpc_connect);
+
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept)
+{
+ return be32_to_cpu(ept->dst_vers);
+}
+EXPORT_SYMBOL(msm_rpc_get_vers);
+
+/* TODO: permission check? */
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ int rc;
+ union rr_control_msg msg;
+ struct rr_server *server;
+
+ server = rpcrouter_create_server(ept->pid, ept->cid,
+ prog, vers);
+ if (!server)
+ return -ENODEV;
+
+ msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+ msg.srv.pid = ept->pid;
+ msg.srv.cid = ept->cid;
+ msg.srv.prog = prog;
+ msg.srv.vers = vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ ept->pid, ept->cid, prog, vers);
+
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+ return 0;
+}
+
+/* TODO: permission check -- disallow unreg of somebody else's server */
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ struct rr_server *server;
+ server = rpcrouter_lookup_server(prog, vers);
+
+ if (!server)
+ return -ENOENT;
+
+ ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
+ wake_unlock(&ept->read_q_wake_lock);
+ rpcrouter_destroy_server(server);
+ return 0;
+}
+
+static int msm_rpcrouter_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ /* Initialize what we need to start processing */
+ INIT_LIST_HEAD(&local_endpoints);
+ INIT_LIST_HEAD(&remote_endpoints);
+
+ init_waitqueue_head(&newserver_wait);
+ init_waitqueue_head(&smd_wait);
+ wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL");
+
+ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
+ if (!rpcrouter_workqueue)
+ return -ENOMEM;
+
+ rc = msm_rpcrouter_init_devices();
+ if (rc < 0)
+ goto fail_destroy_workqueue;
+
+ /* Open up SMD channel 2 */
+ initialized = 0;
+ rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
+ if (rc < 0)
+ goto fail_remove_devices;
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return 0;
+
+ fail_remove_devices:
+ msm_rpcrouter_exit_devices();
+ fail_destroy_workqueue:
+ destroy_workqueue(rpcrouter_workqueue);
+ return rc;
+}
+
+static int msm_rpcrouter_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ /* Wait until the worker thread has waited at least once so that it
+ * gets a chance to release its wakelock.
+ */
+ int wait_count = smd_wait_count;
+ if (!(smd_wait_count & 1))
+ wait_event(smd_wait, smd_wait_count != wait_count);
+ return 0;
+}
+
+static struct platform_driver msm_smd_channel2_driver = {
+ .probe = msm_rpcrouter_probe,
+ .driver = {
+ .name = "SMD_RPCCALL",
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_rpcrouter_suspend,
+};
+
+static int __init rpcrouter_init(void)
+{
+ return platform_driver_register(&msm_smd_channel2_driver);
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h
new file mode 100644
index 0000000..2bf541a
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.h
@@ -0,0 +1,202 @@
+/** arch/arm/mach-msm/smd_rpcrouter.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.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 _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_rpcrouter.h>
+
+/* definitions for the R2R wire protcol */
+
+#define RPCROUTER_VERSION 1
+#define RPCROUTER_PROCESSORS_MAX 4
+#define RPCROUTER_MSGSIZE_MAX 512
+#define RPCROUTER_DATASIZE_MAX 500
+
+#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff
+#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe
+
+#define RPCROUTER_PID_LOCAL 1
+#define RPCROUTER_PID_REMOTE 0
+
+#define RPCROUTER_CTRL_CMD_DATA 1
+#define RPCROUTER_CTRL_CMD_HELLO 2
+#define RPCROUTER_CTRL_CMD_BYE 3
+#define RPCROUTER_CTRL_CMD_NEW_SERVER 4
+#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5
+#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6
+#define RPCROUTER_CTRL_CMD_RESUME_TX 7
+#define RPCROUTER_CTRL_CMD_EXIT 8
+
+#define RPCROUTER_DEFAULT_RX_QUOTA 5
+
+union rr_control_msg {
+ uint32_t cmd;
+ struct {
+ uint32_t cmd;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t pid;
+ uint32_t cid;
+ } srv;
+ struct {
+ uint32_t cmd;
+ uint32_t pid;
+ uint32_t cid;
+ } cli;
+};
+
+struct rr_header {
+ uint32_t version;
+ uint32_t type;
+ uint32_t src_pid;
+ uint32_t src_cid;
+ uint32_t confirm_rx;
+ uint32_t size;
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+};
+
+/* internals */
+
+#define RPCROUTER_MAX_REMOTE_SERVERS 100
+
+struct rr_fragment {
+ unsigned char data[RPCROUTER_MSGSIZE_MAX];
+ uint32_t length;
+ struct rr_fragment *next;
+};
+
+struct rr_packet {
+ struct list_head list;
+ struct rr_fragment *first;
+ struct rr_fragment *last;
+ struct rr_header hdr;
+ uint32_t mid;
+ uint32_t length;
+};
+
+#define PACMARK_LAST(n) ((n) & 0x80000000)
+#define PACMARK_MID(n) (((n) >> 16) & 0xFF)
+#define PACMARK_LEN(n) ((n) & 0xFFFF)
+
+static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first,
+ uint32_t last)
+{
+ return (len & 0xFFFF) |
+ ((mid & 0xFF) << 16) |
+ ((!!first) << 30) |
+ ((!!last) << 31);
+}
+
+struct rr_server {
+ struct list_head list;
+
+ uint32_t pid;
+ uint32_t cid;
+ uint32_t prog;
+ uint32_t vers;
+
+ dev_t device_number;
+ struct cdev cdev;
+ struct device *device;
+ struct rpcsvr_platform_device p_device;
+ char pdev_name[32];
+};
+
+struct rr_remote_endpoint {
+ uint32_t pid;
+ uint32_t cid;
+
+ int tx_quota_cntr;
+ spinlock_t quota_lock;
+ wait_queue_head_t quota_wait;
+
+ struct list_head list;
+};
+
+struct msm_reply_route {
+ uint32_t xid;
+ uint32_t pid;
+ uint32_t cid;
+ uint32_t unused;
+};
+
+#define MAX_REPLY_ROUTE 4
+
+struct msm_rpc_endpoint {
+ struct list_head list;
+
+ /* incomplete packets waiting for assembly */
+ struct list_head incomplete;
+
+ /* complete packets waiting to be read */
+ struct list_head read_q;
+ spinlock_t read_q_lock;
+ struct wake_lock read_q_wake_lock;
+ wait_queue_head_t wait_q;
+ unsigned flags;
+
+ /* endpoint address */
+ uint32_t pid;
+ uint32_t cid;
+
+ /* bound remote address
+ * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail
+ * RPC_CALLs must be to the prog/vers below or they will fail
+ */
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+ uint32_t dst_prog; /* be32 */
+ uint32_t dst_vers; /* be32 */
+
+ /* RPC_REPLY writes must be routed to the pid/cid of the
+ * RPC_CALL they are in reply to. Keep a cache of valid
+ * xid/pid/cid groups. pid 0xffffffff -> not valid.
+ */
+ unsigned next_rroute;
+ struct msm_reply_route rroute[MAX_REPLY_ROUTE];
+
+ /* device node if this endpoint is accessed via userspace */
+ dev_t dev;
+};
+
+/* shared between smd_rpcrouter*.c */
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag,
+ unsigned len, long timeout);
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server);
+int msm_rpcrouter_create_server_pdev(struct rr_server *server);
+
+int msm_rpcrouter_init_devices(void);
+void msm_rpcrouter_exit_devices(void);
+
+extern dev_t msm_rpcrouter_devno;
+extern struct class *msm_rpcrouter_class;
+#endif
diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c
new file mode 100644
index 0000000..1ae83b5
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter_device.c
@@ -0,0 +1,377 @@
+/* arch/arm/mach-msm/smd_rpcrouter_device.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/msm_rpcrouter.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "smd_rpcrouter.h"
+
+#define SAFETY_MEM_SIZE 65536
+
+/* Next minor # available for a remote server */
+static int next_minor = 1;
+
+struct class *msm_rpcrouter_class;
+dev_t msm_rpcrouter_devno;
+
+static struct cdev rpcrouter_cdev;
+static struct device *rpcrouter_device;
+
+static int rpcrouter_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+ struct msm_rpc_endpoint *ept;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
+ if (!ept)
+ return -ENOMEM;
+
+ filp->private_data = ept;
+ return 0;
+}
+
+static int rpcrouter_release(struct inode *inode, struct file *filp)
+{
+ struct msm_rpc_endpoint *ept;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+
+static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_fragment *frag, *next;
+ int rc;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ rc = __msm_rpc_read(ept, &frag, count, -1);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ while (frag != NULL) {
+ if (copy_to_user(buf, frag->data, frag->length)) {
+ printk(KERN_ERR
+ "rpcrouter: could not copy all read data to user!\n");
+ rc = -EFAULT;
+ }
+ buf += frag->length;
+ next = frag->next;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ int rc = 0;
+ void *k_buffer;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* A check for safety, this seems non-standard */
+ if (count > SAFETY_MEM_SIZE)
+ return -EINVAL;
+
+ k_buffer = kmalloc(count, GFP_KERNEL);
+ if (!k_buffer)
+ return -ENOMEM;
+
+ if (copy_from_user(k_buffer, buf, count)) {
+ rc = -EFAULT;
+ goto write_out_free;
+ }
+
+ rc = msm_rpc_write(ept, k_buffer, count);
+ if (rc < 0)
+ goto write_out_free;
+
+ rc = count;
+write_out_free:
+ kfree(k_buffer);
+ return rc;
+}
+
+static unsigned int rpcrouter_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned mask = 0;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* If there's data already in the read queue, return POLLIN.
+ * Else, wait for the requested amount of time, and check again.
+ */
+
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+
+ if (!mask) {
+ poll_wait(filp, &ept->wait_q, wait);
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+
+static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rpcrouter_ioctl_server_args server_args;
+ int rc = 0;
+ uint32_t n;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+ switch (cmd) {
+
+ case RPC_ROUTER_IOCTL_GET_VERSION:
+ n = RPC_ROUTER_VERSION_V1;
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MTU:
+ /* the pacmark word reduces the actual payload
+ * possible per message
+ */
+ n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_REGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+ msm_rpc_register_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+
+ msm_rpc_unregister_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
+ n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
+ rc = put_user(n, (unsigned int *)arg);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static struct file_operations rpcrouter_server_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+static struct file_operations rpcrouter_router_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server)
+{
+ int rc;
+ uint32_t dev_vers;
+
+ if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
+ printk(KERN_ERR
+ "rpcrouter: Minor numbers exhausted - Increase "
+ "RPCROUTER_MAX_REMOTE_SERVERS\n");
+ return -ENOBUFS;
+ }
+
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ /* Servers with bit 31 set are remote msm servers with hashkey version.
+ * Servers with bit 31 not set are remote msm servers with
+ * backwards compatible version type in which case the minor number
+ * (lower 16 bits) is set to zero.
+ *
+ */
+ if ((server->vers & RPC_VERSION_MODE_MASK))
+ dev_vers = server->vers;
+ else
+ dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
+#else
+ dev_vers = server->vers;
+#endif
+
+ server->device_number =
+ MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);
+
+ server->device =
+ device_create(msm_rpcrouter_class, rpcrouter_device,
+ server->device_number, NULL, "%.8x:%.8x",
+ server->prog, dev_vers);
+ if (IS_ERR(server->device)) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to create device (%ld)\n",
+ PTR_ERR(server->device));
+ return PTR_ERR(server->device);;
+ }
+
+ cdev_init(&server->cdev, &rpcrouter_server_fops);
+ server->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&server->cdev, server->device_number, 1);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to add chrdev (%d)\n", rc);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ return rc;
+ }
+ return 0;
+}
+
+/* for backward compatible version type (31st bit cleared)
+ * clearing minor number (lower 16 bits) in device name
+ * is neccessary for driver binding
+ */
+int msm_rpcrouter_create_server_pdev(struct rr_server *server)
+{
+ sprintf(server->pdev_name, "rs%.8x:%.8x",
+ server->prog,
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ (server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
+ (server->vers & RPC_VERSION_MAJOR_MASK));
+#else
+ server->vers);
+#endif
+
+ server->p_device.base.id = -1;
+ server->p_device.base.name = server->pdev_name;
+
+ server->p_device.prog = server->prog;
+ server->p_device.vers = server->vers;
+
+ platform_device_register(&server->p_device.base);
+ return 0;
+}
+
+int msm_rpcrouter_init_devices(void)
+{
+ int rc;
+ int major;
+
+ /* Create the device nodes */
+ msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
+ if (IS_ERR(msm_rpcrouter_class)) {
+ rc = -ENOMEM;
+ printk(KERN_ERR
+ "rpcrouter: failed to create oncrpc class\n");
+ goto fail;
+ }
+
+ rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1,
+ "oncrpc");
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
+ goto fail_destroy_class;
+ }
+
+ major = MAJOR(msm_rpcrouter_devno);
+ rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
+ msm_rpcrouter_devno, NULL, "%.8x:%d",
+ 0, 0);
+ if (IS_ERR(rpcrouter_device)) {
+ rc = -ENOMEM;
+ goto fail_unregister_cdev_region;
+ }
+
+ cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
+ rpcrouter_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
+ if (rc < 0)
+ goto fail_destroy_device;
+
+ return 0;
+
+fail_destroy_device:
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+fail_unregister_cdev_region:
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+fail_destroy_class:
+ class_destroy(msm_rpcrouter_class);
+fail:
+ return rc;
+}
+
+void msm_rpcrouter_exit_devices(void)
+{
+ cdev_del(&rpcrouter_cdev);
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+ class_destroy(msm_rpcrouter_class);
+}
+
diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c
new file mode 100644
index 0000000..8396002
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c
@@ -0,0 +1,230 @@
+/* arch/arm/mach-msm/rpc_servers.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_rpcrouter.h>
+#include <linux/uaccess.h>
+
+#include <mach/msm_rpcrouter.h>
+#include "smd_rpcrouter.h"
+
+static struct msm_rpc_endpoint *endpoint;
+
+#define FLAG_REGISTERED 0x0001
+
+static LIST_HEAD(rpc_server_list);
+static DEFINE_MUTEX(rpc_server_list_lock);
+static int rpc_servers_active;
+static struct wake_lock rpc_servers_wake_lock;
+
+static void rpc_server_register(struct msm_rpc_server *server)
+{
+ int rc;
+ rc = msm_rpc_register_server(endpoint, server->prog, server->vers);
+ if (rc < 0)
+ printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n",
+ server, server->prog, server->vers);
+}
+
+static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if ((server->prog == prog) &&
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ msm_rpc_is_compatible_version(server->vers, vers)) {
+#else
+ server->vers == vers) {
+#endif
+ mutex_unlock(&rpc_server_list_lock);
+ return server;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+ return NULL;
+}
+
+static void rpc_server_register_all(void)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if (!(server->flags & FLAG_REGISTERED)) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+}
+
+int msm_rpc_create_server(struct msm_rpc_server *server)
+{
+ /* make sure we're in a sane state first */
+ server->flags = 0;
+ INIT_LIST_HEAD(&server->list);
+
+ mutex_lock(&rpc_server_list_lock);
+ list_add(&server->list, &rpc_server_list);
+ if (rpc_servers_active) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ mutex_unlock(&rpc_server_list_lock);
+
+ return 0;
+}
+
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+ uint32_t xid, uint32_t accept_status)
+{
+ int rc = 0;
+ uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+ struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+ reply->xid = cpu_to_be32(xid);
+ reply->type = cpu_to_be32(1); /* reply */
+ reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+ reply->data.acc_hdr.verf_flavor = 0;
+ reply->data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf));
+ if (rc < 0)
+ printk(KERN_ERR
+ "%s: could not write response: %d\n",
+ __FUNCTION__, rc);
+
+ return rc;
+}
+
+static int rpc_servers_thread(void *data)
+{
+ void *buffer;
+ struct rpc_request_hdr *req;
+ struct msm_rpc_server *server;
+ int rc;
+
+ for (;;) {
+ wake_unlock(&rpc_servers_wake_lock);
+ rc = wait_event_interruptible(endpoint->wait_q,
+ !list_empty(&endpoint->read_q));
+ wake_lock(&rpc_servers_wake_lock);
+ rc = msm_rpc_read(endpoint, &buffer, -1, -1);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: could not read: %d\n",
+ __FUNCTION__, rc);
+ break;
+ }
+ req = (struct rpc_request_hdr *)buffer;
+
+ req->type = be32_to_cpu(req->type);
+ req->xid = be32_to_cpu(req->xid);
+ req->rpc_vers = be32_to_cpu(req->rpc_vers);
+ req->prog = be32_to_cpu(req->prog);
+ req->vers = be32_to_cpu(req->vers);
+ req->procedure = be32_to_cpu(req->procedure);
+
+ server = rpc_server_find(req->prog, req->vers);
+
+ if (req->rpc_vers != 2)
+ continue;
+ if (req->type != 0)
+ continue;
+ if (!server) {
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ continue;
+ }
+
+ rc = server->rpc_call(server, req, rc);
+
+ switch (rc) {
+ case 0:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ break;
+ default:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ break;
+ }
+
+ kfree(buffer);
+ }
+
+ do_exit(0);
+}
+
+static int rpcservers_probe(struct platform_device *pdev)
+{
+ struct task_struct *server_thread;
+
+ endpoint = msm_rpc_open();
+ if (IS_ERR(endpoint))
+ return PTR_ERR(endpoint);
+
+ /* we're online -- register any servers installed beforehand */
+ rpc_servers_active = 1;
+ rpc_server_register_all();
+
+ /* start the kernel thread */
+ server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd");
+ if (IS_ERR(server_thread))
+ return PTR_ERR(server_thread);
+
+ return 0;
+}
+
+static struct platform_driver rpcservers_driver = {
+ .probe = rpcservers_probe,
+ .driver = {
+ .name = "oncrpc_router",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rpc_servers_init(void)
+{
+ wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server");
+ return platform_driver_register(&rpcservers_driver);
+}
+
+module_init(rpc_servers_init);
+
+MODULE_DESCRIPTION("MSM RPC Servers");
+MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
new file mode 100644
index 0000000..8715544
--- /dev/null
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -0,0 +1,229 @@
+/* arch/arm/mach-msm/smd_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <mach/msm_smd.h>
+
+#define MAX_SMD_TTYS 32
+
+static DEFINE_MUTEX(smd_tty_lock);
+
+struct smd_tty_info {
+ smd_channel_t *ch;
+ struct tty_struct *tty;
+ struct wake_lock wake_lock;
+ int open_count;
+};
+
+static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
+
+static const struct smd_tty_channel_desc smd_default_tty_channels[] = {
+ { .id = 0, .name = "SMD_DS" },
+ { .id = 27, .name = "SMD_GPSNMEA" },
+};
+
+static const struct smd_tty_channel_desc *smd_tty_channels =
+ smd_default_tty_channels;
+static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels);
+
+int smd_set_channel_list(const struct smd_tty_channel_desc *channels, int len)
+{
+ smd_tty_channels = channels;
+ smd_tty_channels_len = len;
+ return 0;
+}
+
+static void smd_tty_notify(void *priv, unsigned event)
+{
+ unsigned char *ptr;
+ int avail;
+ struct smd_tty_info *info = priv;
+ struct tty_struct *tty = info->tty;
+
+ if (!tty)
+ return;
+
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ for (;;) {
+ if (test_bit(TTY_THROTTLED, &tty->flags)) break;
+ avail = smd_read_avail(info->ch);
+ if (avail == 0) break;
+
+ avail = tty_prepare_flip_string(tty, &ptr, avail);
+
+ if (smd_read(info->ch, ptr, avail) != avail) {
+ /* shouldn't be possible since we're in interrupt
+ ** context here and nobody else could 'steal' our
+ ** characters.
+ */
+ printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
+ }
+
+ wake_lock_timeout(&info->wake_lock, HZ / 2);
+ tty_flip_buffer_push(tty);
+ }
+
+ /* XXX only when writable and necessary */
+ tty_wakeup(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+ int res = 0;
+ int n = tty->index;
+ struct smd_tty_info *info;
+ const char *name = NULL;
+ int i;
+
+ for (i = 0; i < smd_tty_channels_len; i++) {
+ if (smd_tty_channels[i].id == n) {
+ name = smd_tty_channels[i].name;
+ break;
+ }
+ }
+ if (!name)
+ return -ENODEV;
+
+ info = smd_tty + n;
+
+ mutex_lock(&smd_tty_lock);
+ wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, name);
+ tty->driver_data = info;
+
+ if (info->open_count++ == 0) {
+ info->tty = tty;
+ if (info->ch) {
+ smd_kick(info->ch);
+ } else {
+ res = smd_open(name, &info->ch, info, smd_tty_notify);
+ }
+ }
+ mutex_unlock(&smd_tty_lock);
+
+ return res;
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = tty->driver_data;
+
+ if (info == 0)
+ return;
+
+ mutex_lock(&smd_tty_lock);
+ if (--info->open_count == 0) {
+ info->tty = 0;
+ tty->driver_data = 0;
+ wake_lock_destroy(&info->wake_lock);
+ if (info->ch) {
+ smd_close(info->ch);
+ info->ch = 0;
+ }
+ }
+ mutex_unlock(&smd_tty_lock);
+}
+
+static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ int avail;
+
+ /* if we're writing to a packet channel we will
+ ** never be able to write more data than there
+ ** is currently space for
+ */
+ avail = smd_write_avail(info->ch);
+ if (len > avail)
+ len = avail;
+
+ return smd_write(info->ch, buf, len);
+}
+
+static int smd_tty_write_room(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ return smd_write_avail(info->ch);
+}
+
+static int smd_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ return smd_read_avail(info->ch);
+}
+
+static void smd_tty_unthrottle(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ smd_kick(info->ch);
+}
+
+static struct tty_operations smd_tty_ops = {
+ .open = smd_tty_open,
+ .close = smd_tty_close,
+ .write = smd_tty_write,
+ .write_room = smd_tty_write_room,
+ .chars_in_buffer = smd_tty_chars_in_buffer,
+ .unthrottle = smd_tty_unthrottle,
+};
+
+static struct tty_driver *smd_tty_driver;
+
+static int __init smd_tty_init(void)
+{
+ int ret, i;
+
+ smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
+ if (smd_tty_driver == 0)
+ return -ENOMEM;
+
+ smd_tty_driver->owner = THIS_MODULE;
+ smd_tty_driver->driver_name = "smd_tty_driver";
+ smd_tty_driver->name = "smd";
+ smd_tty_driver->major = 0;
+ smd_tty_driver->minor_start = 0;
+ smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ smd_tty_driver->init_termios = tty_std_termios;
+ smd_tty_driver->init_termios.c_iflag = 0;
+ smd_tty_driver->init_termios.c_oflag = 0;
+ smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ smd_tty_driver->init_termios.c_lflag = 0;
+ smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(smd_tty_driver, &smd_tty_ops);
+
+ ret = tty_register_driver(smd_tty_driver);
+ if (ret) return ret;
+
+ for (i = 0; i < smd_tty_channels_len; i++)
+ tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0);
+
+ return 0;
+}
+
+module_init(smd_tty_init);
diff --git a/arch/arm/mach-msm/ssbi.c b/arch/arm/mach-msm/ssbi.c
new file mode 100644
index 0000000..b9cefa2
--- /dev/null
+++ b/arch/arm/mach-msm/ssbi.c
@@ -0,0 +1,304 @@
+/* arch/arm/mach-msm/ssbi.c
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010, Google Inc.
+ *
+ * Original authors: Code Aurura Forum
+ *
+ * Author: Dima Zavin <dima@android.com>
+ * - Largely rewritten from original to not be an i2c driver.
+ *
+ * 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/msm_ssbi.h>
+#include <mach/remote_spinlock.h>
+
+/* SSBI 2.0 controller registers */
+#define SSBI2_CTL 0x0000
+#define SSBI2_RESET 0x0004
+#define SSBI2_CMD 0x0008
+#define SSBI2_RD 0x0010
+#define SSBI2_STATUS 0x0014
+#define SSBI2_PRIORITIES 0x0018
+#define SSBI2_MODE2 0x001C
+
+/* SSBI_CMD fields */
+#define SSBI_CMD_SEND_TERM_SYM (1 << 27)
+#define SSBI_CMD_WAKEUP_SLAVE (1 << 26)
+#define SSBI_CMD_USE_ENABLE (1 << 25)
+#define SSBI_CMD_RDWRN (1 << 24)
+
+/* SSBI_STATUS fields */
+#define SSBI_STATUS_DATA_IN (1 << 4)
+#define SSBI_STATUS_RD_CLOBBERED (1 << 3)
+#define SSBI_STATUS_RD_READY (1 << 2)
+#define SSBI_STATUS_READY (1 << 1)
+#define SSBI_STATUS_MCHN_BUSY (1 << 0)
+
+/* SSBI_RD fields */
+#define SSBI_RD_USE_ENABLE (1 << 25)
+#define SSBI_RD_RDWRN (1 << 24)
+
+/* SSBI_MODE2 fields */
+#define SSBI_MODE2_SSBI2_MODE (1 << 0)
+
+#define SSBI_TIMEOUT_US 100
+
+struct msm_ssbi {
+ struct device *dev;
+ struct device *slave;
+ void __iomem *base;
+ remote_spinlock_t rspin_lock;
+};
+
+#define to_msm_ssbi(dev) platform_get_drvdata(to_platform_device(dev))
+
+static inline u32 ssbi_readl(struct msm_ssbi *ssbi, unsigned long reg)
+{
+ return readl(ssbi->base + reg);
+}
+
+static inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val,
+ unsigned long reg)
+{
+ writel(val, ssbi->base + reg);
+}
+
+//poll_for_device_ready === SSBI_STATUS_READY
+//poll_for_transfer_completed === SSBI_STATUS_MCHN_BUSY
+//poll_for_read_completed === SSBI_STATUS_RD_READY
+static int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask)
+{
+ u32 timeout = SSBI_TIMEOUT_US;
+ u32 val;
+
+ while (timeout--) {
+ val = ssbi_readl(ssbi, SSBI2_STATUS);
+ if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
+ return 0;
+ udelay(1);
+ }
+
+ dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n",
+ __func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask);
+ return -ETIMEDOUT;
+}
+
+int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
+{
+ struct msm_ssbi *ssbi = to_msm_ssbi(dev);
+ unsigned long flags;
+ u32 read_cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
+ u32 mode2;
+ int ret = 0;
+
+ BUG_ON(ssbi->dev != dev);
+
+ remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
+
+ mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+ if (mode2 & SSBI_MODE2_SSBI2_MODE) {
+ mode2 = (mode2 & 0xf) | (((addr >> 8) & 0x7f) << 4);
+ ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+ }
+
+ while (len) {
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+ if (ret)
+ goto err;
+
+ ssbi_writel(ssbi, read_cmd, SSBI2_CMD);
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
+ if (ret)
+ goto err;
+ *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
+ len--;
+ }
+
+err:
+ remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(msm_ssbi_read);
+
+int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
+{
+ struct msm_ssbi *ssbi = to_msm_ssbi(dev);
+ unsigned long flags;
+ u32 mode2;
+ int ret = 0;
+
+ BUG_ON(ssbi->dev != dev);
+
+ remote_spin_lock_irqsave(&ssbi->rspin_lock, flags);
+
+ mode2 = readl(ssbi->base + SSBI2_MODE2);
+ if (mode2 & SSBI_MODE2_SSBI2_MODE) {
+ mode2 = (mode2 & 0xf) | (((addr >> 8) & 0x7f) << 4);
+ ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+ }
+
+ while (len) {
+ ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+ if (ret)
+ goto err;
+
+ ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
+ ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
+ if (ret)
+ goto err;
+ buf++;
+ len--;
+ }
+
+err:
+ remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(msm_ssbi_write);
+
+static int __init msm_ssbi_add_slave(struct msm_ssbi *ssbi,
+ struct msm_ssbi_slave_info *slave)
+{
+ struct platform_device *slave_pdev;
+ struct resource slave_irq_res;
+ int ret;
+
+ if (ssbi->slave) {
+ pr_err("%s: slave already attached??\n", __func__);
+ return -EBUSY;
+ }
+
+ slave_pdev = platform_device_alloc(slave->name, -1);
+ if (!slave_pdev) {
+ pr_err("%s: cannot allocate pdev for slave '%s'", __func__,
+ slave->name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ slave_pdev->dev.parent = ssbi->dev;
+ slave_pdev->dev.platform_data = slave->platform_data;
+
+ memset(&slave_irq_res, 0, sizeof(struct resource));
+ slave_irq_res.start = slave->irq;
+ slave_irq_res.end = slave->irq;
+ slave_irq_res.flags = IORESOURCE_IRQ;
+ ret = platform_device_add_resources(slave_pdev, &slave_irq_res, 1);
+ if (ret) {
+ pr_err("%s: can't add irq resource for '%s'\n", __func__,
+ slave->name);
+ goto err;
+ }
+
+ ret = platform_device_add(slave_pdev);
+ if (ret) {
+ pr_err("%s: cannot add slave platform device for '%s'\n",
+ __func__, slave->name);
+ goto err;
+ }
+
+ ssbi->slave = &slave_pdev->dev;
+ return 0;
+
+err:
+ if (slave_pdev)
+ platform_device_put(slave_pdev);
+ return ret;
+}
+
+static int __init msm_ssbi_probe(struct platform_device *pdev)
+{
+ struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *mem_res;
+ struct msm_ssbi *ssbi;
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("%s: missing platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL);
+ if (!ssbi) {
+ pr_err("%s: cannot allocate ssbi_data\n", __func__);
+ return -ENOMEM;
+ }
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ pr_err("%s: missing mem resource\n", __func__);
+ ret = -EINVAL;
+ goto err_get_mem_res;
+ }
+
+ ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
+ if (!ssbi->base) {
+ pr_err("%s: ioremap of 0x%p failed\n", __func__,
+ (void *)mem_res->start);
+ ret = -EINVAL;
+ goto err_ioremap;
+ }
+ ssbi->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ssbi);
+
+ ret = remote_spin_lock_init(&ssbi->rspin_lock, pdata->rspinlock_name);
+ if (ret) {
+ pr_err("%s: cannot init remote spinlock '%s'\n", __func__,
+ pdata->rspinlock_name);
+ goto err_remote_spinlock_init;
+ }
+
+ ret = msm_ssbi_add_slave(ssbi, &pdata->slave);
+ if (ret)
+ goto err_ssbi_add_slave;
+
+ pr_info("msm_ssbi: io=%08x rsl='%s'\n", mem_res->start,
+ pdata->rspinlock_name);
+
+ return 0;
+
+err_remote_spinlock_init:
+ platform_set_drvdata(pdev, NULL);
+err_ssbi_add_slave:
+ iounmap(ssbi->base);
+err_ioremap:
+err_get_mem_res:
+ kfree(ssbi);
+ return ret;
+}
+
+static struct platform_driver msm_ssbi_driver = {
+ .probe = msm_ssbi_probe,
+ .driver = {
+ .name = "msm_ssbi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_ssbi_init(void)
+{
+ return platform_driver_register(&msm_ssbi_driver);
+}
+
+postcore_initcall(msm_ssbi_init);
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index dec5ca6..07d456f 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -25,10 +25,15 @@
#include <asm/mach/time.h>
#include <mach/msm_iomap.h>
-#ifndef MSM_DGT_BASE
-#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10)
-#endif
-#define MSM_DGT_SHIFT (5)
+#include "smd_private.h"
+
+enum {
+ MSM_TIMER_DEBUG_SYNC_STATE = 1U << 0,
+ MSM_TIMER_DEBUG_SYNC_UPDATE = 1U << 1,
+ MSM_TIMER_DEBUG_SYNC = 1U << 2,
+};
+static int msm_timer_debug_mask;
+module_param_named(debug_mask, msm_timer_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0004
@@ -41,7 +46,23 @@
#define CSR_PROTECTION_EN 1
#define GPT_HZ 32768
+
+#if defined(CONFIG_ARCH_QSD8X50)
+#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
+#define MSM_DGT_SHIFT (0)
+#elif defined(CONFIG_ARCH_MSM7X30)
+#define DGT_HZ (24576000 / 4) /* 24.576 MHz (LPXO) / 4 by default */
+#define MSM_DGT_SHIFT (0)
+#else
#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
+#define MSM_DGT_SHIFT (5)
+#endif
+
+enum {
+ MSM_CLOCK_FLAGS_UNSTABLE_COUNT = 1U << 0,
+ MSM_CLOCK_FLAGS_ODD_MATCH_WRITE = 1U << 1,
+ MSM_CLOCK_FLAGS_DELAYED_WRITE_POST = 1U << 2,
+};
struct msm_clock {
struct clock_event_device clockevent;
@@ -50,40 +71,108 @@
void __iomem *regbase;
uint32_t freq;
uint32_t shift;
+ uint32_t flags;
+ uint32_t write_delay;
+ uint32_t last_set;
+ uint32_t offset;
+ uint32_t alarm_vtime;
+ uint32_t smem_offset;
+ uint32_t smem_in_sync;
+ cycle_t stopped_tick;
+ int stopped;
};
+enum {
+ MSM_CLOCK_GPT,
+ MSM_CLOCK_DGT,
+};
+static struct msm_clock msm_clocks[];
+static struct msm_clock *msm_active_clock;
+static DEFINE_SPINLOCK(msm_fast_timer_lock);
+static int msm_fast_timer_enabled;
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
- evt->event_handler(evt);
+ if (evt->event_handler)
+ evt->event_handler(evt);
return IRQ_HANDLED;
}
+static uint32_t msm_read_timer_count(struct msm_clock *clock)
+{
+ uint32_t t1, t2;
+ int loop_count = 0;
+
+ t1 = readl(clock->regbase + TIMER_COUNT_VAL);
+ if (!(clock->flags & MSM_CLOCK_FLAGS_UNSTABLE_COUNT))
+ return t1;
+ while (1) {
+ t2 = readl(clock->regbase + TIMER_COUNT_VAL);
+ if (t1 == t2)
+ return t1;
+ if (loop_count++ > 10) {
+ printk(KERN_ERR "msm_read_timer_count timer %s did not"
+ "stabilize %u != %u\n", clock->clockevent.name,
+ t2, t1);
+ return t2;
+ }
+ t1 = t2;
+ }
+}
+
static cycle_t msm_gpt_read(struct clocksource *cs)
{
- return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT];
+ if (clock->stopped)
+ return clock->stopped_tick;
+ else
+ return msm_read_timer_count(clock) + clock->offset;
}
static cycle_t msm_dgt_read(struct clocksource *cs)
{
- return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT];
+ if (clock->stopped)
+ return clock->stopped_tick;
+ return (msm_read_timer_count(clock) + clock->offset) >> MSM_DGT_SHIFT;
}
static int msm_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
- struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
- uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
- uint32_t alarm = now + (cycles << clock->shift);
+ int i;
+ struct msm_clock *clock;
+ uint32_t now;
+ uint32_t alarm;
int late;
+ clock = container_of(evt, struct msm_clock, clockevent);
+ now = msm_read_timer_count(clock);
+ alarm = now + (cycles << clock->shift);
+ if (clock->flags & MSM_CLOCK_FLAGS_ODD_MATCH_WRITE)
+ while (now == clock->last_set)
+ now = msm_read_timer_count(clock);
writel(alarm, clock->regbase + TIMER_MATCH_VAL);
- now = readl(clock->regbase + TIMER_COUNT_VAL);
+ if (clock->flags & MSM_CLOCK_FLAGS_DELAYED_WRITE_POST) {
+ /* read the counter four extra times to make sure write posts
+ before reading the time */
+ for (i = 0; i < 4; i++)
+ readl(clock->regbase + TIMER_COUNT_VAL);
+ }
+ now = msm_read_timer_count(clock);
+ clock->last_set = now;
+ clock->alarm_vtime = alarm + clock->offset;
late = now - alarm;
- if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
- printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
- "alarm already expired, now %x, alarm %x, late %d\n",
- cycles, clock->clockevent.name, now, alarm, late);
+ if (late >= (int)(-clock->write_delay << clock->shift) && late < DGT_HZ*5) {
+ static int print_limit = 10;
+ if (print_limit > 0) {
+ print_limit--;
+ printk(KERN_NOTICE "msm_timer_set_next_event(%lu) "
+ "clock %s, alarm already expired, now %x, "
+ "alarm %x, late %d%s\n",
+ cycles, clock->clockevent.name, now, alarm, late,
+ print_limit ? "" : " stop printing");
+ }
return -ETIME;
}
return 0;
@@ -92,23 +181,370 @@
static void msm_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
- struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
+ struct msm_clock *clock;
+ unsigned long irq_flags;
+
+ clock = container_of(evt, struct msm_clock, clockevent);
+ local_irq_save(irq_flags);
+
switch (mode) {
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
break;
case CLOCK_EVT_MODE_ONESHOT:
+ clock->stopped = 0;
+ clock->offset = -msm_read_timer_count(clock) + clock->stopped_tick;
+ msm_active_clock = clock;
writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
+ msm_active_clock = NULL;
+ clock->smem_in_sync = 0;
+ clock->stopped = 1;
+ clock->stopped_tick = (msm_read_timer_count(clock) +
+ clock->offset) >> clock->shift;
writel(0, clock->regbase + TIMER_ENABLE);
break;
}
+ local_irq_restore(irq_flags);
}
+static inline int check_timeout(struct msm_clock *clock, uint32_t timeout)
+{
+ return (int32_t)(msm_read_timer_count(clock) - timeout) <= 0;
+}
+
+#ifndef CONFIG_ARCH_MSM_SCORPION
+
+static uint32_t msm_timer_sync_smem_clock(int exit_sleep)
+{
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT];
+ uint32_t *smem_clock;
+ uint32_t smem_clock_val;
+ uint32_t timeout;
+ uint32_t entry_time;
+ uint32_t timeout_delta;
+ uint32_t last_state;
+ uint32_t state;
+ uint32_t new_offset;
+
+ smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE,
+ sizeof(uint32_t));
+
+ if (smem_clock == NULL) {
+ printk(KERN_ERR "no smem clock\n");
+ return 0;
+ }
+
+ if (!exit_sleep && clock->smem_in_sync)
+ return 0;
+
+ timeout_delta = (clock->freq >> (7 - clock->shift)); /* 7.8ms */
+
+ last_state = state = smsm_get_state(SMSM_STATE_MODEM);
+ if (*smem_clock) {
+ printk(KERN_INFO "get_smem_clock: invalid start state %x "
+ "clock %u\n", state, *smem_clock);
+ smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEWAIT, SMSM_TIMEINIT);
+ entry_time = msm_read_timer_count(clock);
+ timeout = entry_time + timeout_delta;
+ while (*smem_clock != 0 && check_timeout(clock, timeout))
+ ;
+ if (*smem_clock) {
+ printk(KERN_INFO "get_smem_clock: timeout still "
+ "invalid state %x clock %u in %d ticks\n",
+ state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ return 0;
+ }
+ }
+ entry_time = msm_read_timer_count(clock);
+ timeout = entry_time + timeout_delta;
+ smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEINIT, SMSM_TIMEWAIT);
+ do {
+ smem_clock_val = *smem_clock;
+ state = smsm_get_state(SMSM_STATE_MODEM);
+ if (state != last_state) {
+ last_state = state;
+ if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE)
+ pr_info("get_smem_clock: state %x clock %u\n",
+ state, smem_clock_val);
+ }
+ } while (smem_clock_val == 0 && check_timeout(clock, timeout));
+ if (smem_clock_val) {
+ new_offset = smem_clock_val - msm_read_timer_count(clock);
+ if (clock->offset + clock->smem_offset != new_offset) {
+ if (exit_sleep)
+ clock->offset = new_offset - clock->smem_offset;
+ else
+ clock->smem_offset = new_offset - clock->offset;
+ clock->smem_in_sync = 1;
+ if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_UPDATE)
+ printk(KERN_INFO "get_smem_clock: state %x "
+ "clock %u new offset %u+%u\n",
+ state, smem_clock_val,
+ clock->offset, clock->smem_offset);
+ } else if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) {
+ printk(KERN_INFO "get_smem_clock: state %x "
+ "clock %u offset %u+%u\n",
+ state, smem_clock_val,
+ clock->offset, clock->smem_offset);
+ }
+ } else {
+ printk(KERN_INFO "get_smem_clock: timeout state %x clock %u "
+ "in %d ticks\n", state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ }
+ smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEWAIT, SMSM_TIMEINIT);
+ entry_time = msm_read_timer_count(clock);
+ timeout = entry_time + timeout_delta;
+ while (*smem_clock != 0 && check_timeout(clock, timeout)) {
+ uint32_t astate = smsm_get_state(SMSM_STATE_APPS);
+ if ((astate & SMSM_TIMEWAIT) || !(astate & SMSM_TIMEINIT)) {
+ if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE)
+ pr_info("get_smem_clock: modem overwrote "
+ "apps state %x\n", astate);
+ smsm_change_state(SMSM_STATE_APPS,
+ SMSM_TIMEWAIT, SMSM_TIMEINIT);
+ }
+ }
+ if (*smem_clock)
+ printk(KERN_INFO "get_smem_clock: exit timeout state %x "
+ "clock %u in %d ticks\n", state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ return smem_clock_val;
+}
+
+#else
+
+/* Time Master State Bits */
+#define DEM_TIME_MASTER_TIME_PENDING_APPS BIT(0)
+
+/* Time Slave State Bits */
+#define DEM_TIME_SLAVE_TIME_REQUEST 0x0400
+#define DEM_TIME_SLAVE_TIME_POLL 0x0800
+#define DEM_TIME_SLAVE_TIME_INIT 0x1000
+
+static uint32_t msm_timer_sync_smem_clock(int exit_sleep)
+{
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT];
+ uint32_t *smem_clock;
+ uint32_t smem_clock_val;
+ uint32_t bad_clock = 0;
+ uint32_t timeout;
+ uint32_t entry_time;
+ uint32_t timeout_delta;
+ uint32_t last_state;
+ uint32_t state;
+ uint32_t new_offset;
+
+ smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE,
+ sizeof(uint32_t));
+
+ if (smem_clock == NULL) {
+ printk(KERN_ERR "no smem clock\n");
+ return 0;
+ }
+
+ if (!exit_sleep && clock->smem_in_sync)
+ return 0;
+
+ timeout_delta = (clock->freq >> (7 - clock->shift)); /* 7.8ms */
+
+ entry_time = msm_read_timer_count(clock);
+ last_state = state = smsm_get_state(SMSM_STATE_TIME_MASTER_DEM);
+ timeout = entry_time + timeout_delta;
+ while ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM)
+ & DEM_TIME_MASTER_TIME_PENDING_APPS)
+ && check_timeout(clock, timeout))
+ ;
+ if ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) &
+ DEM_TIME_MASTER_TIME_PENDING_APPS)) {
+ printk(KERN_INFO "get_smem_clock: invalid start state %x "
+ "clock %u in %d ticks\n",
+ state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ bad_clock = *smem_clock;
+ }
+ entry_time = msm_read_timer_count(clock);
+ timeout = entry_time + timeout_delta;
+ smsm_change_state(SMSM_STATE_APPS_DEM,
+ DEM_TIME_SLAVE_TIME_INIT, DEM_TIME_SLAVE_TIME_REQUEST);
+ while (!(smsm_get_state(SMSM_STATE_TIME_MASTER_DEM)
+ & DEM_TIME_MASTER_TIME_PENDING_APPS)
+ && check_timeout(clock, timeout))
+ ;
+ if (!(smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) &
+ DEM_TIME_MASTER_TIME_PENDING_APPS)) {
+ printk(KERN_INFO "get_smem_clock: invalid start state %x "
+ "clock %u in %d ticks\n",
+ state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ bad_clock = *smem_clock;
+ }
+ smsm_change_state(SMSM_STATE_APPS_DEM,
+ DEM_TIME_SLAVE_TIME_REQUEST, DEM_TIME_SLAVE_TIME_POLL);
+ do {
+ smem_clock_val = *smem_clock;
+ state = smsm_get_state(SMSM_STATE_TIME_MASTER_DEM);
+ if (state != last_state) {
+ last_state = state;
+ if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE)
+ pr_info("get_smem_clock: state %x clock %u\n",
+ state, smem_clock_val);
+ }
+ } while ((!smem_clock_val || smem_clock_val == bad_clock)
+ && check_timeout(clock, timeout));
+ if (smem_clock_val && smem_clock_val != bad_clock) {
+ new_offset = smem_clock_val - msm_read_timer_count(clock);
+ if (clock->offset + clock->smem_offset != new_offset) {
+ if (exit_sleep)
+ clock->offset = new_offset - clock->smem_offset;
+ else
+ clock->smem_offset = new_offset - clock->offset;
+ clock->smem_in_sync = 1;
+ if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_UPDATE)
+ printk(KERN_INFO "get_smem_clock: state %x "
+ "clock %u new offset %u+%u\n",
+ state, smem_clock_val,
+ clock->offset, clock->smem_offset);
+ } else if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) {
+ printk(KERN_INFO "get_smem_clock: state %x "
+ "clock %u offset %u+%u\n",
+ state, smem_clock_val,
+ clock->offset, clock->smem_offset);
+ }
+ } else {
+ printk(KERN_INFO "get_smem_clock: timeout state %x clock %u "
+ "in %d ticks\n", state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+ }
+ smsm_change_state(SMSM_STATE_APPS_DEM,
+ DEM_TIME_SLAVE_TIME_POLL, DEM_TIME_SLAVE_TIME_INIT);
+#if 1 /* debug */
+ entry_time = msm_read_timer_count(clock);
+ timeout = entry_time + timeout_delta;
+ while ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM)
+ & DEM_TIME_MASTER_TIME_PENDING_APPS)
+ && check_timeout(clock, timeout))
+ ;
+ if (smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) &
+ DEM_TIME_MASTER_TIME_PENDING_APPS)
+ printk(KERN_INFO "get_smem_clock: exit timeout state %x "
+ "clock %u in %d ticks\n", state, *smem_clock,
+ msm_read_timer_count(clock) - entry_time);
+#endif
+ return smem_clock_val;
+}
+
+#endif
+
+static void msm_timer_reactivate_alarm(struct msm_clock *clock)
+{
+ long alarm_delta = clock->alarm_vtime - clock->offset -
+ msm_read_timer_count(clock);
+ if (alarm_delta < (long)clock->write_delay + 4)
+ alarm_delta = clock->write_delay + 4;
+ while (msm_timer_set_next_event(alarm_delta, &clock->clockevent))
+ ;
+}
+
+int64_t msm_timer_enter_idle(void)
+{
+ struct msm_clock *clock = msm_active_clock;
+ uint32_t alarm;
+ uint32_t count;
+ int32_t delta;
+
+ if (clock != &msm_clocks[MSM_CLOCK_GPT] || msm_fast_timer_enabled)
+ return 0;
+
+ msm_timer_sync_smem_clock(0);
+
+ count = msm_read_timer_count(clock);
+ if (clock->stopped++ == 0)
+ clock->stopped_tick = (count + clock->offset) >> clock->shift;
+ alarm = clock->alarm_vtime - clock->offset;
+ delta = alarm - count;
+ if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) {
+ /* timer should have triggered 1ms ago */
+ printk(KERN_ERR "msm_timer_enter_idle: timer late %d, "
+ "reprogram it\n", delta);
+ msm_timer_reactivate_alarm(clock);
+ }
+ if (delta <= 0)
+ return 0;
+ return clocksource_cyc2ns((alarm - count) >> clock->shift,
+ clock->clocksource.mult, clock->clocksource.shift);
+}
+
+void msm_timer_exit_idle(int low_power)
+{
+ struct msm_clock *clock = msm_active_clock;
+ uint32_t smem_clock;
+
+ if (clock != &msm_clocks[MSM_CLOCK_GPT])
+ return;
+
+ if (low_power) {
+ if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) {
+ writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
+ smem_clock = msm_timer_sync_smem_clock(1);
+ }
+ msm_timer_reactivate_alarm(clock);
+ }
+ clock->stopped--;
+}
+
+unsigned long long sched_clock(void)
+{
+ static cycle_t saved_ticks;
+ static int saved_ticks_valid;
+ static unsigned long long base;
+ static unsigned long long last_result;
+
+ unsigned long irq_flags;
+ static cycle_t last_ticks;
+ cycle_t ticks;
+ static unsigned long long result;
+ struct clocksource *cs;
+ struct msm_clock *clock = msm_active_clock;
+
+ local_irq_save(irq_flags);
+ if (clock) {
+ cs = &clock->clocksource;
+
+ last_ticks = saved_ticks;
+ saved_ticks = ticks = cs->read(cs);
+ if (!saved_ticks_valid) {
+ saved_ticks_valid = 1;
+ last_ticks = ticks;
+ base -= clocksource_cyc2ns(ticks, cs->mult, cs->shift);
+ }
+ if (ticks < last_ticks) {
+ base += clocksource_cyc2ns(cs->mask,
+ cs->mult, cs->shift);
+ base += clocksource_cyc2ns(1, cs->mult, cs->shift);
+ }
+ last_result = result =
+ clocksource_cyc2ns(ticks, cs->mult, cs->shift) + base;
+ } else {
+ base = result = last_result;
+ saved_ticks_valid = 0;
+ }
+ local_irq_restore(irq_flags);
+ return result;
+}
+
+#ifdef CONFIG_MSM7X00A_USE_GP_TIMER
+ #define DG_TIMER_RATING 100
+#else
+ #define DG_TIMER_RATING 300
+#endif
+
static struct msm_clock msm_clocks[] = {
- {
+ [MSM_CLOCK_GPT] = {
.clockevent = {
.name = "gp_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
@@ -122,49 +558,116 @@
.rating = 200,
.read = msm_gpt_read,
.mask = CLOCKSOURCE_MASK(32),
- .shift = 24,
+ .shift = 17,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
.irq = {
.name = "gp_timer",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
+ .flags = IRQF_DISABLED | IRQF_TIMER |
+ IRQF_TRIGGER_RISING,
.handler = msm_timer_interrupt,
.dev_id = &msm_clocks[0].clockevent,
.irq = INT_GP_TIMER_EXP
},
.regbase = MSM_GPT_BASE,
- .freq = GPT_HZ
+ .freq = GPT_HZ,
+ .flags =
+ MSM_CLOCK_FLAGS_UNSTABLE_COUNT |
+ MSM_CLOCK_FLAGS_ODD_MATCH_WRITE |
+ MSM_CLOCK_FLAGS_DELAYED_WRITE_POST,
+ .write_delay = 9,
},
- {
+ [MSM_CLOCK_DGT] = {
.clockevent = {
.name = "dg_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.shift = 32 + MSM_DGT_SHIFT,
- .rating = 300,
+ .rating = DG_TIMER_RATING,
.set_next_event = msm_timer_set_next_event,
.set_mode = msm_timer_set_mode,
},
.clocksource = {
.name = "dg_timer",
- .rating = 300,
+ .rating = DG_TIMER_RATING,
.read = msm_dgt_read,
- .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
+ .mask = CLOCKSOURCE_MASK((32-MSM_DGT_SHIFT)),
.shift = 24 - MSM_DGT_SHIFT,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
.irq = {
.name = "dg_timer",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
+ .flags = IRQF_DISABLED | IRQF_TIMER |
+ IRQF_TRIGGER_RISING,
.handler = msm_timer_interrupt,
.dev_id = &msm_clocks[1].clockevent,
.irq = INT_DEBUG_TIMER_EXP
},
.regbase = MSM_DGT_BASE,
.freq = DGT_HZ >> MSM_DGT_SHIFT,
- .shift = MSM_DGT_SHIFT
+ .shift = MSM_DGT_SHIFT,
+ .write_delay = 2,
}
};
+/**
+ * msm_enable_fast_timer - Enable fast timer
+ *
+ * Prevents low power idle, but the caller must call msm_disable_fast_timer
+ * before suspend completes.
+ * Reference counted.
+ */
+void msm_enable_fast_timer(void)
+{
+ u32 max;
+ unsigned long irq_flags;
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT];
+
+ spin_lock_irqsave(&msm_fast_timer_lock, irq_flags);
+ if (msm_fast_timer_enabled++)
+ goto done;
+ if (msm_active_clock == &msm_clocks[MSM_CLOCK_DGT]) {
+ pr_warning("msm_enable_fast_timer: timer already in use, "
+ "returned time will jump when hardware timer wraps\n");
+ goto done;
+ }
+ max = (clock->clockevent.mult >> (clock->clockevent.shift - 32)) - 1;
+ writel(max, clock->regbase + TIMER_MATCH_VAL);
+ writel(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN,
+ clock->regbase + TIMER_ENABLE);
+done:
+ spin_unlock_irqrestore(&msm_fast_timer_lock, irq_flags);
+}
+
+/**
+ * msm_enable_fast_timer - Disable fast timer
+ */
+void msm_disable_fast_timer(void)
+{
+ unsigned long irq_flags;
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT];
+
+ spin_lock_irqsave(&msm_fast_timer_lock, irq_flags);
+ if (!WARN(!msm_fast_timer_enabled, "msm_disable_fast_timer undeflow")
+ && !--msm_fast_timer_enabled
+ && msm_active_clock != &msm_clocks[MSM_CLOCK_DGT])
+ writel(0, clock->regbase + TIMER_ENABLE);
+ spin_unlock_irqrestore(&msm_fast_timer_lock, irq_flags);
+}
+
+/**
+ * msm_enable_fast_timer - Read fast timer
+ *
+ * Returns 32bit nanosecond time value.
+ */
+u32 msm_read_fast_timer(void)
+{
+ cycle_t ticks;
+ struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT];
+ ticks = msm_read_timer_count(clock) >> MSM_DGT_SHIFT;
+ return clocksource_cyc2ns(ticks, clock->clocksource.mult,
+ clock->clocksource.shift);
+}
+
static void __init msm_timer_init(void)
{
int i;
@@ -177,13 +680,15 @@
writel(0, clock->regbase + TIMER_ENABLE);
writel(0, clock->regbase + TIMER_CLEAR);
writel(~0, clock->regbase + TIMER_MATCH_VAL);
+ while (msm_read_timer_count(clock)) ; /* wait for clock to clear */
ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
/* allow at least 10 seconds to notice that the timer wrapped */
ce->max_delta_ns =
clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
- /* 4 gets rounded down to 3 */
- ce->min_delta_ns = clockevent_delta2ns(4, ce);
+ /* ticks gets rounded down by one */
+ ce->min_delta_ns =
+ clockevent_delta2ns(clock->write_delay + 4, ce);
ce->cpumask = cpumask_of(0);
cs->mult = clocksource_hz2mult(clock->freq, cs->shift);
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index cbfb2ed..79c864a 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -23,6 +23,10 @@
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
+#ifdef CONFIG_ARCH_MSM_SCORPION
+#include <asm/io.h>
+#include <mach/msm_iomap.h>
+#endif
#include "fault.h"
@@ -452,6 +456,49 @@
return 1;
}
+#ifdef CONFIG_ARCH_MSM_SCORPION
+#define __str(x) #x
+#define MRC(x, v1, v2, v4, v5, v6) do { \
+ unsigned int __##x; \
+ asm("mrc " __str(v1) ", " __str(v2) ", %0, " __str(v4) ", " \
+ __str(v5) ", " __str(v6) "\n" \
+ : "=r" (__##x)); \
+ pr_info("%s: %s = 0x%.8x\n", __func__, #x, __##x); \
+} while(0)
+
+#define MSM_TCSR_SPARE2 (MSM_TCSR_BASE + 0x60)
+
+#endif
+
+static int
+do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+{
+#ifdef CONFIG_ARCH_MSM_SCORPION
+ MRC(ADFSR, p15, 0, c5, c1, 0);
+ MRC(DFSR, p15, 0, c5, c0, 0);
+ MRC(ACTLR, p15, 0, c1, c0, 1);
+ MRC(EFSR, p15, 7, c15, c0, 1);
+ MRC(L2SR, p15, 3, c15, c1, 0);
+ MRC(L2CR0, p15, 3, c15, c0, 1);
+ MRC(L2CPUESR, p15, 3, c15, c1, 1);
+ MRC(L2CPUCR, p15, 3, c15, c0, 2);
+ MRC(SPESR, p15, 1, c9, c7, 0);
+ MRC(SPCR, p15, 0, c9, c7, 0);
+ MRC(DMACHSR, p15, 1, c11, c0, 0);
+ MRC(DMACHESR, p15, 1, c11, c0, 1);
+ MRC(DMACHCR, p15, 0, c11, c0, 2);
+
+ /* clear out EFSR and ADFSR after fault */
+ asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t"
+ "mcr p15, 0, %0, c5, c1, 0"
+ : : "r" (0));
+#endif
+#ifdef CONFIG_ARCH_MSM_SCORPION
+ pr_info("%s: TCSR_SPARE2 = 0x%.8x\n", __func__, readl(MSM_TCSR_SPARE2));
+#endif
+ return 1;
+}
+
static struct fsr_info {
int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
int sig;
@@ -494,7 +541,7 @@
{ do_bad, SIGBUS, 0, "unknown 19" },
{ do_bad, SIGBUS, 0, "lock abort" }, /* xscale */
{ do_bad, SIGBUS, 0, "unknown 21" },
- { do_bad, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */
+ { do_imprecise_ext, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */
{ do_bad, SIGBUS, 0, "unknown 23" },
{ do_bad, SIGBUS, 0, "dcache parity error" }, /* xscale */
{ do_bad, SIGBUS, 0, "unknown 25" },
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 2858941..b01f7cd 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -668,7 +668,7 @@
create_mapping(io_desc + i);
}
-static unsigned long __initdata vmalloc_reserve = SZ_128M;
+static unsigned long __initdata vmalloc_reserve = CONFIG_VMALLOC_RESERVE;
/*
* vmalloc=size forces the vmalloc area to be exactly 'size'
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 7aaf88a..df74916 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -240,8 +240,6 @@
mcr p15, 0, r10, c2, c0, 2 @ TTB control register
orr r4, r4, #TTB_FLAGS
mcr p15, 0, r4, c2, c0, 1 @ load TTB1
- mov r10, #0x1f @ domains 0, 1 = manager
- mcr p15, 0, r10, c3, c0, 0 @ load domain access register
/*
* Memory region attributes with SCTLR.TRE=1
*
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index fb8445c..7635e42 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -45,6 +45,7 @@
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
+#include <linux/serial_core.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -86,6 +87,28 @@
struct sk_buff_head tx_wait_q; /* HCILL wait queue */
};
+#ifdef CONFIG_SERIAL_MSM_HS
+void msm_hs_request_clock_off(struct uart_port *uport);
+void msm_hs_request_clock_on(struct uart_port *uport);
+
+static void __ll_msm_serial_clock_on(struct tty_struct *tty) {
+ struct uart_state *state = tty->driver_data;
+ struct uart_port *port = state->uart_port;
+
+ msm_hs_request_clock_on(port);
+}
+
+static void __ll_msm_serial_clock_request_off(struct tty_struct *tty) {
+ struct uart_state *state = tty->driver_data;
+ struct uart_port *port = state->uart_port;
+
+ msm_hs_request_clock_off(port);
+}
+#else
+static inline void __ll_msm_serial_clock_on(struct tty_struct *tty) {}
+static inline void __ll_msm_serial_clock_request_off(struct tty_struct *tty) {}
+#endif
+
/*
* Builds and sends an HCILL command packet.
* These are very simple packets with only 1 cmd byte
@@ -217,6 +240,10 @@
BT_DBG("dual wake-up-indication");
/* deliberate fall-through - do not add break */
case HCILL_ASLEEP:
+ /* Make sure clock is on - we may have turned clock off since
+ * receiving the wake up indicator
+ */
+ __ll_msm_serial_clock_on(hu->tty);
/* acknowledge device wake up */
if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
BT_ERR("cannot acknowledge device wake up");
@@ -270,6 +297,11 @@
/* actually send the sleep ack packet */
hci_uart_tx_wakeup(hu);
+
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+ if (ll->hcill_state == HCILL_ASLEEP)
+ __ll_msm_serial_clock_request_off(hu->tty);
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
}
/*
@@ -321,6 +353,7 @@
break;
case HCILL_ASLEEP:
BT_DBG("device asleep, waking up and queueing packet");
+ __ll_msm_serial_clock_on(hu->tty);
/* save packet for later */
skb_queue_tail(&ll->tx_wait_q, skb);
/* awake device */
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 91091b0..ae08b38 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -329,7 +329,7 @@
vma->vm_ops = &mmap_mem_ops;
/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
- if (remap_pfn_range(vma,
+ if (io_remap_pfn_range(vma,
vma->vm_start,
vma->vm_pgoff,
size,
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index cd650ca..92ab03d 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -144,7 +144,6 @@
old_fops = file->f_op;
file->f_op = new_fops;
if (file->f_op->open) {
- file->private_data = c;
err=file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bceafbf..42a396a 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -431,6 +431,22 @@
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
+config I2C_MSM
+ tristate "MSM"
+ depends on I2C && ARCH_MSM
+ default y
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the MSM7X00A family processors.
+
+config I2C_QUP
+ tristate "I2C_QUP"
+ depends on I2C && ARCH_MSM7X30
+ default y
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the MSM family processors.
+
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 936880b..d8d2931 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -41,6 +41,8 @@
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
+obj-$(CONFIG_I2C_MSM) += i2c-msm.o
+obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c
new file mode 100644
index 0000000..0322262
--- /dev/null
+++ b/drivers/i2c/busses/i2c-msm.c
@@ -0,0 +1,607 @@
+/* drivers/i2c/busses/i2c-msm.c
+ *
+ * Copyright (C) 2007 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/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/wakelock.h>
+#include <mach/system.h>
+
+#define DEBUG 0
+
+enum {
+ I2C_WRITE_DATA = 0x00,
+ I2C_CLK_CTL = 0x04,
+ I2C_STATUS = 0x08,
+ I2C_READ_DATA = 0x0c,
+ I2C_INTERFACE_SELECT = 0x10,
+
+ I2C_WRITE_DATA_DATA_BYTE = 0xff,
+ I2C_WRITE_DATA_ADDR_BYTE = 1U << 8,
+ I2C_WRITE_DATA_LAST_BYTE = 1U << 9,
+
+ I2C_CLK_CTL_FS_DIVIDER_VALUE = 0xff,
+ I2C_CLK_CTL_HS_DIVIDER_VALUE = 7U << 8,
+
+ I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
+ I2C_STATUS_RD_BUFFER_FULL = 1U << 1,
+ I2C_STATUS_BUS_ERROR = 1U << 2,
+ I2C_STATUS_PACKET_NACKED = 1U << 3,
+ I2C_STATUS_ARB_LOST = 1U << 4,
+ I2C_STATUS_INVALID_WRITE = 1U << 5,
+ I2C_STATUS_FAILED = 3U << 6,
+ I2C_STATUS_BUS_ACTIVE = 1U << 8,
+ I2C_STATUS_BUS_MASTER = 1U << 9,
+ I2C_STATUS_ERROR_MASK = 0xfc,
+
+ I2C_INTERFACE_SELECT_INTF_SELECT = 1U << 0,
+ I2C_INTERFACE_SELECT_SCL = 1U << 8,
+ I2C_INTERFACE_SELECT_SDA = 1U << 9,
+};
+
+struct msm_i2c_dev {
+ struct device *dev;
+ void __iomem *base; /* virtual */
+ int irq;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+
+ spinlock_t lock;
+
+ struct i2c_msg *msg;
+ int rem;
+ int pos;
+ int cnt;
+ int ret;
+ bool need_flush;
+ int flush_cnt;
+ void *complete;
+ struct wake_lock wakelock;
+ bool is_suspended;
+};
+
+#if DEBUG
+static void
+dump_status(uint32_t status)
+{
+ printk("STATUS (0x%.8x): ", status);
+ if (status & I2C_STATUS_BUS_MASTER)
+ printk("MST ");
+ if (status & I2C_STATUS_BUS_ACTIVE)
+ printk("ACT ");
+ if (status & I2C_STATUS_INVALID_WRITE)
+ printk("INV_WR ");
+ if (status & I2C_STATUS_ARB_LOST)
+ printk("ARB_LST ");
+ if (status & I2C_STATUS_PACKET_NACKED)
+ printk("NAK ");
+ if (status & I2C_STATUS_BUS_ERROR)
+ printk("BUS_ERR ");
+ if (status & I2C_STATUS_RD_BUFFER_FULL)
+ printk("RD_FULL ");
+ if (status & I2C_STATUS_WR_BUFFER_FULL)
+ printk("WR_FULL ");
+ if (status & I2C_STATUS_FAILED)
+ printk("FAIL 0x%x", (status & I2C_STATUS_FAILED));
+ printk("\n");
+}
+#endif
+
+static void msm_i2c_write_delay(struct msm_i2c_dev *dev)
+{
+ /* If scl is still high we have >4us (for 100kbps) to write the data
+ * register before we risk hitting a bug where the controller releases
+ * scl to soon after driving sda low. Writing the data after the
+ * scheduled release time for scl also avoids the bug.
+ */
+ if (readl(dev->base + I2C_INTERFACE_SELECT) & I2C_INTERFACE_SELECT_SCL)
+ return;
+ udelay(6);
+}
+
+static bool msm_i2c_fill_write_buffer(struct msm_i2c_dev *dev)
+{
+ uint16_t val;
+ if (dev->pos < 0) {
+ val = I2C_WRITE_DATA_ADDR_BYTE | dev->msg->addr << 1;
+ if (dev->msg->flags & I2C_M_RD)
+ val |= 1;
+ if (dev->rem == 1 && dev->msg->len == 0)
+ val |= I2C_WRITE_DATA_LAST_BYTE;
+ msm_i2c_write_delay(dev);
+ writel(val, dev->base + I2C_WRITE_DATA);
+ dev->pos++;
+ return true;
+ }
+
+ if (dev->msg->flags & I2C_M_RD)
+ return false;
+
+ if (!dev->cnt)
+ return false;
+
+ /* Ready to take a byte */
+ val = dev->msg->buf[dev->pos];
+ if (dev->cnt == 1 && dev->rem == 1)
+ val |= I2C_WRITE_DATA_LAST_BYTE;
+
+ msm_i2c_write_delay(dev);
+ writel(val, dev->base + I2C_WRITE_DATA);
+ dev->pos++;
+ dev->cnt--;
+ return true;
+}
+
+static void msm_i2c_read_buffer(struct msm_i2c_dev *dev)
+{
+ /*
+ * Theres something in the FIFO.
+ * Are we expecting data or flush crap?
+ */
+ if ((dev->msg->flags & I2C_M_RD) && dev->pos >= 0 && dev->cnt) {
+ switch (dev->cnt) {
+ case 1:
+ if (dev->pos != 0)
+ break;
+ dev->need_flush = true;
+ /* fall-trough */
+ case 2:
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ }
+ dev->msg->buf[dev->pos] = readl(dev->base + I2C_READ_DATA);
+ dev->cnt--;
+ dev->pos++;
+ } else { /* FLUSH */
+ if (dev->flush_cnt & 1) {
+ /*
+ * Stop requests are sometimes ignored, but writing
+ * more than one request generates a write error.
+ */
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ }
+ readl(dev->base + I2C_READ_DATA);
+ if (dev->need_flush)
+ dev->need_flush = false;
+ else
+ dev->flush_cnt++;
+ }
+}
+
+static void msm_i2c_interrupt_locked(struct msm_i2c_dev *dev)
+{
+ uint32_t status = readl(dev->base + I2C_STATUS);
+ bool not_done = true;
+
+#if DEBUG
+ dump_status(status);
+#endif
+ if (!dev->msg) {
+ dev_err(dev->dev,
+ "IRQ but nothing to do!, status %x\n", status);
+ return;
+ }
+ if (status & I2C_STATUS_ERROR_MASK)
+ goto out_err;
+
+ if (!(status & I2C_STATUS_WR_BUFFER_FULL))
+ not_done = msm_i2c_fill_write_buffer(dev);
+ if (status & I2C_STATUS_RD_BUFFER_FULL)
+ msm_i2c_read_buffer(dev);
+
+ if (dev->pos >= 0 && dev->cnt == 0) {
+ if (dev->rem > 1) {
+ dev->rem--;
+ dev->msg++;
+ dev->pos = -1;
+ dev->cnt = dev->msg->len;
+ } else if (!not_done && !dev->need_flush)
+ goto out_complete;
+ }
+ return;
+
+out_err:
+ dev_err(dev->dev, "error, status %x\n", status);
+ dev->ret = -EIO;
+out_complete:
+ complete(dev->complete);
+}
+
+static irqreturn_t
+msm_i2c_interrupt(int irq, void *devid)
+{
+ struct msm_i2c_dev *dev = devid;
+
+ spin_lock(&dev->lock);
+ msm_i2c_interrupt_locked(dev);
+ spin_unlock(&dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int
+msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn)
+{
+ uint32_t retries = 0;
+
+ while (retries != 200) {
+ uint32_t status = readl(dev->base + I2C_STATUS);
+
+ if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+ if (retries && warn)
+ dev_warn(dev->dev,
+ "Warning bus was busy (%d)\n", retries);
+ return 0;
+ }
+ if (retries++ > 100)
+ msleep(10);
+ }
+ dev_err(dev->dev, "Error waiting for notbusy (%d)\n", warn);
+ return -ETIMEDOUT;
+}
+
+static int
+msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev)
+{
+ int i;
+ uint32_t status = readl(dev->base + I2C_STATUS);
+ int gpio_clk, gpio_dat;
+ bool gpio_clk_status = false;
+
+ if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL)))
+ return 0;
+
+ msm_set_i2c_mux(true, &gpio_clk, &gpio_dat);
+
+ if (status & I2C_STATUS_RD_BUFFER_FULL) {
+ dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA);
+ readl(dev->base + I2C_READ_DATA);
+ }
+ else if (status & I2C_STATUS_BUS_MASTER) {
+ dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ writel(I2C_WRITE_DATA_LAST_BYTE | 0xff,
+ dev->base + I2C_WRITE_DATA);
+ }
+
+ dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
+ gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
+
+ for (i = 0; i < 9; i++) {
+ if (gpio_get_value(gpio_dat) && gpio_clk_status)
+ break;
+ gpio_direction_output(gpio_clk, 0);
+ udelay(5);
+ gpio_direction_output(gpio_dat, 0);
+ udelay(5);
+ gpio_direction_input(gpio_clk);
+ udelay(5);
+ if (!gpio_get_value(gpio_clk))
+ udelay(20);
+ if (!gpio_get_value(gpio_clk))
+ msleep(10);
+ gpio_clk_status = gpio_get_value(gpio_clk);
+ gpio_direction_input(gpio_dat);
+ udelay(5);
+ }
+ msm_set_i2c_mux(false, NULL, NULL);
+ udelay(10);
+
+ status = readl(dev->base + I2C_STATUS);
+ if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+ dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
+ "status %x, intf %x\n",
+ i, status, readl(dev->base + I2C_INTERFACE_SELECT));
+ return 0;
+ }
+
+ dev_warn(dev->dev, "Bus still busy, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ return -EBUSY;
+}
+
+
+static int
+msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ DECLARE_COMPLETION_ONSTACK(complete);
+ struct msm_i2c_dev *dev = i2c_get_adapdata(adap);
+ int ret;
+ long timeout;
+ unsigned long flags;
+
+ if (WARN_ON(!num))
+ return -EINVAL;
+
+ /*
+ * If there is an i2c_xfer after driver has been suspended,
+ * grab wakelock to abort suspend.
+ */
+ if (dev->is_suspended)
+ wake_lock(&dev->wakelock);
+ clk_enable(dev->clk);
+ enable_irq(dev->irq);
+
+ ret = msm_i2c_poll_notbusy(dev, 1);
+ if (ret) {
+ ret = msm_i2c_recover_bus_busy(dev);
+ if (ret)
+ goto err;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->flush_cnt) {
+ dev_warn(dev->dev, "%d unrequested bytes read\n",
+ dev->flush_cnt);
+ }
+ dev->msg = msgs;
+ dev->rem = num;
+ dev->pos = -1;
+ dev->ret = num;
+ dev->need_flush = false;
+ dev->flush_cnt = 0;
+ dev->cnt = msgs->len;
+ dev->complete = &complete;
+
+ msm_i2c_interrupt_locked(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /*
+ * Now that we've setup the xfer, the ISR will transfer the data
+ * and wake us up with dev->err set if there was an error
+ */
+
+ timeout = wait_for_completion_timeout(&complete, HZ);
+ msm_i2c_poll_notbusy(dev, 0); /* Read may not have stopped in time */
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->flush_cnt) {
+ dev_warn(dev->dev, "%d unrequested bytes read\n",
+ dev->flush_cnt);
+ }
+ ret = dev->ret;
+ dev->complete = NULL;
+ dev->msg = NULL;
+ dev->rem = 0;
+ dev->pos = 0;
+ dev->ret = 0;
+ dev->flush_cnt = 0;
+ dev->cnt = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!timeout) {
+ dev_err(dev->dev, "Transaction timed out\n");
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret < 0) {
+ dev_err(dev->dev, "Error during data xfer %x (%d)\n",
+ msgs[0].addr, ret);
+ msm_i2c_recover_bus_busy(dev);
+ }
+err:
+ disable_irq(dev->irq);
+ clk_disable(dev->clk);
+ if (dev->is_suspended)
+ wake_unlock(&dev->wakelock);
+
+ return ret;
+}
+
+static u32
+msm_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm msm_i2c_algo = {
+ .master_xfer = msm_i2c_xfer,
+ .functionality = msm_i2c_func,
+};
+
+static int
+msm_i2c_probe(struct platform_device *pdev)
+{
+ struct msm_i2c_dev *dev;
+ struct resource *mem, *irq, *ioarea;
+ int ret;
+ int fs_div;
+ int hs_div;
+ int i2c_clk;
+ int clk_ctl;
+ int target_clk;
+ struct clk *clk;
+
+ printk(KERN_INFO "msm_i2c_probe\n");
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+ clk = clk_get(&pdev->dev, "i2c_clk");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_get_failed;
+ }
+
+ dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_alloc_dev_failed;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->irq = irq->start;
+ dev->clk = clk;
+ dev->base = ioremap(mem->start, (mem->end - mem->start) + 1);
+ if (!dev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap_failed;
+ }
+
+ spin_lock_init(&dev->lock);
+ wake_lock_init(&dev->wakelock, WAKE_LOCK_SUSPEND, "i2c");
+ platform_set_drvdata(pdev, dev);
+
+ msm_set_i2c_mux(false, NULL, NULL);
+
+ clk_enable(clk);
+
+ /* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */
+ /* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */
+ /* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */
+ i2c_clk = 19200000; /* input clock */
+ target_clk = 100000;
+ /* target_clk = 200000; */
+ fs_div = ((i2c_clk / target_clk) / 2) - 3;
+ hs_div = 3;
+ clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+ writel(clk_ctl, dev->base + I2C_CLK_CTL);
+ printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n",
+ clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3)));
+ clk_disable(clk);
+
+ i2c_set_adapdata(&dev->adapter, dev);
+ dev->adapter.algo = &msm_i2c_algo;
+ strncpy(dev->adapter.name,
+ "MSM I2C adapter",
+ sizeof(dev->adapter.name));
+
+ dev->adapter.nr = pdev->id;
+ ret = i2c_add_numbered_adapter(&dev->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "i2c_add_adapter failed\n");
+ goto err_i2c_add_adapter_failed;
+ }
+
+ ret = request_irq(dev->irq, msm_i2c_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_request_irq_failed;
+ }
+ disable_irq(dev->irq);
+ return 0;
+
+/* free_irq(dev->irq, dev); */
+err_request_irq_failed:
+ i2c_del_adapter(&dev->adapter);
+err_i2c_add_adapter_failed:
+ iounmap(dev->base);
+err_ioremap_failed:
+ kfree(dev);
+err_alloc_dev_failed:
+ clk_put(clk);
+err_clk_get_failed:
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return ret;
+}
+
+static int
+msm_i2c_remove(struct platform_device *pdev)
+{
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ enable_irq(dev->irq);
+ free_irq(dev->irq, dev);
+ i2c_del_adapter(&dev->adapter);
+ wake_lock_destroy(&dev->wakelock);
+ clk_put(dev->clk);
+ iounmap(dev->base);
+ kfree(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return 0;
+}
+
+static int msm_i2c_suspend_noirq(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+
+ /* Block to allow any i2c_xfers to finish */
+ i2c_lock_adapter(&dev->adapter);
+ dev->is_suspended = true;
+ i2c_unlock_adapter(&dev->adapter);
+ return 0;
+}
+
+static int msm_i2c_resume_noirq(struct device *device) {
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+
+ /* Block to allow any i2c_xfers to finish */
+ i2c_lock_adapter(&dev->adapter);
+ dev->is_suspended = false;
+ i2c_unlock_adapter(&dev->adapter);
+ return 0;
+}
+
+static struct dev_pm_ops msm_i2c_pm_ops = {
+ .suspend_noirq = msm_i2c_suspend_noirq,
+ .resume_noirq = msm_i2c_resume_noirq,
+};
+
+static struct platform_driver msm_i2c_driver = {
+ .probe = msm_i2c_probe,
+ .remove = msm_i2c_remove,
+ .driver = {
+ .name = "msm_i2c",
+ .owner = THIS_MODULE,
+ .pm = &msm_i2c_pm_ops,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+static int __init
+msm_i2c_init_driver(void)
+{
+ return platform_driver_register(&msm_i2c_driver);
+}
+subsys_initcall(msm_i2c_init_driver);
+
+static void __exit msm_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&msm_i2c_driver);
+}
+module_exit(msm_i2c_exit_driver);
+
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
new file mode 100644
index 0000000..9983dd8
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -0,0 +1,930 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this software
+ * may be relicensed by the recipient under the terms of the GNU General Public
+ * License version 2 ("GPL") and only version 2, in which case the provisions of
+ * the GPL apply INSTEAD OF those given above. If the recipient relicenses the
+ * software under the GPL, then the identification text in the MODULE_LICENSE
+ * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a
+ * recipient changes the license terms to the GPL, subsequent recipients shall
+ * not relicense under alternate licensing terms, including the BSD or dual
+ * BSD/GPL terms. In addition, the following license statement immediately
+ * below and between the words START and END shall also then apply when this
+ * software is relicensed under the GPL:
+ *
+ * START
+ *
+ * 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * END
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/msm_qup.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION("0.2");
+MODULE_ALIAS("platform:i2c_qup");
+
+/* QUP Registers */
+enum {
+ QUP_CONFIG = 0x0,
+ QUP_STATE = 0x4,
+ QUP_IO_MODE = 0x8,
+ QUP_SW_RESET = 0xC,
+ QUP_OPERATIONAL = 0x18,
+ QUP_ERROR_FLAGS = 0x1C,
+ QUP_ERROR_FLAGS_EN = 0x20,
+ QUP_MX_READ_CNT = 0x208,
+ QUP_MX_INPUT_CNT = 0x200,
+ QUP_OUT_DEBUG = 0x108,
+ QUP_OUT_FIFO_CNT = 0x10C,
+ QUP_OUT_FIFO_BASE = 0x110,
+ QUP_IN_READ_CUR = 0x20C,
+ QUP_IN_DEBUG = 0x210,
+ QUP_IN_FIFO_CNT = 0x214,
+ QUP_IN_FIFO_BASE = 0x218,
+ QUP_I2C_CLK_CTL = 0x400,
+ QUP_I2C_STATUS = 0x404,
+};
+
+/* QUP States and reset values */
+enum {
+ QUP_RESET_STATE = 0,
+ QUP_RUN_STATE = 1U,
+ QUP_STATE_MASK = 3U,
+ QUP_PAUSE_STATE = 3U,
+ QUP_STATE_VALID = 1U << 2,
+ QUP_I2C_MAST_GEN = 1U << 4,
+ QUP_OPERATIONAL_RESET = 0xFF0,
+ QUP_I2C_STATUS_RESET = 0xFFFFFC,
+};
+
+/* I2C mini core related values */
+enum {
+ I2C_MINI_CORE = 2U << 8,
+ I2C_N_VAL = 0xF,
+
+};
+
+/* Packing Unpacking words in FIFOs */
+enum {
+ QUP_UNPACK_EN = 1U << 14,
+ QUP_PACK_EN = 1U << 15,
+};
+
+/* QUP tags */
+enum {
+ QUP_OUT_NOP = 0,
+ QUP_OUT_START = 1U << 8,
+ QUP_OUT_DATA = 2U << 8,
+ QUP_OUT_STOP = 3U << 8,
+ QUP_OUT_REC = 4U << 8,
+ QUP_IN_DATA = 5U << 8,
+ QUP_IN_STOP = 6U << 8,
+ QUP_IN_NACK = 7U << 8,
+};
+
+/* Status, Error flags */
+enum {
+ I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
+ I2C_STATUS_ERROR_MASK = 0xfc,
+ QUP_IN_NOT_EMPTY = 1U << 5,
+ QUP_STATUS_ERROR_MASK = 0x7F,
+ QUP_STATUS_ERROR_FLAGS = 0x7C,
+};
+
+struct qup_i2c_dev {
+ struct device *dev;
+ void __iomem *base; /* virtual */
+ void __iomem *gsbi; /* virtual */
+ int in_irq;
+ int out_irq;
+ int err_irq;
+ struct clk *clk;
+ struct clk *pclk;
+ struct i2c_adapter adapter;
+
+ struct i2c_msg *msg;
+ int pos;
+ int cnt;
+ int err;
+ int mode;
+ int clk_ctl;
+ int out_fifo_sz;
+ int in_fifo_sz;
+ int out_blk_sz;
+ int in_blk_sz;
+ struct msm_qup_i2c_platform_data *pdata;
+ void *complete;
+};
+
+#ifdef DEBUG
+static void
+qup_print_status(struct qup_i2c_dev *dev)
+{
+ uint32_t val;
+ val = readl(dev->base+QUP_CONFIG);
+ dev_dbg(dev->dev, "Qup config is :0x%x\n", val);
+ val = readl(dev->base+QUP_STATE);
+ dev_dbg(dev->dev, "Qup state is :0x%x\n", val);
+ val = readl(dev->base+QUP_IO_MODE);
+ dev_dbg(dev->dev, "Qup mode is :0x%x\n", val);
+}
+#else
+static inline void qup_print_status(struct qup_i2c_dev *dev)
+{
+}
+#endif
+
+static irqreturn_t
+qup_i2c_interrupt(int irq, void *devid)
+{
+ struct qup_i2c_dev *dev = devid;
+ uint32_t status = readl(dev->base + QUP_I2C_STATUS);
+ uint32_t status1 = readl(dev->base + QUP_ERROR_FLAGS);
+ int err = 0;
+
+ if (status & I2C_STATUS_ERROR_MASK) {
+ dev_err(dev->dev, "QUP: Got i2c error :0x%x\n", status);
+ err = -status;
+ goto intr_done;
+ }
+
+ if (status1 & 0x7F) {
+ dev_err(dev->dev, "QUP: Got QUP error :0x%x\n", status1);
+ err = -status1;
+ goto intr_done;
+ }
+
+ /* Ignore output buffer empty interrupt for READ transaction */
+ if (dev->msg && dev->msg->flags == I2C_M_RD && irq == dev->out_irq)
+ return IRQ_HANDLED;
+ else if (!dev->msg)
+ return IRQ_HANDLED;
+
+intr_done:
+ dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n",
+ irq, status, status1);
+ qup_print_status(dev);
+ dev->err = err;
+ complete(dev->complete);
+ return IRQ_HANDLED;
+}
+
+static int
+qup_i2c_poll_writeready(struct qup_i2c_dev *dev)
+{
+ uint32_t retries = 0;
+
+ while (retries != 2000) {
+ uint32_t status = readl(dev->base + QUP_I2C_STATUS);
+
+ if (!(status & I2C_STATUS_WR_BUFFER_FULL))
+ return 0;
+ if (retries++ == 1000)
+ udelay(100);
+ }
+ qup_print_status(dev);
+ return -ETIMEDOUT;
+}
+
+static int
+qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t state)
+{
+ uint32_t retries = 0;
+
+ dev_dbg(dev->dev, "Polling Status for state:0x%x\n", state);
+
+ while (retries != 2000) {
+ uint32_t status = readl(dev->base + QUP_STATE);
+
+ if ((status & (QUP_STATE_VALID | state)) ==
+ (QUP_STATE_VALID | state))
+ return 0;
+ else if (retries++ == 1000)
+ udelay(100);
+ }
+ return -ETIMEDOUT;
+}
+
+#ifdef DEBUG
+static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
+ uint32_t addr, int rdwr)
+{
+ if (rdwr)
+ dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+ else
+ dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+}
+#else
+static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
+ uint32_t addr, int rdwr)
+{
+}
+#endif
+
+static void
+qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
+ uint32_t carry_over)
+{
+ uint16_t addr = (msg->addr << 1) | 1;
+
+ /* QUP limit 256 bytes per read */
+ if (*idx % 4) {
+ writel(carry_over | ((QUP_OUT_START | addr) << 16),
+ dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */
+
+ qup_verify_fifo(dev, carry_over |
+ ((QUP_OUT_START | addr) << 16), (uint32_t)dev->base
+ + QUP_OUT_FIFO_BASE + (*idx - 2), 1);
+ writel((QUP_OUT_REC | dev->cnt),
+ dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */
+
+ qup_verify_fifo(dev, (QUP_OUT_REC | dev->cnt),
+ (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1);
+ } else {
+ writel(((QUP_OUT_REC | dev->cnt) << 16) | QUP_OUT_START | addr,
+ dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */
+
+ qup_verify_fifo(dev, QUP_OUT_REC << 16 | dev->cnt << 16 |
+ QUP_OUT_START | addr,
+ (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
+ }
+ *idx += 4;
+}
+
+static void
+qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
+ int *idx, uint32_t *carry_over)
+{
+ int entries = dev->cnt;
+ int i = 0;
+ uint32_t val = 0;
+ uint32_t last_entry = 0;
+ uint16_t addr = msg->addr << 1;
+ if (dev->pos == 0)
+ entries++;
+
+ if (dev->pos == 0) {
+ if (*idx % 4) {
+ writel(*carry_over | ((QUP_OUT_START | addr) << 16),
+ dev->base + QUP_OUT_FIFO_BASE);
+
+ qup_verify_fifo(dev, *carry_over | QUP_OUT_DATA << 16 |
+ addr << 16, (uint32_t)dev->base +
+ QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+ } else
+ val = QUP_OUT_START | addr;
+ *idx += 2;
+ i++;
+ } else if (*idx % 4) {
+ val = (QUP_OUT_NOP | 1);
+ i++;
+ }
+
+ for (; i < (entries - 1); i++) {
+ if (*idx % 4) {
+ writel(val | ((QUP_OUT_DATA |
+ msg->buf[dev->pos]) << 16),
+ dev->base + QUP_OUT_FIFO_BASE);
+
+ qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
+ msg->buf[dev->pos] << 16, (uint32_t)dev->base +
+ QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+ } else
+ val = QUP_OUT_DATA | msg->buf[dev->pos];
+ (*idx) += 2;
+ dev->pos++;
+ }
+ if (dev->pos < (dev->cnt - 1))
+ last_entry = QUP_OUT_DATA;
+ else if (rem > 1) /* not last array entry */
+ last_entry = QUP_OUT_DATA;
+ else
+ last_entry = QUP_OUT_STOP;
+ if ((*idx % 4) == 0) {
+ /*
+ * If read-start and read-command end up in different fifos, it
+ * may result in extra-byte being read due to extra-read cycle.
+ * Avoid that by inserting NOP as the last entry of fifo only
+ * if write command(s) leave 1 space in fifo.
+ */
+ if (rem > 1) {
+ struct i2c_msg *next = msg + 1;
+ if (next->addr == msg->addr && (next->flags | I2C_M_RD)
+ && *idx == ((dev->out_fifo_sz*2) - 4)) {
+ writel(((last_entry | msg->buf[dev->pos]) |
+ ((1 | QUP_OUT_NOP) << 16)), dev->base +
+ QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+ *idx += 2;
+ } else
+ *carry_over = (last_entry | msg->buf[dev->pos]);
+ } else {
+ writel((last_entry | msg->buf[dev->pos]),
+ dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+
+ qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
+ (uint32_t)dev->base + QUP_OUT_FIFO_BASE +
+ (*idx), 0);
+ }
+ } else {
+ writel(val | ((last_entry | msg->buf[dev->pos]) << 16),
+ dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
+
+ qup_verify_fifo(dev, val | (last_entry << 16) |
+ (msg->buf[dev->pos] << 16), (uint32_t)dev->base +
+ QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+ }
+
+ *idx += 2;
+ dev->pos++;
+ dev->cnt = msg->len - dev->pos;
+}
+
+static int
+qup_update_state(struct qup_i2c_dev *dev, uint32_t state)
+{
+ if (qup_i2c_poll_state(dev, 0) != 0)
+ return -EIO;
+ writel(state, dev->base + QUP_STATE);
+ if (qup_i2c_poll_state(dev, state) != 0)
+ return -EIO;
+ return 0;
+}
+
+static int
+qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ DECLARE_COMPLETION_ONSTACK(complete);
+ struct qup_i2c_dev *dev = i2c_get_adapdata(adap);
+ int ret;
+ int rem = num;
+ long timeout;
+ int err;
+
+ /* Initialize QUP registers during first transfer */
+ if (dev->clk_ctl == 0) {
+ int fs_div;
+ int hs_div;
+ int i2c_clk;
+ uint32_t fifo_reg;
+ writel(0x2 << 4, dev->gsbi);
+
+ i2c_clk = 19200000; /* input clock */
+ fs_div = ((i2c_clk / dev->pdata->clk_freq) / 2) - 3;
+ hs_div = 3;
+ dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+ fifo_reg = readl(dev->base + QUP_IO_MODE);
+ if (fifo_reg & 0x3)
+ dev->out_blk_sz = (fifo_reg & 0x3) * 16;
+ else
+ dev->out_blk_sz = 16;
+ if (fifo_reg & 0x60)
+ dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
+ else
+ dev->in_blk_sz = 16;
+ /*
+ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+ * associated with each byte written/received
+ */
+ dev->out_blk_sz /= 2;
+ dev->in_blk_sz /= 2;
+ dev->out_fifo_sz = dev->out_blk_sz *
+ (2 << ((fifo_reg & 0x1C) >> 2));
+ dev->in_fifo_sz = dev->in_blk_sz *
+ (2 << ((fifo_reg & 0x380) >> 7));
+ dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
+ dev->in_blk_sz, dev->in_fifo_sz,
+ dev->out_blk_sz, dev->out_fifo_sz);
+ }
+
+ enable_irq(dev->in_irq);
+ enable_irq(dev->out_irq);
+ enable_irq(dev->err_irq);
+ writel(QUP_RESET_STATE, dev->base + QUP_STATE);
+ ret = qup_i2c_poll_state(dev, QUP_RESET_STATE);
+ if (ret) {
+ dev_err(dev->dev, "QUP Busy:Trying to recover\n");
+ goto out_err;
+ }
+
+ /* Initialize QUP registers */
+ writel(1, dev->base + QUP_SW_RESET);
+ writel(0, dev->base + QUP_CONFIG);
+ writel(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
+ writel(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
+
+ writel(QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
+ writel(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
+
+ /* Initialize I2C mini core registers */
+ writel(0, dev->base + QUP_I2C_CLK_CTL);
+ writel(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
+
+ dev->cnt = msgs->len;
+ dev->pos = 0;
+ dev->msg = msgs;
+ while (rem) {
+ bool filled = false;
+
+ /* Wait for WR buffer not full */
+ ret = qup_i2c_poll_writeready(dev);
+ if (ret) {
+ dev_err(dev->dev,
+ "Error waiting for write ready before addr\n");
+ goto out_err;
+ }
+
+ dev->err = 0;
+ dev->complete = &complete;
+
+ if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN) != 0) {
+ ret = -EIO;
+ goto out_err;
+ }
+
+ qup_print_status(dev);
+ /* HW limits Read upto 256 bytes in 1 read without stop
+ * only FIFO mode supported right now, so read size of
+ * in_fifo supported in 1 read
+ */
+ if (dev->msg->flags == I2C_M_RD) {
+ if (dev->cnt > dev->in_fifo_sz) {
+ dev_err(dev->dev, "No Block mode support\n");
+ ret = -EPROTONOSUPPORT;
+ goto out_err;
+ }
+ writel(dev->cnt, dev->base + QUP_MX_READ_CNT);
+ } else {
+ if (dev->cnt > dev->out_fifo_sz) {
+ dev_err(dev->dev, "No Block mode support\n");
+ ret = -EPROTONOSUPPORT;
+ goto out_err;
+ } else if (rem > 1) {
+ struct i2c_msg *next = msgs + 1;
+ if (next->addr == msgs->addr &&
+ next->flags == I2C_M_RD) {
+ if (next->len > dev->in_fifo_sz) {
+ dev_err(dev->dev,
+ "No Block mode support\n");
+ ret = -EPROTONOSUPPORT;
+ goto out_err;
+ }
+ writel(next->len, dev->base +
+ QUP_MX_READ_CNT);
+ }
+ }
+ }
+
+ err = qup_update_state(dev, QUP_RUN_STATE);
+ if (err < 0) {
+ ret = err;
+ goto out_err;
+ }
+
+ qup_print_status(dev);
+ writel(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
+
+ do {
+ int idx = 0;
+ uint32_t carry_over = 0;
+
+ /* Transition to PAUSE state only possible from RUN */
+ err = qup_update_state(dev, QUP_PAUSE_STATE);
+ if (err < 0) {
+ ret = err;
+ goto out_err;
+ }
+
+ qup_print_status(dev);
+ /* This operation is Write, check the next operation
+ * and decide mode
+ */
+ while (filled == false) {
+ if (msgs->flags & I2C_M_RD)
+ qup_issue_read(dev, msgs, &idx,
+ carry_over);
+ else
+ qup_issue_write(dev, msgs, rem, &idx,
+ &carry_over);
+ if (idx >= dev->out_fifo_sz)
+ filled = true;
+ /* Start new message */
+ if (filled == false) {
+ if (msgs->flags & I2C_M_RD)
+ filled = true;
+ else if (rem > 1) {
+ /* Only combine operations with
+ * same address
+ */
+ struct i2c_msg *next = msgs + 1;
+ if (next->addr != msgs->addr)
+ filled = true;
+ else {
+ rem--;
+ msgs++;
+ dev->msg = msgs;
+ dev->pos = 0;
+ dev->cnt = msgs->len;
+ }
+ } else
+ filled = true;
+ }
+ }
+ err = qup_update_state(dev, QUP_RUN_STATE);
+ if (err < 0) {
+ ret = err;
+ goto out_err;
+ }
+ dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n",
+ idx, rem, num, dev->mode);
+
+ qup_print_status(dev);
+ timeout = wait_for_completion_timeout(&complete,
+ msecs_to_jiffies(dev->out_fifo_sz));
+ if (!timeout) {
+ dev_err(dev->dev, "Transaction timed out\n");
+ writel(1, dev->base + QUP_SW_RESET);
+ msleep(10);
+ ret = -ETIMEDOUT;
+ goto out_err;
+ }
+ if (dev->err) {
+ dev_err(dev->dev,
+ "Error during data xfer (%d)\n",
+ dev->err);
+ ret = dev->err;
+ goto out_err;
+ }
+ if (dev->msg->flags & I2C_M_RD) {
+ int i;
+ uint32_t dval = 0;
+ for (i = 0; dev->pos < dev->msg->len; i++,
+ dev->pos++) {
+ uint32_t rd_status = readl(dev->base +
+ QUP_OPERATIONAL);
+ if (i % 2 == 0) {
+ if ((rd_status &
+ QUP_IN_NOT_EMPTY) == 0)
+ break;
+ dval = readl(dev->base +
+ QUP_IN_FIFO_BASE);
+ dev->msg->buf[dev->pos] =
+ dval & 0xFF;
+ } else
+ dev->msg->buf[dev->pos] =
+ ((dval & 0xFF0000) >>
+ 16);
+ }
+ dev->cnt -= i;
+ } else
+ filled = false; /* refill output FIFO */
+ } while (dev->cnt > 0);
+ if (dev->cnt == 0) {
+ rem--;
+ msgs++;
+ if (rem) {
+ dev->pos = 0;
+ dev->cnt = msgs->len;
+ dev->msg = msgs;
+ }
+ }
+ }
+
+ ret = num;
+ out_err:
+ dev->complete = NULL;
+ dev->msg = NULL;
+ dev->pos = 0;
+ dev->err = 0;
+ dev->cnt = 0;
+ disable_irq(dev->err_irq);
+ disable_irq(dev->in_irq);
+ disable_irq(dev->out_irq);
+ return ret;
+}
+
+static u32
+qup_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm qup_i2c_algo = {
+ .master_xfer = qup_i2c_xfer,
+ .functionality = qup_i2c_func,
+};
+
+static int __devinit
+qup_i2c_probe(struct platform_device *pdev)
+{
+ struct qup_i2c_dev *dev;
+ struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io;
+ struct resource *in_irq, *out_irq, *err_irq;
+ struct clk *clk, *pclk;
+ int ret = 0;
+ struct msm_qup_i2c_platform_data *pdata;
+
+ dev_dbg(&pdev->dev, "qup_i2c_probe\n");
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data not initialized\n");
+ return -ENOSYS;
+ }
+ qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "qup_phys_addr");
+ if (!qup_mem) {
+ dev_err(&pdev->dev, "no qup mem resource?\n");
+ return -ENODEV;
+ }
+ gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "gsbi_qup_i2c_addr");
+ if (!gsbi_mem) {
+ dev_err(&pdev->dev, "no gsbi mem resource?\n");
+ return -ENODEV;
+ }
+
+ in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "qup_in_intr");
+ if (!in_irq) {
+ dev_err(&pdev->dev, "no input irq resource?\n");
+ return -ENODEV;
+ }
+ out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "qup_out_intr");
+ if (!out_irq) {
+ dev_err(&pdev->dev, "no output irq resource?\n");
+ return -ENODEV;
+ }
+ err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "qup_err_intr");
+ if (!err_irq) {
+ dev_err(&pdev->dev, "no error irq resource?\n");
+ return -ENODEV;
+ }
+
+ qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
+ pdev->name);
+ if (!qup_io) {
+ dev_err(&pdev->dev, "QUP region already claimed\n");
+ return -EBUSY;
+ }
+ gsbi_io = request_mem_region(gsbi_mem->start, resource_size(gsbi_mem),
+ pdev->name);
+ if (!gsbi_io) {
+ dev_err(&pdev->dev, "GSBI region already claimed\n");
+ return -EBUSY;
+ }
+
+ clk = clk_get(&pdev->dev, "qup_clk");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_get_failed;
+ }
+
+ pclk = clk_get(&pdev->dev, "qup_pclk");
+ if (IS_ERR(clk))
+ pclk = NULL;
+
+ if (!(pdata->msm_i2c_config_gpio)) {
+ dev_err(&pdev->dev, "config_gpio function not initialized\n");
+ ret = -ENOSYS;
+ goto err_config_failed;
+ }
+
+ /* We support frequencies upto FAST Mode(400KHz) */
+ if (pdata->clk_freq <= 0 ||
+ pdata->clk_freq > 400000) {
+ dev_err(&pdev->dev, "clock frequency not supported\n");
+ ret = -EIO;
+ goto err_config_failed;
+ }
+
+ dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_alloc_dev_failed;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->in_irq = in_irq->start;
+ dev->out_irq = out_irq->start;
+ dev->err_irq = err_irq->start;
+ dev->clk = clk;
+ dev->pclk = pclk;
+ dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
+ if (!dev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap_failed;
+ }
+
+ /* Configure GSBI block to use I2C functionality */
+ dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
+ if (!dev->gsbi) {
+ ret = -ENOMEM;
+ goto err_gsbi_failed;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ clk_enable(clk);
+ if (pclk)
+ clk_enable(pclk);
+ dev->pdata = pdata;
+ dev->clk_ctl = 0;
+
+ i2c_set_adapdata(&dev->adapter, dev);
+ dev->adapter.algo = &qup_i2c_algo;
+ strlcpy(dev->adapter.name,
+ "QUP I2C adapter",
+ sizeof(dev->adapter.name));
+
+ dev->adapter.nr = pdev->id;
+ ret = i2c_add_numbered_adapter(&dev->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "i2c_add_adapter failed\n");
+ goto err_i2c_add_adapter_failed;
+ }
+
+ ret = request_irq(dev->in_irq, qup_i2c_interrupt,
+ IRQF_TRIGGER_RISING, "qup_in_intr", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_out_irq failed\n");
+ goto err_request_irq_failed;
+ }
+ ret = request_irq(dev->out_irq, qup_i2c_interrupt,
+ IRQF_TRIGGER_RISING, "qup_out_intr", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_in_irq failed\n");
+ free_irq(dev->in_irq, dev);
+ goto err_request_irq_failed;
+ }
+ ret = request_irq(dev->err_irq, qup_i2c_interrupt,
+ IRQF_TRIGGER_RISING, "qup_err_intr", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_err_irq failed\n");
+ free_irq(dev->out_irq, dev);
+ free_irq(dev->in_irq, dev);
+ goto err_request_irq_failed;
+ }
+ disable_irq(dev->err_irq);
+ disable_irq(dev->in_irq);
+ disable_irq(dev->out_irq);
+ pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
+
+ return 0;
+
+err_request_irq_failed:
+ i2c_del_adapter(&dev->adapter);
+err_i2c_add_adapter_failed:
+ clk_disable(clk);
+ if (pclk)
+ clk_disable(pclk);
+ iounmap(dev->gsbi);
+err_gsbi_failed:
+ iounmap(dev->base);
+err_ioremap_failed:
+ kfree(dev);
+err_alloc_dev_failed:
+err_config_failed:
+ clk_put(clk);
+ if (pclk)
+ clk_put(pclk);
+err_clk_get_failed:
+ release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
+ release_mem_region(qup_mem->start, resource_size(qup_mem));
+ return ret;
+}
+
+static int __devexit
+qup_i2c_remove(struct platform_device *pdev)
+{
+ struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *qup_mem, *gsbi_mem;
+
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->out_irq, dev);
+ free_irq(dev->in_irq, dev);
+ free_irq(dev->err_irq, dev);
+ i2c_del_adapter(&dev->adapter);
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ if (dev->pclk) {
+ clk_disable(dev->pclk);
+ clk_put(dev->pclk);
+ }
+ iounmap(dev->gsbi);
+ iounmap(dev->base);
+ kfree(dev);
+ gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "gsbi_qup_i2c_addr");
+ release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
+ qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "qup_phys_addr");
+ release_mem_region(qup_mem->start, resource_size(qup_mem));
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int qup_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable(dev->clk);
+ if (dev->pclk)
+ clk_disable(dev->pclk);
+ return 0;
+}
+
+static int qup_i2c_resume(struct platform_device *pdev)
+{
+ struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+
+ clk_enable(dev->clk);
+ if (dev->pclk)
+ clk_enable(dev->pclk);
+ return 0;
+}
+#else
+#define qup_i2c_suspend NULL
+#define qup_i2c_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver qup_i2c_driver = {
+ .probe = qup_i2c_probe,
+ .remove = __devexit_p(qup_i2c_remove),
+ .suspend = qup_i2c_suspend,
+ .resume = qup_i2c_resume,
+ .driver = {
+ .name = "qup_i2c",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* QUP may be needed to bring up other drivers */
+static int __init
+qup_i2c_init_driver(void)
+{
+ return platform_driver_register(&qup_i2c_driver);
+}
+subsys_initcall(qup_i2c_init_driver);
+
+static void __exit qup_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&qup_i2c_driver);
+}
+module_exit(qup_i2c_exit_driver);
+
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
new file mode 100644
index 0000000..f58e49a
--- /dev/null
+++ b/drivers/i2c/chips/Kconfig
@@ -0,0 +1,16 @@
+#
+# Miscellaneous I2C chip drivers configuration
+#
+# *** DEPRECATED! Do not add new entries! See Makefile ***
+#
+
+menu "Miscellaneous I2C Chip support"
+
+config SENSORS_MT9T013
+ tristate "MT9T013 Camera Driver"
+ depends on I2C
+ default y
+ help
+ MT9T013 Camera Driver implemented by HTC.
+
+endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
new file mode 100644
index 0000000..23b7b7b
--- /dev/null
+++ b/drivers/i2c/chips/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for miscellaneous I2C chip drivers.
+#
+# Do not add new drivers to this directory! It is DEPRECATED.
+#
+# Device drivers are better grouped according to the functionality they
+# implement rather than to the bus they are connected to. In particular:
+# * Hardware monitoring chip drivers go to drivers/hwmon
+# * RTC chip drivers go to drivers/rtc
+# * I/O expander drivers go to drivers/gpio
+#
+
+obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o
+
+ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c
new file mode 100755
index 0000000..200a9f8
--- /dev/null
+++ b/drivers/i2c/chips/mt9t013.c
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (C) 2007-2008 HTC 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/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/clk.h>
+#include <linux/wakelock.h>
+#include <net/sock.h>
+#include <asm/gpio.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/vreg.h>
+#include <mach/board.h>
+#include <linux/mt9t013.h> /* define ioctls */
+
+
+#define ALLOW_USPACE_RW 0
+
+static const uint32_t fps_divider = 1;
+
+#define AF_I2C_ID 0x18 /* actuator's slave address */
+
+static struct i2c_client *pclient;
+
+/* we need this to set the clock rate */
+static struct clk *vfe_clk;
+
+/* camif clocks */
+static struct clk *vfe_mdc_clk;
+static struct clk *mdc_clk;
+
+static int mdc_clk_enabled;
+static int vfe_mdc_clk_enabled;
+static int vfe_clk_enabled;
+static int opened;
+static int pclk_set;
+
+static const struct mt9t013_reg_pat mt9t013_reg_pattern = { .reg = {
+ { /* preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */
+ 10, /*vt_pix_clk_div REG=0x0300*/
+ /*update get_snapshot_fps if this change*/
+ 1, /*vt_sys_clk_div REG=0x0302*/
+ /*update get_snapshot_fps if this change*/
+ 3, /*2, pre_pll_clk_div REG=0x0304*/
+ /*update get_snapshot_fps if this change*/
+ 80, /*40, pll_multiplier REG=0x0306*/
+ /*60 for 30fps preview, 40 for 20fps preview*/
+ 10, /*op_pix_clk_div REG=0x0308*/
+ 1, /*op_sys_clk_div REG=0x030A*/
+ 16, /*scale_m REG=0x0404*/
+ 0x0111, /*row_speed REG=0x3016*/
+ 8, /*x_addr_start REG=0x3004*/
+ 2053, /*x_addr_end REG=0x3008*/
+ 8, /*y_addr_start REG=0x3002*/
+ 1541, /*y_addr_end REG=0x3006*/
+ 0x046C, /*read_mode REG=0x3040*/
+ 1024, /*x_output_size REG=0x034C*/
+ 768, /*y_output_size REG=0x034E*/
+ 3540, /*2616, line_length_pck REG=0x300C*/
+ 861, /*916, frame_length_lines REG=0x300A*/
+ 16, /*coarse_integration_time REG=0x3012*/
+ 1461 /*fine_integration_time REG=0x3014*/
+ },
+ { /* snapshot */
+ 10, /*vt_pix_clk_div REG=0x0300*/
+ /*update get_snapshot_fps if this change*/
+ 1, /*vt_sys_clk_div REG=0x0302*/
+ /*update get_snapshot_fps if this change*/
+ 3, /*2, pre_pll_clk_div REG=0x0304*/
+ /*update get_snapshot_fps if this change*/
+ 80, /*40, pll_multiplier REG=0x0306*/
+ /*50 for 15fps snapshot, 40 for 10fps snapshot*/
+ 10, /*op_pix_clk_div REG=0x0308*/
+ 1, /*op_sys_clk_div REG=0x030A*/
+ 16, /*scale_m REG=0x0404*/
+ 0x0111, /*row_speed REG=0x3016*/
+ 8, /*0, x_addr_start REG=0x3004*/
+ 2063, /*2061, x_addr_end REG=0x3008*/
+ 8, /*2, y_addr_start REG=0x3002*/
+ 1551, /*1545, y_addr_end REG=0x3006*/
+ 0x0024, /*read_mode REG=0x3040*/
+ 2063, /*output_size REG=0x034C*/
+ 1544, /*y_output_size REG=0x034E*/
+ 4800, /*2952, line_length_pck REG=0x300C*/
+ 1629, /*frame_length_lines REG=0x300A*/
+ 16, /*coarse_integration_time REG=0x3012*/
+ 733 /*fine_integration_time REG=0x3014*/
+ }
+}};
+
+#define MT9T013_MU3M0VC_REG_MODEL_ID 0x0000
+#define MT9T013_MU3M0VC_MODEL_ID 0x2600
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x0100
+#define GROUPED_PARAMETER_UPDATE 0x0000
+#define REG_COARSE_INTEGRATION_TIME 0x3012
+#define REG_VT_PIX_CLK_DIV 0x0300
+#define REG_VT_SYS_CLK_DIV 0x0302
+#define REG_PRE_PLL_CLK_DIV 0x0304
+#define REG_PLL_MULTIPLIER 0x0306
+#define REG_OP_PIX_CLK_DIV 0x0308
+#define REG_OP_SYS_CLK_DIV 0x030A
+#define REG_SCALE_M 0x0404
+#define REG_FRAME_LENGTH_LINES 0x300A
+#define REG_LINE_LENGTH_PCK 0x300C
+#define REG_X_ADDR_START 0x3004
+#define REG_Y_ADDR_START 0x3002
+#define REG_X_ADDR_END 0x3008
+#define REG_Y_ADDR_END 0x3006
+#define REG_X_OUTPUT_SIZE 0x034C
+#define REG_Y_OUTPUT_SIZE 0x034E
+#define REG_FINE_INTEGRATION_TIME 0x3014
+#define REG_ROW_SPEED 0x3016
+#define MT9T013_REG_RESET_REGISTER 0x301A
+#define MT9T013_RESET_REGISTER_PWON 0x10CC /*enable paralled and start streaming*/
+#define MT9T013_RESET_REGISTER_PWOFF 0x1008 //0x10C8 /*stop streaming*/
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+static struct wake_lock mt9t013_wake_lock;
+
+static inline void init_suspend(void)
+{
+ wake_lock_init(&mt9t013_wake_lock, WAKE_LOCK_IDLE, "mt9t013");
+}
+
+static inline void deinit_suspend(void)
+{
+ wake_lock_destroy(&mt9t013_wake_lock);
+}
+
+static inline void prevent_suspend(void)
+{
+ wake_lock(&mt9t013_wake_lock);
+}
+
+static inline void allow_suspend(void)
+{
+ wake_unlock(&mt9t013_wake_lock);
+}
+
+#define CLK_GET(clk) do { \
+ if (!clk) { \
+ clk = clk_get(NULL, #clk); \
+ printk(KERN_INFO \
+ "mt9t013: clk_get(%s): %p\n", #clk, clk); \
+ } \
+} while(0)
+
+DECLARE_MUTEX(sem);
+
+static struct msm_camera_legacy_device_platform_data *cam;
+
+#define out_dword(addr, val) \
+ (*((volatile unsigned long *)(addr)) = ((unsigned long)(val)))
+
+#define out_dword_masked_ns(io, mask, val, current_reg_content) \
+ (void) out_dword(io, ((current_reg_content & (uint32_t)(~(mask))) | \
+ ((uint32_t)((val) & (mask)))))
+
+#define __inpdw(port) (*((volatile uint32_t *) (port)))
+#define in_dword_masked(addr, mask) (__inpdw(addr) & (uint32_t)mask )
+
+#define HWIO_MDDI_CAMIF_CFG_ADDR MSM_MDC_BASE
+#define HWIO_MDDI_CAMIF_CFG_RMSK 0x1fffff
+#define HWIO_MDDI_CAMIF_CFG_IN \
+ in_dword_masked(HWIO_MDDI_CAMIF_CFG_ADDR, HWIO_MDDI_CAMIF_CFG_RMSK)
+
+#define HWIO_MDDI_CAMIF_CFG_OUTM(m,v) \
+ out_dword_masked_ns(HWIO_MDDI_CAMIF_CFG_ADDR,m,v,HWIO_MDDI_CAMIF_CFG_IN);
+#define __msmhwio_outm(hwiosym, mask, val) HWIO_##hwiosym##_OUTM(mask, val)
+#define HWIO_OUTM(hwiosym, mask, val) __msmhwio_outm(hwiosym, mask, val)
+
+#define HWIO_MDDI_CAMIF_CFG_CAM_SEL_BMSK 0x2
+#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_BMSK 0x80000
+#define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define HWIO_MDDI_CAMIF_CFG_CAM_SEL_SHFT 0x1
+#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_SHFT 0x11
+#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_SHFT 0x13
+#define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define __msmhwio_shft(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_SHFT
+#define HWIO_SHFT(hwio_regsym, hwio_fldsym) __msmhwio_shft(hwio_regsym, hwio_fldsym)
+
+#define __msmhwio_fmsk(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_BMSK
+#define HWIO_FMSK(hwio_regsym, hwio_fldsym) __msmhwio_fmsk(hwio_regsym, hwio_fldsym)
+
+#define HWIO_APPS_RESET_ADDR (MSM_CLK_CTL_BASE + 0x00000210)
+#define HWIO_APPS_RESET_RMSK 0x1fff
+#define HWIO_APPS_RESET_VFE_BMSK 1
+#define HWIO_APPS_RESET_VFE_SHFT 0
+#define HWIO_APPS_RESET_IN in_dword_masked(HWIO_APPS_RESET_ADDR, HWIO_APPS_RESET_RMSK)
+#define HWIO_APPS_RESET_OUTM(m,v) out_dword_masked_ns(HWIO_APPS_RESET_ADDR,m,v,HWIO_APPS_RESET_IN)
+
+struct mt9t013_data {
+ struct work_struct work;
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(g_data_ready_wait_queue);
+
+static int mt9t013_i2c_sensor_init(struct mt9t013_init *init);
+static int mt9t013_i2c_sensor_setting(unsigned long arg);
+static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line,
+ uint16_t gain);
+static int mt9t013_i2c_move_focus(uint16_t position);
+static int mt9t013_i2c_set_default_focus(uint8_t step);
+static int mt9t013_i2c_power_up(void);
+static int mt9t013_i2c_power_down(void);
+static int mt9t013_camif_pad_reg_reset(void);
+static int mt9t013_lens_power(int on);
+
+int mt_i2c_lens_tx_data(unsigned char slave_addr, char* txData, int length)
+{
+ int rc;
+ struct i2c_msg msg[] = {
+ {
+ .addr = slave_addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+#if 0
+ {
+ int i;
+ /* printk(KERN_INFO "mt_i2c_lens_tx_data: af i2c client addr = %x,"
+ " register addr = 0x%02x%02x:\n", slave_addr, txData[0], txData[1]);
+ */
+ for (i = 0; i < length - 2; i++)
+ printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, txData[i+2]);
+ }
+#endif
+
+ rc = i2c_transfer(pclient->adapter, msg, 1);
+ if (rc < 0) {
+ printk(KERN_ERR "mt_i2c_lens_tx_data: i2c_transfer error %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int mt9t013_i2c_lens_write(unsigned char slave_addr, unsigned char u_addr, unsigned char u_data)
+{
+ unsigned char buf[2] = { u_addr, u_data };
+ return mt_i2c_lens_tx_data(slave_addr, buf, sizeof(buf));
+}
+
+static int mt_i2c_rx_data(char* rxData, int length)
+{
+ int rc;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = pclient->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxData,
+ },
+ {
+ .addr = pclient->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ rc = i2c_transfer(pclient->adapter, msgs, 2);
+ if (rc < 0) {
+ printk(KERN_ERR "mt9t013: mt_i2c_rx_data error %d\n", rc);
+ return rc;
+ }
+#if 0
+ else {
+ int i;
+ for (i = 0; i < length; i++)
+ printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, rxData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+int mt_i2c_tx_data(char* txData, int length)
+{
+ int rc;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = pclient->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ rc = i2c_transfer(pclient->adapter, msg, 1);
+ if (rc < 0) {
+ printk(KERN_ERR "mt9t013: mt_i2c_tx_data error %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int mt9t013_i2c_write(unsigned short u_addr, unsigned short u_data)
+{
+ int rc;
+ unsigned char buf[4];
+
+ buf[0] = (u_addr & 0xFF00) >> 8;
+ buf[1] = u_addr & 0x00FF;
+ buf[2] = (u_data & 0xFF00) >> 8;
+ buf[3] = u_data & 0x00FF;
+
+ rc = mt_i2c_tx_data(buf, sizeof(buf));
+ if(rc < 0)
+ printk(KERN_ERR "mt9t013: txdata error %d add:0x%02x data:0x%02x\n",
+ rc, u_addr, u_data);
+ return rc;
+}
+
+static int mt9t013_i2c_read(unsigned short u_addr, unsigned short *pu_data)
+{
+ int rc;
+ unsigned char buf[2];
+
+ buf[0] = (u_addr & 0xFF00)>>8;
+ buf[1] = (u_addr & 0x00FF);
+ rc = mt_i2c_rx_data(buf, 2);
+ if (!rc)
+ *pu_data = buf[0]<<8 | buf[1];
+ else printk(KERN_ERR "mt9t013: i2c read failed\n");
+ return rc;
+}
+
+static int msm_camio_clk_enable (int clk_type)
+{
+ struct clk *clk = NULL;
+ int *enabled = NULL;
+
+ switch (clk_type) {
+ case CAMIO_VFE_MDC_CLK:
+ CLK_GET(vfe_mdc_clk);
+ clk = vfe_mdc_clk;
+ enabled = &vfe_mdc_clk_enabled;
+ break;
+ case CAMIO_MDC_CLK:
+ CLK_GET(mdc_clk);
+ clk = mdc_clk;
+ enabled = &mdc_clk_enabled;
+ break;
+ default:
+ break;
+ }
+
+ if (clk != NULL && !*enabled) {
+ int rc = clk_enable(clk);
+ *enabled = !rc;
+ return rc;
+ }
+
+ return -EINVAL;
+}
+
+static int msm_camio_clk_disable(int clk_type)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+ int *enabled = NULL;
+
+ switch (clk_type) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = vfe_mdc_clk;
+ enabled = &vfe_mdc_clk_enabled;
+ break;
+ case CAMIO_MDC_CLK:
+ clk = mdc_clk;
+ enabled = &mdc_clk_enabled;
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ if (clk != NULL && *enabled) {
+ clk_disable(clk);
+ *enabled = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int msm_camio_vfe_clk_enable(void)
+{
+ CLK_GET(vfe_clk);
+ if (vfe_clk && !vfe_clk_enabled) {
+ vfe_clk_enabled = !clk_enable(vfe_clk);
+ printk(KERN_INFO "mt9t013: enable vfe_clk\n");
+ }
+ return vfe_clk_enabled ? 0 : -EIO;
+}
+
+static int msm_camio_clk_rate_set(int rate)
+{
+ int rc = msm_camio_vfe_clk_enable();
+ if (!rc && vfe_clk_enabled)
+ rc = clk_set_rate(vfe_clk, rate);
+ return rc;
+}
+
+static int clk_select(int internal)
+{
+ int rc = -EIO;
+ printk(KERN_INFO "mt9t013: clk select %d\n", internal);
+ CLK_GET(vfe_clk);
+ if (vfe_clk != NULL) {
+ extern int clk_set_flags(struct clk *clk, unsigned long flags);
+ rc = clk_set_flags(vfe_clk, 0x00000100 << internal);
+ if (!rc && internal) rc = msm_camio_vfe_clk_enable();
+ }
+ return rc;
+}
+
+static void mt9t013_sensor_init(void)
+{
+ int ret;
+ printk(KERN_INFO "mt9t013: init\n");
+ if (!pclient)
+ return;
+
+ /*pull hi reset*/
+ printk(KERN_INFO "mt9t013: mt9t013_register_init\n");
+ ret = gpio_request(cam->sensor_reset, "mt9t013");
+ if (!ret) {
+ gpio_direction_output(cam->sensor_reset, 1);
+ printk(KERN_INFO "mt9t013: camera sensor_reset set as 1\n");
+ } else
+ printk(KERN_ERR "mt9t013 error: request gpio %d failed: "
+ "%d\n", cam->sensor_reset, ret);
+ mdelay(2);
+
+ /* pull down power down */
+ ret = gpio_request(cam->sensor_pwd, "mt9t013");
+ if (!ret || ret == -EBUSY)
+ gpio_direction_output(cam->sensor_pwd, 0);
+ else printk(KERN_ERR "mt913t013 error: request gpio %d failed: "
+ "%d\n", cam->sensor_pwd, ret);
+ gpio_free(cam->sensor_pwd);
+
+ /* enable clk */
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+
+ /* reset CAMIF */
+ mt9t013_camif_pad_reg_reset();
+
+ /* set mclk */
+ ret = msm_camio_clk_rate_set(24000000);
+ if(ret < 0)
+ printk(KERN_ERR "camio clk rate select error\n");
+ mdelay(2);
+
+ /* enable gpio */
+ cam->config_gpio_on();
+ /* delay 2 ms */
+ mdelay(2);
+
+ /* reset sensor sequency */
+ gpio_direction_output(cam->sensor_reset, 0);
+ mdelay(2);
+ gpio_direction_output(cam->sensor_reset, 1);
+ gpio_free(cam->sensor_reset);
+ mdelay(2);
+
+ printk(KERN_INFO "mt9t013: camera sensor init sequence done\n");
+}
+
+#define CLK_DISABLE_AND_PUT(clk) do { \
+ if (clk) { \
+ if (clk##_enabled) { \
+ printk(KERN_INFO "mt9t013: disabling "#clk"\n");\
+ clk_disable(clk); \
+ clk##_enabled = 0; \
+ } \
+ printk(KERN_INFO \
+ "mt9t013: clk_put(%s): %p\n", #clk, clk); \
+ clk_put(clk); \
+ clk = NULL; \
+ } \
+} while(0)
+
+static void mt9t013_sensor_suspend(void)
+{
+ printk(KERN_INFO "mt9t013: camera sensor suspend sequence\n");
+ if (!pclient) {
+ return;
+ }
+ /*disable clk*/
+ msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ CLK_DISABLE_AND_PUT(vfe_clk); /* this matches clk_select(1) */
+ /* disable gpios */
+ cam->config_gpio_off();
+ printk(KERN_INFO "mt9t013: camera sensor suspend sequence done\n");
+}
+
+static int mt9t013_open(struct inode *ip, struct file *fp)
+{
+ int rc = -EBUSY;
+ down(&sem);
+ printk(KERN_INFO "mt9t013: open\n");
+ if (!opened) {
+ printk(KERN_INFO "mt9t013: prevent collapse on idle\n");
+ prevent_suspend();
+ cam->config_gpio_on();
+ opened = 1;
+ rc = 0;
+ }
+ up(&sem);
+ return rc;
+}
+
+static int mt9t013_release(struct inode *ip, struct file *fp)
+{
+ int rc = -EBADF;
+ printk(KERN_INFO "mt9t013: release\n");
+ down(&sem);
+ if (opened) {
+ printk(KERN_INFO "mt9t013: release clocks\n");
+
+
+ /* mt9t013_i2c_power_down() should be called before closing MCLK */
+ /* otherwise I2C_WRITE will always fail */
+ mt9t013_i2c_power_down();
+
+ CLK_DISABLE_AND_PUT(mdc_clk);
+ CLK_DISABLE_AND_PUT(vfe_mdc_clk);
+ CLK_DISABLE_AND_PUT(vfe_clk);
+ mt9t013_lens_power(0);
+
+ cam->config_gpio_off();
+
+ printk(KERN_INFO "mt9t013: allow collapse on idle\n");
+ allow_suspend();
+ rc = pclk_set = opened = 0;
+ }
+ up(&sem);
+ return rc;
+}
+
+#undef CLK_DISABLE_AND_PUT
+
+#define CHECK() ({ \
+ if (!mdc_clk_enabled || !vfe_mdc_clk_enabled) { \
+ printk(KERN_ERR "mt9t013 error: one or more clocks" \
+ " are NULL.\n"); \
+ rc = -EIO; \
+ } \
+ !rc; })
+
+static int mt9t013_camif_pad_reg_reset(void)
+{
+ int rc = clk_select(1);
+ if(rc < 0) {
+ printk(KERN_ERR "mt9t013 error switching to internal clock\n");
+ return rc;
+ }
+ HWIO_OUTM (MDDI_CAMIF_CFG,
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_SEL) |
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) |
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_INVERT),
+ 1 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_SEL) |
+ 3 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) |
+ 0 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_INVERT));
+ msleep(10);
+ HWIO_OUTM (MDDI_CAMIF_CFG,
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
+ 1 << HWIO_SHFT (MDDI_CAMIF_CFG,
+ CAM_PAD_REG_SW_RESET));
+ msleep(10);
+ HWIO_OUTM (MDDI_CAMIF_CFG,
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
+ 0 << HWIO_SHFT (MDDI_CAMIF_CFG,
+ CAM_PAD_REG_SW_RESET));
+ msleep(10);
+ rc = clk_select(0); /* external */
+ if(rc < 0) {
+ printk(KERN_ERR "mt9t013 error switching to external clock\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+#if ALLOW_USPACE_RW
+#define COPY_FROM_USER(size) ({ \
+ if (copy_from_user(rwbuf, argp, size)) rc = -EFAULT; \
+ !rc; })
+#endif
+
+static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int rc = 0;
+
+#if ALLOW_USPACE_RW
+ unsigned short addr = 0;
+ unsigned short data = 0;
+ char rwbuf[4];
+#endif
+
+ down(&sem);
+
+ switch(cmd) {
+#if ALLOW_USPACE_RW
+ case MT9T013_I2C_IOCTL_W:
+ if (/* CHECK() && */ COPY_FROM_USER(4)) {
+ addr = *((unsigned short *)rwbuf);
+ data = *((unsigned short *)(rwbuf+2));
+ rc = mt9t013_i2c_write(addr, data);
+ } else
+ printk(KERN_ERR "mt9t013: write: err %d\n", rc);
+ break;
+
+ case MT9T013_I2C_IOCTL_R:
+ if (/* CHECK() && */ COPY_FROM_USER(4)) {
+ addr = *((unsigned short*) rwbuf);
+ rc = mt9t013_i2c_read(addr, (unsigned short *)(rwbuf+2));
+ if (!rc) {
+ if (copy_to_user(argp, rwbuf, 4)) {
+ printk(KERN_ERR "mt9t013: read: err " \
+ "writeback -EFAULT\n");
+ rc = -EFAULT;
+ }
+ }
+ } else
+ printk(KERN_ERR "mt9t013: read: err %d\n", rc);
+ break;
+
+ case MT9T013_I2C_IOCTL_AF_W:
+ if (/* CHECK() && */ COPY_FROM_USER(3))
+ rc = mt9t013_i2c_lens_write(*rwbuf, *(rwbuf + 1), *(rwbuf + 2));
+ else
+ printk(KERN_ERR "mt9t013: af write: err %d\n", rc);
+ break;
+#endif /* ALLOW_USPACE_RW */
+
+ case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET:
+ printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET\n");
+ if (CHECK())
+ rc = mt9t013_camif_pad_reg_reset();
+ break;
+
+ case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET_2:
+ printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET_2 (pclk_set %d)\n",
+ pclk_set);
+ if (!pclk_set)
+ rc = -EIO;
+ else if (CHECK()) {
+ HWIO_OUTM (MDDI_CAMIF_CFG,
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
+ 1 << HWIO_SHFT (MDDI_CAMIF_CFG,
+ CAM_PAD_REG_SW_RESET));
+ msleep(10);
+ HWIO_OUTM (MDDI_CAMIF_CFG,
+ HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
+ 0 << HWIO_SHFT (MDDI_CAMIF_CFG,
+ CAM_PAD_REG_SW_RESET));
+ msleep(10);
+ }
+ break;
+
+ case MT9T013_I2C_IOCTL_CAMIF_APPS_RESET:
+ printk(KERN_INFO "mt9t013: CAMIF_APPS_RESET\n");
+ if (CHECK()) {
+ rc = clk_select(1);
+ if(rc < 0) {
+ printk(KERN_ERR "mt9t013 error switching to internal clock\n");
+ break;
+ }
+ HWIO_OUTM (APPS_RESET,
+ HWIO_FMSK(APPS_RESET,VFE),
+ 1 << HWIO_SHFT(APPS_RESET,VFE));
+ udelay(10);
+ HWIO_OUTM (APPS_RESET,
+ HWIO_FMSK(APPS_RESET,VFE),
+ 0 << HWIO_SHFT(APPS_RESET,VFE));
+ udelay(10);
+ rc = clk_select(0); /* external */
+ if(rc < 0) {
+ printk(KERN_ERR "mt9t013 error switching to external clock\n");
+ break;
+ }
+ }
+ break;
+
+ case CAMERA_LENS_POWER_ON:
+ rc = mt9t013_lens_power(1);
+ break;
+
+ case CAMERA_LENS_POWER_OFF:
+ rc = mt9t013_lens_power(0);
+ break;
+
+ case MT9T013_I2C_IOCTL_CLK_ENABLE:
+ printk(KERN_INFO "mt9t013: clk enable %ld\n", arg);
+ rc = msm_camio_clk_enable(arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_CLK_DISABLE:
+ printk(KERN_INFO "mt9t013: clk disable %ld\n", arg);
+ rc = msm_camio_clk_disable(arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_CLK_SELECT:
+ printk(KERN_INFO "mt9t013: clk select %ld\n", arg);
+ rc = clk_select(!!arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_CLK_FREQ_PROG:
+ printk(KERN_INFO "mt9t013: clk rate select %ld\n", arg);
+ rc = msm_camio_clk_rate_set(arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_GET_REGISTERS:
+ printk(KERN_INFO "mt9t013: get registers\n");
+ if (copy_to_user(argp, &mt9t013_reg_pattern.reg, sizeof(mt9t013_reg_pattern.reg)))
+ rc = -EFAULT;
+ break;
+
+ case MT9T013_I2C_IOCTL_SENSOR_SETTING:
+ printk(KERN_INFO "mt9t013: sensor setting 0x%lx\n", arg);
+ rc = mt9t013_i2c_sensor_setting(arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_EXPOSURE_GAIN: {
+ struct mt9t013_exposure_gain exp;
+ if (copy_from_user(&exp, argp, sizeof(exp))) {
+ printk(KERN_ERR "mt9t013: (exposure gain) invalid user pointer\n");
+ rc = -EFAULT;
+ break;
+ }
+ rc = mt9t013_i2c_exposure_gain(exp.mode, exp.line, exp.gain);
+ }
+ break;
+
+ case MT9T013_I2C_IOCTL_MOVE_FOCUS:
+ printk(KERN_INFO "mt9t013: move focus %ld\n", arg);
+ rc = mt9t013_i2c_move_focus((uint16_t)arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_SET_DEFAULT_FOCUS:
+ printk(KERN_INFO "mt9t013: set default focus %ld\n", arg);
+ rc = mt9t013_i2c_set_default_focus((uint8_t)arg);
+ break;
+
+ case MT9T013_I2C_IOCTL_POWER_DOWN:
+ rc = mt9t013_i2c_power_down();
+ break;
+
+ case MT9T013_I2C_IOCTL_INIT: {
+ struct mt9t013_init init;
+ printk(KERN_INFO "mt9t013: init\n");
+ if (copy_from_user(&init, argp, sizeof(init))) {
+ printk(KERN_ERR "mt9t013: (init) invalid user pointer\n");
+ rc = -EFAULT;
+ break;
+ }
+ rc = mt9t013_i2c_sensor_init(&init);
+ if (copy_to_user(argp, &init, sizeof(init)))
+ rc = -EFAULT;
+ }
+ break;
+
+ case CAMERA_CONFIGURE_GPIOS:
+ case CAMERA_UNCONFIGURE_GPIOS:
+ break;
+
+ default:
+ printk(KERN_INFO "mt9t013: unknown ioctl %d\n", cmd);
+ break;
+ }
+
+ up(&sem);
+
+ return rc;
+}
+
+#undef CHECK
+
+static int mt9t013_lens_power(int on)
+{
+ int rc;
+ printk(KERN_INFO "mt9t013: lens power %d\n", on);
+ rc = gpio_request(cam->vcm_pwd, "mt9t013");
+ if (!rc)
+ gpio_direction_output(cam->vcm_pwd, !on);
+ else printk(KERN_ERR "mt9t013 error: request gpio %d failed:"
+ " %d\n", cam->vcm_pwd, rc);
+ gpio_free(cam->vcm_pwd);
+ return rc;
+}
+
+#define I2C_WRITE(reg,data) if (!mt9t013_i2c_write(reg, data) < 0) return -EIO
+#define MT9T013_MU3M0VC_RESET_DELAY_MSECS 66
+
+static int mt9t013_i2c_sensor_init(struct mt9t013_init *init)
+{
+ int rc;
+
+ /* RESET the sensor via I2C register */
+ I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc & 0xfffe);
+ msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS);
+
+ if ((rc = mt9t013_i2c_read(MT9T013_MU3M0VC_REG_MODEL_ID, &init->chipid)) < 0) {
+ printk(KERN_ERR "mt9t013: could not read chip id: %d\n", rc);
+ return rc;
+ }
+ printk(KERN_INFO "mt9t013: chip id: %d\n", init->chipid);
+
+ if (init->chipid != MT9T013_MU3M0VC_MODEL_ID) {
+ printk(KERN_INFO "mt9t013: chip id %d is invalid\n",
+ init->chipid);
+ return -EINVAL;
+ }
+
+ I2C_WRITE(0x306E, 0x9080);
+ I2C_WRITE(0x301A, 0x10CC);
+ I2C_WRITE(0x3064, 0x0805);
+ msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS);
+
+ if ((rc = mt9t013_i2c_sensor_setting(CAMSENSOR_REG_INIT |
+ ((init->preview ? 0 : 1) << 1))) < 0) {
+ printk(KERN_INFO "mt9t013: failed to configure the sensor\n");
+ return rc;
+ }
+
+ mt9t013_i2c_power_up();
+
+ return 0;
+}
+
+static int mt9t013_mu3m0vc_set_lc(void)
+{
+ /* lens shading 85% TL84 */
+ I2C_WRITE(0x360A, 0x0290); // P_RD_P0Q0
+ I2C_WRITE(0x360C, 0xC92D); // P_RD_P0Q1
+ I2C_WRITE(0x360E, 0x0771); // P_RD_P0Q2
+ I2C_WRITE(0x3610, 0xE38C); // P_RD_P0Q3
+ I2C_WRITE(0x3612, 0xD74F); // P_RD_P0Q4
+ I2C_WRITE(0x364A, 0x168C); // P_RD_P1Q0
+ I2C_WRITE(0x364C, 0xCACB); // P_RD_P1Q1
+ I2C_WRITE(0x364E, 0x8C4C); // P_RD_P1Q2
+ I2C_WRITE(0x3650, 0x0BEA); // P_RD_P1Q3
+ I2C_WRITE(0x3652, 0xDC0F); // P_RD_P1Q4
+ I2C_WRITE(0x368A, 0x70B0); // P_RD_P2Q0
+ I2C_WRITE(0x368C, 0x200B); // P_RD_P2Q1
+ I2C_WRITE(0x368E, 0x30B2); // P_RD_P2Q2
+ I2C_WRITE(0x3690, 0xD04F); // P_RD_P2Q3
+ I2C_WRITE(0x3692, 0xACF5); // P_RD_P2Q4
+ I2C_WRITE(0x36CA, 0xF7C9); // P_RD_P3Q0
+ I2C_WRITE(0x36CC, 0x2AED); // P_RD_P3Q1
+ I2C_WRITE(0x36CE, 0xA652); // P_RD_P3Q2
+ I2C_WRITE(0x36D0, 0x8192); // P_RD_P3Q3
+ I2C_WRITE(0x36D2, 0x3A15); // P_RD_P3Q4
+ I2C_WRITE(0x370A, 0xDA30); // P_RD_P4Q0
+ I2C_WRITE(0x370C, 0x2E2F); // P_RD_P4Q1
+ I2C_WRITE(0x370E, 0xBB56); // P_RD_P4Q2
+ I2C_WRITE(0x3710, 0x8195); // P_RD_P4Q3
+ I2C_WRITE(0x3712, 0x02F9); // P_RD_P4Q4
+ I2C_WRITE(0x3600, 0x0230); // P_GR_P0Q0
+ I2C_WRITE(0x3602, 0x58AD); // P_GR_P0Q1
+ I2C_WRITE(0x3604, 0x18D1); // P_GR_P0Q2
+ I2C_WRITE(0x3606, 0x260D); // P_GR_P0Q3
+ I2C_WRITE(0x3608, 0xF530); // P_GR_P0Q4
+ I2C_WRITE(0x3640, 0x17EB); // P_GR_P1Q0
+ I2C_WRITE(0x3642, 0x3CAB); // P_GR_P1Q1
+ I2C_WRITE(0x3644, 0x87CE); // P_GR_P1Q2
+ I2C_WRITE(0x3646, 0xC02E); // P_GR_P1Q3
+ I2C_WRITE(0x3648, 0xF48F); // P_GR_P1Q4
+ I2C_WRITE(0x3680, 0x5350); // P_GR_P2Q0
+ I2C_WRITE(0x3682, 0x7EAF); // P_GR_P2Q1
+ I2C_WRITE(0x3684, 0x4312); // P_GR_P2Q2
+ I2C_WRITE(0x3686, 0xC652); // P_GR_P2Q3
+ I2C_WRITE(0x3688, 0xBC15); // P_GR_P2Q4
+ I2C_WRITE(0x36C0, 0xB8AD); // P_GR_P3Q0
+ I2C_WRITE(0x36C2, 0xBDCD); // P_GR_P3Q1
+ I2C_WRITE(0x36C4, 0xE4B2); // P_GR_P3Q2
+ I2C_WRITE(0x36C6, 0xB50F); // P_GR_P3Q3
+ I2C_WRITE(0x36C8, 0x5B95); // P_GR_P3Q4
+ I2C_WRITE(0x3700, 0xFC90); // P_GR_P4Q0
+ I2C_WRITE(0x3702, 0x8C51); // P_GR_P4Q1
+ I2C_WRITE(0x3704, 0xCED6); // P_GR_P4Q2
+ I2C_WRITE(0x3706, 0xB594); // P_GR_P4Q3
+ I2C_WRITE(0x3708, 0x0A39); // P_GR_P4Q4
+ I2C_WRITE(0x3614, 0x0230); // P_BL_P0Q0
+ I2C_WRITE(0x3616, 0x160D); // P_BL_P0Q1
+ I2C_WRITE(0x3618, 0x08D1); // P_BL_P0Q2
+ I2C_WRITE(0x361A, 0x98AB); // P_BL_P0Q3
+ I2C_WRITE(0x361C, 0xEA50); // P_BL_P0Q4
+ I2C_WRITE(0x3654, 0xB4EA); // P_BL_P1Q0
+ I2C_WRITE(0x3656, 0xEA6C); // P_BL_P1Q1
+ I2C_WRITE(0x3658, 0xFE08); // P_BL_P1Q2
+ I2C_WRITE(0x365A, 0x2C6E); // P_BL_P1Q3
+ I2C_WRITE(0x365C, 0xEB0E); // P_BL_P1Q4
+ I2C_WRITE(0x3694, 0x6DF0); // P_BL_P2Q0
+ I2C_WRITE(0x3696, 0x3ACF); // P_BL_P2Q1
+ I2C_WRITE(0x3698, 0x3E0F); // P_BL_P2Q2
+ I2C_WRITE(0x369A, 0xB2B1); // P_BL_P2Q3
+ I2C_WRITE(0x369C, 0xC374); // P_BL_P2Q4
+ I2C_WRITE(0x36D4, 0xF2AA); // P_BL_P3Q0
+ I2C_WRITE(0x36D6, 0x8CCC); // P_BL_P3Q1
+ I2C_WRITE(0x36D8, 0xDEF2); // P_BL_P3Q2
+ I2C_WRITE(0x36DA, 0xFA11); // P_BL_P3Q3
+ I2C_WRITE(0x36DC, 0x42F5); // P_BL_P3Q4
+ I2C_WRITE(0x3714, 0xF4F1); // P_BL_P4Q0
+ I2C_WRITE(0x3716, 0xF6F0); // P_BL_P4Q1
+ I2C_WRITE(0x3718, 0x8FD6); // P_BL_P4Q2
+ I2C_WRITE(0x371A, 0xEA14); // P_BL_P4Q3
+ I2C_WRITE(0x371C, 0x6338); // P_BL_P4Q4
+ I2C_WRITE(0x361E, 0x0350); // P_GB_P0Q0
+ I2C_WRITE(0x3620, 0x91AE); // P_GB_P0Q1
+ I2C_WRITE(0x3622, 0x0571); // P_GB_P0Q2
+ I2C_WRITE(0x3624, 0x100D); // P_GB_P0Q3
+ I2C_WRITE(0x3626, 0xCA70); // P_GB_P0Q4
+ I2C_WRITE(0x365E, 0xE6CB); // P_GB_P1Q0
+ I2C_WRITE(0x3660, 0x50ED); // P_GB_P1Q1
+ I2C_WRITE(0x3662, 0x3DAE); // P_GB_P1Q2
+ I2C_WRITE(0x3664, 0xAA4F); // P_GB_P1Q3
+ I2C_WRITE(0x3666, 0xDC50); // P_GB_P1Q4
+ I2C_WRITE(0x369E, 0x5470); // P_GB_P2Q0
+ I2C_WRITE(0x36A0, 0x1F6E); // P_GB_P2Q1
+ I2C_WRITE(0x36A2, 0x6671); // P_GB_P2Q2
+ I2C_WRITE(0x36A4, 0xC010); // P_GB_P2Q3
+ I2C_WRITE(0x36A6, 0x8DF5); // P_GB_P2Q4
+ I2C_WRITE(0x36DE, 0x0B0C); // P_GB_P3Q0
+ I2C_WRITE(0x36E0, 0x84CE); // P_GB_P3Q1
+ I2C_WRITE(0x36E2, 0x8493); // P_GB_P3Q2
+ I2C_WRITE(0x36E4, 0xA610); // P_GB_P3Q3
+ I2C_WRITE(0x36E6, 0x50B5); // P_GB_P3Q4
+ I2C_WRITE(0x371E, 0x9651); // P_GB_P4Q0
+ I2C_WRITE(0x3720, 0x1EAB); // P_GB_P4Q1
+ I2C_WRITE(0x3722, 0xAF76); // P_GB_P4Q2
+ I2C_WRITE(0x3724, 0xE4F4); // P_GB_P4Q3
+ I2C_WRITE(0x3726, 0x79F8); // P_GB_P4Q4
+ I2C_WRITE(0x3782, 0x0410); // Original LC 2 // POLY_ORIGIN_C
+ I2C_WRITE(0x3784, 0x0320); // POLY_ORIGIN_R
+ I2C_WRITE(0x3780, 0x8000); // POLY_SC_ENABLE
+
+ return 0;
+}
+
+static int mt9t013_set_pclk(int rt, int div_adj)
+{
+ int rc;
+ if ((rc = mt9t013_i2c_power_down()) < 0) return rc;
+ I2C_WRITE(REG_VT_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_pix_clk_div);
+ I2C_WRITE(REG_VT_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_sys_clk_div);
+ I2C_WRITE(REG_PRE_PLL_CLK_DIV, mt9t013_reg_pattern.reg[rt].pre_pll_clk_div * div_adj);
+ I2C_WRITE(REG_PLL_MULTIPLIER, mt9t013_reg_pattern.reg[rt].pll_multiplier);
+ I2C_WRITE(REG_OP_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_pix_clk_div);
+ I2C_WRITE(REG_OP_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_sys_clk_div);
+ if ((rc = mt9t013_i2c_power_up()) < 0) return rc;
+ pclk_set = 1;
+ return 0;
+}
+
+static int mt9t013_i2c_sensor_setting(unsigned long arg)
+{
+ uint32_t update = arg & 1;
+ uint32_t rt = (arg & 2) >> 1;
+
+ if (rt > 1 || update > 1) {
+ printk(KERN_ERR "mt9t013: invalid values %d of rt or %d of update\n",
+ rt, update);
+ return -EINVAL;
+ }
+
+ switch (update) {
+ case CAMSENSOR_REG_UPDATE_PERIODIC: {
+ uint16_t pclk_div_adj = arg >> 16;
+
+ printk(KERN_INFO "CAMSENSOR_REG_UPDATE_PERIODIC (rt %d)\n", rt);
+
+ if (!pclk_div_adj || pclk_div_adj > 2) {
+ printk(KERN_ERR "mt9t013: invalid value %d of pclk_div_adj\n",
+ pclk_div_adj);
+ return -EINVAL;
+ }
+
+ if (mt9t013_set_pclk(rt, pclk_div_adj) < 0)
+ return -EIO;
+
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
+ I2C_WRITE(REG_ROW_SPEED, mt9t013_reg_pattern.reg[rt].row_speed);
+ I2C_WRITE(REG_X_ADDR_START, mt9t013_reg_pattern.reg[rt].x_addr_start);
+ I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end);
+ I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start);
+ I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end);
+
+ if (machine_is_sapphire()) {
+ if (rt == 0) {
+ I2C_WRITE(REG_READ_MODE, 0x046F);
+ } else {
+ I2C_WRITE(REG_READ_MODE, 0x0027);
+ }
+ } else {
+ I2C_WRITE(REG_READ_MODE,
+ mt9t013_reg_pattern.reg[rt].read_mode);
+ }
+
+ I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m);
+ I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size);
+ I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size);
+ I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck);
+ I2C_WRITE(REG_FRAME_LENGTH_LINES, (uint16_t) (mt9t013_reg_pattern.reg[rt].frame_length_lines * fps_divider));
+ I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time);
+ I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time);
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
+ }
+ break;
+
+ case CAMSENSOR_REG_INIT:
+ printk(KERN_INFO "CAMSENSOR_REG_INIT (rt %d)\n", rt);
+
+ if (mt9t013_set_pclk(rt, 1) < 0) return -EIO;
+
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
+
+ /* additional power saving mode ok around 38.2MHz */
+ I2C_WRITE(0x3084, 0x2409);
+ I2C_WRITE(0x3092, 0x0A49);
+ I2C_WRITE(0x3094, 0x4949);
+ I2C_WRITE(0x3096, 0x4949);
+
+ /* set preview or snapshot mode */
+ I2C_WRITE(REG_ROW_SPEED,
+ mt9t013_reg_pattern.reg[rt].row_speed);
+ I2C_WRITE(REG_X_ADDR_START,
+ mt9t013_reg_pattern.reg[rt].x_addr_start);
+ I2C_WRITE(REG_X_ADDR_END,
+ mt9t013_reg_pattern.reg[rt].x_addr_end);
+ I2C_WRITE(REG_Y_ADDR_START,
+ mt9t013_reg_pattern.reg[rt].y_addr_start);
+ I2C_WRITE(REG_Y_ADDR_END,
+ mt9t013_reg_pattern.reg[rt].y_addr_end);
+
+ if (machine_is_sapphire()) {
+ if (rt == 0) {
+ I2C_WRITE(REG_READ_MODE, 0x046F);
+ } else {
+ I2C_WRITE(REG_READ_MODE, 0x0027);
+ }
+ } else {
+ I2C_WRITE(REG_READ_MODE,
+ mt9t013_reg_pattern.reg[rt].read_mode);
+ }
+
+ I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m);
+ I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size);
+ I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size);
+ I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck);
+ I2C_WRITE(REG_FRAME_LENGTH_LINES, mt9t013_reg_pattern.reg[rt].frame_length_lines);
+ I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time);
+ I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time);
+
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
+
+ /* load lens shading */
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
+ if(mt9t013_mu3m0vc_set_lc() < 0) return -EIO;
+ I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line,
+ uint16_t gain)
+{
+ static const uint16_t max_legal_gain = 0x01FF;
+
+ if (gain > max_legal_gain) gain = max_legal_gain;
+
+ gain |= 0x200; /* set digital gain */
+
+ /*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);*/
+ I2C_WRITE(REG_GLOBAL_GAIN, gain);
+ I2C_WRITE(REG_COARSE_INTEGRATION_TIME, line);
+ /*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);*/
+ if (mode == 1) {
+ /* RESET REGISTER RESTART */
+ I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc|0x0002);
+ }
+ return 0;
+}
+
+#define I2C_AF_WRITE(command, data) if (mt9t013_i2c_lens_write(AF_I2C_ID >> 1, command, data) < 0) return -EIO;
+
+static int mt9t013_i2c_move_focus(uint16_t position)
+{
+ uint8_t code_val_msb = (position >> 2) | ((position << 4) >> 6);
+ uint8_t code_val_lsb = (position & 0x03) << 6;
+
+ I2C_AF_WRITE(code_val_msb, code_val_lsb);
+ return 0;
+}
+
+static int mt9t013_i2c_set_default_focus(uint8_t step)
+{
+ I2C_AF_WRITE(0x01, step);
+ return 0;
+}
+
+static int powered;
+
+static int mt9t013_i2c_power_up(void)
+{
+ printk(KERN_INFO "mt9t013: power up\n");
+ if (powered) {
+ printk(KERN_INFO "mt9t013: already powered up\n");
+ return 0;
+ }
+ I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWON);
+ mdelay(5);
+ powered = 1;
+ return 0;
+}
+
+static int mt9t013_i2c_power_down(void)
+{
+ int i = 0, try_more = 100;
+
+ printk(KERN_INFO "mt9t013: power down\n");
+ if (!powered) {
+ printk(KERN_INFO "mt9t013: already powered down\n");
+ return 0;
+ }
+
+ /* I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF); */
+ /* Modified by Horng for more tries while I2C write fail */
+ /* -------------------------------------------------------------------- */
+ while(mt9t013_i2c_write(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF) < 0)
+ {
+ if (i >= try_more)
+ return -EIO;
+ else {
+ i++;
+ printk(KERN_INFO "mt9p012: in mt9p012_i2c_power_down() call mt9p012_i2c_write() failed !!! (try %d times)\n", i);
+ mdelay(i+5);
+ }
+ }
+ /* -------------------------------------------------------------------- */
+ mdelay(5);
+ powered = pclk_set = 0;
+ return 0;
+}
+
+#undef I2C_WRITE
+#undef I2C_AF_WRITE
+
+static int mt9t013_init_client(struct i2c_client *client)
+{
+ /* Initialize the MT9T013 Chip */
+ init_waitqueue_head(&g_data_ready_wait_queue);
+ return 0;
+}
+
+static struct file_operations mt9t013_fops = {
+ .owner = THIS_MODULE,
+ .open = mt9t013_open,
+ .release = mt9t013_release,
+ .unlocked_ioctl = mt9t013_ioctl,
+};
+
+static struct miscdevice mt9t013_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mt9t013",
+ .fops = &mt9t013_fops,
+};
+
+static const char *MT9T013Vendor = "micron";
+static const char *MT9T013NAME = "mt9t013";
+static const char *MT9T013Size = "3M";
+
+
+
+static ssize_t sensor_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ sprintf(buf, "%s %s %s\n", MT9T013Vendor, MT9T013NAME, MT9T013Size);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL);
+
+
+static struct kobject *android_mt9t013 = NULL;
+
+static int mt9t013_sysfs_init(void)
+{
+ int ret ;
+ printk(KERN_INFO "mt9t013:kobject creat and add\n");
+ android_mt9t013 = kobject_create_and_add("android_camera", NULL);
+ if (android_mt9t013 == NULL) {
+ printk(KERN_INFO "mt9t013_sysfs_init: subsystem_register " \
+ "failed\n");
+ ret = -ENOMEM;
+ return ret ;
+ }
+ printk(KERN_INFO "mt9t013:sysfs_create_file\n");
+ ret = sysfs_create_file(android_mt9t013, &dev_attr_sensor.attr);
+ if (ret) {
+ printk(KERN_INFO "mt9t013_sysfs_init: sysfs_create_file " \
+ "failed\n");
+ kobject_del(android_mt9t013);
+ }
+ return 0 ;
+}
+
+
+
+static int mt9t013_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mt9t013_data *mt;
+ int err = 0;
+ printk(KERN_INFO "mt9t013: probe\n");
+
+ if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ goto exit_check_functionality_failed;
+
+ if(!(mt = kzalloc( sizeof(struct mt9t013_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ i2c_set_clientdata(client, mt);
+ mt9t013_init_client(client);
+ pclient = client;
+ mt9t013_sensor_init();
+ mt9t013_sensor_suspend();
+
+ /* Register a misc device */
+ err = misc_register(&mt9t013_device);
+ if(err) {
+ printk(KERN_ERR "mt9t013_probe: misc_register failed \n");
+ goto exit_misc_device_register_failed;
+ }
+ init_suspend();
+ mt9t013_sysfs_init();
+ return 0;
+
+exit_misc_device_register_failed:
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+
+ return err;
+}
+
+
+static int mt9t013_remove(struct i2c_client *client)
+{
+ struct mt9t013_data *mt = i2c_get_clientdata(client);
+ free_irq(client->irq, mt);
+ deinit_suspend();
+ pclient = NULL;
+ misc_deregister(&mt9t013_device);
+ kfree(mt);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t013_id[] = {
+ { "mt9t013", 0 },
+ { }
+};
+
+static struct i2c_driver mt9t013_driver = {
+ .probe = mt9t013_probe,
+ .remove = mt9t013_remove,
+ .id_table = mt9t013_id,
+ .driver = {
+ .name = "mt9t013",
+ },
+};
+
+static int mt9t013_plat_probe(struct platform_device *pdev __attribute__((unused)))
+{
+ int rc = -EFAULT;
+
+ if(pdev->dev.platform_data)
+ {
+ printk(KERN_INFO "pdev->dev.platform_data is not NULL\n");
+ cam = pdev->dev.platform_data;
+ rc = i2c_add_driver(&mt9t013_driver);
+ }
+ return rc;
+}
+
+static struct platform_driver mt9t013_plat_driver = {
+ .probe = mt9t013_plat_probe,
+ .driver = {
+ .name = "camera",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9t013_init(void)
+{
+ return platform_driver_register(&mt9t013_plat_driver);
+}
+
+module_init(mt9t013_init);
+
+MODULE_AUTHOR("Kidd Chen");
+MODULE_DESCRIPTION("MT9T013 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 1ba2514..1985cbf 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -324,6 +324,13 @@
To compile this driver as a module, choose M here; the
module will be called opencores-kbd.
+config KEYBOARD_PM8058
+ bool "Qualcomm PM8058 Matrix Keypad support"
+ depends on PM8058
+ help
+ Say Y here to enable the driver for the keypad matrix interface
+ on the Qualcomm PM8058 power management I/C device.
+
config KEYBOARD_PXA27x
tristate "PXA27x/PXA3xx keypad support"
depends on PXA27x || PXA3xx
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 4596d0c..8aeec81 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -29,6 +29,7 @@
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
+obj-$(CONFIG_KEYBOARD_PM8058) += pm8058-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
diff --git a/drivers/input/keyboard/pm8058-keypad.c b/drivers/input/keyboard/pm8058-keypad.c
new file mode 100644
index 0000000..f1aefd4
--- /dev/null
+++ b/drivers/input/keyboard/pm8058-keypad.c
@@ -0,0 +1,417 @@
+/* drivers/input/keyboard/pm8058-keypad.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2009 Code Aurora Forum
+ *
+ * Author: Dima Zavin <dima@android.com>
+ * - Heavily based on the driver from the Code Aurora Forum.
+ *
+ * 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/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8058.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+enum {
+ DEBUG_IRQ = 1U << 0,
+ DEBUG_KEYS = 1U << 1,
+};
+static int debug_mask = 0;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define REG_KEYP_CTRL 0x148
+#define REG_KEYP_SCAN 0x149
+#define REG_KEYP_TEST 0x14a
+#define REG_KEYP_NEW_DATA 0x14b
+#define REG_KEYP_OLD_DATA 0x14c
+
+#define KP_SNS_MIN 5
+#define KP_SNS_MAX 8
+#define KP_DRV_MIN 5
+#define KP_DRV_MAX 18
+
+#define KP_CLOCK_FREQ 32768
+
+#define KPF_HAS_SYNC_READ 0x00000001
+
+struct pm8058_keypad {
+ struct device *dev;
+ struct input_dev *input_dev;
+ int sense_irq;
+ int stuck_irq;
+
+ int num_sns;
+ int num_drv;
+ const unsigned short *keymap;
+
+ u8 key_state[KP_DRV_MAX];
+ u8 stuck_state[KP_DRV_MAX];
+
+ u32 flags;
+};
+
+/* convenience wrapers */
+static inline int kp_writeb(struct pm8058_keypad *kp, u16 addr, u8 val)
+{
+ return pm8058_writeb(kp->dev->parent, addr, val);
+}
+
+static inline int kp_readb(struct pm8058_keypad *kp, u16 addr, u8 *val)
+{
+ return pm8058_readb(kp->dev->parent, addr, val);
+}
+
+static inline int kp_read_buf(struct pm8058_keypad *kp, u16 addr,
+ u8 *buf, int cnt)
+{
+ return pm8058_read_buf(kp->dev->parent, addr, buf, cnt);
+}
+
+static int kp_hw_init(struct pm8058_keypad *kp,
+ struct pm8058_keypad_platform_data *pdata)
+{
+ int ret;
+ u8 val;
+ u8 drv_bits[] = {
+ 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
+ };
+ u8 sns_bits[] = {
+ 0, 0, 0, 0, 0, 0, 1, 2, 3,
+ };
+
+ val = ((drv_bits[pdata->num_drv] << 2) |
+ (sns_bits[pdata->num_sns] << 5));
+ ret = kp_writeb(kp, REG_KEYP_CTRL, val);
+ if (ret) {
+ pr_err("%s: can't write kp ctrl\n", __func__);
+ goto out;
+ }
+
+ val = ((((pdata->drv_hold_clks - 1) & 0x3) << 6) |
+ ((pdata->scan_delay_shift & 0x7) << 3) |
+ ((((pdata->debounce_ms / 5) & 0x3)) << 1));
+ ret = kp_writeb(kp, REG_KEYP_SCAN, val);
+ if (ret) {
+ pr_err("%s: can't write kp scan\n", __func__);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int kp_get_scan_data(struct pm8058_keypad *kp, u8 *old, u8 *new)
+{
+ int ret;
+ u8 val;
+
+ /* XXX: B0 only? */
+ if (kp->flags & KPF_HAS_SYNC_READ) {
+ ret = kp_readb(kp, REG_KEYP_SCAN, &val);
+ if (ret)
+ goto err;
+ ret = kp_writeb(kp, REG_KEYP_SCAN, val | 1);
+ if (ret)
+ goto err;
+ /* 2 * 32KHz clocks */
+ udelay((2 * USEC_PER_SEC / KP_CLOCK_FREQ) + 1);
+ }
+
+ if (old) {
+ ret = kp_read_buf(kp, REG_KEYP_OLD_DATA, old, kp->num_drv);
+ if (ret)
+ goto done;
+ }
+
+ ret = kp_read_buf(kp, REG_KEYP_NEW_DATA, new, kp->num_drv);
+ if (ret)
+ goto done;
+
+done:
+ if (kp->flags & KPF_HAS_SYNC_READ) {
+ /* 4 * 32KHz clocks */
+ udelay((4 * USEC_PER_SEC / KP_CLOCK_FREQ) + 1);
+ ret = kp_readb(kp, REG_KEYP_SCAN, &val);
+ if (ret)
+ goto err;
+ ret = kp_writeb(kp, REG_KEYP_SCAN, val & (~0x1));
+ if (ret)
+ goto err;
+ }
+
+err:
+ if (ret)
+ pr_err("%s: can't get scan data\n", __func__);
+ return ret;
+}
+
+static int kp_process_scan_data(struct pm8058_keypad *kp, u8 *old, u8 *new)
+{
+ int drv;
+ int sns;
+
+ for (drv = 0; drv < kp->num_drv; ++drv) {
+ unsigned long bits_changed = (new[drv] ^ old[drv]) & 0xff;
+
+ for_each_set_bit(sns, &bits_changed, kp->num_sns) {
+ int key_idx = drv * kp->num_sns + sns;
+ unsigned int code = kp->keymap[key_idx] ?: KEY_UNKNOWN;
+ int down = !(new[drv] & (1 << sns));
+
+ if (debug_mask & DEBUG_KEYS)
+ pr_info("%s: key [%d:%d] %s\n", __func__,
+ drv, sns, down ? "down" : "up");
+ input_event(kp->input_dev, EV_MSC, MSC_SCAN, key_idx);
+ input_report_key(kp->input_dev, code, down);
+ input_sync(kp->input_dev);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * NOTE: We are reading recent and old data registers blindly
+ * whenever key-stuck interrupt happens, because events counter doesn't
+ * get updated when this interrupt happens due to key stuck doesn't get
+ * considered as key state change.
+ *
+ * We are not using old data register contents after they are being read
+ * because it might report the key which was pressed before the key being stuck
+ * as stuck key because it's pressed status is stored in the old data
+ * register.
+ */
+static irqreturn_t kp_stuck_irq_handler(int irq, void *dev_id)
+{
+ struct pm8058_keypad *kp = dev_id;
+ u8 old[KP_DRV_MAX];
+ u8 new[KP_DRV_MAX];
+ int ret;
+
+ if (debug_mask & DEBUG_IRQ)
+ pr_info("%s: key stuck!\n", __func__);
+
+ ret = kp_get_scan_data(kp, old, new);
+ if (ret) {
+ pr_err("%s: couldn't get scan data\n", __func__);
+ goto out;
+ }
+ kp_process_scan_data(kp, kp->stuck_state, new);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t kp_sense_irq_handler(int irq, void *dev_id)
+{
+ struct pm8058_keypad *kp = dev_id;
+ int ret;
+ u8 old[KP_DRV_MAX];
+ u8 new[KP_DRV_MAX];
+ u8 val;
+
+ if (debug_mask & DEBUG_IRQ)
+ pr_info("%s: key event!!\n", __func__);
+ ret = kp_readb(kp, REG_KEYP_CTRL, &val);
+ if (ret) {
+ pr_err("%s: can't read events\n", __func__);
+ goto out;
+ }
+
+ /* events counter is gray coded */
+ switch(val & 0x3) {
+ case 0x1:
+ ret = kp_get_scan_data(kp, NULL, new);
+ if (ret)
+ goto out;
+ kp_process_scan_data(kp, kp->key_state, new);
+ memcpy(kp->key_state, new, sizeof(new));
+ break;
+
+ case 0x2:
+ pr_debug("%s: some key events were missed\n", __func__);
+ case 0x3:
+ ret = kp_get_scan_data(kp, old, new);
+ if (ret)
+ goto out;
+ /* first process scan data in relation to last known
+ * key state */
+ kp_process_scan_data(kp, kp->key_state, old);
+ kp_process_scan_data(kp, old, new);
+ memcpy(kp->key_state, new, sizeof(new));
+ break;
+
+ case 0x0:
+ pr_warning("%s: interrupt without any events?!\n", __func__);
+ break;
+ }
+
+out:
+ if (ret)
+ pr_err("%s: couldn't get scan data\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int pm8058_keypad_probe(struct platform_device *pdev)
+{
+ struct pm8058_keypad_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8058_keypad *kp;
+ int sense_irq;
+ int stuck_irq;
+ int ret;
+ int i;
+ u8 val;
+
+ sense_irq = platform_get_irq_byname(pdev, "kp_sense");
+ stuck_irq = platform_get_irq_byname(pdev, "kp_stuck");
+
+ if (!pdata || sense_irq < 0 || stuck_irq < 0) {
+ pr_err("%s: missing platform data/resources\n", __func__);
+ return -EINVAL;
+ }
+
+ if (pdata->num_sns > KP_SNS_MAX || pdata->num_drv > KP_DRV_MAX ||
+ (pdata->drv_hold_clks == 0) || !pdata->keymap) {
+ pr_err("%s: invalid plaform data\n", __func__);
+ return -EINVAL;
+ }
+
+ kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+ if (!kp) {
+ pr_err("%s: can't allocate memory for kp struct\n", __func__);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, kp);
+ kp->dev = &pdev->dev;
+ kp->num_sns = pdata->num_sns;
+ kp->num_drv = pdata->num_drv;
+ kp->keymap = pdata->keymap;
+ kp->sense_irq = sense_irq;
+ kp->stuck_irq = stuck_irq;
+
+ memset(kp->key_state, 0xff, sizeof(kp->key_state));
+ memset(kp->stuck_state, 0xff, sizeof(kp->stuck_state));
+
+ /* b0 and up have sync_read support */
+ kp->flags = KPF_HAS_SYNC_READ;
+
+ kp->input_dev = input_allocate_device();
+ if (!kp->input_dev) {
+ ret = -ENOMEM;
+ pr_err("%s: Failed to allocate input device\n", __func__);
+ goto err_input_dev_alloc;
+ }
+
+ kp->input_dev->name = pdata->name;
+ input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(kp->input_dev, kp);
+
+ for (i = 0; i < kp->num_drv * kp->num_sns; ++i) {
+ unsigned short keycode = kp->keymap[i];
+ BUG_ON(keycode && keycode > KEY_MAX);
+ if (keycode)
+ input_set_capability(kp->input_dev, EV_KEY, keycode);
+ }
+
+ ret = input_register_device(kp->input_dev);
+ if (ret) {
+ pr_err("%s: can't register input device '%s'\n", __func__,
+ pdata->name);
+ goto err_input_dev_reg;
+ }
+
+ ret = kp_hw_init(kp, pdata);
+ if (ret) {
+ pr_err("%s: can't initialize keypad hardware\n", __func__);
+ goto err_kp_hw_init;
+ }
+
+ if (pdata->init) {
+ ret = pdata->init(kp->dev);
+ if (ret) {
+ pr_err("%s: can't call board's init\n", __func__);
+ goto err_pdata_init;
+ }
+ }
+
+ ret = request_threaded_irq(kp->sense_irq, NULL, kp_sense_irq_handler,
+ IRQF_TRIGGER_RISING, "pm8058-keypad-sense", kp);
+ if (ret) {
+ pr_err("%s: can't request sense_irq\n", __func__);
+ goto err_req_sense_irq;
+ }
+ ret = request_threaded_irq(kp->stuck_irq, NULL, kp_stuck_irq_handler,
+ IRQF_TRIGGER_RISING, "pm8058-keypad-stuck", kp);
+ if (ret) {
+ pr_err("%s: can't request stuck\n", __func__);
+ goto err_req_stuck_irq;
+ }
+
+ enable_irq_wake(kp->sense_irq);
+
+ ret = kp_readb(kp, REG_KEYP_CTRL, &val);
+ if (ret) {
+ pr_err("%s: can't read kp ctrl\n", __func__);
+ goto err_read_kp_ctrl;
+ }
+ val |= 1 << 7;
+ ret = kp_writeb(kp, REG_KEYP_CTRL, val);
+ if (ret) {
+ pr_err("%s: can't enable kp\n", __func__);
+ goto err_kp_enable;
+ }
+
+ pr_info("%s: %dx%d matrix keypad '%s' registered\n", __func__,
+ kp->num_drv, kp->num_sns, pdata->name);
+
+ return 0;
+
+err_kp_enable:
+err_read_kp_ctrl:
+ disable_irq_wake(kp->sense_irq);
+ free_irq(kp->stuck_irq, kp);
+err_req_stuck_irq:
+ free_irq(kp->sense_irq, kp);
+err_req_sense_irq:
+err_pdata_init:
+err_kp_hw_init:
+ input_unregister_device(kp->input_dev);
+err_input_dev_reg:
+ input_free_device(kp->input_dev);
+err_input_dev_alloc:
+ platform_set_drvdata(pdev, NULL);
+ kfree(kp);
+ return ret;
+}
+
+static struct platform_driver pm8058_keypad_driver = {
+ .probe = pm8058_keypad_probe,
+ .driver = {
+ .name = "pm8058-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8058_keypad_init(void)
+{
+ return platform_driver_register(&pm8058_keypad_driver);
+}
+device_initcall(pm8058_keypad_init);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7861016..e4dc20a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -406,4 +406,10 @@
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
+config INPUT_CAPELLA_CM3602
+ tristate "Capella CM3602 proximity and light sensor"
+ help
+ Say Y here to enable the Capella CM3602 Short Distance Proximity
+ Sensor with Ambient Light Sensor.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index d234834..6d323e4 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -13,6 +13,7 @@
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
+obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
diff --git a/drivers/input/misc/capella_cm3602.c b/drivers/input/misc/capella_cm3602.c
new file mode 100644
index 0000000..b03eb62
--- /dev/null
+++ b/drivers/input/misc/capella_cm3602.c
@@ -0,0 +1,268 @@
+/* drivers/input/misc/capella_cm3602.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Iliyan Malchev <malchev@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/capella_cm3602.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+
+#define D(x...) pr_info(x)
+
+static struct capella_cm3602_data {
+ struct input_dev *input_dev;
+ struct capella_cm3602_platform_data *pdata;
+ int enabled;
+} the_data;
+
+static int misc_opened;
+
+static int capella_cm3602_report(struct capella_cm3602_data *data)
+{
+ int val = gpio_get_value(data->pdata->p_out);
+ if (val < 0) {
+ pr_err("%s: gpio_get_value error %d\n", __func__, val);
+ return val;
+ }
+
+ D("proximity %d\n", val);
+
+ /* 0 is close, 1 is far */
+ input_report_abs(data->input_dev, ABS_DISTANCE, val);
+ input_sync(data->input_dev);
+ return val;
+}
+
+static irqreturn_t capella_cm3602_irq_handler(int irq, void *data)
+{
+ struct capella_cm3602_data *ip = data;
+ int val = capella_cm3602_report(ip);
+ return IRQ_HANDLED;
+}
+
+static int capella_cm3602_enable(struct capella_cm3602_data *data)
+{
+ D("%s\n", __func__);
+ if (data->enabled) {
+ D("%s: already enabled\n", __func__);
+ } else {
+ data->pdata->power(1);
+ data->enabled = 1;
+ capella_cm3602_report(data);
+ }
+ return 0;
+}
+
+static int capella_cm3602_disable(struct capella_cm3602_data *data)
+{
+ D("%s\n", __func__);
+ if (data->enabled) {
+ data->pdata->power(0);
+ data->enabled = 0;
+ } else {
+ D("%s: already disabled\n", __func__);
+ }
+ return 0;
+}
+
+static int capella_cm3602_setup(struct capella_cm3602_data *ip)
+{
+ int rc = -EIO;
+ struct capella_cm3602_platform_data *pdata = ip->pdata;
+ int irq = gpio_to_irq(pdata->p_out);
+
+ D("%s\n", __func__);
+
+ rc = gpio_request(pdata->p_out, "gpio_proximity_out");
+ if (rc < 0) {
+ pr_err("%s: gpio %d request failed (%d)\n",
+ __func__, pdata->p_out, rc);
+ goto done;
+ }
+
+ rc = gpio_direction_input(pdata->p_out);
+ if (rc < 0) {
+ pr_err("%s: failed to set gpio %d as input (%d)\n",
+ __func__, pdata->p_out, rc);
+ goto fail_free_p_out;
+ }
+
+ rc = request_irq(irq,
+ capella_cm3602_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "capella_cm3602",
+ ip);
+ if (rc < 0) {
+ pr_err("%s: request_irq(%d) failed for gpio %d (%d)\n",
+ __func__, irq,
+ pdata->p_out, rc);
+ goto fail_free_p_out;
+ }
+
+ rc = set_irq_wake(irq, 1);
+ if (rc < 0) {
+ pr_err("%s: failed to set irq %d as a wake interrupt\n",
+ __func__, irq);
+ goto fail_free_irq;
+
+ }
+
+ goto done;
+
+fail_free_irq:
+ free_irq(irq, 0);
+fail_free_p_out:
+ gpio_free(pdata->p_out);
+done:
+ return rc;
+}
+
+static int capella_cm3602_open(struct inode *inode, struct file *file)
+{
+ D("%s\n", __func__);
+ if (misc_opened)
+ return -EBUSY;
+ misc_opened = 1;
+ return 0;
+}
+
+static int capella_cm3602_release(struct inode *inode, struct file *file)
+{
+ D("%s\n", __func__);
+ misc_opened = 0;
+ return capella_cm3602_disable(&the_data);
+}
+
+static long capella_cm3602_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int val;
+ D("%s cmd %d\n", __func__, _IOC_NR(cmd));
+ switch (cmd) {
+ case CAPELLA_CM3602_IOCTL_ENABLE:
+ if (get_user(val, (unsigned long __user *)arg))
+ return -EFAULT;
+ if (val)
+ return capella_cm3602_enable(&the_data);
+ else
+ return capella_cm3602_disable(&the_data);
+ break;
+ case CAPELLA_CM3602_IOCTL_GET_ENABLED:
+ return put_user(the_data.enabled, (unsigned long __user *)arg);
+ break;
+ default:
+ pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+ return -EINVAL;
+ }
+}
+
+static struct file_operations capella_cm3602_fops = {
+ .owner = THIS_MODULE,
+ .open = capella_cm3602_open,
+ .release = capella_cm3602_release,
+ .unlocked_ioctl = capella_cm3602_ioctl
+};
+
+struct miscdevice capella_cm3602_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "cm3602",
+ .fops = &capella_cm3602_fops
+};
+
+static int capella_cm3602_probe(struct platform_device *pdev)
+{
+ int rc = -EIO;
+ struct input_dev *input_dev;
+ struct capella_cm3602_data *ip;
+ struct capella_cm3602_platform_data *pdata;
+
+ D("%s: probe\n", __func__);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ pr_err("%s: missing pdata!\n", __func__);
+ goto done;
+ }
+ if (!pdata->power) {
+ pr_err("%s: incomplete pdata!\n", __func__);
+ goto done;
+ }
+
+ ip = &the_data;
+ platform_set_drvdata(pdev, ip);
+
+ D("%s: allocating input device\n", __func__);
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("%s: could not allocate input device\n", __func__);
+ rc = -ENOMEM;
+ goto done;
+ }
+ ip->input_dev = input_dev;
+ ip->pdata = pdata;
+ input_set_drvdata(input_dev, ip);
+
+ input_dev->name = "proximity";
+
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+
+ D("%s: registering input device\n", __func__);
+ rc = input_register_device(input_dev);
+ if (rc < 0) {
+ pr_err("%s: could not register input device\n", __func__);
+ goto err_free_input_device;
+ }
+
+ D("%s: registering misc device\n", __func__);
+ rc = misc_register(&capella_cm3602_misc);
+ if (rc < 0) {
+ pr_err("%s: could not register misc device\n", __func__);
+ goto err_unregister_input_device;
+ }
+
+ rc = capella_cm3602_setup(ip);
+ if (!rc)
+ goto done;
+
+ misc_deregister(&capella_cm3602_misc);
+err_unregister_input_device:
+ input_unregister_device(input_dev);
+ goto done;
+err_free_input_device:
+ input_free_device(input_dev);
+done:
+ return rc;
+}
+
+static struct platform_driver capella_cm3602_driver = {
+ .probe = capella_cm3602_probe,
+ .driver = {
+ .name = CAPELLA_CM3602,
+ .owner = THIS_MODULE
+ },
+};
+
+static int __init capella_cm3602_init(void)
+{
+ return platform_driver_register(&capella_cm3602_driver);
+}
+
+device_initcall(capella_cm3602_init);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c4e276a..55edb04 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -99,6 +99,17 @@
To compile this driver as a module, choose M here: the
module will be called h3600_ts_input.
+config TOUCHSCREEN_CYPRESS_TMG
+ tristate "Support for Cypress TMC i2c touchscreen"
+ depends on I2C
+ help
+ Say Y here to enable support for cy8ctmg touchcreens.
+
+ If unsure, say N
+
+ To compile this driver as a module, choose M here: the
+ module will be called h3600_ts_input.
+
config TOUCHSCREEN_DA9034
tristate "Touchscreen support for Dialog Semiconductor DA9034"
depends on PMIC_DA903X
@@ -177,6 +188,10 @@
To compile this driver as a module, choose M here: the
module will be called gunze.
+config TOUCHSCREEN_ELAN_I2C_8232
+ tristate "Elan 8232 I2C touchscreen"
+ depends on I2C
+
config TOUCHSCREEN_ELO
tristate "Elo serial touchscreens"
select SERIO
@@ -292,6 +307,15 @@
To compile this driver as a module, choose M here: the
module will be called penmount.
+config TOUCHSCREEN_MSM
+ bool "Qualcomm MSM touchscreen controller"
+ depends on ARCH_MSM
+ default n
+ help
+ Say Y here if you have a 4-wire resistive touchscreen panel
+ connected to the TSSC touchscreen controller on a
+ Qualcomm MSM/QSD based SoC.
+
config TOUCHSCREEN_MIGOR
tristate "Renesas MIGO-R touchscreen"
depends on SH_MIGOR && I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0559522..ff3cb78 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,10 +12,12 @@
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_TMG) += cy8c_tmg_ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN_I2C_8232) += elan8232_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
@@ -24,6 +26,7 @@
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+obj-$(CONFIG_TOUCHSCREEN_MSM) += msm_ts.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
diff --git a/drivers/input/touchscreen/cy8c_tmg_ts.c b/drivers/input/touchscreen/cy8c_tmg_ts.c
new file mode 100644
index 0000000..f48374e
--- /dev/null
+++ b/drivers/input/touchscreen/cy8c_tmg_ts.c
@@ -0,0 +1,467 @@
+/* drivers/input/touchscreen/cy8c_tmg_ts.c
+ *
+ * Copyright (C) 2007-2008 HTC 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/cy8c_tmg_ts.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CY8C_REG_START_NEW_SCAN 0x0F
+#define CY8C_REG_INTR_STATUS 0x3C
+#define CY8C_REG_VERSION 0x3E
+
+struct cy8c_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int use_irq;
+ struct hrtimer timer;
+ struct work_struct work;
+ uint16_t version;
+ int (*power) (int on);
+ struct early_suspend early_suspend;
+};
+
+struct workqueue_struct *cypress_touch_wq;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h);
+static void cy8c_ts_late_resume(struct early_suspend *h);
+#endif
+
+uint16_t sample_count, X_mean, Y_mean, first_touch;
+
+static s32 cy8c_read_word_data(struct i2c_client *client,
+ u8 command, uint16_t * data)
+{
+ s32 ret = i2c_smbus_read_word_data(client, command);
+ if (ret != -1) {
+ *data = (u16) ((ret << 8) | (ret >> 8));
+ }
+ return ret;
+}
+
+static int cy8c_init_panel(struct cy8c_ts_data *ts)
+{
+ int ret;
+ sample_count = X_mean = Y_mean = first_touch = 0;
+
+ /* clean intr busy */
+ ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS,
+ 0x00);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "cy8c_init_panel failed for clean intr busy\n");
+ goto exit;
+ }
+
+ /* start new scan */
+ ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_START_NEW_SCAN,
+ 0x01);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "cy8c_init_panel failed for start new scan\n");
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static void cy8c_ts_reset(struct i2c_client *client)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->power) {
+ ts->power(0);
+ msleep(10);
+ ts->power(1);
+ msleep(10);
+ }
+
+ cy8c_init_panel(ts);
+}
+
+static void cy8c_ts_work_func(struct work_struct *work)
+{
+ struct cy8c_ts_data *ts = container_of(work, struct cy8c_ts_data, work);
+ uint16_t x1, y1, x2, y2;
+ uint8_t is_touch, start_reg, force, area, finger2_pressed;
+ uint8_t buf[11];
+ struct i2c_msg msg[2];
+ int ret = 0;
+
+ x2 = y2 = 0;
+
+ /*printk("%s: enter\n",__func__);*/
+ is_touch = i2c_smbus_read_byte_data(ts->client, 0x20);
+ dev_dbg(&ts->client->dev, "fIsTouch %d,\n", is_touch);
+ if (is_touch < 0 || is_touch > 3) {
+ pr_err("%s: invalid is_touch = %d\n", __func__, is_touch);
+ cy8c_ts_reset(ts->client);
+ msleep(10);
+ goto done;
+ }
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ start_reg = 0x16;
+ msg[0].buf = &start_reg;
+
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(buf);
+ msg[1].buf = buf;
+
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0)
+ goto done;
+
+ /* parse data */
+ force = buf[0];
+ area = buf[1];
+ x1 = (buf[2] << 8) | buf[3];
+ y1 = (buf[6] << 8) | buf[7];
+ is_touch = buf[10];
+
+ if (is_touch == 2) {
+ x2 = (buf[4] << 8) | buf[5];
+ y2 = (buf[8] << 8) | buf[9];
+ finger2_pressed = 1;
+ }
+
+ dev_dbg(&ts->client->dev,
+ "bFingerForce %d, bFingerArea %d \n", force, area);
+ dev_dbg(&ts->client->dev, "x1: %d, y1: %d \n", x1, y1);
+ if (finger2_pressed)
+ dev_dbg(&ts->client->dev, "x2: %d, y2: %d \n", x2, y2);
+
+ /* drop the first one? */
+ if ((is_touch == 1) && (first_touch == 0)) {
+ first_touch = 1;
+ goto done;
+ }
+
+ if (!first_touch)
+ goto done;
+
+ if (is_touch == 2)
+ finger2_pressed = 1;
+
+ input_report_abs(ts->input_dev, ABS_X, x1);
+ input_report_abs(ts->input_dev, ABS_Y, y1);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, force);
+ input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, area);
+ input_report_key(ts->input_dev, BTN_TOUCH, is_touch);
+ input_report_key(ts->input_dev, BTN_2, finger2_pressed);
+
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_HAT0X, x2);
+ input_report_abs(ts->input_dev, ABS_HAT0Y, y2);
+ }
+ input_sync(ts->input_dev);
+
+done:
+ if (is_touch == 0)
+ first_touch = sample_count = 0;
+
+ /* prepare for next intr */
+ i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, 0x00);
+ if (!ts->use_irq)
+ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+ else
+ enable_irq(ts->client->irq);
+}
+
+static enum hrtimer_restart cy8c_ts_timer_func(struct hrtimer *timer)
+{
+ struct cy8c_ts_data *ts;
+
+ ts = container_of(timer, struct cy8c_ts_data, timer);
+ queue_work(cypress_touch_wq, &ts->work);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t cy8c_ts_irq_handler(int irq, void *dev_id)
+{
+ struct cy8c_ts_data *ts = dev_id;
+
+ disable_irq_nosync(ts->client->irq);
+ queue_work(cypress_touch_wq, &ts->work);
+ return IRQ_HANDLED;
+}
+
+static int cy8c_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cy8c_ts_data *ts;
+ struct cy8c_i2c_platform_data *pdata;
+ uint16_t panel_version;
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL);
+ if (ts == NULL) {
+ dev_err(&client->dev, "allocate cy8c_ts_data failed\n");
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+
+ INIT_WORK(&ts->work, cy8c_ts_work_func);
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ pdata = client->dev.platform_data;
+ if (pdata) {
+ ts->version = pdata->version;
+ ts->power = pdata->power;
+ }
+
+ if (ts->power) {
+ ret = ts->power(1);
+ msleep(10);
+ if (ret < 0) {
+ dev_err(&client->dev, "power on failed\n");
+ goto err_power_failed;
+ }
+ }
+
+ ret = cy8c_read_word_data(ts->client, CY8C_REG_VERSION, &panel_version);
+ if (ret < 0) {
+ dev_err(&client->dev, "init panel failed\n");
+ goto err_detect_failed;
+ }
+ dev_info(&client->dev, "Panel Version %04X\n", panel_version);
+ if (pdata) {
+ while (pdata->version > panel_version) {
+ dev_info(&client->dev, "old tp detected, "
+ "panel version = %x\n", panel_version);
+ pdata++;
+ }
+ }
+
+ ret = cy8c_init_panel(ts);
+ if (ret < 0) {
+ dev_err(&client->dev, "init panel failed\n");
+ goto err_detect_failed;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ ts->input_dev->name = "cy8c-touchscreen";
+
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+ input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+ input_set_capability(ts->input_dev, EV_KEY, BTN_2);
+
+ input_set_abs_params(ts->input_dev, ABS_X,
+ pdata->abs_x_min, pdata->abs_x_max, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y,
+ pdata->abs_y_min, pdata->abs_y_max, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0X,
+ pdata->abs_x_min, pdata->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0Y,
+ pdata->abs_y_min, pdata->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE,
+ pdata->abs_pressure_min, pdata->abs_pressure_max,
+ 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH,
+ pdata->abs_width_min, pdata->abs_width_max, 0, 0);
+
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "cy8c_ts_probe: Unable to register %s input device\n",
+ ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+
+ if (client->irq) {
+ ret = request_irq(client->irq, cy8c_ts_irq_handler,
+ IRQF_TRIGGER_LOW, CYPRESS_TMG_NAME, ts);
+ if (ret == 0)
+ ts->use_irq = 1;
+ else
+ dev_err(&client->dev, "request_irq failed\n");
+ }
+
+ if (!ts->use_irq) {
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = cy8c_ts_timer_func;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = cy8c_ts_early_suspend;
+ ts->early_suspend.resume = cy8c_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ dev_info(&client->dev, "Start touchscreen %s in %s mode\n",
+ ts->input_dev->name, (ts->use_irq ? "interrupt" : "polling"));
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+ if (ts->power)
+ ts->power(0);
+
+err_detect_failed:
+err_power_failed:
+ kfree(ts);
+
+err_alloc_data_failed:
+err_check_functionality_failed:
+ return ret;
+}
+
+static int cy8c_ts_remove(struct i2c_client *client)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+ unregister_early_suspend(&ts->early_suspend);
+
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+ int ret;
+
+ if (ts->use_irq)
+ disable_irq_nosync(client->irq);
+ else
+ hrtimer_cancel(&ts->timer);
+
+ ret = cancel_work_sync(&ts->work);
+ if (ret && ts->use_irq)
+ enable_irq(client->irq);
+
+ if (ts->power)
+ ts->power(0);
+
+ return 0;
+}
+
+static int cy8c_ts_resume(struct i2c_client *client)
+{
+ int ret;
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "cy8c_ts_resume power on failed\n");
+ msleep(10);
+
+ cy8c_init_panel(ts);
+ }
+
+ if (ts->use_irq)
+ enable_irq(client->irq);
+ else
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h)
+{
+ struct cy8c_ts_data *ts;
+ ts = container_of(h, struct cy8c_ts_data, early_suspend);
+ cy8c_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void cy8c_ts_late_resume(struct early_suspend *h)
+{
+ struct cy8c_ts_data *ts;
+ ts = container_of(h, struct cy8c_ts_data, early_suspend);
+ cy8c_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id cy8c_ts_i2c_id[] = {
+ {CYPRESS_TMG_NAME, 0},
+ {}
+};
+
+static struct i2c_driver cy8c_ts_driver = {
+ .id_table = cy8c_ts_i2c_id,
+ .probe = cy8c_ts_probe,
+ .remove = cy8c_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = cy8c_ts_suspend,
+ .resume = cy8c_ts_resume,
+#endif
+ .driver = {
+ .name = CYPRESS_TMG_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit cy8c_ts_init(void)
+{
+ cypress_touch_wq = create_singlethread_workqueue("cypress_touch_wq");
+ if (!cypress_touch_wq)
+ return -ENOMEM;
+
+ return i2c_add_driver(&cy8c_ts_driver);
+}
+
+static void __exit cy8c_ts_exit(void)
+{
+ if (cypress_touch_wq)
+ destroy_workqueue(cypress_touch_wq);
+
+ i2c_del_driver(&cy8c_ts_driver);
+}
+
+module_init(cy8c_ts_init);
+module_exit(cy8c_ts_exit);
+
+MODULE_DESCRIPTION("Cypress TMG Touchscreen Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/elan8232_i2c.c b/drivers/input/touchscreen/elan8232_i2c.c
new file mode 100644
index 0000000..4870b22
--- /dev/null
+++ b/drivers/input/touchscreen/elan8232_i2c.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2007-2008 HTC 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/input.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/elan_i2c.h>
+#include <linux/earlysuspend.h>
+#include <linux/gpio.h>
+
+
+static const char EKT8232NAME[] = "elan-touch";
+
+#define ELAN_TS_FUZZ 0
+#define ELAN_TS_FLAT 0
+#define IDX_PACKET_SIZE 9
+
+enum {
+ STATE_DEEP_SLEEP = 0,
+ STATE_NORMAL = 1U,
+ STATE_MASK = 0x08,
+ cmd_reponse_packet = 0x52,
+ read_cmd_packet = 0x53,
+ write_cmd_packet = 0x54,
+ hello_packet = 0x55,
+ enable_int = 0xa6,
+ disable_int = 0x56,
+ idx_coordinate_packet = 0x5a,
+};
+
+enum {
+ idx_finger_width = 7,
+ idx_finger_state = 8,
+};
+
+static struct workqueue_struct *elan_wq;
+
+static struct ekt8232_data {
+ int intr_gpio;
+ int use_irq;
+ /* delete when finish migration */
+ int fw_ver;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct i2c_client *client;
+ struct input_dev *input;
+ wait_queue_head_t wait;
+ int (*power)(int on);
+ struct early_suspend early_suspend;
+} ekt_data;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void elan_ts_early_suspend(struct early_suspend *h);
+static void elan_ts_late_resume(struct early_suspend *h);
+#endif
+
+static ssize_t touch_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ sprintf(buf, "%s_%#x\n", EKT8232NAME, ekt_data.fw_ver);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static DEVICE_ATTR(vendor, 0444, touch_vendor_show, NULL);
+
+static struct kobject *android_touch_kobj;
+
+static int touch_sysfs_init(void)
+{
+ int ret ;
+
+ android_touch_kobj = kobject_create_and_add("android_touch", NULL) ;
+ if (android_touch_kobj == NULL) {
+ printk(KERN_INFO
+ "touch_sysfs_init: subsystem_register failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_vendor.attr);
+ if (ret) {
+ printk(KERN_INFO
+ "touch_sysfs_init: sysfs_create_group failed\n");
+ goto err4;
+ }
+
+ return 0 ;
+err4:
+ kobject_del(android_touch_kobj);
+err:
+ return ret ;
+}
+
+static int ekt8232_detect_int_level(void)
+{
+ unsigned v;
+ v = gpio_get_value(ekt_data.intr_gpio);
+ /* printk("ekt8232_detect_int_level: v = %0x\n", v); */
+ return v;
+}
+
+static int __ekt8232_poll(struct i2c_client *client)
+{
+ int status = 0, retry = 10;
+
+ do {
+ status = ekt8232_detect_int_level();
+ dev_dbg(&client->dev, "%s: status = %d\n", __func__, status);
+ retry--;
+ mdelay(20);
+ } while (status == 1 && retry > 0);
+
+ dev_dbg(&client->dev, "%s: poll interrupt status %s\n",
+ __func__, status == 1 ? "high" : "low");
+ return (status == 0 ? 0 : -ETIMEDOUT);
+}
+
+static int ekt8232_poll(struct i2c_client *client)
+{
+ return __ekt8232_poll(client);
+}
+
+static int ekt8232_get_data(struct i2c_client *client, uint8_t *cmd,
+ uint8_t *buf, size_t size, int sleep)
+{
+ int rc;
+ unsigned time_out = msecs_to_jiffies(10);
+
+ dev_dbg(&client->dev, "%s: enter.\n", __func__);
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ if ((i2c_master_send(client, cmd, 4)) != 4) {
+ dev_err(&client->dev,
+ "%s: i2c_master_send failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sleep == 1) {
+ rc = wait_event_timeout(ekt_data.wait,
+ i2c_master_recv(client, buf, size) == size &&
+ buf[0] == cmd_reponse_packet, time_out);
+ if (rc == 0) {
+ dev_err(&client->dev,
+ "%s: i2c_master_recv failed\n", __func__);
+ return -ETIMEDOUT;
+ }
+ } else {
+ rc = ekt8232_poll(client);
+ if (rc < 0)
+ return -EINVAL;
+ else {
+ if (i2c_master_recv(client, buf, size) != size ||
+ buf[0] != cmd_reponse_packet)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int __hello_packet_handler(struct i2c_client *client)
+{
+ int rc;
+ uint8_t buf_recv[4] = { 0 };
+
+ rc = ekt8232_poll(client);
+ if (rc < 0) {
+ dev_err(&client->dev, "%s: failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = i2c_master_recv(client, buf_recv, 4);
+ if (rc != 4) {
+ dev_err(&client->dev,
+ "%s: get hello packet failed!, rc = %d\n",
+ __func__, rc);
+ return rc;
+ } else {
+ int i;
+ dev_dbg(&client->dev,
+ "dump hello packet: %0x, %0x, %0x, %0x\n",
+ buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]);
+
+ for (i = 0; i < 4; i++)
+ if (buf_recv[i] != hello_packet)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __fw_packet_handler(struct i2c_client *client)
+{
+ int rc;
+ int major, minor;
+ uint8_t cmd[] = { read_cmd_packet, 0x00, 0x00, 0x01 };
+ uint8_t buf_recv[4] = { 0 };
+
+ rc = ekt8232_get_data(client, cmd, buf_recv, 4, 0);
+ if (rc < 0)
+ return rc;
+
+ major = ((buf_recv[1] & 0x0f) << 4) | ((buf_recv[2] & 0xf0) >> 4);
+ minor = ((buf_recv[2] & 0x0f) << 4) | ((buf_recv[3] & 0xf0) >> 4);
+
+ /* delete after migration */
+ ekt_data.fw_ver = major << 8 | minor;
+
+ printk(KERN_INFO "%s: firmware version: 0x%x\n",
+ __func__, ekt_data.fw_ver);
+ return 0;
+}
+
+static int __set_report_type(struct i2c_client *client)
+{
+ return 0;
+}
+
+static inline int ekt8232_parse_xy(uint8_t *data, uint16_t *x, uint16_t *y)
+{
+ *x = (data[0] & 0xf0);
+ *x <<= 4;
+ *x |= data[1];
+
+ *y = (data[0] & 0x0f);
+ *y <<= 8;
+ *y |= data[2];
+
+ return 0;
+}
+
+/* ekt8232_ts_init -- hand shaking with touch panel
+ *
+ * 1. recv hello packet
+ * 2. check its' firmware version
+ * 3. set up sensitivity, report rate, ...
+ */
+static int ekt8232_ts_init(struct i2c_client *client)
+{
+ int rc;
+
+ rc = __hello_packet_handler(client);
+ if (rc < 0)
+ goto hand_shake_failed;
+ dev_dbg(&client->dev, "%s: hello packet got.\n", __func__);
+
+ rc = __fw_packet_handler(client);
+ if (rc < 0)
+ goto hand_shake_failed;
+ dev_dbg(&client->dev, "%s: firmware checking done.\n", __func__);
+
+ rc = __set_report_type(client);
+ if (rc < 0)
+ goto hand_shake_failed;
+ dev_dbg(&client->dev,
+ "%s: channging operating mode done.\n", __func__);
+
+ if (ekt_data.fw_ver == 0x103) {
+ uint8_t cmd[4] = {0x5c, 0x10, 0x00, 0x01};
+ if ((i2c_master_send(client, cmd, 4)) != 4) {
+ dev_err(&client->dev,
+ "%s: set adc failed\n", __func__);
+ }
+ cmd[0] = 0x54;
+ cmd[0] = 0x43;
+ cmd[0] = 0x00;
+ cmd[0] = 0x01;
+ if ((i2c_master_send(client, cmd, 4)) != 4) {
+ dev_err(&client->dev,
+ "%s: set gain failed\n", __func__);
+ }
+ }
+
+hand_shake_failed:
+ return rc;
+}
+
+static int ekt8232_set_power_state(struct i2c_client *client, int state)
+{
+ uint8_t cmd[] = {write_cmd_packet, 0x50, 0x00, 0x01};
+
+ dev_dbg(&client->dev, "%s: enter.\n", __func__);
+
+ cmd[1] |= (state << 3);
+
+ dev_dbg(&client->dev,
+ "dump cmd: %02x, %02x, %02x, %02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3]);
+
+ if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) {
+ dev_err(&client->dev,
+ "%s: i2c_master_send failed\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ekt8232_get_power_state(struct i2c_client *client)
+{
+ int rc = 0;
+ uint8_t cmd[] = { read_cmd_packet, 0x50, 0x00, 0x01 };
+ uint8_t buf[4], power_state;
+
+ rc = ekt8232_get_data(client, cmd, buf, 4, 0);
+ if (rc)
+ return rc;
+ else {
+ power_state = buf[1];
+ dev_dbg(&client->dev, "dump repsponse: %0x\n", power_state);
+
+ power_state = (power_state & STATE_MASK) >> 3;
+ dev_dbg(&client->dev, "power state = %s\n",
+ power_state == STATE_DEEP_SLEEP ?
+ "Deep Sleep" : "Normal/Idle");
+ return power_state;
+ }
+}
+
+static int ekt8232_recv_data(struct i2c_client *client, uint8_t *buf)
+{
+ int rc, bytes_to_recv = IDX_PACKET_SIZE;
+ int retry = 5;
+
+ if (ekt_data.fw_ver == 0x101)
+ bytes_to_recv = 8;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ memset(buf, 0, bytes_to_recv);
+ rc = i2c_master_recv(client, buf, bytes_to_recv);
+
+ if (rc != bytes_to_recv) {
+ dev_err(&client->dev,
+ "%s: i2c_master_recv error?! \n", __func__);
+ /* power off level shift */
+ ekt_data.power(0);
+ msleep(5);
+ /* power on level shift */
+ ekt_data.power(1);
+ /* re-initial */
+ if (ekt_data.fw_ver > 0x101) {
+ msleep(100);
+ rc = ekt8232_ts_init(client);
+ } else {
+ do {
+ rc = ekt8232_set_power_state(client,
+ STATE_NORMAL);
+
+ rc = ekt8232_get_power_state(client);
+ if (rc != STATE_NORMAL)
+ dev_err(&client->dev,
+ "%s: wake up tp failed! \
+ err = %d\n",
+ __func__, rc);
+ else
+ break;
+ } while (--retry);
+ }
+ if (ekt8232_detect_int_level() == 0)
+ queue_work(elan_wq, &ekt_data.work);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static inline void ekt8232_parse_width(uint8_t data, uint8_t *w1, uint8_t *w2)
+{
+ *w1 = *w2 = 0;
+ *w1 = (data & 0xf0) >> 4;
+ *w2 = data & 0x0f;
+}
+
+static void ekt8232_report_data(struct i2c_client *client, uint8_t *buf)
+{
+ static unsigned report_time;
+ unsigned report_time2;
+
+ switch (buf[0]) {
+ case idx_coordinate_packet: {
+ uint16_t x1, x2, y1, y2;
+ uint8_t finger_stat, w1 = 1, w2 = 1;
+
+ ekt8232_parse_xy(&buf[1], &x1, &y1);
+ if (ekt_data.fw_ver == 0x101) {
+ finger_stat = buf[7] >> 1;
+ } else {
+ ekt8232_parse_width(buf[idx_finger_width], &w1, &w2);
+ finger_stat = buf[idx_finger_state] >> 1;
+ }
+
+ if (finger_stat != 0) {
+ input_report_abs(ekt_data.input, ABS_X, x1);
+ if (ekt_data.fw_ver == 0x101)
+ input_report_abs(ekt_data.input, ABS_Y,
+ (544 - 1) - y1);
+ else
+ input_report_abs(ekt_data.input, ABS_Y, y1);
+ /* only report finger width at y */
+ input_report_abs(ekt_data.input, ABS_TOOL_WIDTH, w2);
+ }
+
+ dev_dbg(&client->dev,
+ "x1 = %d, y1 = %d, \
+ w1 = %d, w2 = %d, finger status = %d\n",
+ x1, y1, w1, w2, finger_stat);
+
+ input_report_abs(ekt_data.input, ABS_PRESSURE, 100);
+ input_report_key(ekt_data.input, BTN_TOUCH, finger_stat);
+ input_report_key(ekt_data.input, BTN_2, finger_stat == 2);
+
+ if (finger_stat > 1) {
+ ekt8232_parse_xy(&buf[4], &x2, &y2);
+ dev_dbg(&client->dev, "x2 = %d, y2 = %d\n", x2, y2);
+ input_report_abs(ekt_data.input, ABS_HAT0X, x2);
+ input_report_abs(ekt_data.input, ABS_HAT0Y, y2);
+ }
+ input_sync(ekt_data.input);
+ break;
+ }
+ default:
+ dev_err(&client->dev,
+ "%s: Unknown packet type: %0x\n", __func__, buf[0]);
+ break;
+ }
+
+ report_time2 = jiffies;
+ dev_dbg(&client->dev,
+ "report time = %d\n",
+ jiffies_to_msecs(report_time2 - report_time));
+
+ report_time = report_time2;
+
+}
+
+static void ekt8232_work_func(struct work_struct *work)
+{
+ int rc;
+ uint8_t buf[IDX_PACKET_SIZE] = { 0 };
+ struct i2c_client *client = ekt_data.client;
+
+ /* dev_dbg(&client->dev, "%s: enter. \n", __func__); */
+
+ /* this means that we have already serviced it */
+ if (ekt8232_detect_int_level())
+ return;
+
+ rc = ekt8232_recv_data(client, buf);
+ if (rc < 0)
+ return;
+
+ ekt8232_report_data(client, buf);
+}
+
+static irqreturn_t ekt8232_ts_interrupt(int irq, void *dev_id)
+{
+ /* the queue_work has spin_lock protection */
+ /* disable_irq(irq); */
+ queue_work(elan_wq, &ekt_data.work);
+
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart ekt8232_ts_timer_func(struct hrtimer *timer)
+{
+ queue_work(elan_wq, &ekt_data.work);
+ hrtimer_start(&ekt_data.timer,
+ ktime_set(0, 12500000),
+ HRTIMER_MODE_REL);
+
+ return HRTIMER_NORESTART;
+}
+
+static int ekt8232_register_interrupt(struct i2c_client *client)
+{
+ int err = 0;
+
+ if (client->irq) {
+ ekt_data.use_irq = 1;
+
+ err = request_irq(client->irq, ekt8232_ts_interrupt, 0,
+ EKT8232NAME, &ekt_data);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s(%s): Can't allocate irq %d\n",
+ __FILE__, __func__, client->irq);
+ ekt_data.use_irq = 0;
+ }
+ }
+
+ if (!ekt_data.use_irq) {
+ hrtimer_init(&ekt_data.timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ekt_data.timer.function = ekt8232_ts_timer_func;
+ hrtimer_start(&ekt_data.timer, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+ }
+
+ dev_dbg(&client->dev,
+ "elan starts in %s mode.\n",
+ ekt_data.use_irq == 1 ? "interrupt":"polling");
+ return 0;
+}
+
+static int ekt8232_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct elan_i2c_platform_data *pdata;
+ int x_max, y_max;
+ uint8_t x_resolution_cmd[] = { read_cmd_packet, 0x60, 0x00, 0x01 };
+ uint8_t y_resolution_cmd[] = { read_cmd_packet, 0x63, 0x00, 0x01 };
+ uint8_t buf_recv[4] = { 0 };
+
+ elan_wq = create_singlethread_workqueue("elan_wq");
+ if (!elan_wq) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ printk(KERN_INFO "ekt8232_probe enter.\n");
+ dev_dbg(&client->dev, "ekt8232_probe enter.\n");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "No supported i2c func what we need?!!\n");
+ err = -ENOTSUPP;
+ goto fail;
+ }
+
+ ekt_data.client = client;
+ strlcpy(client->name, EKT8232NAME, I2C_NAME_SIZE);
+ i2c_set_clientdata(client, &ekt_data);
+ INIT_WORK(&ekt_data.work, ekt8232_work_func);
+ init_waitqueue_head(&ekt_data.wait);
+
+ ekt_data.input = input_allocate_device();
+ if (ekt_data.input == NULL) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ pdata = client->dev.platform_data;
+ if (likely(pdata != NULL)) {
+ ekt_data.intr_gpio =
+ ((struct elan_i2c_platform_data *)pdata)->intr_gpio;
+ ekt_data.power =
+ ((struct elan_i2c_platform_data *)pdata)->power;
+ ekt_data.power(1);
+ dev_info(&client->dev, "touch panel is powered on. \n");
+ mdelay(500); /* elan will be ready after about 500 ms */
+ } else {
+ dev_err(&client->dev, "without platform data??!!\n");
+ }
+
+ err = ekt8232_ts_init(client);
+ if (err < 0) {
+ printk(KERN_INFO "looks like it's not Elan, so..i'll quit\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (pdata) {
+ while (pdata->version > ekt_data.fw_ver) {
+ printk(KERN_INFO "ekt8232_probe: old tp detected, "
+ "panel version = 0x%x\n",
+ ekt_data.fw_ver);
+ pdata++;
+ }
+ }
+ printk(KERN_INFO "ekt8232_register_input\n");
+
+ ekt_data.input->name = EKT8232NAME;
+ ekt_data.input->id.bustype = BUS_I2C;
+ set_bit(EV_SYN, ekt_data.input->evbit);
+ set_bit(EV_KEY, ekt_data.input->evbit);
+ set_bit(BTN_TOUCH, ekt_data.input->keybit);
+ set_bit(BTN_2, ekt_data.input->keybit);
+ set_bit(EV_ABS, ekt_data.input->evbit);
+
+ if (ekt_data.fw_ver >= 0x104) {
+ err = ekt8232_get_data(ekt_data.client, x_resolution_cmd,
+ buf_recv, 4, 0);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: get x resolution failed, err = %d\n",
+ __func__, err);
+ goto fail;
+ }
+
+ x_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff));
+ printk(KERN_INFO "ekt8232_probe: x_max: %d\n", x_max);
+
+ err = ekt8232_get_data(ekt_data.client, y_resolution_cmd,
+ buf_recv, 4, 0);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: get y resolution failed, err = %d\n",
+ __func__, err);
+ goto fail;
+ }
+
+ y_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff));
+ printk(KERN_INFO "ekt8232_probe: y_max: %d\n", y_max);
+ input_set_abs_params(ekt_data.input, ABS_X,
+ pdata->abs_x_min, x_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_Y,
+ pdata->abs_y_min, y_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_HAT0X,
+ pdata->abs_x_min, x_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_HAT0Y,
+ pdata->abs_y_min, y_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ } else {
+ input_set_abs_params(ekt_data.input, ABS_X,
+ pdata->abs_x_min, pdata->abs_x_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_Y,
+ pdata->abs_y_min, pdata->abs_y_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_HAT0X,
+ pdata->abs_x_min, pdata->abs_x_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_HAT0Y,
+ pdata->abs_y_min, pdata->abs_y_max,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_PRESSURE, 0, 255,
+ ELAN_TS_FUZZ, ELAN_TS_FLAT);
+ input_set_abs_params(ekt_data.input, ABS_TOOL_WIDTH, 1, 8,
+ 1, ELAN_TS_FLAT);
+ }
+
+ err = input_register_device(ekt_data.input);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: input_register_device failed, err = %d\n",
+ __func__, err);
+ goto fail;
+ }
+
+ ekt8232_register_interrupt(ekt_data.client);
+
+ /* checking the interrupt to avoid missing any interrupt */
+ if (ekt8232_detect_int_level() == 0)
+ ekt8232_ts_interrupt(client->irq, NULL);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ekt_data.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ekt_data.early_suspend.suspend = elan_ts_early_suspend;
+ ekt_data.early_suspend.resume = elan_ts_late_resume;
+ register_early_suspend(&ekt_data.early_suspend);
+#endif
+ touch_sysfs_init();
+ return 0;
+
+fail:
+ input_free_device(ekt_data.input);
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+ return err;
+}
+
+static int ekt8232_remove(struct i2c_client *client)
+{
+ struct ekt8232_data *tp = i2c_get_clientdata(client);
+
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+
+ dev_dbg(&client->dev, "%s: enter.\n", __func__);
+
+ input_unregister_device(tp->input);
+
+ if (ekt_data.use_irq)
+ free_irq(client->irq, tp);
+ else
+ hrtimer_cancel(&ekt_data.timer);
+ return 0;
+}
+
+static int ekt8232_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ uint8_t cmd[4];
+ int rc = 0;
+
+ dev_dbg(&client->dev, "%s: enter. irq = %d\n", __func__, client->irq);
+
+ cancel_work_sync(&ekt_data.work);
+
+ rc = ekt8232_set_power_state(client, STATE_DEEP_SLEEP);
+/*
+ rc = ekt8232_get_power_state(client);
+ if (rc < 0 || rc != STATE_DEEP_SLEEP)
+ dev_err(&client->dev,
+ "%s: put tp into sleep failed, err = %d!\n",
+ __func__, rc);
+*/
+ /* disable tp interrupt */
+ if (ekt_data.fw_ver > 0x101) {
+ memset(cmd, disable_int, 4);
+ if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd))
+ dev_err(&client->dev,
+ "%s: tp disable interrupt failed\n", __func__);
+ }
+
+ /* power off level shift */
+ ekt_data.power(0);
+
+ return 0;
+}
+
+static int ekt8232_resume(struct i2c_client *client)
+{
+ int rc = 0, retry = 5;
+
+ dev_dbg(&client->dev,
+ "%s: enter. irq = %d\n", __func__, client->irq);
+
+ disable_irq(client->irq);
+
+ /* power on level shift */
+ ekt_data.power(1);
+
+ /* re-initial */
+ if (ekt_data.fw_ver > 0x101) {
+ msleep(500);
+ rc = ekt8232_ts_init(client);
+ } else {
+ do {
+ rc = ekt8232_set_power_state(client, STATE_NORMAL);
+ rc = ekt8232_get_power_state(client);
+ if (rc != STATE_NORMAL)
+ dev_err(&client->dev,
+ "%s: wake up tp failed! err = %d\n",
+ __func__, rc);
+ else
+ break;
+ } while (--retry);
+ }
+
+ enable_irq(client->irq);
+
+ if (ekt8232_detect_int_level() == 0)
+ ekt8232_ts_interrupt(client->irq, NULL);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void elan_ts_early_suspend(struct early_suspend *h)
+{
+ struct i2c_client *client = ekt_data.client;
+
+ dev_dbg(&client->dev, "%s enter.\n", __func__);
+ ekt8232_suspend(client, PMSG_SUSPEND);
+}
+
+static void elan_ts_late_resume(struct early_suspend *h)
+{
+ struct i2c_client *client = ekt_data.client;
+
+ dev_dbg(&client->dev, "%s enter.\n", __func__);
+ ekt8232_resume(client);
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+static const struct i2c_device_id ekt8232_ts_id[] = {
+ { ELAN_8232_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver ekt8232_driver = {
+ .probe = ekt8232_probe,
+ .remove = ekt8232_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = ekt8232_suspend,
+ .resume = ekt8232_resume,
+#endif
+ .id_table = ekt8232_ts_id,
+ .driver = {
+ .name = ELAN_8232_I2C_NAME,
+ },
+};
+
+static int __init ekt8232_init(void)
+{
+ return i2c_add_driver(&ekt8232_driver);
+}
+
+static void __exit ekt8232_exit(void)
+{
+ i2c_del_driver(&ekt8232_driver);
+}
+
+module_init(ekt8232_init);
+module_exit(ekt8232_exit);
+
+MODULE_AUTHOR("Shan-Fu Chiou <sfchiou@gmail.com>, "
+ "Jay Tu <jay_tu@htc.com>");
+MODULE_DESCRIPTION("ELAN ekt8232 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/msm_ts.c b/drivers/input/touchscreen/msm_ts.c
new file mode 100644
index 0000000..dff730a
--- /dev/null
+++ b/drivers/input/touchscreen/msm_ts.c
@@ -0,0 +1,344 @@
+/* drivers/input/touchscreen/msm_ts.c
+ *
+ * Copyright (C) 2008 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.
+ *
+ * TODO:
+ * - Add a timer to simulate a pen_up in case there's a timeout.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/msm_ts.h>
+
+#define TSSC_CTL 0x100
+#define TSSC_CTL_PENUP_IRQ (1 << 12)
+#define TSSC_CTL_DATA_FLAG (1 << 11)
+#define TSSC_CTL_DEBOUNCE_EN (1 << 6)
+#define TSSC_CTL_EN_AVERAGE (1 << 5)
+#define TSSC_CTL_MODE_MASTER (3 << 3)
+#define TSSC_CTL_ENABLE (1 << 0)
+#define TSSC_OPN 0x104
+#define TSSC_OPN_NOOP 0x00
+#define TSSC_OPN_4WIRE_X 0x01
+#define TSSC_OPN_4WIRE_Y 0x02
+#define TSSC_OPN_4WIRE_Z1 0x03
+#define TSSC_OPN_4WIRE_Z2 0x04
+#define TSSC_SAMPLING_INT 0x108
+#define TSSC_STATUS 0x10c
+#define TSSC_AVG_12 0x110
+#define TSSC_AVG_34 0x114
+#define TSSC_SAMPLE(op,samp) ((0x118 + ((op & 0x3) * 0x20)) + \
+ ((samp & 0x7) * 0x4))
+#define TSSC_TEST_1 0x198
+#define TSSC_TEST_2 0x19c
+
+struct msm_ts {
+ struct msm_ts_platform_data *pdata;
+ struct input_dev *input_dev;
+ void __iomem *tssc_base;
+ uint32_t ts_down:1;
+ struct ts_virt_key *vkey_down;
+};
+
+static uint32_t msm_tsdebug;
+module_param_named(tsdebug, msm_tsdebug, uint, 0664);
+
+#define tssc_readl(t, a) (readl(((t)->tssc_base) + (a)))
+#define tssc_writel(t, v, a) do {writel(v, ((t)->tssc_base) + (a));} while(0)
+
+static void setup_next_sample(struct msm_ts *ts)
+{
+ uint32_t tmp;
+
+ /* 1.2ms debounce time */
+ tmp = ((2 << 7) | TSSC_CTL_DEBOUNCE_EN | TSSC_CTL_EN_AVERAGE |
+ TSSC_CTL_MODE_MASTER | TSSC_CTL_ENABLE);
+ tssc_writel(ts, tmp, TSSC_CTL);
+}
+
+static struct ts_virt_key *find_virt_key(struct msm_ts *ts,
+ struct msm_ts_virtual_keys *vkeys,
+ uint32_t val)
+{
+ int i;
+
+ if (!vkeys)
+ return NULL;
+
+ for (i = 0; i < vkeys->num_keys; ++i)
+ if ((val >= vkeys->keys[i].min) && (val <= vkeys->keys[i].max))
+ return &vkeys->keys[i];
+ return NULL;
+}
+
+
+static irqreturn_t msm_ts_irq(int irq, void *dev_id)
+{
+ struct msm_ts *ts = dev_id;
+ struct msm_ts_platform_data *pdata = ts->pdata;
+
+ uint32_t tssc_avg12, tssc_avg34, tssc_status, tssc_ctl;
+ int x, y, z1, z2;
+ int was_down;
+ int down;
+
+ tssc_ctl = tssc_readl(ts, TSSC_CTL);
+ tssc_status = tssc_readl(ts, TSSC_STATUS);
+ tssc_avg12 = tssc_readl(ts, TSSC_AVG_12);
+ tssc_avg34 = tssc_readl(ts, TSSC_AVG_34);
+
+ setup_next_sample(ts);
+
+ x = tssc_avg12 & 0xffff;
+ y = tssc_avg12 >> 16;
+ z1 = tssc_avg34 & 0xffff;
+ z2 = tssc_avg34 >> 16;
+
+ /* invert the inputs if necessary */
+ if (pdata->inv_x) x = pdata->inv_x - x;
+ if (pdata->inv_y) y = pdata->inv_y - y;
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ down = !(tssc_ctl & TSSC_CTL_PENUP_IRQ);
+ was_down = ts->ts_down;
+ ts->ts_down = down;
+
+ /* no valid data */
+ if (down && !(tssc_ctl & TSSC_CTL_DATA_FLAG))
+ return IRQ_HANDLED;
+
+ if (msm_tsdebug & 2)
+ printk("%s: down=%d, x=%d, y=%d, z1=%d, z2=%d, status %x\n",
+ __func__, down, x, y, z1, z2, tssc_status);
+
+ if (!was_down && down) {
+ struct ts_virt_key *vkey = NULL;
+
+ if (pdata->vkeys_y && (y > pdata->virt_y_start))
+ vkey = find_virt_key(ts, pdata->vkeys_y, x);
+ if (!vkey && ts->pdata->vkeys_x && (x > pdata->virt_x_start))
+ vkey = find_virt_key(ts, pdata->vkeys_x, y);
+
+ if (vkey) {
+ WARN_ON(ts->vkey_down != NULL);
+ if(msm_tsdebug)
+ printk("%s: virtual key down %d\n", __func__,
+ vkey->key);
+ ts->vkey_down = vkey;
+ input_report_key(ts->input_dev, vkey->key, 1);
+ input_sync(ts->input_dev);
+ return IRQ_HANDLED;
+ }
+ } else if (ts->vkey_down != NULL) {
+ if (!down) {
+ if(msm_tsdebug)
+ printk("%s: virtual key up %d\n", __func__,
+ ts->vkey_down->key);
+ input_report_key(ts->input_dev, ts->vkey_down->key, 0);
+ input_sync(ts->input_dev);
+ ts->vkey_down = NULL;
+ }
+ return IRQ_HANDLED;
+ }
+
+ if (down) {
+ input_report_abs(ts->input_dev, ABS_X, x);
+ input_report_abs(ts->input_dev, ABS_Y, y);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, z1);
+ }
+ input_report_key(ts->input_dev, BTN_TOUCH, down);
+ input_sync(ts->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static void dump_tssc_regs(struct msm_ts *ts)
+{
+#define __dump_tssc_reg(r) \
+ do { printk(#r " %x\n", tssc_readl(ts, (r))); } while(0)
+
+ __dump_tssc_reg(TSSC_CTL);
+ __dump_tssc_reg(TSSC_OPN);
+ __dump_tssc_reg(TSSC_SAMPLING_INT);
+ __dump_tssc_reg(TSSC_STATUS);
+ __dump_tssc_reg(TSSC_AVG_12);
+ __dump_tssc_reg(TSSC_AVG_34);
+#undef __dump_tssc_reg
+}
+
+static int __devinit msm_ts_hw_init(struct msm_ts *ts)
+{
+ uint32_t tmp;
+
+ /* Enable the register clock to tssc so we can configure it. */
+ tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL);
+
+ /* op1 - measure X, 1 sample, 12bit resolution */
+ tmp = (TSSC_OPN_4WIRE_X << 16) | (2 << 8) | (2 << 0);
+ /* op2 - measure Y, 1 sample, 12bit resolution */
+ tmp |= (TSSC_OPN_4WIRE_Y << 20) | (2 << 10) | (2 << 2);
+ /* op3 - measure Z1, 1 sample, 8bit resolution */
+ tmp |= (TSSC_OPN_4WIRE_Z1 << 24) | (2 << 12) | (0 << 4);
+
+ /* XXX: we don't actually need to measure Z2 (thus 0 samples) when
+ * doing voltage-driven measurement */
+ /* op4 - measure Z2, 0 samples, 8bit resolution */
+ tmp |= (TSSC_OPN_4WIRE_Z2 << 28) | (0 << 14) | (0 << 6);
+ tssc_writel(ts, tmp, TSSC_OPN);
+
+ /* 16ms sampling interval */
+ tssc_writel(ts, 16, TSSC_SAMPLING_INT);
+
+ setup_next_sample(ts);
+
+ return 0;
+}
+
+static int __devinit msm_ts_probe(struct platform_device *pdev)
+{
+ struct msm_ts_platform_data *pdata = pdev->dev.platform_data;
+ struct msm_ts *ts;
+ struct resource *tssc_res;
+ struct resource *irq1_res;
+ struct resource *irq2_res;
+ int err = 0;
+ int i;
+
+ tssc_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tssc");
+ irq1_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc1");
+ irq2_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc2");
+
+ if (!tssc_res || !irq1_res || !irq2_res) {
+ pr_err("%s: required resources not defined\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdata == NULL) {
+ pr_err("%s: missing platform_data\n", __func__);
+ return -ENODEV;
+ }
+
+ ts = kzalloc(sizeof(struct msm_ts), GFP_KERNEL);
+ if (ts == NULL) {
+ pr_err("%s: No memory for struct msm_ts\n", __func__);
+ return -ENOMEM;
+ }
+ ts->pdata = pdata;
+
+ ts->tssc_base = ioremap(tssc_res->start, resource_size(tssc_res));
+ if (ts->tssc_base == NULL) {
+ pr_err("%s: Can't ioremap region (0x%08x - 0x%08x)\n", __func__,
+ (uint32_t)tssc_res->start, (uint32_t)tssc_res->end);
+ err = -ENOMEM;
+ goto err_ioremap_tssc;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ pr_err("failed to allocate touchscreen input device\n");
+ err = -ENOMEM;
+ goto err_alloc_input_dev;
+ }
+ ts->input_dev->name = "msm-touchscreen";
+ input_set_drvdata(ts->input_dev, ts);
+
+ input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+
+ input_set_abs_params(ts->input_dev, ABS_X, pdata->min_x, pdata->max_x,
+ 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y, pdata->min_y, pdata->max_y,
+ 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, pdata->min_press,
+ pdata->max_press, 0, 0);
+
+ for (i = 0; pdata->vkeys_x && (i < pdata->vkeys_x->num_keys); ++i)
+ input_set_capability(ts->input_dev, EV_KEY,
+ pdata->vkeys_x->keys[i].key);
+ for (i = 0; pdata->vkeys_y && (i < pdata->vkeys_y->num_keys); ++i)
+ input_set_capability(ts->input_dev, EV_KEY,
+ pdata->vkeys_y->keys[i].key);
+
+ err = input_register_device(ts->input_dev);
+ if (err != 0) {
+ pr_err("%s: failed to register input device\n", __func__);
+ goto err_input_dev_reg;
+ }
+
+ msm_ts_hw_init(ts);
+
+ err = request_irq(irq1_res->start, msm_ts_irq,
+ (irq1_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED,
+ "msm_touchscreen", ts);
+ if (err != 0) {
+ pr_err("%s: Cannot register irq1 (%d)\n", __func__, err);
+ goto err_request_irq1;
+ }
+
+ err = request_irq(irq2_res->start, msm_ts_irq,
+ (irq2_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED,
+ "msm_touchscreen", ts);
+ if (err != 0) {
+ pr_err("%s: Cannot register irq2 (%d)\n", __func__, err);
+ goto err_request_irq2;
+ }
+
+ platform_set_drvdata(pdev, ts);
+
+ pr_info("%s: tssc_base=%p irq1=%d irq2=%d\n", __func__,
+ ts->tssc_base, (int)irq1_res->start, (int)irq2_res->start);
+ return 0;
+
+err_request_irq2:
+ free_irq(irq1_res->start, ts);
+
+err_request_irq1:
+ /* disable the tssc */
+ tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL);
+
+err_input_dev_reg:
+ input_set_drvdata(ts->input_dev, NULL);
+ input_free_device(ts->input_dev);
+
+err_alloc_input_dev:
+ iounmap(ts->tssc_base);
+
+err_ioremap_tssc:
+ kfree(ts);
+ return err;
+}
+
+static struct platform_driver msm_touchscreen_driver = {
+ .driver = {
+ .name = "msm_touchscreen",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_ts_probe,
+};
+
+static int __init msm_ts_init(void)
+{
+ return platform_driver_register(&msm_touchscreen_driver);
+}
+device_initcall(msm_ts_init);
+
+MODULE_DESCRIPTION("Qualcomm MSM/QSD Touchscreen controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 65dd750..115b099 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -176,6 +176,13 @@
To compile this driver as a module, choose M here: the
module will be called leds-lp3944.
+config LEDS_CPLD
+ tristate "LED Support for CPLD connected LEDs"
+ default y
+ depends on LEDS_CLASS
+ help
+ This option enables support for the LEDs connected to CPLD
+
config LEDS_CLEVO_MAIL
tristate "Mail LED on Clevo notebook"
depends on X86 && SERIO_I8042 && DMI
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index b68fcf6..f88e24a 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -23,6 +23,7 @@
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
+obj-$(CONFIG_LEDS_CPLD) += leds-cpld.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
diff --git a/drivers/leds/leds-cpld.c b/drivers/leds/leds-cpld.c
new file mode 100644
index 0000000..eab004c
--- /dev/null
+++ b/drivers/leds/leds-cpld.c
@@ -0,0 +1,405 @@
+/* include/asm/mach-msm/leds-cpld.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ *
+ * Author: Farmer Tseng
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/mach-types.h>
+
+#define DEBUG_LED_CHANGE 0
+
+static int _g_cpld_led_addr;
+
+struct CPLD_LED_data {
+ spinlock_t data_lock;
+ struct led_classdev leds[4]; /* blue, green, red */
+};
+
+static ssize_t led_blink_solid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ uint8_t reg_val;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ if (!strcmp(led_cdev->name, "red"))
+ idx = 0;
+ else if (!strcmp(led_cdev->name, "green"))
+ idx = 1;
+ else
+ idx = 2;
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ reg_val = reg_val >> (2 * idx + 1);
+ reg_val &= 0x1;
+ spin_unlock(&CPLD_LED->data_lock);
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", reg_val);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t led_blink_solid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ uint8_t reg_val;
+ char *after;
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+ size_t count;
+
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ if (!strcmp(led_cdev->name, "red"))
+ idx = 0;
+ else if (!strcmp(led_cdev->name, "green"))
+ idx = 1;
+ else
+ idx = 2;
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+ state = simple_strtoul(buf, &after, 10);
+
+ count = after - buf;
+
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ ret = count;
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ if (state)
+ reg_val |= 1 << (2 * idx + 1);
+ else
+ reg_val &= ~(1 << (2 * idx + 1));
+
+ writeb(reg_val, _g_cpld_led_addr);
+ spin_unlock(&CPLD_LED->data_lock);
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store);
+
+static ssize_t cpldled_blink_all_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ uint8_t reg_val;
+ struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ reg_val &= 0x2A;
+ if (reg_val == 0x2A)
+ reg_val = 1;
+ else
+ reg_val = 0;
+ spin_unlock(&CPLD_LED->data_lock);
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", reg_val);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t cpldled_blink_all_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint8_t reg_val;
+ char *after;
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+ size_t count;
+ struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+
+ state = simple_strtoul(buf, &after, 10);
+
+ count = after - buf;
+
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ ret = count;
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ if (state)
+ reg_val |= 0x2A;
+ else
+ reg_val &= ~0x2A;
+
+ writeb(reg_val, _g_cpld_led_addr);
+ spin_unlock(&CPLD_LED->data_lock);
+ }
+
+ return ret;
+}
+
+static struct device_attribute dev_attr_blink_all = {
+ .attr = {
+ .name = "blink",
+ .mode = 0644,
+ },
+ .show = cpldled_blink_all_show,
+ .store = cpldled_blink_all_store,
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ struct led_classdev *led;
+ uint8_t reg_val;
+
+ if (!strcmp(led_cdev->name, "jogball-backlight")) {
+ if (brightness > 7)
+ reg_val = 1;
+ else
+ reg_val = brightness;
+ writeb(0, _g_cpld_led_addr + 0x8);
+ writeb(reg_val, _g_cpld_led_addr + 0x8);
+#if DEBUG_LED_CHANGE
+ printk(KERN_INFO "LED change: jogball backlight = %d \n",
+ reg_val);
+#endif
+ return;
+ } else if (!strcmp(led_cdev->name, "red")) {
+ idx = 0;
+ } else if (!strcmp(led_cdev->name, "green")) {
+ idx = 1;
+ } else {
+ idx = 2;
+ }
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ led = &CPLD_LED->leds[idx];
+
+ if (led->brightness > LED_OFF)
+ reg_val |= 1 << (2 * idx);
+ else
+ reg_val &= ~(1 << (2 * idx));
+
+ writeb(reg_val, _g_cpld_led_addr);
+#if DEBUG_LED_CHANGE
+ printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness);
+#endif
+ spin_unlock(&CPLD_LED->data_lock);
+}
+
+static ssize_t cpldled_grpfreq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grpfreq_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return 0;
+}
+
+static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store);
+
+static ssize_t cpldled_grppwm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grppwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return 0;
+}
+
+static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store);
+
+static int CPLD_LED_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i, j;
+ struct resource *res;
+ struct CPLD_LED_data *CPLD_LED;
+
+ CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL);
+ if (CPLD_LED == NULL) {
+ printk(KERN_ERR "CPLD_LED_probe: no memory for device\n");
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ _g_cpld_led_addr = res->start;
+ if (!_g_cpld_led_addr) {
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data));
+ writeb(0x00, _g_cpld_led_addr);
+
+ CPLD_LED->leds[0].name = "red";
+ CPLD_LED->leds[0].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[1].name = "green";
+ CPLD_LED->leds[1].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[2].name = "blue";
+ CPLD_LED->leds[2].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[3].name = "jogball-backlight";
+ CPLD_LED->leds[3].brightness_set = led_brightness_set;
+
+ spin_lock_init(&CPLD_LED->data_lock);
+
+ for (i = 0; i < 4; i++) { /* red, green, blue jogball */
+ ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: led_classdev_register failed\n");
+ goto err_led_classdev_register_failed;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret =
+ device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: device_create_file failed\n");
+ goto err_out_attr_blink;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, CPLD_LED);
+ ret = device_create_file(&pdev->dev, &dev_attr_blink_all);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_blink_all failed\n");
+ goto err_out_attr_blink;
+ }
+ ret = device_create_file(&pdev->dev, &dev_attr_grppwm);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_grppwm failed\n");
+ goto err_out_attr_grppwm;
+ }
+ ret = device_create_file(&pdev->dev, &dev_attr_grpfreq);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_grpfreq failed\n");
+ goto err_out_attr_grpfreq;
+ }
+
+ return 0;
+
+err_out_attr_grpfreq:
+ device_remove_file(&pdev->dev, &dev_attr_grppwm);
+err_out_attr_grppwm:
+ device_remove_file(&pdev->dev, &dev_attr_blink_all);
+err_out_attr_blink:
+ for (j = 0; j < i; j++)
+ device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink);
+ i = 3;
+
+err_led_classdev_register_failed:
+ for (j = 0; j < i; j++)
+ led_classdev_unregister(&CPLD_LED->leds[j]);
+
+err_alloc_failed:
+ kfree(CPLD_LED);
+
+ return ret;
+}
+
+static int __devexit CPLD_LED_remove(struct platform_device *pdev)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int i;
+
+ CPLD_LED = platform_get_drvdata(pdev);
+
+ for (i = 0; i < 3; i++) {
+ device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+ led_classdev_unregister(&CPLD_LED->leds[i]);
+ }
+
+ device_remove_file(&pdev->dev, &dev_attr_blink_all);
+ device_remove_file(&pdev->dev, &dev_attr_grppwm);
+ device_remove_file(&pdev->dev, &dev_attr_grpfreq);
+
+ kfree(CPLD_LED);
+ return 0;
+}
+
+static struct platform_driver CPLD_LED_driver = {
+ .probe = CPLD_LED_probe,
+ .remove = __devexit_p(CPLD_LED_remove),
+ .driver = {
+ .name = "leds-cpld",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init CPLD_LED_init(void)
+{
+ return platform_driver_register(&CPLD_LED_driver);
+}
+
+static void __exit CPLD_LED_exit(void)
+{
+ platform_driver_unregister(&CPLD_LED_driver);
+}
+
+MODULE_AUTHOR("Farmer Tseng");
+MODULE_DESCRIPTION("CPLD_LED driver");
+MODULE_LICENSE("GPL");
+
+module_init(CPLD_LED_init);
+module_exit(CPLD_LED_exit);
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index bdbc9d3..13e7409 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1126,8 +1126,12 @@
This driver can be compiled as a module, called s2255drv.
endif # V4L_USB_DRIVERS
+
endif # VIDEO_CAPTURE_DRIVERS
+# MSM camera does not require V4L2
+source "drivers/media/video/msm/Kconfig"
+
menuconfig V4L_MEM2MEM_DRIVERS
bool "Memory-to-memory multimedia devices"
depends on VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index cc93859..fbd3486 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -179,6 +179,8 @@
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+obj-$(CONFIG_MSM_CAMERA) += msm/
+
obj-$(CONFIG_ARCH_OMAP) += omap/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
new file mode 100644
index 0000000..b4796d3
--- /dev/null
+++ b/drivers/media/video/msm/Kconfig
@@ -0,0 +1,48 @@
+comment "Qualcomm MSM Camera And Video"
+
+menuconfig MSM_CAMERA
+ bool "Qualcomm MSM camera and video capture support"
+ depends on ARCH_MSM
+ help
+ Say Y here to enable selecting the video adapters for
+ Qualcomm msm camera and video encoding
+
+config MSM_CAMERA_V4L2
+ bool "Video For Linux interface to MSM camera"
+ depends on MSM_CAMERA && VIDEO_V4L2 && EXPERIMENTAL
+ default y
+ help
+ Say Y here to enable the V4L2 interface for the MSM camera.
+ Not everything works through this interface and it has not
+ been thoroughly tested.
+
+config MSM_CAMERA_DEBUG
+ bool "Qualcomm MSM camera debugging with printk"
+ depends on MSM_CAMERA
+ help
+ Enable printk() debug for msm camera
+
+comment "Camera Sensor Selection"
+config MT9T013
+ bool "Sensor mt9t013 (BAYER 3M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 3M Bayer Sensor with AutoFocus
+
+config MT9D112
+ bool "Sensor mt9d112 (YUV 2M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 2M YUV Sensor
+
+config MT9P012
+ bool "Sensor mt9p012 (BAYER 5M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 5M Bayer Sensor with Autofocus
+
+config S5K3E2FX
+ bool "Sensor s5k3e2fx (Samsung 5M)"
+ depends on MSM_CAMERA
+ ---help---
+ Samsung 5M with Autofocus
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
new file mode 100755
index 0000000..61e370c
--- /dev/null
+++ b/drivers/media/video/msm/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o
+obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o
+obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o
+obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
+obj-$(CONFIG_MSM_CAMERA_V4L2) += msm_v4l2.o
+obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o
+obj-$(CONFIG_ARCH_MSM7X00A) += msm_vfe7x.o msm_io7x.o
+obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o
+obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31.o msm_io_vfe31.o
diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c
new file mode 100644
index 0000000..42cd3ad
--- /dev/null
+++ b/drivers/media/video/msm/msm_camera.c
@@ -0,0 +1,2718 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+/* FIXME: management of mutexes */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <mach/board.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/android_pmem.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+#include <asm/cacheflush.h>
+
+#define MSM_MAX_CAMERA_SENSORS 5
+#define CAMERA_STOP_SNAPSHOT 42
+
+#define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \
+ __func__, __LINE__, ((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+#define ERR_COPY_TO_USER() ERR_USER_COPY(1)
+
+static struct class *msm_class;
+static dev_t msm_devno;
+static LIST_HEAD(msm_sensors);
+
+#define __CONTAINS(r, v, l, field) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = __v >= __r->field && \
+ __e <= __r->field + __r->len; \
+ res; \
+})
+
+#define CONTAINS(r1, r2, field) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->field, __r2->len, field); \
+})
+
+#define IN_RANGE(r, v, field) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->field) && \
+ (__vv < (__r->field + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2, field) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->field) __v = __r2->field; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v, field) || \
+ IN_RANGE(__r1, __e, field)); \
+ res; \
+})
+
+static inline void free_qcmd(struct msm_queue_cmd *qcmd)
+{
+ if (!qcmd || !qcmd->on_heap)
+ return;
+ if (!--qcmd->on_heap)
+ kfree(qcmd);
+}
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_info("%s: queue %s new max is %d\n", __func__,
+ queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ CDBG("%s: woke up %s\n", __func__, queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+#define msm_dequeue(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+})
+
+#define msm_queue_drain(queue, member) do { \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ CDBG("%s: draining queue %s\n", __func__, __q->name); \
+ while (!list_empty(&__q->list)) { \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ free_qcmd(qcmd); \
+ }; \
+ __q->len = 0; \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while(0)
+
+static int check_overlap(struct hlist_head *ptype,
+ unsigned long paddr,
+ unsigned long len)
+{
+ struct msm_pmem_region *region;
+ struct msm_pmem_region t = { .paddr = paddr, .len = len };
+ struct hlist_node *node;
+
+ hlist_for_each_entry(region, node, ptype, list) {
+ if (CONTAINS(region, &t, paddr) ||
+ CONTAINS(&t, region, paddr) ||
+ OVERLAPS(region, &t, paddr)) {
+ printk(KERN_ERR
+ " region (PHYS %p len %ld)"
+ " clashes with registered region"
+ " (paddr %p len %ld)\n",
+ (void *)t.paddr, t.len,
+ (void *)region->paddr, region->len);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int check_pmem_info(struct msm_pmem_info *info, int len)
+{
+ if (info->offset & (PAGE_SIZE - 1)) {
+ pr_err("%s: pmem offset is not page-aligned\n", __func__);
+ goto error;
+ }
+
+ if (info->offset < len &&
+ info->offset + info->len <= len &&
+ info->y_off < len &&
+ info->cbcr_off < len)
+ return 0;
+
+error:
+ pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n",
+ __func__,
+ info->offset,
+ info->len,
+ info->y_off,
+ info->cbcr_off,
+ len);
+ return -EINVAL;
+}
+
+static int msm_pmem_table_add(struct hlist_head *ptype,
+ struct msm_pmem_info *info)
+{
+ struct file *file;
+ unsigned long paddr;
+ unsigned long kvstart;
+ unsigned long len;
+ int rc;
+ struct msm_pmem_region *region;
+
+ rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file);
+ if (rc < 0) {
+ pr_err("%s: get_pmem_file fd %d error %d\n",
+ __func__,
+ info->fd, rc);
+ return rc;
+ }
+
+ if (!info->len)
+ info->len = len;
+
+ rc = check_pmem_info(info, len);
+ if (rc < 0)
+ return rc;
+
+ paddr += info->offset;
+ kvstart += info->offset;
+ len = info->len;
+
+ if (check_overlap(ptype, paddr, len) < 0)
+ return -EINVAL;
+
+ CDBG("%s: type %d, paddr 0x%lx, vaddr 0x%lx\n",
+ __func__,
+ info->type, paddr, (unsigned long)info->vaddr);
+
+ region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ INIT_HLIST_NODE(®ion->list);
+
+ region->paddr = paddr;
+ region->kvaddr = kvstart;
+ region->len = len;
+ region->file = file;
+ memcpy(®ion->info, info, sizeof(region->info));
+
+ if (info->vfe_can_write) {
+ dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE);
+ }
+
+ hlist_add_head(&(region->list), ptype);
+
+ return 0;
+}
+
+/* return of 0 means failure */
+static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype,
+ int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount)
+{
+ struct msm_pmem_region *region;
+ struct msm_pmem_region *regptr;
+ struct hlist_node *node, *n;
+
+ uint8_t rc = 0;
+
+ regptr = reg;
+
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ if (region->info.type == pmem_type &&
+ region->info.vfe_can_write) {
+ *regptr = *region;
+ rc += 1;
+ if (rc >= maxcount)
+ break;
+ regptr++;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync,
+ unsigned long pyaddr, unsigned long pcbcraddr,
+ struct msm_pmem_region **pmem_region,
+ int take_from_vfe)
+{
+ struct hlist_node *node, *n;
+ struct msm_pmem_region *region;
+
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ if (pyaddr == (region->paddr + region->info.y_off) &&
+ pcbcraddr == (region->paddr +
+ region->info.cbcr_off) &&
+ region->info.vfe_can_write) {
+ *pmem_region = region;
+ dmac_unmap_area((void*)region->kvaddr, region->len,
+ DMA_FROM_DEVICE);
+ region->info.vfe_can_write = !take_from_vfe;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync,
+ unsigned long addr, int *fd)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ if (addr == region->paddr && region->info.vfe_can_write) {
+ /* offset since we could pass vaddr inside a
+ * registered pmem buffer */
+ *fd = region->info.fd;
+ dmac_unmap_area((void*)region->kvaddr, region->len,
+ DMA_FROM_DEVICE);
+ region->info.vfe_can_write = 0;
+ return (unsigned long)(region->info.vaddr);
+ }
+ }
+
+ return 0;
+}
+
+static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync,
+ unsigned long buffer,
+ uint32_t yoff, uint32_t cbcroff, int fd)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+
+ hlist_for_each_entry_safe(region,
+ node, n, &sync->pmem_frames, list) {
+ if (((unsigned long)(region->info.vaddr) == buffer) &&
+ (region->info.y_off == yoff) &&
+ (region->info.cbcr_off == cbcroff) &&
+ (region->info.fd == fd) &&
+ (region->info.vfe_can_write == 0)) {
+ dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE);
+ region->info.vfe_can_write = 1;
+ return region->paddr;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned long msm_pmem_stats_vtop_lookup(
+ struct msm_sync *sync,
+ unsigned long buffer,
+ int fd)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ if (((unsigned long)(region->info.vaddr) == buffer) &&
+ (region->info.fd == fd) &&
+ region->info.vfe_can_write == 0) {
+ dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE);
+ region->info.vfe_can_write = 1;
+ return region->paddr;
+ }
+ }
+
+ return 0;
+}
+
+static int __msm_pmem_table_del(struct msm_sync *sync,
+ struct msm_pmem_info *pinfo)
+{
+ int rc = 0;
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+
+ switch (pinfo->type) {
+ case MSM_PMEM_OUTPUT1:
+ case MSM_PMEM_OUTPUT2:
+ case MSM_PMEM_VIDEO:
+ case MSM_PMEM_PREVIEW:
+ case MSM_PMEM_THUMBNAIL:
+ case MSM_PMEM_MAINIMG:
+ case MSM_PMEM_RAW_MAINIMG:
+ hlist_for_each_entry_safe(region, node, n,
+ &sync->pmem_frames, list) {
+
+ if (pinfo->type == region->info.type &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+ put_pmem_file(region->file);
+ if (region->info.vfe_can_write) {
+ dmac_unmap_area((void*)region->kvaddr, region->len,
+ DMA_FROM_DEVICE);
+ }
+ kfree(region);
+ }
+ }
+ break;
+
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ hlist_for_each_entry_safe(region, node, n,
+ &sync->pmem_stats, list) {
+
+ if (pinfo->type == region->info.type &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+ put_pmem_file(region->file);
+ if (region->info.vfe_can_write) {
+ dmac_unmap_area((void*)region->kvaddr, region->len,
+ DMA_FROM_DEVICE);
+ }
+ kfree(region);
+ }
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_pmem_table_del(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_pmem_info info;
+
+ if (copy_from_user(&info, arg, sizeof(info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ return __msm_pmem_table_del(sync, &info);
+}
+
+static int __msm_get_frame(struct msm_sync *sync,
+ struct msm_frame *frame)
+{
+ int rc = 0;
+
+ struct msm_pmem_region *region;
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_vfe_resp *vdata;
+ struct msm_vfe_phy_info *pphy;
+
+ qcmd = msm_dequeue(&sync->frame_q, list_frame);
+
+ if (!qcmd) {
+ pr_err("%s: no preview frame.\n", __func__);
+ return -EAGAIN;
+ }
+
+ vdata = (struct msm_vfe_resp *)(qcmd->command);
+ pphy = &vdata->phy;
+
+ rc = msm_pmem_frame_ptov_lookup(sync,
+ pphy->y_phy,
+ pphy->cbcr_phy,
+ ®ion,
+ 1); /* give frame to user space */
+
+ if (rc < 0) {
+ pr_err("%s: cannot get frame, invalid lookup address "
+ "y %x cbcr %x\n",
+ __func__,
+ pphy->y_phy,
+ pphy->cbcr_phy);
+ goto err;
+ }
+
+ frame->buffer = (unsigned long)region->info.vaddr;
+ frame->y_off = region->info.y_off;
+ frame->cbcr_off = region->info.cbcr_off;
+ frame->fd = region->info.fd;
+ frame->path = vdata->phy.output_id;
+
+ CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n",
+ __func__,
+ pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer);
+
+err:
+ free_qcmd(qcmd);
+ return rc;
+}
+
+static int msm_get_frame(struct msm_sync *sync, void __user *arg)
+{
+ int rc = 0;
+ struct msm_frame frame;
+
+ if (copy_from_user(&frame,
+ arg,
+ sizeof(struct msm_frame))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ rc = __msm_get_frame(sync, &frame);
+ if (rc < 0)
+ return rc;
+
+ if (sync->croplen) {
+ if (frame.croplen != sync->croplen) {
+ pr_err("%s: invalid frame croplen %d,"
+ "expecting %d\n",
+ __func__,
+ frame.croplen,
+ sync->croplen);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)frame.cropinfo,
+ sync->cropinfo,
+ sync->croplen)) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ }
+
+ if (copy_to_user((void *)arg,
+ &frame, sizeof(struct msm_frame))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ }
+
+ CDBG("%s: got frame\n", __func__);
+
+ return rc;
+}
+
+static int msm_enable_vfe(struct msm_sync *sync, void __user *arg)
+{
+ int rc = -EIO;
+ struct camera_enable_cmd cfg;
+
+ if (copy_from_user(&cfg,
+ arg,
+ sizeof(struct camera_enable_cmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ if (sync->vfefn.vfe_enable)
+ rc = sync->vfefn.vfe_enable(&cfg);
+
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_disable_vfe(struct msm_sync *sync, void __user *arg)
+{
+ int rc = -EIO;
+ struct camera_enable_cmd cfg;
+
+ if (copy_from_user(&cfg,
+ arg,
+ sizeof(struct camera_enable_cmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ if (sync->vfefn.vfe_disable)
+ rc = sync->vfefn.vfe_disable(&cfg, NULL);
+
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+static struct msm_queue_cmd *__msm_control(struct msm_sync *sync,
+ struct msm_device_queue *queue,
+ struct msm_queue_cmd *qcmd,
+ int timeout)
+{
+ int rc;
+
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+
+ if (!queue)
+ return NULL;
+
+ /* wait for config status */
+ rc = wait_event_interruptible_timeout(
+ queue->wait,
+ !list_empty_careful(&queue->list),
+ timeout);
+ if (list_empty_careful(&queue->list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0) {
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ /* qcmd may be still on the event_q, in which case we
+ * need to remove it. Alternatively, qcmd may have
+ * been dequeued and processed already, in which case
+ * the list removal will be a no-op.
+ */
+ list_del_init(&qcmd->list_config);
+ return ERR_PTR(rc);
+ }
+ }
+
+ qcmd = msm_dequeue(queue, list_control);
+ BUG_ON(!qcmd);
+
+ return qcmd;
+}
+
+static struct msm_queue_cmd *__msm_control_nb(struct msm_sync *sync,
+ struct msm_queue_cmd *qcmd_to_copy)
+{
+ /* Since this is a non-blocking command, we cannot use qcmd_to_copy and
+ * its data, since they are on the stack. We replicate them on the heap
+ * and mark them on_heap so that they get freed when the config thread
+ * dequeues them.
+ */
+
+ struct msm_ctrl_cmd *udata;
+ struct msm_ctrl_cmd *udata_to_copy = qcmd_to_copy->command;
+
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(*qcmd_to_copy) +
+ sizeof(*udata_to_copy) +
+ udata_to_copy->length,
+ GFP_KERNEL);
+ if (!qcmd) {
+ pr_err("%s: out of memory\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ *qcmd = *qcmd_to_copy;
+ udata = qcmd->command = qcmd + 1;
+ memcpy(udata, udata_to_copy, sizeof(*udata));
+ udata->value = udata + 1;
+ memcpy(udata->value, udata_to_copy->value, udata_to_copy->length);
+
+ qcmd->on_heap = 1;
+
+ /* qcmd_resp will be set to NULL */
+ return __msm_control(sync, NULL, qcmd, 0);
+}
+
+static int msm_control(struct msm_control_device *ctrl_pmsm,
+ int block,
+ void __user *arg)
+{
+ int rc = 0;
+
+ struct msm_sync *sync = ctrl_pmsm->pmsm->sync;
+ void __user *uptr;
+ struct msm_ctrl_cmd udata;
+ struct msm_queue_cmd qcmd;
+ struct msm_queue_cmd *qcmd_resp = NULL;
+ uint8_t data[50];
+
+ if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+
+ uptr = udata.value;
+ udata.value = data;
+ if (udata.type == CAMERA_STOP_SNAPSHOT)
+ sync->get_pic_abort = 1;
+ qcmd.on_heap = 0;
+ qcmd.type = MSM_CAM_Q_CTRL;
+ qcmd.command = &udata;
+
+ if (udata.length) {
+ if (udata.length > sizeof(data)) {
+ pr_err("%s: user data too large (%d, max is %d)\n",
+ __func__,
+ udata.length,
+ sizeof(data));
+ rc = -EIO;
+ goto end;
+ }
+ if (copy_from_user(udata.value, uptr, udata.length)) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+ if (unlikely(!block)) {
+ qcmd_resp = __msm_control_nb(sync, &qcmd);
+ goto end;
+ }
+
+ qcmd_resp = __msm_control(sync,
+ &ctrl_pmsm->ctrl_q,
+ &qcmd, MAX_SCHEDULE_TIMEOUT);
+
+ if (!qcmd_resp || IS_ERR(qcmd_resp)) {
+ /* Do not free qcmd_resp here. If the config thread read it,
+ * then it has already been freed, and we timed out because
+ * we did not receive a MSM_CAM_IOCTL_CTRL_CMD_DONE. If the
+ * config thread itself is blocked and not dequeueing commands,
+ * then it will either eventually unblock and process them,
+ * or when it is killed, qcmd will be freed in
+ * msm_release_config.
+ */
+ rc = PTR_ERR(qcmd_resp);
+ qcmd_resp = NULL;
+ goto end;
+ }
+
+ if (qcmd_resp->command) {
+ udata = *(struct msm_ctrl_cmd *)qcmd_resp->command;
+ if (udata.length > 0) {
+ if (copy_to_user(uptr,
+ udata.value,
+ udata.length)) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+ udata.value = uptr;
+
+ if (copy_to_user((void *)arg, &udata,
+ sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+end:
+ free_qcmd(qcmd_resp);
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+/* Divert frames for post-processing by delivering them to the config thread;
+ * when post-processing is done, it will return the frame to the frame thread.
+ */
+static int msm_divert_frame(struct msm_sync *sync,
+ struct msm_vfe_resp *data,
+ struct msm_stats_event_ctrl *se)
+{
+ struct msm_pmem_region *region;
+ struct msm_postproc buf;
+ int rc;
+
+ pr_info("%s: preview PP sync->pp_mask %d\n", __func__, sync->pp_mask);
+
+ if (!(sync->pp_mask & PP_PREV)) {
+ pr_err("%s: diverting preview frame but not in PP_PREV!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_pmem_frame_ptov_lookup(sync,
+ data->phy.y_phy,
+ data->phy.cbcr_phy,
+ ®ion,
+ 0); /* vfe can still write to frame */
+ if (rc < 0) {
+ CDBG("%s: msm_pmem_frame_ptov_lookup failed\n", __func__);
+ return rc;
+ }
+
+ buf.fmain.buffer = (unsigned long)region->info.vaddr;
+ buf.fmain.y_off = region->info.y_off;
+ buf.fmain.cbcr_off = region->info.cbcr_off;
+ buf.fmain.fd = region->info.fd;
+
+ CDBG("%s: buf %ld fd %d\n",
+ __func__, buf.fmain.buffer,
+ buf.fmain.fd);
+ if (copy_to_user((void *)(se->stats_event.data),
+ &(buf.fmain),
+ sizeof(struct msm_frame))) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_divert_snapshot(struct msm_sync *sync,
+ struct msm_vfe_resp *data,
+ struct msm_stats_event_ctrl *se)
+{
+ struct msm_postproc buf;
+ struct msm_pmem_region region;
+
+ CDBG("%s: preview PP sync->pp_mask %d\n", __func__, sync->pp_mask);
+
+ if (!(sync->pp_mask & (PP_SNAP|PP_RAW_SNAP))) {
+ pr_err("%s: diverting snapshot but not in PP_SNAP!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(®ion, 0, sizeof(region));
+ buf.fmnum = msm_pmem_region_lookup(&sync->pmem_frames,
+ MSM_PMEM_MAINIMG,
+ ®ion, 1);
+ if (buf.fmnum == 1) {
+ buf.fmain.buffer = (uint32_t)region.info.vaddr;
+ buf.fmain.y_off = region.info.y_off;
+ buf.fmain.cbcr_off = region.info.cbcr_off;
+ buf.fmain.fd = region.info.fd;
+ } else {
+ if (buf.fmnum > 1)
+ pr_err("%s: MSM_PMEM_MAINIMG lookup found %d\n",
+ __func__, buf.fmnum);
+ buf.fmnum = msm_pmem_region_lookup(&sync->pmem_frames,
+ MSM_PMEM_RAW_MAINIMG,
+ ®ion, 1);
+ if (buf.fmnum == 1) {
+ buf.fmain.path = MSM_FRAME_PREV_2;
+ buf.fmain.buffer = (uint32_t)region.info.vaddr;
+ buf.fmain.fd = region.info.fd;
+ } else {
+ pr_err("%s: pmem lookup fail (found %d)\n",
+ __func__, buf.fmnum);
+ return -EIO;
+ }
+ }
+
+ CDBG("%s: snapshot copy_to_user!\n", __func__);
+ if (copy_to_user((void *)(se->stats_event.data), &buf, sizeof(buf))) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_get_stats(struct msm_sync *sync, void __user *arg)
+{
+ int timeout;
+ int rc = 0;
+
+ struct msm_stats_event_ctrl se;
+
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_ctrl_cmd *ctrl = NULL;
+ struct msm_vfe_resp *data = NULL;
+ struct msm_stats_buf stats;
+
+ if (copy_from_user(&se, arg,
+ sizeof(struct msm_stats_event_ctrl))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ timeout = (int)se.timeout_ms;
+
+ CDBG("%s: timeout %d\n", __func__, timeout);
+ rc = wait_event_interruptible_timeout(
+ sync->event_q.wait,
+ !list_empty_careful(&sync->event_q.list),
+ msecs_to_jiffies(timeout));
+ if (list_empty_careful(&sync->event_q.list)) {
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ if (rc < 0) {
+ pr_err("%s: error %d\n", __func__, rc);
+ return rc;
+ }
+ }
+ CDBG("%s: returned from wait: %d\n", __func__, rc);
+
+ rc = 0;
+
+ qcmd = msm_dequeue(&sync->event_q, list_config);
+ BUG_ON(!qcmd);
+
+ CDBG("%s: received from DSP %d\n", __func__, qcmd->type);
+
+ switch (qcmd->type) {
+ case MSM_CAM_Q_VFE_EVT:
+ case MSM_CAM_Q_VFE_MSG:
+ data = (struct msm_vfe_resp *)(qcmd->command);
+
+ /* adsp event and message */
+ se.resptype = MSM_CAM_RESP_STAT_EVT_MSG;
+
+ /* 0 - msg from aDSP, 1 - event from mARM */
+ se.stats_event.type = data->evt_msg.type;
+ se.stats_event.msg_id = data->evt_msg.msg_id;
+ se.stats_event.len = data->evt_msg.len;
+
+ CDBG("%s: qcmd->type %d length %d msd_id %d\n", __func__,
+ qcmd->type,
+ se.stats_event.len,
+ se.stats_event.msg_id);
+
+ if (data->type == VFE_MSG_STATS_AF ||
+ data->type == VFE_MSG_STATS_WE ||
+ (data->type >= VFE_MSG_STATS_AEC &&
+ data->type <= VFE_MSG_STATS_SKIN)) {
+ /* the check above includes all stats type. */
+ stats.buffer =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->phy.sbuf_phy,
+ &(stats.fd));
+ if (!stats.buffer) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ if (copy_to_user((void *)(se.stats_event.data),
+ &stats,
+ sizeof(struct msm_stats_buf))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto failure;
+ }
+ } else if ((data->evt_msg.len > 0) &&
+ (data->type == VFE_MSG_GENERAL)) {
+ if (copy_to_user((void *)(se.stats_event.data),
+ data->evt_msg.data,
+ data->evt_msg.len)) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto failure;
+ }
+ } else {
+ if ((sync->pp_mask & PP_PREV) &&
+ (data->type == VFE_MSG_OUTPUT1 ||
+ data->type == VFE_MSG_OUTPUT2 ||
+ data->type == VFE_MSG_OUTPUT_P))
+ rc = msm_divert_frame(sync, data, &se);
+ else if ((sync->pp_mask & (PP_SNAP|PP_RAW_SNAP)) &&
+ (data->type == VFE_MSG_SNAPSHOT ||
+ data->type == VFE_MSG_OUTPUT_S))
+ rc = msm_divert_snapshot(sync,
+ data, &se);
+ }
+ break;
+
+ case MSM_CAM_Q_CTRL:
+ /* control command from control thread */
+ ctrl = (struct msm_ctrl_cmd *)(qcmd->command);
+
+ CDBG("%s: qcmd->type %d length %d\n", __func__,
+ qcmd->type, ctrl->length);
+
+ if (ctrl->length > 0) {
+ if (copy_to_user((void *)(se.ctrl_cmd.value),
+ ctrl->value,
+ ctrl->length)) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto failure;
+ }
+ }
+
+ se.resptype = MSM_CAM_RESP_CTRL;
+
+ /* what to control */
+ se.ctrl_cmd.type = ctrl->type;
+ se.ctrl_cmd.length = ctrl->length;
+ se.ctrl_cmd.resp_fd = ctrl->resp_fd;
+ break;
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ case MSM_CAM_Q_V4L2_REQ:
+ /* control command from v4l2 client */
+ ctrl = (struct msm_ctrl_cmd *)(qcmd->command);
+
+ CDBG("%s: qcmd->type %d len %d\n", __func__, qcmd->type, ctrl->length);
+
+ if (ctrl->length > 0) {
+ if (copy_to_user((void *)(se.ctrl_cmd.value),
+ ctrl->value, ctrl->length)) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto failure;
+ }
+ }
+
+ /* 2 tells config thread this is v4l2 request */
+ se.resptype = MSM_CAM_RESP_V4L2;
+
+ /* what to control */
+ se.ctrl_cmd.type = ctrl->type;
+ se.ctrl_cmd.length = ctrl->length;
+ break;
+#endif
+
+ default:
+ rc = -EFAULT;
+ goto failure;
+ } /* switch qcmd->type */
+
+ if (copy_to_user((void *)arg, &se, sizeof(se))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto failure;
+ }
+
+failure:
+ free_qcmd(qcmd);
+
+ CDBG("%s: %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm,
+ void __user *arg)
+{
+ void __user *uptr;
+ struct msm_queue_cmd *qcmd = &ctrl_pmsm->qcmd;
+ struct msm_ctrl_cmd *command = &ctrl_pmsm->ctrl;
+
+ if (copy_from_user(command, arg, sizeof(*command))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ qcmd->on_heap = 0;
+ qcmd->command = command;
+ uptr = command->value;
+
+ if (command->length > 0) {
+ command->value = ctrl_pmsm->ctrl_data;
+ if (command->length > sizeof(ctrl_pmsm->ctrl_data)) {
+ pr_err("%s: user data %d is too big (max %d)\n",
+ __func__, command->length,
+ sizeof(ctrl_pmsm->ctrl_data));
+ return -EINVAL;
+ }
+ if (copy_from_user(command->value,
+ uptr,
+ command->length)) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ } else
+ command->value = NULL;
+
+ CDBG("%s: end\n", __func__);
+
+ /* wake up control thread */
+ msm_enqueue(&ctrl_pmsm->ctrl_q, &qcmd->list_control);
+
+ return 0;
+}
+
+static int msm_config_vfe(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_vfe_cfg_cmd cfgcmd;
+ struct msm_pmem_region region[8];
+ struct axidata axi_data;
+
+ if (!sync->vfefn.vfe_config) {
+ pr_err("%s: no vfe_config!\n", __func__);
+ return -EIO;
+ }
+
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ memset(&axi_data, 0, sizeof(axi_data));
+
+ CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
+
+ switch (cfgcmd.cmd_type) {
+ case CMD_STATS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AF, ®ion[axi_data.bufnum1],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1 || !axi_data.bufnum2) {
+ pr_err("%s: pmem region lookup error\n", __func__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AF_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AF, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AEC_AWB_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AEC_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AWB_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+
+ case CMD_STATS_IHIST_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_IHIST, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_STATS_RS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_RS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_STATS_CS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_CS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_GENERAL:
+ case CMD_STATS_DISABLE:
+ return sync->vfefn.vfe_config(&cfgcmd, NULL);
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd.cmd_type);
+ }
+
+ return -EINVAL;
+}
+
+static int msm_frame_axi_cfg(struct msm_sync *sync,
+ struct msm_vfe_cfg_cmd *cfgcmd)
+{
+ int rc = -EIO;
+ struct axidata axi_data;
+ void *data = &axi_data;
+ struct msm_pmem_region region[8];
+ int pmem_type;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+
+ switch (cfgcmd->cmd_type) {
+ case CMD_AXI_CFG_OUT1:
+ pmem_type = MSM_PMEM_OUTPUT1;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT2:
+ pmem_type = MSM_PMEM_OUTPUT2;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error (empty %d)\n",
+ __func__, __LINE__,
+ hlist_empty(&sync->pmem_frames));
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_PREVIEW:
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error (empty %d)\n",
+ __func__, __LINE__,
+ hlist_empty(&sync->pmem_frames));
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO:
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_VIDEO;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (8-(axi_data.bufnum1)));
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_O1_AND_O2:
+ pmem_type = MSM_PMEM_OUTPUT1;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_OUTPUT2;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (8-(axi_data.bufnum1)));
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP:
+ case CMD_AXI_CFG_SNAP_O1_AND_O2:
+ pmem_type = MSM_PMEM_THUMBNAIL;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (8-(axi_data.bufnum1)));
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_RAW_PICT_AXI_CFG:
+ pmem_type = MSM_PMEM_RAW_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_GENERAL:
+ data = NULL;
+ break;
+
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ axi_data.region = ®ion[0];
+
+ /* send the AXI configuration command to driver */
+ if (sync->vfefn.vfe_config)
+ rc = sync->vfefn.vfe_config(cfgcmd, data);
+
+ return rc;
+}
+
+static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg)
+{
+ int rc = 0;
+ struct msm_camsensor_info info;
+ struct msm_camera_sensor_info *sdata;
+
+ if (copy_from_user(&info,
+ arg,
+ sizeof(struct msm_camsensor_info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ sdata = sync->pdev->dev.platform_data;
+ CDBG("%s: sensor_name %s\n", __func__, sdata->sensor_name);
+
+ memcpy(&info.name[0],
+ sdata->sensor_name,
+ MAX_SENSOR_NAME);
+ info.flash_enabled = !!sdata->camera_flash;
+
+ /* copy back to user space */
+ if (copy_to_user((void *)arg,
+ &info,
+ sizeof(struct msm_camsensor_info))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ }
+
+ return rc;
+}
+
+static int __msm_put_frame_buf(struct msm_sync *sync,
+ struct msm_frame *pb)
+{
+ unsigned long pphy;
+ struct msm_vfe_cfg_cmd cfgcmd;
+
+ int rc = -EIO;
+
+ pphy = msm_pmem_frame_vtop_lookup(sync,
+ pb->buffer,
+ pb->y_off, pb->cbcr_off, pb->fd);
+
+ if (pphy != 0) {
+ CDBG("%s: rel: vaddr %lx, paddr %lx\n",
+ __func__,
+ pb->buffer, pphy);
+ cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE;
+ cfgcmd.value = (void *)pb;
+ if (sync->vfefn.vfe_config)
+ rc = sync->vfefn.vfe_config(&cfgcmd, &pphy);
+ } else {
+ pr_err("%s: msm_pmem_frame_vtop_lookup failed. "
+ "buffer=0x%lx, y_off=%d, cbcr_off=%d, fd=%d\n",
+ __func__, pb->buffer, pb->y_off, pb->cbcr_off, pb->fd);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int msm_put_frame_buffer(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_frame buf;
+
+ if (copy_from_user(&buf,
+ arg,
+ sizeof(struct msm_frame))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ return __msm_put_frame_buf(sync, &buf);
+}
+
+static int __msm_register_pmem(struct msm_sync *sync,
+ struct msm_pmem_info *pinfo)
+{
+ int rc = 0;
+
+ switch (pinfo->type) {
+ case MSM_PMEM_OUTPUT1:
+ case MSM_PMEM_OUTPUT2:
+ case MSM_PMEM_VIDEO:
+ case MSM_PMEM_PREVIEW:
+ case MSM_PMEM_THUMBNAIL:
+ case MSM_PMEM_MAINIMG:
+ case MSM_PMEM_RAW_MAINIMG:
+ rc = msm_pmem_table_add(&sync->pmem_frames, pinfo);
+ break;
+
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ case MSM_PMEM_AEC:
+ case MSM_PMEM_AWB:
+ case MSM_PMEM_RS:
+ case MSM_PMEM_CS:
+ case MSM_PMEM_IHIST:
+ case MSM_PMEM_SKIN:
+
+ rc = msm_pmem_table_add(&sync->pmem_stats, pinfo);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_register_pmem(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_pmem_info info;
+
+ if (copy_from_user(&info, arg, sizeof(info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ return __msm_register_pmem(sync, &info);
+}
+
+static int msm_stats_axi_cfg(struct msm_sync *sync,
+ struct msm_vfe_cfg_cmd *cfgcmd)
+{
+ int rc = -EIO;
+ struct axidata axi_data;
+ void *data = &axi_data;
+
+ struct msm_pmem_region region[3];
+ int pmem_type = MSM_PMEM_MAX;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+
+ switch (cfgcmd->cmd_type) {
+ case CMD_STATS_AXI_CFG:
+ pmem_type = MSM_PMEM_AEC_AWB;
+ break;
+ case CMD_STATS_AF_AXI_CFG:
+ pmem_type = MSM_PMEM_AF;
+ break;
+ case CMD_GENERAL:
+ data = NULL;
+ break;
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ if (cfgcmd->cmd_type != CMD_GENERAL) {
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats, pmem_type,
+ ®ion[0], NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ }
+
+ /* send the AEC/AWB STATS configuration command to driver */
+ if (sync->vfefn.vfe_config)
+ rc = sync->vfefn.vfe_config(cfgcmd, &axi_data);
+
+ return rc;
+}
+
+static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg)
+{
+ int rc = -EIO;
+
+ struct msm_stats_buf buf;
+ unsigned long pphy;
+ struct msm_vfe_cfg_cmd cfgcmd;
+
+ if (copy_from_user(&buf, arg,
+ sizeof(struct msm_stats_buf))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ CDBG("%s\n", __func__);
+ pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd);
+
+ if (pphy != 0) {
+ if (buf.type == STAT_AEAW)
+ cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
+ else if (buf.type == STAT_AF)
+ cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE;
+ else if (buf.type == STAT_AEC)
+ cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE;
+ else if (buf.type == STAT_AWB)
+ cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE;
+ else if (buf.type == STAT_IHIST)
+ cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE;
+ else if (buf.type == STAT_RS)
+ cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE;
+ else if (buf.type == STAT_CS)
+ cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
+
+ else {
+ pr_err("%s: invalid buf type %d\n",
+ __func__,
+ buf.type);
+ rc = -EINVAL;
+ goto put_done;
+ }
+
+ cfgcmd.value = (void *)&buf;
+
+ if (sync->vfefn.vfe_config) {
+ rc = sync->vfefn.vfe_config(&cfgcmd, &pphy);
+ if (rc < 0)
+ pr_err("%s: vfe_config error %d\n",
+ __func__, rc);
+ } else
+ pr_err("%s: vfe_config is NULL\n", __func__);
+ } else {
+ pr_err("%s: NULL physical address\n", __func__);
+ rc = -EINVAL;
+ }
+
+put_done:
+ return rc;
+}
+
+static int msm_axi_config(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_vfe_cfg_cmd cfgcmd;
+
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ switch (cfgcmd.cmd_type) {
+ case CMD_AXI_CFG_OUT1:
+ case CMD_AXI_CFG_OUT2:
+ case CMD_AXI_CFG_O1_AND_O2:
+ case CMD_AXI_CFG_SNAP_O1_AND_O2:
+ case CMD_AXI_CFG_VIDEO:
+ case CMD_AXI_CFG_PREVIEW:
+ case CMD_AXI_CFG_SNAP:
+ case CMD_RAW_PICT_AXI_CFG:
+ return msm_frame_axi_cfg(sync, &cfgcmd);
+
+ case CMD_STATS_AXI_CFG:
+ case CMD_STATS_AF_AXI_CFG:
+ return msm_stats_axi_cfg(sync, &cfgcmd);
+
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__,
+ cfgcmd.cmd_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl)
+{
+ int rc = 0;
+ int tm;
+
+ struct msm_queue_cmd *qcmd = NULL;
+
+ tm = (int)ctrl->timeout_ms;
+
+ rc = wait_event_interruptible_timeout(
+ sync->pict_q.wait,
+ !list_empty_careful(
+ &sync->pict_q.list) || sync->get_pic_abort,
+ msecs_to_jiffies(tm));
+
+ if (sync->get_pic_abort == 1) {
+ sync->get_pic_abort = 0;
+ return -ENODATA;
+ }
+ if (list_empty_careful(&sync->pict_q.list)) {
+ if (rc == 0)
+ return -ETIMEDOUT;
+ if (rc < 0) {
+ pr_err("%s: rc %d\n", __func__, rc);
+ return rc;
+ }
+ }
+
+ rc = 0;
+
+ qcmd = msm_dequeue(&sync->pict_q, list_pict);
+ BUG_ON(!qcmd);
+
+ if (qcmd->command != NULL) {
+ struct msm_ctrl_cmd *q =
+ (struct msm_ctrl_cmd *)qcmd->command;
+ ctrl->type = q->type;
+ ctrl->status = q->status;
+ } else {
+ ctrl->type = -1;
+ ctrl->status = -1;
+ }
+
+ free_qcmd(qcmd);
+
+ return rc;
+}
+
+static int msm_get_pic(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_ctrl_cmd ctrlcmd;
+ struct msm_pmem_region *pic_pmem_region = NULL, *region;
+ struct hlist_node *node, *n;
+ int rc;
+ unsigned long end;
+ int cline_mask;
+
+ if (copy_from_user(&ctrlcmd,
+ arg,
+ sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ rc = __msm_get_pic(sync, &ctrlcmd);
+ if (rc < 0)
+ return rc;
+
+ if (sync->croplen) {
+ if (ctrlcmd.length != sync->croplen) {
+ pr_err("%s: invalid len %d < %d\n",
+ __func__,
+ ctrlcmd.length,
+ sync->croplen);
+ return -EINVAL;
+ }
+ if (copy_to_user(ctrlcmd.value,
+ sync->cropinfo,
+ sync->croplen)) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ }
+
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ if (region->info.vfe_can_write &&
+ (region->info.type == MSM_PMEM_MAINIMG ||
+ region->info.type == MSM_PMEM_RAW_MAINIMG)) {
+ pic_pmem_region = region;
+ break;
+ }
+ }
+
+ if (!pic_pmem_region) {
+ pr_err("%s pmem region lookup error\n", __func__);
+ return -EIO;
+ }
+ cline_mask = cache_line_size() - 1;
+ end = pic_pmem_region->kvaddr + pic_pmem_region->len;
+ end = (end + cline_mask) & ~cline_mask;
+
+ pr_info("%s: flushing cache for [%08lx, %08lx)\n",
+ __func__,
+ pic_pmem_region->kvaddr, end);
+
+ /* HACK: Invalidate buffer */
+ dmac_unmap_area((void*)pic_pmem_region->kvaddr, pic_pmem_region->len,
+ DMA_FROM_DEVICE);
+ pic_pmem_region->info.vfe_can_write = 0;
+
+ CDBG("%s: copy snapshot frame to user\n", __func__);
+ if (copy_to_user((void *)arg,
+ &ctrlcmd,
+ sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int msm_set_crop(struct msm_sync *sync, void __user *arg)
+{
+ struct crop_info crop;
+
+ if (copy_from_user(&crop,
+ arg,
+ sizeof(struct crop_info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ if (!sync->croplen) {
+ sync->cropinfo = kmalloc(crop.len, GFP_KERNEL);
+ if (!sync->cropinfo)
+ return -ENOMEM;
+ } else if (sync->croplen < crop.len)
+ return -EINVAL;
+
+ if (copy_from_user(sync->cropinfo,
+ crop.info,
+ crop.len)) {
+ ERR_COPY_FROM_USER();
+ kfree(sync->cropinfo);
+ return -EFAULT;
+ }
+
+ sync->croplen = crop.len;
+
+ return 0;
+}
+
+static int msm_pp_grab(struct msm_sync *sync, void __user *arg)
+{
+ uint32_t enable;
+ if (copy_from_user(&enable, arg, sizeof(enable))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ } else {
+ enable &= PP_MASK;
+ if (enable & (enable - 1)) {
+ pr_err("%s: error: more than one PP request!\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (sync->pp_mask) {
+ pr_err("%s: postproc %x is already enabled\n",
+ __func__, sync->pp_mask & enable);
+ return -EINVAL;
+ }
+
+ CDBG("%s: sync->pp_mask %d enable %d\n", __func__,
+ sync->pp_mask, enable);
+ sync->pp_mask |= enable;
+ }
+
+ return 0;
+}
+
+static int msm_pp_release(struct msm_sync *sync, void __user *arg)
+{
+ uint32_t mask;
+ if (copy_from_user(&mask, arg, sizeof(uint32_t))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ mask &= PP_MASK;
+ if (!(sync->pp_mask & mask)) {
+ pr_warning("%s: pp not in progress for %x\n", __func__,
+ mask);
+ return -EINVAL;
+ }
+
+ if ((mask & PP_PREV) && (sync->pp_mask & PP_PREV)) {
+ if (!sync->pp_prev) {
+ pr_err("%s: no preview frame to deliver!\n", __func__);
+ return -EINVAL;
+ }
+ pr_info("%s: delivering pp_prev\n", __func__);
+
+ msm_enqueue(&sync->frame_q, &sync->pp_prev->list_frame);
+ sync->pp_prev = NULL;
+ goto done;
+ }
+
+ if (((mask & PP_SNAP) && (sync->pp_mask & PP_SNAP)) ||
+ ((mask & PP_RAW_SNAP) &&
+ (sync->pp_mask & PP_RAW_SNAP))) {
+ if (!sync->pp_snap) {
+ pr_err("%s: no snapshot to deliver!\n", __func__);
+ return -EINVAL;
+ }
+ pr_info("%s: delivering pp_snap\n", __func__);
+ msm_enqueue(&sync->pict_q, &sync->pp_snap->list_pict);
+ sync->pp_snap = NULL;
+ }
+
+done:
+ sync->pp_mask = 0;
+ return 0;
+}
+
+static long msm_ioctl_common(struct msm_device *pmsm,
+ unsigned int cmd,
+ void __user *argp)
+{
+ CDBG("%s\n", __func__);
+ switch (cmd) {
+ case MSM_CAM_IOCTL_REGISTER_PMEM:
+ return msm_register_pmem(pmsm->sync, argp);
+ case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+ return msm_pmem_table_del(pmsm->sync, argp);
+ default:
+ return -EINVAL;
+ }
+}
+
+int msm_camera_flash(struct msm_sync *sync, int level)
+{
+ int flash_level;
+
+ if (!sync->sdata->camera_flash) {
+ pr_err("%s: camera flash is not supported.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!sync->sdata->num_flash_levels) {
+ pr_err("%s: no flash levels.\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (level) {
+ case MSM_CAMERA_LED_HIGH:
+ flash_level = sync->sdata->num_flash_levels - 1;
+ break;
+ case MSM_CAMERA_LED_LOW:
+ flash_level = sync->sdata->num_flash_levels / 2;
+ break;
+ case MSM_CAMERA_LED_OFF:
+ flash_level = 0;
+ break;
+ default:
+ pr_err("%s: invalid flash level %d.\n", __func__, level);
+ return -EINVAL;
+ }
+
+ return sync->sdata->camera_flash(level);
+}
+
+static long msm_ioctl_config(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct msm_device *pmsm = filep->private_data;
+
+ CDBG("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+ rc = msm_get_sensor_info(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_CONFIG_VFE:
+ /* Coming from config thread for update */
+ rc = msm_config_vfe(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_GET_STATS:
+ /* Coming from config thread wait
+ * for vfe statistics and control requests */
+ rc = msm_get_stats(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_ENABLE_VFE:
+ /* This request comes from control thread:
+ * enable either QCAMTASK or VFETASK */
+ rc = msm_enable_vfe(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_DISABLE_VFE:
+ /* This request comes from control thread:
+ * disable either QCAMTASK or VFETASK */
+ rc = msm_disable_vfe(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_VFE_APPS_RESET:
+ msm_camio_vfe_blk_reset();
+ rc = 0;
+ break;
+
+ case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER:
+ rc = msm_put_stats_buffer(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_AXI_CONFIG:
+ rc = msm_axi_config(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_SET_CROP:
+ rc = msm_set_crop(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_PP:
+ /* Grab one preview frame or one snapshot
+ * frame.
+ */
+ rc = msm_pp_grab(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_PP_DONE:
+ /* Release the preview of snapshot frame
+ * that was grabbed.
+ */
+ rc = msm_pp_release(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_SENSOR_IO_CFG:
+ rc = pmsm->sync->sctrl.s_config(argp);
+ break;
+
+ case MSM_CAM_IOCTL_FLASH_LED_CFG: {
+ uint32_t led_state;
+ if (copy_from_user(&led_state, argp, sizeof(led_state))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else
+ rc = msm_camera_flash(pmsm->sync, led_state);
+ break;
+ }
+
+ case MSM_CAM_IOCTL_ENABLE_OUTPUT_IND: {
+ uint32_t enable;
+ if (copy_from_user(&enable, argp, sizeof(enable))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ break;
+ }
+ pr_info("%s: copying all preview frames to config: %d\n",
+ __func__, enable);
+ pmsm->sync->report_preview_to_config = enable;
+ rc = 0;
+ break;
+ }
+
+ default:
+ rc = msm_ioctl_common(pmsm, cmd, argp);
+ break;
+ }
+
+ CDBG("%s: cmd %d DONE\n", __func__, _IOC_NR(cmd));
+ return rc;
+}
+
+static int msm_unblock_poll_frame(struct msm_sync *);
+
+static long msm_ioctl_frame(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct msm_device *pmsm = filep->private_data;
+
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_GETFRAME:
+ /* Coming from frame thread to get frame
+ * after SELECT is done */
+ rc = msm_get_frame(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER:
+ rc = msm_put_frame_buffer(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME:
+ rc = msm_unblock_poll_frame(pmsm->sync);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+
+static long msm_ioctl_control(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct msm_control_device *ctrl_pmsm = filep->private_data;
+ struct msm_device *pmsm = ctrl_pmsm->pmsm;
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_CTRL_COMMAND:
+ /* Coming from control thread, may need to wait for
+ * command status */
+ rc = msm_control(ctrl_pmsm, 1, argp);
+ break;
+ case MSM_CAM_IOCTL_CTRL_COMMAND_2:
+ /* Sends a message, returns immediately */
+ rc = msm_control(ctrl_pmsm, 0, argp);
+ break;
+ case MSM_CAM_IOCTL_CTRL_CMD_DONE:
+ /* Config thread calls the control thread to notify it
+ * of the result of a MSM_CAM_IOCTL_CTRL_COMMAND.
+ */
+ rc = msm_ctrl_cmd_done(ctrl_pmsm, argp);
+ break;
+ case MSM_CAM_IOCTL_GET_PICTURE:
+ rc = msm_get_pic(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+ rc = msm_get_sensor_info(pmsm->sync, argp);
+ break;
+ default:
+ rc = msm_ioctl_common(pmsm, cmd, argp);
+ break;
+ }
+
+ return rc;
+}
+
+static int __msm_release(struct msm_sync *sync)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *hnode;
+ struct hlist_node *n;
+
+ mutex_lock(&sync->lock);
+ if (sync->opencnt)
+ sync->opencnt--;
+
+ if (!sync->opencnt) {
+ /* need to clean up system resource */
+ if (sync->vfefn.vfe_release)
+ sync->vfefn.vfe_release(sync->pdev);
+
+ kfree(sync->cropinfo);
+ sync->cropinfo = NULL;
+ sync->croplen = 0;
+
+ hlist_for_each_entry_safe(region, hnode, n,
+ &sync->pmem_frames, list) {
+ hlist_del(hnode);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+
+ hlist_for_each_entry_safe(region, hnode, n,
+ &sync->pmem_stats, list) {
+ hlist_del(hnode);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ msm_queue_drain(&sync->pict_q, list_pict);
+
+ wake_unlock(&sync->wake_lock);
+
+ sync->apps_id = NULL;
+ CDBG("%s: completed\n", __func__);
+ }
+ mutex_unlock(&sync->lock);
+
+ return 0;
+}
+
+static int msm_release_config(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_device *pmsm = filep->private_data;
+ CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&pmsm->sync->event_q, list_config);
+ atomic_set(&pmsm->opened, 0);
+ }
+ return rc;
+}
+
+static int msm_release_control(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_control_device *ctrl_pmsm = filep->private_data;
+ struct msm_device *pmsm = ctrl_pmsm->pmsm;
+ CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control);
+ kfree(ctrl_pmsm);
+ }
+ return rc;
+}
+
+static int msm_release_frame(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_device *pmsm = filep->private_data;
+ CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&pmsm->sync->frame_q, list_frame);
+ atomic_set(&pmsm->opened, 0);
+ }
+ return rc;
+}
+
+static int msm_unblock_poll_frame(struct msm_sync *sync)
+{
+ unsigned long flags;
+ CDBG("%s\n", __func__);
+ spin_lock_irqsave(&sync->frame_q.lock, flags);
+ sync->unblock_poll_frame = 1;
+ wake_up(&sync->frame_q.wait);
+ spin_unlock_irqrestore(&sync->frame_q.lock, flags);
+ return 0;
+}
+
+static unsigned int __msm_poll_frame(struct msm_sync *sync,
+ struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ poll_wait(filep, &sync->frame_q.wait, pll_table);
+
+ spin_lock_irqsave(&sync->frame_q.lock, flags);
+ if (!list_empty_careful(&sync->frame_q.list))
+ /* frame ready */
+ rc = POLLIN | POLLRDNORM;
+ if (sync->unblock_poll_frame) {
+ CDBG("%s: sync->unblock_poll_frame is true\n", __func__);
+ rc |= POLLPRI;
+ sync->unblock_poll_frame = 0;
+ }
+ spin_unlock_irqrestore(&sync->frame_q.lock, flags);
+
+ return rc;
+}
+
+static unsigned int msm_poll_frame(struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ struct msm_device *pmsm = filep->private_data;
+ return __msm_poll_frame(pmsm->sync, filep, pll_table);
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+static void *msm_vfe_sync_alloc(int size,
+ void *syncdata __attribute__((unused)),
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+ if (qcmd) {
+ qcmd->on_heap = 1;
+ return qcmd + 1;
+ }
+ return NULL;
+}
+
+static void msm_vfe_sync_free(void *ptr)
+{
+ if (ptr) {
+ struct msm_queue_cmd *qcmd =
+ (struct msm_queue_cmd *)ptr;
+ qcmd--;
+ if (qcmd->on_heap)
+ kfree(qcmd);
+ }
+}
+
+/*
+ * This function may execute in interrupt context.
+ */
+
+static void msm_vfe_sync(struct msm_vfe_resp *vdata,
+ enum msm_queue qtype, void *syncdata,
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_sync *sync = (struct msm_sync *)syncdata;
+
+ if (!sync) {
+ pr_err("%s: no context in dsp callback.\n", __func__);
+ return;
+ }
+
+ if (!sync->opencnt) {
+ pr_err("%s: SPURIOUS INTERRUPT\n", __func__);
+ return;
+ }
+
+ qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+ qcmd->type = qtype;
+ qcmd->command = vdata;
+
+ if (qtype != MSM_CAM_Q_VFE_MSG)
+ goto for_config;
+
+ CDBG("%s: vdata->type %d\n", __func__, vdata->type);
+ switch (vdata->type) {
+ case VFE_MSG_OUTPUT1:
+ case VFE_MSG_OUTPUT2:
+ case VFE_MSG_OUTPUT_P:
+ if (sync->pp_mask & PP_PREV) {
+ CDBG("%s: PP_PREV in progress: phy_y %x phy_cbcr %x\n",
+ __func__,
+ vdata->phy.y_phy,
+ vdata->phy.cbcr_phy);
+ if (sync->pp_prev)
+ pr_warning("%s: overwriting pp_prev!\n",
+ __func__);
+ pr_info("%s: sending preview to config\n", __func__);
+ sync->pp_prev = qcmd;
+ break;
+ }
+
+ if (sync->report_preview_to_config) {
+ if (qcmd->on_heap)
+ qcmd->on_heap++;
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ break;
+ }
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ return;
+
+ case VFE_MSG_OUTPUT_V:
+ CDBG("%s: msm_enqueue video frame_q\n", __func__);
+ if (qcmd->on_heap)
+ qcmd->on_heap++;
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ break;
+
+ case VFE_MSG_SNAPSHOT:
+ if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) {
+ CDBG("%s: PP_SNAP in progress: pp_mask %x\n",
+ __func__, sync->pp_mask);
+ if (sync->pp_snap)
+ pr_warning("%s: overwriting pp_snap!\n",
+ __func__);
+ pr_info("%s: sending snapshot to config\n", __func__);
+ sync->pp_snap = qcmd;
+ break;
+ }
+
+ if (qcmd->on_heap)
+ qcmd->on_heap++;
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ break;
+
+ case VFE_MSG_STATS_AWB:
+ CDBG("%s: qtype %d, AWB stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_AEC:
+ CDBG("%s: qtype %d, AEC stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_IHIST:
+ CDBG("%s: qtype %d, ihist stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_RS:
+ CDBG("%s: qtype %d, rs stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_CS:
+ CDBG("%s: qtype %d, cs stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+
+ case VFE_MSG_GENERAL:
+ CDBG("%s: qtype %d, general msg, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ default:
+ CDBG("%s: qtype %d not handled\n", __func__, vdata->type);
+ /* fall through, send to config. */
+ }
+
+for_config:
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+}
+
+static struct msm_vfe_callback msm_vfe_s = {
+ .vfe_resp = msm_vfe_sync,
+ .vfe_alloc = msm_vfe_sync_alloc,
+ .vfe_free = msm_vfe_sync_free,
+ .flash_ctrl = msm_camera_flash,
+};
+
+static int __msm_open(struct msm_sync *sync, const char *const apps_id)
+{
+ int rc = 0;
+
+ mutex_lock(&sync->lock);
+ if (sync->apps_id && strcmp(sync->apps_id, apps_id)) {
+ pr_err("%s(%s): sensor %s is already opened for %s\n",
+ __func__,
+ apps_id,
+ sync->sdata->sensor_name,
+ sync->apps_id);
+ rc = -EBUSY;
+ goto msm_open_done;
+ }
+
+ sync->apps_id = apps_id;
+
+ if (!sync->opencnt) {
+ wake_lock(&sync->wake_lock);
+
+ msm_camvfe_fn_init(&sync->vfefn, sync);
+ if (sync->vfefn.vfe_init) {
+ sync->get_pic_abort = 0;
+ rc = sync->vfefn.vfe_init(&msm_vfe_s,
+ sync->pdev);
+ if (rc < 0) {
+ pr_err("%s: vfe_init failed at %d\n",
+ __func__, rc);
+ goto msm_open_done;
+ }
+ rc = sync->sctrl.s_init(sync->sdata);
+ if (rc < 0) {
+ pr_err("%s: sensor init failed: %d\n",
+ __func__, rc);
+ goto msm_open_done;
+ }
+ } else {
+ pr_err("%s: no sensor init func\n", __func__);
+ rc = -ENODEV;
+ goto msm_open_done;
+ }
+
+ if (rc >= 0) {
+ INIT_HLIST_HEAD(&sync->pmem_frames);
+ INIT_HLIST_HEAD(&sync->pmem_stats);
+ sync->unblock_poll_frame = 0;
+ }
+ }
+ sync->opencnt++;
+
+msm_open_done:
+ mutex_unlock(&sync->lock);
+ return rc;
+}
+
+static int msm_open_common(struct inode *inode, struct file *filep,
+ int once)
+{
+ int rc;
+ struct msm_device *pmsm =
+ container_of(inode->i_cdev, struct msm_device, cdev);
+
+ CDBG("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name);
+
+ if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) {
+ pr_err("%s: %s is already opened.\n",
+ __func__,
+ filep->f_path.dentry->d_name.name);
+ return -EBUSY;
+ }
+
+ rc = nonseekable_open(inode, filep);
+ if (rc < 0) {
+ pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = __msm_open(pmsm->sync, MSM_APPS_ID_PROP);
+ if (rc < 0)
+ return rc;
+
+ filep->private_data = pmsm;
+
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_open(struct inode *inode, struct file *filep)
+{
+ return msm_open_common(inode, filep, 1);
+}
+
+static int msm_open_control(struct inode *inode, struct file *filep)
+{
+ int rc;
+
+ struct msm_control_device *ctrl_pmsm =
+ kmalloc(sizeof(struct msm_control_device), GFP_KERNEL);
+ if (!ctrl_pmsm)
+ return -ENOMEM;
+
+ rc = msm_open_common(inode, filep, 0);
+ if (rc < 0) {
+ kfree(ctrl_pmsm);
+ return rc;
+ }
+
+ ctrl_pmsm->pmsm = filep->private_data;
+ filep->private_data = ctrl_pmsm;
+
+ msm_queue_init(&ctrl_pmsm->ctrl_q, "control");
+
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+static int __msm_v4l2_control(struct msm_sync *sync,
+ struct msm_ctrl_cmd *out)
+{
+ int rc = 0;
+
+ struct msm_queue_cmd *qcmd = NULL, *rcmd = NULL;
+ struct msm_ctrl_cmd *ctrl;
+ struct msm_device_queue FIXME;
+
+ /* wake up config thread, 4 is for V4L2 application */
+ qcmd = kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!qcmd) {
+ pr_err("%s: cannot allocate buffer\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+ qcmd->type = MSM_CAM_Q_V4L2_REQ;
+ qcmd->command = out;
+ qcmd->on_heap = 1;
+
+ rcmd = __msm_control(sync, &FIXME, qcmd, out->timeout_ms);
+ if (IS_ERR(rcmd)) {
+ rc = PTR_ERR(rcmd);
+ goto end;
+ }
+
+ ctrl = (struct msm_ctrl_cmd *)(rcmd->command);
+ /* FIXME: we should just set out->length = ctrl->length; */
+ BUG_ON(out->length < ctrl->length);
+ memcpy(out->value, ctrl->value, ctrl->length);
+
+end:
+ free_qcmd(rcmd);
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+#endif
+
+static const struct file_operations msm_fops_config = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .unlocked_ioctl = msm_ioctl_config,
+ .release = msm_release_config,
+};
+
+static const struct file_operations msm_fops_control = {
+ .owner = THIS_MODULE,
+ .open = msm_open_control,
+ .unlocked_ioctl = msm_ioctl_control,
+ .release = msm_release_control,
+};
+
+static const struct file_operations msm_fops_frame = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .unlocked_ioctl = msm_ioctl_frame,
+ .release = msm_release_frame,
+ .poll = msm_poll_frame,
+};
+
+static int msm_setup_cdev(struct msm_device *msm,
+ int node,
+ dev_t devno,
+ const char *suffix,
+ const struct file_operations *fops)
+{
+ int rc = -ENODEV;
+
+ struct device *device =
+ device_create(msm_class, NULL,
+ devno, NULL,
+ "%s%d", suffix, node);
+
+ if (IS_ERR(device)) {
+ rc = PTR_ERR(device);
+ pr_err("%s: error creating device: %d\n", __func__, rc);
+ return rc;
+ }
+
+ cdev_init(&msm->cdev, fops);
+ msm->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&msm->cdev, devno, 1);
+ if (rc < 0) {
+ pr_err("%s: error adding cdev: %d\n", __func__, rc);
+ device_destroy(msm_class, devno);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_tear_down_cdev(struct msm_device *msm, dev_t devno)
+{
+ cdev_del(&msm->cdev);
+ device_destroy(msm_class, devno);
+ return 0;
+}
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+int msm_v4l2_register(struct msm_v4l2_driver *drv)
+{
+ /* FIXME: support multiple sensors */
+ if (list_empty(&msm_sensors))
+ return -ENODEV;
+
+ drv->sync = list_first_entry(&msm_sensors, struct msm_sync, list);
+ drv->open = __msm_open;
+ drv->release = __msm_release;
+ drv->ctrl = __msm_v4l2_control;
+ drv->reg_pmem = __msm_register_pmem;
+ drv->get_frame = __msm_get_frame;
+ drv->put_frame = __msm_put_frame_buf;
+ drv->get_pict = __msm_get_pic;
+ drv->drv_poll = __msm_poll_frame;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_v4l2_register);
+
+int msm_v4l2_unregister(struct msm_v4l2_driver *drv)
+{
+ drv->sync = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(msm_v4l2_unregister);
+#endif
+
+static int msm_sync_init(struct msm_sync *sync,
+ struct platform_device *pdev,
+ int (*sensor_probe)(const struct msm_camera_sensor_info *,
+ struct msm_sensor_ctrl *))
+{
+ int rc = 0;
+ struct msm_sensor_ctrl sctrl;
+ sync->sdata = pdev->dev.platform_data;
+
+ msm_queue_init(&sync->event_q, "event");
+ msm_queue_init(&sync->frame_q, "frame");
+ msm_queue_init(&sync->pict_q, "pict");
+
+ wake_lock_init(&sync->wake_lock, WAKE_LOCK_SUSPEND, "msm_camera");
+
+ rc = msm_camio_probe_on(pdev);
+ if (rc < 0)
+ return rc;
+ rc = sensor_probe(sync->sdata, &sctrl);
+ if (rc >= 0) {
+ sync->pdev = pdev;
+ sync->sctrl = sctrl;
+ }
+ msm_camio_probe_off(pdev);
+ if (rc < 0) {
+ pr_err("%s: failed to initialize %s\n",
+ __func__,
+ sync->sdata->sensor_name);
+ wake_lock_destroy(&sync->wake_lock);
+ return rc;
+ }
+
+ sync->opencnt = 0;
+ mutex_init(&sync->lock);
+ CDBG("%s: initialized %s\n", __func__, sync->sdata->sensor_name);
+ return rc;
+}
+
+static int msm_sync_destroy(struct msm_sync *sync)
+{
+ wake_lock_destroy(&sync->wake_lock);
+ return 0;
+}
+
+static int msm_device_init(struct msm_device *pmsm,
+ struct msm_sync *sync,
+ int node)
+{
+ int dev_num = 3 * node;
+ int rc = msm_setup_cdev(pmsm, node,
+ MKDEV(MAJOR(msm_devno), dev_num),
+ "control", &msm_fops_control);
+ if (rc < 0) {
+ pr_err("%s: error creating control node: %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = msm_setup_cdev(pmsm + 1, node,
+ MKDEV(MAJOR(msm_devno), dev_num + 1),
+ "config", &msm_fops_config);
+ if (rc < 0) {
+ pr_err("%s: error creating config node: %d\n", __func__, rc);
+ msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno),
+ dev_num));
+ return rc;
+ }
+
+ rc = msm_setup_cdev(pmsm + 2, node,
+ MKDEV(MAJOR(msm_devno), dev_num + 2),
+ "frame", &msm_fops_frame);
+ if (rc < 0) {
+ pr_err("%s: error creating frame node: %d\n", __func__, rc);
+ msm_tear_down_cdev(pmsm,
+ MKDEV(MAJOR(msm_devno), dev_num));
+ msm_tear_down_cdev(pmsm + 1,
+ MKDEV(MAJOR(msm_devno), dev_num + 1));
+ return rc;
+ }
+
+ atomic_set(&pmsm[0].opened, 0);
+ atomic_set(&pmsm[1].opened, 0);
+ atomic_set(&pmsm[2].opened, 0);
+
+ pmsm[0].sync = sync;
+ pmsm[1].sync = sync;
+ pmsm[2].sync = sync;
+
+ return rc;
+}
+
+int msm_camera_drv_start(struct platform_device *dev,
+ int (*sensor_probe)(const struct msm_camera_sensor_info *,
+ struct msm_sensor_ctrl *))
+{
+ struct msm_device *pmsm = NULL;
+ struct msm_sync *sync;
+ int rc = -ENODEV;
+ static int camera_node;
+
+ if (camera_node >= MSM_MAX_CAMERA_SENSORS) {
+ pr_err("%s: too many camera sensors\n", __func__);
+ return rc;
+ }
+
+ if (!msm_class) {
+ /* There are three device nodes per sensor */
+ rc = alloc_chrdev_region(&msm_devno, 0,
+ 3 * MSM_MAX_CAMERA_SENSORS,
+ "msm_camera");
+ if (rc < 0) {
+ pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ msm_class = class_create(THIS_MODULE, "msm_camera");
+ if (IS_ERR(msm_class)) {
+ rc = PTR_ERR(msm_class);
+ pr_err("%s: create device class failed: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ pmsm = kzalloc(sizeof(struct msm_device) * 3 +
+ sizeof(struct msm_sync), GFP_ATOMIC);
+ if (!pmsm)
+ return -ENOMEM;
+ sync = (struct msm_sync *)(pmsm + 3);
+
+ rc = msm_sync_init(sync, dev, sensor_probe);
+ if (rc < 0) {
+ kfree(pmsm);
+ return rc;
+ }
+
+ CDBG("%s: setting camera node %d\n", __func__, camera_node);
+ rc = msm_device_init(pmsm, sync, camera_node);
+ if (rc < 0) {
+ msm_sync_destroy(sync);
+ kfree(pmsm);
+ return rc;
+ }
+
+ camera_node++;
+ list_add(&sync->list, &msm_sensors);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_drv_start);
diff --git a/drivers/media/video/msm/msm_io7x.c b/drivers/media/video/msm/msm_io7x.c
new file mode 100644
index 0000000..63c9b64
--- /dev/null
+++ b/drivers/media/video/msm/msm_io7x.c
@@ -0,0 +1,311 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7
+#define APPS_RESET_OFFSET 0x00000210
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+int clk_set_flags(struct clk *clk, unsigned long flags);
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = -1;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk = clk_get(NULL, "vfe_mdc_clk");
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk = clk_get(NULL, "mdc_clk");
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk = clk_get(NULL, "vfe_clk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = -1;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk;
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+
+ if (clk != ERR_PTR(-ENOENT))
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camio_ext = camdev->ioext;
+
+ appio = request_mem_region(camio_ext.appphy,
+ camio_ext.appsz, pdev->name);
+ if (!appio) {
+ rc = -EBUSY;
+ goto enable_fail;
+ }
+
+ appbase = ioremap(camio_ext.appphy, camio_ext.appsz);
+ if (!appbase) {
+ rc = -ENOMEM;
+ goto apps_no_mem;
+ }
+
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio) {
+ rc = -EBUSY;
+ goto mdc_busy;
+ }
+
+ mdcbase = ioremap(camio_ext.mdcphy, camio_ext.mdcsz);
+ if (!mdcbase) {
+ rc = -ENOMEM;
+ goto mdc_no_mem;
+ }
+
+ camdev->camera_gpio_on();
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ return 0;
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+mdc_busy:
+ iounmap(appbase);
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+
+ camdev->camera_gpio_off();
+
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+
+ mask = CAM_SEL_BMSK | CAM_PCLK_SRC_SEL_BMSK | CAM_PCLK_INVERT_BMSK;
+
+ value = 1 << CAM_SEL_SHFT |
+ 3 << CAM_PCLK_SRC_SEL_SHFT | 0 << CAM_PCLK_INVERT_SHFT;
+
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+ mdelay(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ uint32_t val;
+
+ val = readl(appbase + 0x00000210);
+ val |= 0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000210);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ /* do axi reset */
+ val = readl(appbase + 0x00000208);
+ val |= 0x1;
+ writel(val, appbase + 0x00000208);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000208);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000208);
+ mdelay(10);
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+ struct clk *clk = NULL;
+
+ clk = camio_vfe_clk;
+
+ if (clk != NULL && clk != ERR_PTR(-ENOENT)) {
+ switch (srctype) {
+ case MSM_CAMIO_CLK_SRC_INTERNAL:
+ clk_set_flags(clk, 0x00000100 << 1);
+ break;
+
+ case MSM_CAMIO_CLK_SRC_EXTERNAL:
+ clk_set_flags(clk, 0x00000100);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_VFE_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_VFE_CLK);
+}
diff --git a/drivers/media/video/msm/msm_io8x.c b/drivers/media/video/msm/msm_io8x.c
new file mode 100644
index 0000000..ae4fe44
--- /dev/null
+++ b/drivers/media/video/msm/msm_io8x.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7
+#define APPS_RESET_OFFSET 0x00000210
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_axi_clk;
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+int clk_set_flags(struct clk *clk, unsigned long flags);
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk");
+ break;
+
+ case CAMIO_MDC_CLK:
+ camio_mdc_clk = clk = clk_get(NULL, "mdc_clk");
+ break;
+
+ case CAMIO_VFE_CLK:
+ camio_vfe_clk = clk = clk_get(NULL, "vfe_clk");
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ camio_vfe_axi_clk = clk = clk_get(NULL, "vfe_axi_clk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ /* Set rate here *before* enabling the block to prevent
+ * unstable clock from source.
+ */
+ if (clktype == CAMIO_VFE_CLK && camio_vfe_clk) {
+ clk_set_rate(camio_vfe_clk, 96000000);
+ }
+ clk_enable(clk);
+ }
+ else
+ rc = -1;
+
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk;
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ clk = camio_vfe_axi_clk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ } else
+ rc = -1;
+
+ return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_mdc_clk;
+
+ /* TODO: check return */
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camio_ext = camdev->ioext;
+
+ appio = request_mem_region(camio_ext.appphy,
+ camio_ext.appsz, pdev->name);
+ if (!appio) {
+ rc = -EBUSY;
+ goto enable_fail;
+ }
+
+ appbase = ioremap(camio_ext.appphy, camio_ext.appsz);
+ if (!appbase) {
+ rc = -ENOMEM;
+ goto apps_no_mem;
+ }
+
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio) {
+ rc = -EBUSY;
+ goto mdc_busy;
+ }
+
+ mdcbase = ioremap(camio_ext.mdcphy, camio_ext.mdcsz);
+ if (!mdcbase) {
+ rc = -ENOMEM;
+ goto mdc_no_mem;
+ }
+
+ camdev->camera_gpio_on();
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_AXI_CLK);
+
+ return 0;
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+mdc_busy:
+ iounmap(appbase);
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+
+ camdev->camera_gpio_off();
+
+ msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+
+ mask = CAM_SEL_BMSK |
+ CAM_PCLK_SRC_SEL_BMSK |
+ CAM_PCLK_INVERT_BMSK |
+ EXT_CAM_HSYNC_POL_SEL_BMSK |
+ EXT_CAM_VSYNC_POL_SEL_BMSK | MDDI_CLK_CHICKEN_BIT_BMSK;
+
+ value = 1 << CAM_SEL_SHFT |
+ 3 << CAM_PCLK_SRC_SEL_SHFT |
+ 0 << CAM_PCLK_INVERT_SHFT |
+ 0 << EXT_CAM_HSYNC_POL_SEL_SHFT |
+ 0 << EXT_CAM_VSYNC_POL_SEL_SHFT | 0 << MDDI_CLK_CHICKEN_BIT_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+
+ mdelay(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+#if 0
+ uint32_t val;
+
+ val = readl(appbase + 0x00000210);
+ val |= 0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000210);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+#endif
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+ struct clk *clk = NULL;
+
+ clk = camio_vfe_clk;
+
+ if (clk != NULL) {
+ switch (srctype) {
+ case MSM_CAMIO_CLK_SRC_INTERNAL:
+ clk_set_flags(clk, 0x00000100 << 1);
+ break;
+
+ case MSM_CAMIO_CLK_SRC_EXTERNAL:
+ clk_set_flags(clk, 0x00000100);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void msm_camio_clk_axi_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_axi_clk;
+ /* todo: check return */
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
diff --git a/drivers/media/video/msm/msm_io_vfe31.c b/drivers/media/video/msm/msm_io_vfe31.c
new file mode 100644
index 0000000..523c7f1
--- /dev/null
+++ b/drivers/media/video/msm/msm_io_vfe31.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_camif_clk;
+static struct clk *camio_vfe_pbdg_clk;
+static struct clk *camio_cam_m_clk;
+static struct clk *camio_camif_pad_pbdg_clk;
+static struct msm_camera_io_ext camio_ext;
+static struct resource *camifpadio;
+void __iomem *camifpadbase;
+
+static void camif_io_w(u32 data)
+{
+ writel(data, camifpadbase);
+ wmb();
+}
+
+static u32 camif_io_r(void)
+{
+ uint32_t data = readl(camifpadbase);
+ rmb();
+ return data;
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk");
+ break;
+
+ case CAMIO_MDC_CLK:
+ camio_mdc_clk = clk = clk_get(NULL, "mdc_clk");
+ break;
+
+ case CAMIO_VFE_CLK:
+ camio_vfe_clk = clk = clk_get(NULL, "vfe_clk");
+ clk_set_rate(clk, 122880000);
+ break;
+
+ case CAMIO_VFE_CAMIF_CLK:
+ camio_vfe_camif_clk = clk = clk_get(NULL, "vfe_camif_clk");
+ break;
+
+ case CAMIO_VFE_PBDG_CLK:
+ camio_vfe_pbdg_clk = clk = clk_get(NULL, "vfe_pclk");
+ break;
+
+ case CAMIO_CAM_MCLK_CLK:
+ camio_cam_m_clk = clk = clk_get(NULL, "cam_m_clk");
+ clk_set_rate(clk, 24000000);
+ break;
+
+ case CAMIO_CAMIF_PAD_PBDG_CLK:
+ camio_camif_pad_pbdg_clk = clk = clk_get(NULL, "camif_pad_pclk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk))
+ clk_enable(clk);
+ else
+ rc = -1;
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk;
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ case CAMIO_VFE_CAMIF_CLK:
+ clk = camio_vfe_camif_clk;
+ break;
+
+ case CAMIO_VFE_PBDG_CLK:
+ clk = camio_vfe_pbdg_clk;
+ break;
+
+ case CAMIO_CAM_MCLK_CLK:
+ clk = camio_cam_m_clk;
+ break;
+
+ case CAMIO_CAMIF_PAD_PBDG_CLK:
+ clk = camio_camif_pad_pbdg_clk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ } else
+ rc = -1;
+
+ return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_cam_m_clk;
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camio_ext = camdev->ioext;
+
+ camdev->camera_gpio_on();
+ msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK);
+ msm_camio_clk_enable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ camifpadio = request_mem_region(camio_ext.camifpadphy,
+ camio_ext.camifpadsz, pdev->name);
+ if (!camifpadio) {
+ rc = -EBUSY;
+ goto common_fail;
+ }
+ camifpadbase = ioremap(camio_ext.camifpadphy, camio_ext.camifpadsz);
+ if (!camifpadbase) {
+ rc = -ENOMEM;
+ goto parallel_busy;
+ }
+ msm_camio_clk_enable(CAMIO_VFE_CAMIF_CLK);
+ return 0;
+
+parallel_busy:
+ release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+common_fail:
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+ camdev->camera_gpio_off();
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK);
+ iounmap(camifpadbase);
+ release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+ CDBG("disable clocks\n");
+
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+ camdev->camera_gpio_off();
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+ msleep(10);
+
+ reg = camif_io_r() & CAMIF_CFG_RMSK;
+ reg |= 0x3;
+ camif_io_w(reg);
+ msleep(10);
+
+ reg = camif_io_r() & CAMIF_CFG_RMSK;
+ reg |= 0x10;
+ camif_io_w(reg);
+ msleep(10);
+
+ reg = camif_io_r() & CAMIF_CFG_RMSK;
+ /* Need to be uninverted*/
+ reg &= 0x03;
+ camif_io_w(reg);
+ msleep(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ return;
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+ if (camio_vfe_clk != NULL) {
+ switch (srctype) {
+ case MSM_CAMIO_CLK_SRC_INTERNAL:
+ clk_set_flags(camio_vfe_clk, 0x00000100 << 1);
+ break;
+
+ case MSM_CAMIO_CLK_SRC_EXTERNAL:
+ clk_set_flags(camio_vfe_clk, 0x00000100);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
diff --git a/drivers/media/video/msm/msm_v4l2.c b/drivers/media/video/msm/msm_v4l2.c
new file mode 100644
index 0000000..6abbdb9
--- /dev/null
+++ b/drivers/media/video/msm/msm_v4l2.c
@@ -0,0 +1,790 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <media/v4l2-dev.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <media/v4l2-ioctl.h>
+/*#include <linux/platform_device.h>*/
+
+#define MSM_V4L2_START_SNAPSHOT _IOWR('V', BASE_VIDIOC_PRIVATE+1, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_GET_PICTURE _IOWR('V', BASE_VIDIOC_PRIVATE+2, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_DEVICE_NAME "msm_v4l2"
+
+#define MSM_V4L2_PROC_NAME "msm_v4l2"
+
+#define MSM_V4L2_DEVNUM_MPEG2 0
+#define MSM_V4L2_DEVNUM_YUV 20
+
+/* HVGA-P (portrait) and HVGA-L (landscape) */
+#define MSM_V4L2_WIDTH 480
+#define MSM_V4L2_HEIGHT 320
+
+#if 1
+#define D(fmt, args...) printk(KERN_INFO "msm_v4l2: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define PREVIEW_FRAMES_NUM 4
+
+struct msm_v4l2_device {
+ struct list_head read_queue;
+ struct v4l2_format current_cap_format;
+ struct v4l2_format current_pix_format;
+ struct video_device *pvdev;
+ struct msm_v4l2_driver *drv;
+ uint8_t opencnt;
+
+ spinlock_t read_queue_lock;
+};
+
+static struct msm_v4l2_device *g_pmsm_v4l2_dev;
+
+static DEFINE_MUTEX(msm_v4l2_opencnt_lock);
+
+static int msm_v4l2_open(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->open(g_pmsm_v4l2_dev->drv->sync,
+ MSM_APPS_ID_V4L2);
+ }
+ g_pmsm_v4l2_dev->opencnt++;
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static int msm_v4l2_release(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ g_pmsm_v4l2_dev->opencnt--;
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->release(g_pmsm_v4l2_dev->
+ drv->sync);
+ }
+ }
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static unsigned int msm_v4l2_poll(struct file *f, struct poll_table_struct *w)
+{
+ return g_pmsm_v4l2_dev->drv->drv_poll(g_pmsm_v4l2_dev->drv->sync, f, w);
+}
+
+static long msm_v4l2_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("msm_v4l2_ioctl, cmd = %d, %d\n", cmd, __LINE__);
+
+ switch (cmd) {
+ case MSM_V4L2_START_SNAPSHOT:
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_ioctl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n",
+ cmd);
+ ctrlcmd->type = MSM_V4L2_SNAPSHOT;
+ return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ case MSM_V4L2_GET_PICTURE:
+ D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd);
+ ctrlcmd = (struct msm_ctrl_cmd *)arg;
+ return g_pmsm_v4l2_dev->drv->get_pict(g_pmsm_v4l2_dev->drv->
+ sync, ctrlcmd);
+
+ default:
+ D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd);
+ return video_ioctl2(filep, cmd, arg);
+ }
+}
+
+static void msm_v4l2_release_dev(struct video_device *d)
+{
+ D("%s\n", __func__);
+}
+
+static int msm_v4l2_querycap(struct file *f,
+ void *pctx, struct v4l2_capability *pcaps)
+{
+ D("%s\n", __func__);
+ strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver));
+ strncpy(pcaps->card, MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card));
+ pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id * pnorm)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_queryctrl(struct file *f,
+ void *pctx, struct v4l2_queryctrl *pqctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_queryctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_QUERY_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_queryctrl);
+ ctrlcmd->value = pqctrl;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_g_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_GET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_SET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("%s\n", __func__);
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_reqbufs(struct file *f,
+ void *pctx, struct v4l2_requestbuffers *b)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_pmem_info pmem_buf;
+#if 0
+ __u32 width = 0;
+ __u32 height = 0;
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.width; */
+ width = 640;
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.height; */
+ height = 480;
+
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ __u32 y_pad = pb->bytesused % 4;
+
+ /* V4L2 videodev will do the copy_from_user. */
+
+ memset(&pmem_buf, 0, sizeof(struct msm_pmem_info));
+ pmem_buf.type = MSM_PMEM_OUTPUT2;
+ pmem_buf.vaddr = (void *)pb->m.userptr;
+ pmem_buf.y_off = 0;
+ pmem_buf.fd = (int)pb->reserved;
+ /* pmem_buf.cbcr_off = (y_size + y_pad); */
+ pmem_buf.cbcr_off = (pb->bytesused + y_pad);
+
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf);
+
+ return 0;
+}
+
+static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ /*
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+ __u32 width = 0;
+ __u32 height = 0;
+ */
+
+ __u32 y_pad = 0;
+
+ struct msm_pmem_info meminfo;
+ struct msm_frame frame;
+ static int cnt;
+
+ if ((pb->flags >> 16) & 0x0001) {
+ /* this is for previwe */
+#if 0
+ width = 640;
+ height = 480;
+
+ /* V4L2 videodev will do the copy_from_user. */
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ y_pad = pb->bytesused % 4;
+
+ if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ /* this qbuf is actually for releasing */
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ g_pmsm_v4l2_dev->drv->put_frame(g_pmsm_v4l2_dev->drv->
+ sync, &frame);
+
+ return 0;
+ }
+
+ D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ meminfo.type = MSM_PMEM_OUTPUT2;
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (pb->bytesused + y_pad);
+ meminfo.vfe_can_write =
+ cnt != PREVIEW_FRAMES_NUM - 1;
+ cnt++;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ } else if ((pb->flags) & 0x0001) {
+ /* this is for snapshot */
+
+ __u32 y_size = 0;
+
+ if ((pb->flags >> 8) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_THUMBNAIL;
+ } else if ((pb->flags >> 9) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_MAINIMG;
+ }
+
+ y_pad = y_size % 4;
+
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (y_size + y_pad);
+ meminfo.vfe_can_write = 1;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_frame frame;
+ D("%s\n", __func__);
+
+ /* V4L2 videodev will do the copy_to_user. */
+ if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+
+ D("%s, %d\n", __func__, __LINE__);
+
+ g_pmsm_v4l2_dev->drv->get_frame(g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+
+ pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */
+ pb->reserved = (int)frame.fd;
+ /* pb->length = (int)frame.cbcr_off; */
+
+ pb->bytesused = frame.cbcr_off;
+
+ } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ __u32 y_pad = pb->bytesused % 4;
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ g_pmsm_v4l2_dev->drv->put_frame(g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_ON;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ D("%s after drv->ctrl \n", __func__);
+
+ return 0;
+}
+
+static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_OFF;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+
+ switch (pfmtdesc->index) {
+ case 0:
+ pfmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmtdesc->flags = 0;
+ strncpy(pfmtdesc->description, "YUV 4:2:0",
+ sizeof(pfmtdesc->description));
+ pfmtdesc->pixelformat = V4L2_PIX_FMT_YVU420;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd->length = sizeof(struct v4l2_format);
+ ctrlcmd->value = pfmt;
+ ctrlcmd->timeout_ms = 10000;
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -1;
+
+#if 0
+ /* FIXEME */
+ if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420)
+ return -EINVAL;
+#endif
+
+ /* Ok, but check other params, too. */
+
+#if 0
+ memcpy(&g_pmsm_v4l2_dev->current_pix_format.fmt.pix, pfmt,
+ sizeof(struct v4l2_format));
+#endif
+
+ g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_overlay(struct file *f, void *pctx, unsigned int i)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_g_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_s_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int msm_v4l2_read_proc(char *pbuf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ len += snprintf(pbuf, strlen("stats\n") + 1, "stats\n");
+
+ if (g_pmsm_v4l2_dev) {
+ len += snprintf(pbuf, strlen("mode: ") + 1, "mode: ");
+
+ if (g_pmsm_v4l2_dev->current_cap_format.type
+ == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ len += snprintf(pbuf, strlen("capture\n") + 1,
+ "capture\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, 21, "resolution: %dx%d\n",
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ width,
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ height);
+
+ len += snprintf(pbuf,
+ strlen("pixel format: ") + 1, "pixel format: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.pixelformat
+ == V4L2_PIX_FMT_YVU420)
+ len += snprintf(pbuf, strlen("yvu420\n") + 1,
+ "yvu420\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, strlen("colorspace: ") + 1,
+ "colorspace: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.colorspace
+ == V4L2_COLORSPACE_JPEG)
+ len += snprintf(pbuf, strlen("jpeg\n") + 1, "jpeg\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+#endif
+
+static const struct v4l2_file_operations msm_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_v4l2_open,
+ .poll = msm_v4l2_poll,
+ .release = msm_v4l2_release,
+ .ioctl = msm_v4l2_ioctl,
+};
+
+static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev)
+{
+ pmsm_v4l2_dev->read_queue_lock =
+ __SPIN_LOCK_UNLOCKED(pmsm_v4l2_dev->read_queue_lock);
+ INIT_LIST_HEAD(&pmsm_v4l2_dev->read_queue);
+}
+
+static int msm_v4l2_try_fmt_cap(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+static int mm_v4l2_try_fmt_type_private(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+/*
+ * should the following structure be used instead of the code in the function?
+ * static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
+ * .vidioc_querycap = ....
+ * }
+ */
+static const struct v4l2_ioctl_ops msm_ioctl_ops = {
+ .vidioc_querycap = msm_v4l2_querycap,
+ .vidioc_s_std = msm_v4l2_s_std,
+
+ .vidioc_queryctrl = msm_v4l2_queryctrl,
+ .vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_s_ctrl = msm_v4l2_s_ctrl,
+
+ .vidioc_reqbufs = msm_v4l2_reqbufs,
+ .vidioc_querybuf = msm_v4l2_querybuf,
+ .vidioc_qbuf = msm_v4l2_qbuf,
+ .vidioc_dqbuf = msm_v4l2_dqbuf,
+
+ .vidioc_streamon = msm_v4l2_streamon,
+ .vidioc_streamoff = msm_v4l2_streamoff,
+
+ .vidioc_enum_fmt_vid_overlay = msm_v4l2_enum_fmt_overlay,
+ .vidioc_enum_fmt_vid_cap = msm_v4l2_enum_fmt_cap,
+
+ .vidioc_try_fmt_vid_cap = msm_v4l2_try_fmt_cap,
+ .vidioc_try_fmt_type_private = mm_v4l2_try_fmt_type_private,
+
+ .vidioc_g_fmt_vid_cap = msm_v4l2_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = msm_v4l2_s_fmt_cap,
+ .vidioc_g_fmt_vid_overlay = msm_v4l2_g_fmt_overlay,
+ .vidioc_s_fmt_vid_overlay = msm_v4l2_s_fmt_overlay,
+ .vidioc_overlay = msm_v4l2_overlay,
+
+ .vidioc_g_jpegcomp = msm_v4l2_g_jpegcomp,
+ .vidioc_s_jpegcomp = msm_v4l2_s_jpegcomp,
+};
+
+static int msm_v4l2_video_dev_init(struct video_device *pvd)
+{
+ strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name));
+ pvd->fops = &msm_v4l2_fops;
+ pvd->release = msm_v4l2_release_dev;
+ pvd->minor = -1;
+ pvd->ioctl_ops = &msm_ioctl_ops;
+ return msm_v4l2_register(g_pmsm_v4l2_dev->drv);
+}
+
+static int __init msm_v4l2_init(void)
+{
+ int rc = -ENOMEM;
+ struct video_device *pvdev = NULL;
+ struct msm_v4l2_device *pmsm_v4l2_dev = NULL;
+ D("%s\n", __func__);
+
+ pvdev = video_device_alloc();
+ if (pvdev == NULL)
+ return rc;
+
+ pmsm_v4l2_dev = kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL);
+ if (pmsm_v4l2_dev == NULL) {
+ video_device_release(pvdev);
+ return rc;
+ }
+
+ msm_v4l2_dev_init(pmsm_v4l2_dev);
+
+ g_pmsm_v4l2_dev = pmsm_v4l2_dev;
+ g_pmsm_v4l2_dev->pvdev = pvdev;
+
+ g_pmsm_v4l2_dev->drv =
+ kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL);
+ if (!g_pmsm_v4l2_dev->drv) {
+ video_device_release(pvdev);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ rc = msm_v4l2_video_dev_init(pvdev);
+ if (rc < 0) {
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev->drv);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ if (video_register_device(pvdev,
+ VFL_TYPE_GRABBER, MSM_V4L2_DEVNUM_YUV)) {
+ D("failed to register device\n");
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+ return -ENOENT;
+ }
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(MSM_V4L2_PROC_NAME,
+ 0, NULL, msm_v4l2_read_proc, NULL);
+#endif
+
+ return 0;
+}
+
+static void __exit msm_v4l2_exit(void)
+{
+ struct video_device *pvdev = g_pmsm_v4l2_dev->pvdev;
+ D("%s\n", __func__);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(MSM_V4L2_PROC_NAME, NULL);
+#endif
+ video_unregister_device(pvdev);
+ video_device_release(pvdev);
+
+ msm_v4l2_unregister(g_pmsm_v4l2_dev->drv);
+
+ kfree(g_pmsm_v4l2_dev->drv);
+ g_pmsm_v4l2_dev->drv = NULL;
+
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+}
+
+module_init(msm_v4l2_init);
+module_exit(msm_v4l2_exit);
+
+MODULE_DESCRIPTION("MSM V4L2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c
new file mode 100644
index 0000000..7160229
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.c
@@ -0,0 +1,2314 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <mach/irqs.h>
+#include "msm_vfe31.h"
+#include <mach/camera.h>
+#include <linux/io.h>
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ cmd->length)) { \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+#define vfe31_get_ch_ping_addr(chn) \
+ (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn)))
+#define vfe31_get_ch_pong_addr(chn) \
+ (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn) + 4))
+#define vfe31_get_ch_addr(ping_pong, chn) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn))
+
+#define vfe31_put_ch_ping_addr(chn, addr) \
+ (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn)))
+#define vfe31_put_ch_pong_addr(chn, addr) \
+ (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn) + 4))
+#define vfe31_put_ch_addr(ping_pong, chn, addr) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe31_put_ch_pong_addr((chn), (addr)) : \
+ vfe31_put_ch_ping_addr((chn), (addr)))
+
+static struct vfe31_ctrl_type *vfe31_ctrl;
+static void *vfe_syncdata;
+
+struct vfe31_isr_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct vfe_frame_asf_info vfeAsfFrameInfo;
+ struct vfe_frame_bpc_info vfeBpcFrameInfo;
+ struct vfe_msg_camif_status vfeCamifStatusLocal;
+};
+
+static struct vfe31_cmd_type vfe31_cmd[] = {
+/* 0*/ {V31_DUMMY_0},
+ {V31_SET_CLK},
+ {V31_RESET},
+ {V31_START},
+ {V31_TEST_GEN_START},
+/* 5*/ {V31_OPERATION_CFG, V31_OPERATION_CFG_LEN},
+ {V31_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF},
+ {V31_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF},
+ {V31_AXI_INPUT_CFG},
+ {V31_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+ 0xFF},
+/*10*/ {V31_ROLL_OFF_CFG, V31_ROLL_OFF_CFG_LEN, V31_ROLL_OFF_CFG_OFF,
+ 0xFF},
+ {V31_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+ {V31_DEMOSAIC_0_CFG, V31_DEMOSAIC_0_LEN, V31_DEMOSAIC_0_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_1_CFG, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_2_CFG, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+ 0xFF},
+/*15*/ {V31_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+ {V31_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+ 0xFF},
+ {V31_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF},
+ {V31_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF},
+ {V31_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF},
+/*20*/ {V31_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF },
+ {V31_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF},
+ {V31_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+ 0xFF},
+ {V31_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+ {V31_SK_ENHAN_CFG},
+/*25*/ {V31_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF},
+ {V31_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+ {V31_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+ {V31_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF,
+ 0xFF},
+ {V31_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF, 0xFF},
+/*30*/ {V31_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF, 0xFF},
+ {V31_DUMMY_1},
+ {V31_DUMMY_2},
+ {V31_DUMMY_3},
+ {V31_UPDATE},
+/*35*/ {V31_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+ 0xFF},
+ {V31_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+ {V31_DEMOSAIC_1_UPDATE, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_2_UPDATE, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+ 0xFF},
+ {V31_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+/*40*/ {V31_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+ 0xFF},
+ {V31_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF},
+ {V31_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF,
+ 0xFF},
+ {V31_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF},
+ {V31_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF },
+/*45*/ {V31_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF},
+ {V31_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+ 0xFF},
+ {V31_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+ {V31_SK_ENHAN_UPDATE},
+ {V31_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+/*50*/ {V31_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+ {V31_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF},
+ {V31_FRAME_SKIP_UPDATE},
+ {V31_CAMIF_FRAME_UPDATE},
+ {V31_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*55*/ {V31_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+ {V31_STATS_AWB_UPDATE, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+ {V31_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+ {V31_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+ {V31_STATS_SKIN_UPDATE},
+/*60*/ {V31_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+ {V31_DUMMY_4},
+ {V31_EPOCH1_ACK},
+ {V31_EPOCH2_ACK},
+ {V31_START_RECORDING},
+/*65*/ {V31_STOP_RECORDING},
+ {V31_DUMMY_5},
+ {V31_DUMMY_6},
+ {V31_CAPTURE, V31_CAPTURE_LEN, 0xFF},
+ {V31_DUMMY_7},
+/*70*/ {V31_STOP},
+ {V31_GET_HW_VERSION},
+ {V31_GET_FRAME_SKIP_COUNTS},
+ {V31_OUTPUT1_BUFFER_ENQ},
+ {V31_OUTPUT2_BUFFER_ENQ},
+/*75*/ {V31_OUTPUT3_BUFFER_ENQ},
+ {V31_JPEG_OUT_BUF_ENQ},
+ {V31_RAW_OUT_BUF_ENQ},
+ {V31_RAW_IN_BUF_ENQ},
+ {V31_STATS_AF_ENQ},
+/*80*/ {V31_STATS_AE_ENQ},
+ {V31_STATS_AWB_ENQ},
+ {V31_STATS_RS_ENQ},
+ {V31_STATS_CS_ENQ},
+ {V31_STATS_SKIN_ENQ},
+/*85*/ {V31_STATS_IHIST_ENQ},
+ {V31_DUMMY_8},
+ {V31_JPEG_ENC_CFG},
+ {V31_DUMMY_9},
+ {V31_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*90*/ {V31_STATS_AF_STOP},
+ {V31_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+ {V31_STATS_AE_STOP},
+ {V31_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+ {V31_STATS_AWB_STOP},
+/*95*/ {V31_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+ {V31_STATS_RS_STOP},
+ {V31_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+ {V31_STATS_CS_STOP},
+ {V31_STATS_SKIN_START, V31_STATS_IHIST_LEN,
+ V31_STATS_IHIST_OFF},
+/*100*/ {V31_STATS_SKIN_STOP},
+ {V31_STATS_IHIST_START},
+ {V31_STATS_IHIST_STOP},
+ {V31_DUMMY_10},
+ {V31_SYNC_TIMER_SETTING},
+/*105*/ {V31_ASYNC_TIMER_SETTING},
+};
+
+static void vfe_io_w(u32 data, unsigned long offset)
+{
+ writel(data, vfe31_ctrl->vfebase + offset);
+ wmb();
+}
+
+static u32 vfe_io_r(unsigned long offset)
+{
+ uint32_t data = readl(vfe31_ctrl->vfebase + offset);
+ rmb();
+ return data;
+}
+
+static void msm_io_memcpy(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+ /* memcpy_toio does not work. Use writel for now */
+ for (i = 0; i < len / 4; i++) {
+ writel(*s++, d++);
+ wmb();
+ }
+}
+
+static void vfe_io_w_axi_out_chan(u32 data, int8_t chan)
+{
+ uint32_t temp;
+ vfe_io_w(data, V31_AXI_OUT_OFF + 20 + 24 * chan);
+ temp = vfe_io_r(V31_AXI_OUT_OFF + 20 + 24 * chan);
+}
+
+static void vfe_msg_get_phy_addr(struct msm_vfe_phy_info *pinfo,
+ struct vfe_message *data)
+{
+ pinfo->y_phy = data->_u.msgOut.yBuffer;
+ pinfo->cbcr_phy = data->_u.msgOut.cbcrBuffer;
+}
+
+static void vfe31_proc_ops(enum VFE31_MESSAGE_ID id, void *msg, size_t len)
+{
+ struct msm_vfe_resp *rp;
+ struct vfe_message *vfe_msg_data = NULL;
+
+ rp = vfe31_ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp),
+ vfe31_ctrl->syncdata, GFP_ATOMIC);
+ if (!rp) {
+ pr_err("rp: cannot allocate buffer\n");
+ return;
+ }
+ CDBG("vfe31_proc_ops, msgId = %d\n", id);
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+ rp->evt_msg.len = len;
+ rp->evt_msg.data = msg;
+ vfe_msg_data = (struct vfe_message *)rp->evt_msg.data;
+
+ switch (rp->evt_msg.msg_id) {
+ case MSG_ID_SNAPSHOT_DONE:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_ID_OUTPUT_P:
+ rp->type = VFE_MSG_OUTPUT_P;
+ rp->phy.output_id = OUTPUT_TYPE_P;
+ vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data);
+ break;
+
+ case MSG_ID_OUTPUT_T:
+ rp->type = VFE_MSG_OUTPUT_T;
+ rp->phy.output_id = OUTPUT_TYPE_T;
+ vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data);
+ break;
+
+ case MSG_ID_OUTPUT_S:
+ rp->type = VFE_MSG_OUTPUT_S;
+ rp->phy.output_id = OUTPUT_TYPE_S;
+ vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data);
+ break;
+
+ case MSG_ID_OUTPUT_V:
+ rp->type = VFE_MSG_OUTPUT_V;
+ rp->phy.output_id = OUTPUT_TYPE_V;
+ vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data);
+ break;
+
+ case MSG_ID_STATS_AF:
+ rp->type = VFE_MSG_STATS_AF;
+ rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer;
+ break;
+
+ case MSG_ID_STATS_AWB:
+ rp->type = VFE_MSG_STATS_AWB;
+ rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer;
+ break;
+
+ case MSG_ID_STATS_AEC:
+ rp->type = VFE_MSG_STATS_AEC;
+ rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer;
+ break;
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+ vfe31_ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe31_ctrl->syncdata,
+ GFP_ATOMIC);
+}
+
+static void vfe_send_outmsg(uint8_t msgid, uint32_t pyaddr, uint32_t pcbcraddr)
+{
+ struct vfe_message msg;
+ msg._d = msgid;
+ msg._u.msgOut.yBuffer = pyaddr;
+ msg._u.msgOut.cbcrBuffer = pcbcraddr;
+ vfe31_proc_ops(msgid, &msg, sizeof(struct vfe_message));
+}
+
+static int vfe31_enable(struct camera_enable_cmd *enable)
+{
+ return 0;
+}
+
+void vfe_stop(void)
+{
+ uint8_t axiBusyFlag = true;
+ unsigned long flags;
+
+ /* for reset hw modules, and send msg when reset_irq comes. */
+ spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+ vfe31_ctrl->stop_ack_pending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+ /* disable all interrupts. */
+ vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0);
+ vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0);
+ vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1);
+ vfe_io_w(1, VFE_IRQ_CMD);
+
+ /* in either continuous or snapshot mode, stop command can be issued
+ * at any time. stop camif immediately. */
+ vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND);
+
+ /* axi halt command. */
+ vfe_io_w(AXI_HALT, VFE_AXI_CMD);
+
+ while (axiBusyFlag) {
+ if (vfe_io_r(VFE_AXI_STATUS) & 0x1) {
+ axiBusyFlag = false;
+ }
+ }
+ vfe_io_w(AXI_HALT_CLEAR, VFE_AXI_CMD);
+
+ /* after axi halt, then ok to apply global reset. */
+ /* enable reset_ack and async timer interrupt only while
+ stopping the pipeline.*/
+ vfe_io_w(0xf0000000, VFE_IRQ_MASK_0);
+ vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1);
+ vfe_io_w(VFE_RESET_UPON_STOP_CMD, VFE_GLOBAL_RESET);
+}
+
+static int vfe31_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev)
+{
+ vfe_stop();
+ msm_camio_disable(dev);
+ return 0;
+}
+
+static void vfe31_release(struct platform_device *pdev)
+{
+ struct msm_sensor_ctrl *sctrl =
+ &((struct msm_sync *)vfe_syncdata)->sctrl;
+ struct resource *vfemem, *vfeio;
+
+ if (sctrl)
+ sctrl->s_release();
+
+ vfemem = vfe31_ctrl->vfemem;
+ vfeio = vfe31_ctrl->vfeio;
+
+ kfree(vfe31_ctrl->extdata);
+ free_irq(vfe31_ctrl->vfeirq, 0);
+ iounmap(vfe31_ctrl->vfebase);
+ kfree(vfe31_ctrl);
+ vfe31_ctrl = NULL;
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ msm_camio_disable(pdev);
+ vfe_syncdata = NULL;
+}
+
+static int vfe31_config_axi(int mode, struct axidata *ad, uint32_t *ao)
+{
+ int i;
+ uint32_t *p, *p1, *p2;
+ struct vfe31_output_ch *outp1 = NULL, *outp2 = NULL;
+ struct msm_pmem_region *regp1 = NULL, *regp2 = NULL;
+
+ p = ao + 2;
+
+ CDBG("vfe31_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d\n",
+ mode, ad->bufnum1, ad->bufnum2);
+
+ switch (mode) {
+ case OUTPUT_2:
+ if (ad->bufnum2 != 3)
+ return -EINVAL;
+ *p = 0x200; /* preview with wm0 & wm1 */
+
+ vfe31_ctrl->outpath.out0.ch0 = 0; /* luma */
+ vfe31_ctrl->outpath.out0.ch1 = 1; /* chroma */
+ regp1 = &(ad->region[ad->bufnum1]);
+ outp1 = &(vfe31_ctrl->outpath.out0);
+ vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_PT;
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 12 + i; /* wm1 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+ outp1->free_buf.available = 1;
+ outp1->free_buf.paddr = regp1->paddr;
+ outp1->free_buf.y_off = regp1->info.y_off;
+ outp1->free_buf.cbcr_off = regp1->info.cbcr_off;
+
+ CDBG("vfe31_config_axi: free_buf paddr = 0x%x, y_off = %d,"
+ " cbcr_off = %d\n",
+ outp1->free_buf.paddr, outp1->free_buf.y_off,
+ outp1->free_buf.cbcr_off);
+ break;
+
+ case OUTPUT_1_AND_2:
+ /* use wm0& 4 for thumbnail, wm1&5 for main image.*/
+ if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1))
+ return -EINVAL;
+ /* at least one frame for snapshot. */
+ *p++ = 0x1; /* xbar cfg0 */
+ *p = 0x203; /* xbar cfg1 */
+ vfe31_ctrl->outpath.out0.ch0 = 0; /* thumbnail luma */
+ vfe31_ctrl->outpath.out0.ch1 = 4; /* thumbnail chroma */
+ vfe31_ctrl->outpath.out1.ch0 = 1; /* main image luma */
+ vfe31_ctrl->outpath.out1.ch1 = 5; /* main image chroma */
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_S; /* main image.*/
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_PT; /* thumbnail. */
+
+ regp1 = &(ad->region[0]); /* this is thumbnail buffer. */
+ /* this is main image buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);
+ outp1 = &(vfe31_ctrl->outpath.out0);
+ outp2 = &(vfe31_ctrl->outpath.out1); /* snapshot */
+
+ p1 = ao + 6; /* wm0 ping */
+ *p1++ = (regp1->paddr + regp1->info.y_off);
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ p1 = ao + 30; /* wm4 ping */
+ *p1++ = (regp1->paddr + regp1->info.cbcr_off);
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ p1 = ao + 12; /* wm1 ping */
+ *p1++ = (regp2->paddr + regp2->info.y_off);
+ /* pong = ping,*/
+ *p1 = (regp2->paddr + regp2->info.y_off);
+ p1 = ao + 36; /* wm5 */
+ *p1++ = (regp2->paddr + regp2->info.cbcr_off);
+ *p1 = (regp2->paddr + regp2->info.cbcr_off);
+ break;
+
+ case OUTPUT_1_AND_3:
+ /* use wm0& 4 for preview, wm1&5 for video.*/
+ if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2))
+ return -EINVAL;
+ *p++ = 0x1; /* xbar cfg0 */
+ *p = 0x1a03; /* xbar cfg1 */
+ vfe31_ctrl->outpath.out0.ch0 = 0; /* preview luma */
+ vfe31_ctrl->outpath.out0.ch1 = 4; /* preview chroma */
+ vfe31_ctrl->outpath.out2.ch0 = 1; /* video luma */
+ vfe31_ctrl->outpath.out2.ch1 = 5; /* video chroma */
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_V; /* video*/
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_PT; /* preview */
+
+ regp1 = &(ad->region[0]); /* this is preview buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */
+ outp1 = &(vfe31_ctrl->outpath.out0); /* preview */
+ outp2 = &(vfe31_ctrl->outpath.out2); /* video */
+
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 30 + i; /* wm1 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm0 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+
+ p2 = ao + 36 + i; /* wm1 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2++;
+ }
+ outp1->free_buf.available = 1;
+ outp1->free_buf.paddr = regp1->paddr;
+ outp1->free_buf.y_off = regp1->info.y_off;
+ outp1->free_buf.cbcr_off = regp1->info.cbcr_off;
+
+ outp2->free_buf.available = 1;
+ outp2->free_buf.paddr = regp2->paddr;
+ outp2->free_buf.y_off = regp2->info.y_off;
+ outp2->free_buf.cbcr_off = regp2->info.cbcr_off;
+ CDBG("vfe31_config_axi: preview free_buf"
+ "paddr = 0x%x, y_off = %d, cbcr_off = %d\n",
+ outp1->free_buf.paddr, outp1->free_buf.y_off,
+ outp1->free_buf.cbcr_off);
+ CDBG("vfe31_config_axi: video free_buf"
+ "paddr = 0x%x,y_off = %d, cbcr_off = %d\n",
+ outp2->free_buf.paddr, outp2->free_buf.y_off,
+ outp2->free_buf.cbcr_off);
+ break;
+
+ case CAMIF_TO_AXI_VIA_OUTPUT_2: /* use wm0 only */
+ if (ad->bufnum2 < 1)
+ return -EINVAL;
+ CDBG("config axi for raw snapshot.\n");
+ *p = 0x60; /* raw snapshot with wm0 */
+ vfe31_ctrl->outpath.out1.ch0 = 0; /* raw */
+ regp1 = &(ad->region[ad->bufnum1]);
+ vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_S;
+ p1 = ao + 6; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ break;
+
+ default:
+ break;
+ }
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[V31_AXI_OUT_CFG].offset,
+ ao, vfe31_cmd[V31_AXI_OUT_CFG].length);
+ return 0;
+}
+
+static void vfe31_reset_internal_variables(void)
+{
+ unsigned long flags;
+
+ vfe31_ctrl->vfeImaskCompositePacked = 0;
+ /* state control variables */
+ vfe31_ctrl->start_ack_pending = FALSE;
+
+ spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+ vfe31_ctrl->stop_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+ vfe31_ctrl->reset_ack_pending = FALSE;
+
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ vfe31_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+
+ vfe31_ctrl->req_stop_video_rec = FALSE;
+ vfe31_ctrl->req_start_video_rec = FALSE;
+
+ spin_lock_irqsave(&vfe31_ctrl->state_lock, flags);
+ vfe31_ctrl->vstate = VFE_STATE_IDLE;
+ spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags);
+
+ /* 0 for continuous mode, 1 for snapshot mode */
+ vfe31_ctrl->operation_mode = 0;
+ vfe31_ctrl->outpath.output_mode = 0;
+ vfe31_ctrl->vfe_capture_count = 0;
+
+ /* this is unsigned 32 bit integer. */
+ vfe31_ctrl->vfeFrameId = 0;
+ vfe31_ctrl->output1Pattern = 0xffffffff;
+ vfe31_ctrl->output1Period = 31;
+ vfe31_ctrl->output2Pattern = 0xffffffff;
+ vfe31_ctrl->output2Period = 31;
+ vfe31_ctrl->vfeFrameSkipCount = 0;
+ vfe31_ctrl->vfeFrameSkipPeriod = 31;
+
+ /* Stats control variables. */
+ memset(&vfe31_ctrl->afStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+ memset(&vfe31_ctrl->awbStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+ memset(&vfe31_ctrl->aecStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+ memset(&vfe31_ctrl->ihistStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+ memset(&vfe31_ctrl->rsStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+ memset(&vfe31_ctrl->csStatsControl, 0,
+ sizeof(struct vfe_stats_control));
+}
+
+static void vfe31_reset(void)
+{
+ uint32_t vfe_version;
+
+ vfe31_reset_internal_variables();
+ vfe_version = vfe_io_r(VFE_VERSION);
+ CDBG("vfe_version = 0x%x\n", vfe_version);
+ /* disable all interrupts. vfeImaskLocal is also reset to 0
+ * to begin with. */
+ vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0);
+ vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0);
+ vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1);
+ vfe_io_w(1, VFE_IRQ_CMD);
+
+ /* enable reset_ack interrupt. */
+ vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1);
+
+ /* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+ * is done, hardware interrupt will be generated. VFE ist processes
+ * the interrupt to complete the function call. Note that the reset
+ * function is synchronous. */
+ vfe_io_w(VFE_RESET_UPON_RESET_CMD, VFE_GLOBAL_RESET);
+}
+
+static int vfe31_operation_config(uint32_t *cmd)
+{
+ uint32_t *p = cmd;
+
+ vfe31_ctrl->operation_mode = *p;
+ vfe31_ctrl->stats_comp = *(++p);
+
+ vfe_io_w(*(++p), VFE_CFG_OFF);
+ vfe_io_w(*(++p), VFE_MODULE_CFG);
+ vfe_io_w(*(++p), VFE_REALIGN_BUF);
+ vfe_io_w(*(++p), VFE_CHROMA_UP);
+ vfe_io_w(*(++p), VFE_STATS_CFG);
+ return 0;
+}
+
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PONG_ADDR);
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PONG_ADDR);
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PING_ADDR);
+ addr = ptr[1];
+ vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PONG_ADDR);
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static void vfe31_start_common(void)
+{
+ unsigned long flags;
+
+ vfe31_ctrl->start_ack_pending = TRUE;
+ CDBG("VFE opertaion mode = 0x%x.\n", vfe31_ctrl->operation_mode);
+ CDBG("VFE output path out mode = 0x%x.\n",
+ vfe31_ctrl->outpath.output_mode);
+ vfe_io_w(0x00EFE021, VFE_IRQ_MASK_0);
+ vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1);
+
+ vfe_io_w(1, VFE_REG_UPDATE_CMD);
+ vfe_io_w(1, VFE_CAMIF_COMMAND);
+
+ spin_lock_irqsave(&vfe31_ctrl->state_lock, flags);
+ vfe31_ctrl->vstate = VFE_STATE_ACTIVE;
+ spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags);
+}
+
+static int vfe31_start_recording(void)
+{
+ vfe31_ctrl->req_start_video_rec = TRUE;
+ return 0;
+}
+
+static int vfe31_stop_recording(void)
+{
+ vfe31_ctrl->req_stop_video_rec = TRUE;
+ return 0;
+}
+
+static int vfe31_capture(uint32_t num_frames_capture)
+{
+ uint32_t irq_comp_mask = 0;
+
+ /* capture command is valid for both idle and active state. */
+ vfe31_ctrl->outpath.out1.capture_cnt = num_frames_capture;
+ if (vfe31_ctrl->operation_mode == 1) {
+ vfe31_ctrl->outpath.out0.capture_cnt = num_frames_capture;
+ }
+ vfe31_ctrl->vfe_capture_count = num_frames_capture;
+ irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK);
+
+ if (vfe31_ctrl->operation_mode == 1) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+ 0x1 << vfe31_ctrl->outpath.out0.ch1);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) |
+ 0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0);
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0);
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch1);
+ }
+ } else { /* this is raw snapshot mode. */
+ CDBG("config the comp imask for raw snapshot mode.\n");
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8));
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0);
+ }
+ }
+ vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK);
+ vfe_io_r(VFE_IRQ_COMP_MASK);
+ vfe31_start_common();
+ vfe_io_r(VFE_IRQ_COMP_MASK);
+ /* for debug */
+ vfe_io_w(1, 0x18C);
+ vfe_io_w(1, 0x188);
+ return 0;
+}
+
+static int vfe31_start(void)
+{
+ uint32_t irq_comp_mask = 0;
+
+ /* start command now is only good for continuous mode. */
+ if (vfe31_ctrl->operation_mode & 1)
+ return 0;
+ irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK);
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+ 0x1 << vfe31_ctrl->outpath.out0.ch1);
+ }
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out2.ch0 + 16) |
+ 0x1 << (vfe31_ctrl->outpath.out2.ch1 + 16));
+ }
+
+ vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK);
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0);
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1);
+ }
+ vfe31_start_common();
+ return 0;
+}
+
+static void vfe31_update(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ vfe31_ctrl->update_ack_pending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+ vfe_io_w(1, VFE_REG_UPDATE_CMD);
+}
+
+void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = VFE_DMI_CFG_DEFAULT;
+ value += (uint32_t)bankSel;
+
+ vfe_io_w(value, VFE_DMI_CFG);
+ /* by default, always starts with offset 0. */
+ vfe_io_w(0, VFE_DMI_ADDR);
+}
+
+void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+ const uint32_t *tbl)
+{
+ int i;
+ uint32_t value, value1, value2;
+
+ vfe31_program_dmi_cfg(channel_sel);
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) {
+ value = *tbl++;
+ value1 = value & 0x0000FFFF;
+ value2 = (value & 0xFFFF0000)>>16;
+ vfe_io_w(value1, VFE_DMI_DATA_LO);
+ vfe_io_w(value2, VFE_DMI_DATA_LO);
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static int vfe31_proc_general(struct msm_vfe31_cmd *cmd)
+{
+ int i, rc = 0;
+ uint32_t old_val = 0, new_val = 0;
+ uint32_t *cmdp = NULL;
+ uint32_t *cmdp_local = NULL;
+
+ CDBG("vfe31_proc_general: cmdID = %d, length = %d\n",
+ cmd->id, cmd->length);
+ switch (cmd->id) {
+ case V31_RESET:
+ vfe31_reset();
+ break;
+ case V31_START:
+ rc = vfe31_start();
+ break;
+ case V31_UPDATE:
+ vfe31_update();
+ break;
+ case V31_CAPTURE:
+ rc = vfe31_capture(1);
+ break;
+ case V31_START_RECORDING:
+ rc = vfe31_start_recording();
+ break;
+ case V31_STOP_RECORDING:
+ rc = vfe31_stop_recording();
+ break;
+ case V31_OPERATION_CFG:
+ if (cmd->length != V31_OPERATION_CFG_LEN) {
+ rc = -EINVAL;
+ goto proc_general_done;
+ }
+ cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_KERNEL);
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ V31_OPERATION_CFG_LEN)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe31_operation_config(cmdp);
+ break;
+
+ case V31_STATS_AE_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= AE_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_STATS_AF_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= AF_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_STATS_AWB_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= AWB_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_STATS_IHIST_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= IHIST_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+
+ case V31_STATS_RS_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= RS_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_STATS_CS_START:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val |= CS_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_MCE_UPDATE:
+ case V31_MCE_CFG:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ /* Incrementing with 4 so as to point to the 2nd Register as
+ the 2nd register has the mce_enable bit */
+ old_val = vfe_io_r(V31_CHROMA_EN_OFF + 4);
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+ old_val &= MCE_EN_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 4,
+ &new_val, 4);
+ cmdp_local += 1;
+
+ old_val = vfe_io_r(V31_CHROMA_EN_OFF + 8);
+ new_val = *cmdp_local;
+ old_val &= MCE_Q_K_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 8,
+ &new_val, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_DEMOSAIC_2_UPDATE: /* 38 BPC update */
+ case V31_DEMOSAIC_2_CFG: /* 14 BPC config */
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = vfe_io_r(V31_DEMOSAIC_0_OFF);
+ old_val &= BPC_MASK;
+
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+ cmdp_local, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_DEMOSAIC_1_UPDATE:/* 37 ABF update */
+ case V31_DEMOSAIC_1_CFG: /* 13 ABF config */
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = vfe_io_r(V31_DEMOSAIC_0_OFF);
+ old_val &= ABF_MASK;
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+ cmdp_local, 4);
+
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, vfe31_cmd[cmd->id].length);
+ break;
+
+ case V31_ROLL_OFF_CFG:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, 16);
+ cmdp_local += 4;
+ vfe31_program_dmi_cfg(ROLLOFF_RAM);
+ /* for loop for extrcting init table. */
+ for (i = 0 ; i < (VFE31_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) {
+ vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ CDBG("done writing init table\n");
+ /* by default, always starts with offset 0. */
+ vfe_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, VFE_DMI_ADDR);
+ /* for loop for extracting delta table. */
+ for (i = 0 ; i < (VFE31_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) {
+ vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+ break;
+
+ case V31_LA_CFG:
+ case V31_LA_UPDATE:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+
+ /* If the value is 0x00 then write in to LUT bank0
+ if the value is 0x01 then write in to LUT bank1 */
+ if (*cmdp == 0x0)
+ vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK0);
+ else
+ vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK1);
+ cmdp += 1;
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < VFE31_LA_TABLE_LENGTH ; i++) {
+ vfe_io_w(*cmdp, VFE_DMI_DATA_LO);
+ cmdp++;
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+ cmdp -= 1;
+ break;
+
+ case V31_RGB_G_CFG:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4);
+ cmdp += 1;
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0 , cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0 , cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0 , cmdp);
+ cmdp -= 1;
+ break;
+
+ case V31_RGB_G_UPDATE:
+ cmdp = kmalloc(cmd->length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)cmd->value,
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4);
+ old_val = *cmdp;
+ cmdp += 1;
+
+ if (old_val) {
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1, cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1, cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1, cmdp);
+ } else {
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp);
+ vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp);
+ }
+ cmdp -= 1;
+ break;
+
+ case V31_STATS_AWB_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~AWB_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STATS_AE_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~AE_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STATS_AF_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~AF_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STATS_IHIST_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~IHIST_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STATS_RS_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~RS_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STATS_CS_STOP:
+ old_val = vfe_io_r(VFE_MODULE_CFG);
+ old_val &= ~CS_ENABLE_MASK;
+ vfe_io_w(old_val, VFE_MODULE_CFG);
+ break;
+
+ case V31_STOP:
+ vfe_stop();
+ break;
+
+ default:
+ if (cmd->length != vfe31_cmd[cmd->id].length)
+ return -EINVAL;
+
+ cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_KERNEL);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+
+ CHECKED_COPY_FROM_USER(cmdp);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, vfe31_cmd[cmd->id].length);
+ break;
+ }
+
+proc_general_done:
+ kfree(cmdp);
+
+ return rc;
+}
+
+static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags);
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->afStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+}
+
+static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags);
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->awbStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+}
+
+static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags);
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->aecStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+}
+
+static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags);
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->ihistStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags);
+}
+
+static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags);
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->rsStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags);
+}
+
+static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags);
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->csStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags);
+}
+
+static int vfe31_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ struct msm_vfe31_cmd vfecmd;
+ long rc = 0;
+ uint32_t i = 0;
+ struct vfe_cmd_stats_buf *scfg = NULL;
+ struct msm_pmem_region *regptr = NULL;
+ struct vfe_cmd_stats_ack *sack = NULL;
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd, (void __user *)cmd->value,
+ sizeof(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n", __func__,
+ __LINE__);
+ return -EFAULT;
+ }
+ } else if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE) {
+ /* This must be stats release. */
+ if (!data)
+ return -EFAULT;
+ sack = kmalloc(sizeof(struct vfe_cmd_stats_ack), GFP_KERNEL);
+ if (!sack)
+ return -ENOMEM;
+ sack->nextStatsBuf = *(uint32_t *)data;
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe31_proc_general(&vfecmd);
+ break;
+ case CMD_FRAME_BUF_RELEASE: {
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe31_free_buf *fbuf = NULL;
+ if (!data)
+ return -EFAULT;
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path);
+
+ if (b->path & OUTPUT_TYPE_P) {
+ CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+ fbuf = &vfe31_ctrl->outpath.out0.free_buf;
+ } else if (b->path & OUTPUT_TYPE_S) {
+ fbuf = &vfe31_ctrl->outpath.out1.free_buf;
+ } else if (b->path & OUTPUT_TYPE_V) {
+ fbuf = &vfe31_ctrl->outpath.out2.free_buf;
+ } else
+ return -EFAULT;
+
+ fbuf->paddr = p;
+ fbuf->y_off = b->y_off;
+ fbuf->cbcr_off = b->cbcr_off;
+ fbuf->available = 1;
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+ case CMD_STATS_AEC_BUF_RELEASE:
+ vfe31_stats_aec_ack(sack);
+ break;
+ case CMD_STATS_AF_BUF_RELEASE:
+ vfe31_stats_af_ack(sack);
+ break;
+ case CMD_STATS_AWB_BUF_RELEASE:
+ vfe31_stats_awb_ack(sack);
+ break;
+ case CMD_STATS_IHIST_BUF_RELEASE:
+ vfe31_stats_ihist_ack(sack);
+ break;
+ case CMD_STATS_RS_BUF_RELEASE:
+ vfe31_stats_rs_ack(sack);
+ break;
+ case CMD_STATS_CS_BUF_RELEASE:
+ vfe31_stats_cs_ack(sack);
+ break;
+
+ case CMD_AXI_CFG_PREVIEW: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)vfecmd.value,
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ return -EFAULT;
+ }
+ vfe31_config_axi(OUTPUT_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_RAW_PICT_AXI_CFG: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)vfecmd.value,
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ return -EFAULT;
+ }
+ vfe31_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)vfecmd.value,
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ return -EFAULT;
+ }
+ vfe31_config_axi(OUTPUT_1_AND_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)vfecmd.value,
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ return -EFAULT;
+ }
+ vfe31_config_axi(OUTPUT_1_AND_3, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_STATS_AF_ENABLE:
+ case CMD_STATS_AWB_ENABLE:
+ case CMD_STATS_IHIST_ENABLE:
+ case CMD_STATS_RS_ENABLE:
+ case CMD_STATS_CS_ENABLE:
+ case CMD_STATS_AEC_ENABLE: {
+ struct axidata *axid;
+ axid = (struct axidata *)data;
+ if (!axid)
+ return -EFAULT;
+
+ scfg = kmalloc(sizeof(struct vfe_cmd_stats_buf), GFP_KERNEL);
+ if (!scfg)
+ return -ENOMEM;
+ regptr = axid->region;
+ if (axid->bufnum1 > 0) {
+ for (i = 0; i < axid->bufnum1; i++) {
+ scfg->statsBuf[i] = (uint32_t)(regptr->paddr);
+ regptr++;
+ }
+ }
+ /* individual */
+ switch (cmd->cmd_type) {
+ case CMD_STATS_AEC_ENABLE:
+ rc = vfe_stats_aec_buf_init(scfg);
+ break;
+ case CMD_STATS_AF_ENABLE:
+ rc = vfe_stats_af_buf_init(scfg);
+ break;
+ case CMD_STATS_AWB_ENABLE:
+ rc = vfe_stats_awb_buf_init(scfg);
+ break;
+ case CMD_STATS_IHIST_ENABLE:
+ rc = vfe_stats_ihist_buf_init(scfg);
+ break;
+ case CMD_STATS_RS_ENABLE:
+ rc = vfe_stats_rs_buf_init(scfg);
+ break;
+ case CMD_STATS_CS_ENABLE:
+ rc = vfe_stats_cs_buf_init(scfg);
+ break;
+ }
+ }
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ kfree(scfg);
+ kfree(sack);
+ CDBG("%s done: rc = %d\n", __func__, (int) rc);
+ return rc;
+}
+
+static inline void vfe31_read_irq_status(struct vfe31_irq_status *out)
+{
+ memset(out, 0, sizeof(struct vfe31_irq_status));
+ out->vfeIrqStatus0 = vfe_io_r(VFE_IRQ_STATUS_0);
+ out->vfeIrqStatus1 = vfe_io_r(VFE_IRQ_STATUS_1);
+ out->camifStatus = vfe_io_r(VFE_CAMIF_STATUS);
+ CDBG("camifStatus = 0x%x\n", out->camifStatus);
+}
+
+static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id)
+{
+ struct vfe_message msg;
+
+ CDBG("vfe31_send_msg_no_payload\n");
+ msg._d = id;
+ vfe31_proc_ops(id, &msg, 0);
+}
+
+static void vfe31_process_reg_update_irq(void)
+{
+ unsigned long flags;
+ uint32_t temp;
+
+ if (vfe31_ctrl->req_start_video_rec) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch0);
+ vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch1);
+ }
+ vfe31_ctrl->req_start_video_rec = FALSE;
+ CDBG("start video triggered .\n");
+ } else if (vfe31_ctrl->req_stop_video_rec) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch0);
+ vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch1);
+ }
+ vfe31_ctrl->req_stop_video_rec = FALSE;
+ CDBG("stop video triggered .\n");
+ }
+ if (vfe31_ctrl->start_ack_pending == TRUE) {
+ vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe31_ctrl->start_ack_pending = FALSE;
+ } else {
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ if (vfe31_ctrl->update_ack_pending == TRUE) {
+ spin_unlock_irqrestore(
+ &vfe31_ctrl->update_ack_lock, flags);
+ vfe31_send_msg_no_payload(MSG_ID_UPDATE_ACK);
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ vfe31_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(
+ &vfe31_ctrl->update_ack_lock, flags);
+ } else {
+ spin_unlock_irqrestore(
+ &vfe31_ctrl->update_ack_lock, flags);
+ }
+ }
+ if (vfe31_ctrl->operation_mode & 1) { /* in snapshot mode */
+ /* later we need to add check for live snapshot mode. */
+ vfe31_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ /* stop the bus output: write master enable = 0*/
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ vfe_io_w_axi_out_chan(0,
+ vfe31_ctrl->outpath.out0.ch0);
+ vfe_io_w_axi_out_chan(0,
+ vfe31_ctrl->outpath.out0.ch1);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ vfe_io_w_axi_out_chan(0,
+ vfe31_ctrl->outpath.out1.ch0);
+ vfe_io_w_axi_out_chan(0,
+ vfe31_ctrl->outpath.out1.ch1);
+ }
+ vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ VFE_CAMIF_COMMAND);
+
+ temp = vfe_io_r(VFE_CAMIF_COMMAND);
+ /* then do reg_update. */
+ vfe_io_w(1, VFE_REG_UPDATE_CMD);
+ }
+ } /* if snapshot mode. */
+}
+
+static void vfe31_set_default_reg_values(void)
+{
+ vfe_io_w(0x800080, VFE_DEMUX_GAIN_0);
+ vfe_io_w(0x800080, VFE_DEMUX_GAIN_1);
+ vfe_io_w(0xFFFFF, VFE_CGC_OVERRIDE);
+
+ /* default frame drop period and pattern */
+ vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_Y_CFG);
+ vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_CBCR_CFG);
+ vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_Y_PATTERN);
+ vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+ vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_Y);
+ vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_CBCR);
+ vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_Y_PATTERN);
+ vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+ vfe_io_w(0, VFE_CLAMP_MIN);
+ vfe_io_w(0xFFFFFF, VFE_CLAMP_MAX);
+
+ /* stats UB config */
+ vfe_io_w(0x3900007, VFE_BUS_STATS_AEC_UB_CFG);
+ vfe_io_w(0x3980007, VFE_BUS_STATS_AF_UB_CFG);
+ vfe_io_w(0x3A0000F, VFE_BUS_STATS_AWB_UB_CFG);
+ vfe_io_w(0x3B00007, VFE_BUS_STATS_RS_UB_CFG);
+ vfe_io_w(0x3B8001F, VFE_BUS_STATS_CS_UB_CFG);
+ vfe_io_w(0x3D8001F, VFE_BUS_STATS_HIST_UB_CFG);
+ vfe_io_w(0x3F80007, VFE_BUS_STATS_SKIN_UB_CFG);
+}
+
+static void vfe31_process_reset_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->state_lock, flags);
+ vfe31_ctrl->vstate = VFE_STATE_IDLE;
+ spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags);
+
+ spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+ if (vfe31_ctrl->stop_ack_pending) {
+ vfe31_ctrl->stop_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+ vfe31_send_msg_no_payload(MSG_ID_STOP_ACK);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+ /* this is from reset command. */
+ vfe31_set_default_reg_values();
+
+ /* reload all write masters. (frame & line)*/
+ vfe_io_w(0x7FFF, VFE_BUS_CMD);
+ vfe31_send_msg_no_payload(MSG_ID_RESET_ACK);
+ }
+}
+
+static void vfe31_process_camif_sof_irq(void)
+{
+ uint32_t temp;
+
+ if (vfe31_ctrl->operation_mode == 3) { /* in raw snapshot mode */
+ if (vfe31_ctrl->start_ack_pending) {
+ vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe31_ctrl->start_ack_pending = FALSE;
+ }
+ vfe31_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, VFE_CAMIF_COMMAND);
+ temp = vfe_io_r(VFE_CAMIF_COMMAND);
+ }
+ } /* if raw snapshot mode. */
+
+ vfe31_ctrl->vfeFrameId++;
+ CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId);
+}
+
+static void vfe31_process_output_path_irq_0(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+ uint8_t out_bool = 0;
+
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe31_ctrl->operation_mode & 1) &&
+ (vfe31_ctrl->vfe_capture_count <= 1)) ||
+ (vfe31_ctrl->outpath.out0.free_buf.available);
+ if (out_bool) {
+ ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1);
+
+ CDBG("output path 0, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ if (vfe31_ctrl->outpath.out0.free_buf.available) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0,
+ vfe31_ctrl->outpath.out0.free_buf.paddr +
+ vfe31_ctrl->outpath.out0.free_buf.y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1,
+ vfe31_ctrl->outpath.out0.free_buf.paddr +
+ vfe31_ctrl->outpath.out0.free_buf.cbcr_off);
+ vfe31_ctrl->outpath.out0.free_buf.available = 0;
+ }
+ if (vfe31_ctrl->operation_mode & 1) {
+ /* will add message for multi-shot. */
+ vfe31_ctrl->outpath.out0.capture_cnt--;
+ } else {
+ /* always send message for continous mode. */
+ /* if continuous mode, this is for display. (preview) */
+ vfe_send_outmsg(MSG_ID_OUTPUT_P, pyaddr, pcbcraddr);
+ }
+ } else {
+ vfe31_ctrl->outpath.out0.frame_drop_cnt++;
+ CDBG("path_irq_0 - no free buffer!\n");
+ }
+}
+
+static void vfe31_process_output_path_irq_1(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+ /* this must be snapshot main image output. */
+ uint8_t out_bool;
+
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ -- when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe31_ctrl->operation_mode & 1) &&
+ (vfe31_ctrl->vfe_capture_count <= 1)) ||
+ (vfe31_ctrl->outpath.out1.free_buf.available);
+ if (out_bool) {
+ ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1);
+
+ CDBG("snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ if (vfe31_ctrl->outpath.out1.free_buf.available) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0,
+ vfe31_ctrl->outpath.out1.free_buf.paddr +
+ vfe31_ctrl->outpath.out1.free_buf.y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1,
+ vfe31_ctrl->outpath.out1.free_buf.paddr +
+ vfe31_ctrl->outpath.out1.free_buf.cbcr_off);
+ vfe31_ctrl->outpath.out1.free_buf.available = 0;
+ }
+
+ vfe31_ctrl->outpath.out1.capture_cnt--;
+ } else {
+ vfe31_ctrl->outpath.out1.frame_drop_cnt++;
+ CDBG("path_irq_1 - no free buffer!\n");
+ }
+}
+
+static void vfe31_process_output_path_irq_2(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+ uint8_t out_bool;
+
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ -- when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe31_ctrl->operation_mode & 1) &&
+ (vfe31_ctrl->vfe_capture_count <= 1)) ||
+ (vfe31_ctrl->outpath.out2.free_buf.available);
+ if (out_bool) {
+ ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1);
+
+ CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+
+ if (vfe31_ctrl->outpath.out2.free_buf.available) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0,
+ vfe31_ctrl->outpath.out2.free_buf.paddr +
+ vfe31_ctrl->outpath.out2.free_buf.y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1,
+ vfe31_ctrl->outpath.out2.free_buf.paddr +
+ vfe31_ctrl->outpath.out2.free_buf.cbcr_off);
+ vfe31_ctrl->outpath.out2.free_buf.available = 0;
+ }
+ vfe_send_outmsg(MSG_ID_OUTPUT_V, pyaddr, pcbcraddr);
+ } else {
+ vfe31_ctrl->outpath.out2.frame_drop_cnt++;
+ CDBG("path_irq_2 - no free buffer!\n");
+ }
+}
+
+static void vfe31_process_stats_comb_irq(uint32_t *irqstatus)
+{
+ return;
+}
+
+static uint32_t vfe31_process_stats_irq_common(uint32_t statsNum,
+ uint32_t newAddr) {
+ uint32_t pingpongStatus;
+ uint32_t returnAddr;
+ uint32_t pingpongAddrOffset;
+
+ /* must be 0=ping, 1=pong */
+ pingpongStatus =
+ (vfe_io_r(VFE_BUS_PING_PONG_STATUS)
+ & ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+ /* stats bits starts at 7 */
+ CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+ pingpongAddrOffset =
+ ((uint32_t)(VFE_BUS_STATS_PING_PONG_BASE)) +
+ (3*statsNum)*4 + (1-pingpongStatus)*4;
+ returnAddr = vfe_io_r(pingpongAddrOffset);
+ vfe_io_w(newAddr, pingpongAddrOffset);
+ return returnAddr;
+}
+
+static void vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum)
+{
+ struct vfe_message msg;
+
+ /* fill message with right content. */
+ msg._u.msgStats.frameCounter = vfe31_ctrl->vfeFrameId;
+ msg._u.msgStats.buffer = bufAddress;
+
+ switch (statsNum) {
+ case statsAeNum:
+ msg._d = MSG_ID_STATS_AEC;
+ break;
+ case statsAfNum:
+ msg._d = MSG_ID_STATS_AF;
+ break;
+ case statsAwbNum:
+ msg._d = MSG_ID_STATS_AWB;
+ break;
+ case statsIhistNum:
+ msg._d = MSG_ID_STATS_IHIST;
+ break;
+ case statsRsNum:
+ msg._d = MSG_ID_STATS_RS;
+ break;
+ case statsCsNum:
+ msg._d = MSG_ID_STATS_CS;
+ break;
+ default:
+ goto stats_done;
+ }
+
+ vfe31_proc_ops(msg._d, &msg, sizeof(struct vfe_message));
+stats_done:
+ return;
+}
+
+static void vfe31_process_stats_ae_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags);
+ if (!vfe31_ctrl->aecStatsControl.ackPending) {
+ vfe31_ctrl->aecStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+ vfe31_ctrl->aecStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAeNum,
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->aecStatsControl.bufToRender,
+ statsAeNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags);
+ vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++;
+ }
+
+}
+
+static void vfe31_process_stats_af_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags);
+ if (!vfe31_ctrl->afStatsControl.ackPending) {
+ vfe31_ctrl->afStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+ vfe31_ctrl->afStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAfNum,
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->afStatsControl.bufToRender,
+ statsAfNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags);
+ vfe31_ctrl->afStatsControl.droppedStatsFrameCount++;
+ }
+
+}
+
+static void vfe31_process_stats_awb_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags);
+ if (!vfe31_ctrl->awbStatsControl.ackPending) {
+ vfe31_ctrl->awbStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+ vfe31_ctrl->awbStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAwbNum,
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->awbStatsControl.bufToRender,
+ statsAwbNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags);
+ vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++;
+ }
+
+}
+
+static void vfe31_process_stats_ihist_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags);
+ if (!vfe31_ctrl->ihistStatsControl.ackPending) {
+ vfe31_ctrl->ihistStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags);
+ vfe31_ctrl->ihistStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsIhistNum,
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->ihistStatsControl.bufToRender,
+ statsIhistNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags);
+ vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+
+static void vfe31_process_stats_rs_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags);
+ if (!vfe31_ctrl->rsStatsControl.ackPending) {
+ vfe31_ctrl->rsStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags);
+ vfe31_ctrl->rsStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsRsNum,
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->rsStatsControl.bufToRender,
+ statsRsNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags);
+ vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+static void vfe31_process_stats_cs_irq(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags);
+ if (!vfe31_ctrl->csStatsControl.ackPending) {
+ vfe31_ctrl->csStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags);
+ vfe31_ctrl->csStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsCsNum,
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe31_ctrl->csStatsControl.bufToRender,
+ statsCsNum);
+ } else {
+ spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags);
+ vfe31_ctrl->csStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+static void vfe31_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vfe31_isr_queue_cmd *qcmd = NULL;
+
+ CDBG("=== vfe31_do_tasklet start ===\n");
+
+ spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+ qcmd = list_first_entry(&vfe31_ctrl->tasklet_q,
+ struct vfe31_isr_queue_cmd, list);
+
+ if (!qcmd) {
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+ return;
+ }
+
+ list_del(&qcmd->list);
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+
+ /* interrupt to be processed, *qcmd has the payload. */
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_REG_UPDATE_MASK) {
+ CDBG("irq regUpdateIrq\n");
+ vfe31_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus1 & VFE_IMASK_WHILE_STOPPING_1) {
+ CDBG("irq resetAckIrq\n");
+ vfe31_process_reset_irq();
+ }
+
+ spin_lock_irqsave(&vfe31_ctrl->state_lock, flags);
+ if (vfe31_ctrl->vstate == VFE_STATE_ACTIVE) {
+ /* irqs below are only valid when in active state. */
+ spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags);
+ /* next, check output path related interrupts. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+ vfe31_process_output_path_irq_0();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+ vfe31_process_output_path_irq_1();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) {
+ vfe31_process_output_path_irq_2();
+ }
+ /* in snapshot mode if done then send snapshot done message */
+ if (vfe31_ctrl->operation_mode & 1) {
+ if ((vfe31_ctrl->outpath.out0.capture_cnt == 0) &&
+ (vfe31_ctrl->outpath.out1.capture_cnt == 0)) {
+ vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE);
+ vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND);
+ }
+ }
+ /* then process stats irq. */
+ if (vfe31_ctrl->stats_comp) {
+ /* process stats comb interrupt. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+ CDBG("Stats composite irq occured.\n");
+ vfe31_process_stats_comb_irq(
+ &qcmd->vfeInterruptStatus0);
+ }
+ } else {
+ /* process individual stats interrupt. */
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AEC) {
+ CDBG("Stats AEC irq occured.\n");
+ vfe31_process_stats_ae_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AWB) {
+ CDBG("Stats AWB irq occured.\n");
+ vfe31_process_stats_awb_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AF) {
+ CDBG("Stats AF irq occured.\n");
+ vfe31_process_stats_af_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_IHIST) {
+ CDBG("Stats IHIST irq occured.\n");
+ vfe31_process_stats_ihist_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_RS) {
+ CDBG("Stats RS irq occured.\n");
+ vfe31_process_stats_rs_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_CS) {
+ CDBG("Stats CS irq occured.\n");
+ vfe31_process_stats_cs_irq();
+ }
+ }
+ } else {
+ /* do we really need spin lock for state? */
+ spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags);
+ }
+ if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_CAMIF_SOF_MASK) {
+ vfe31_process_camif_sof_irq();
+ }
+ kfree(qcmd);
+ CDBG("=== vfe31_do_tasklet end ===\n");
+}
+
+DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0);
+
+static irqreturn_t vfe31_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct vfe31_irq_status irq;
+ struct vfe31_isr_queue_cmd *qcmd;
+
+ CDBG("vfe_parse_irq\n");
+ vfe31_read_irq_status(&irq);
+
+ if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+ CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+ return IRQ_HANDLED;
+ }
+
+ qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd), GFP_ATOMIC);
+ if (!qcmd) {
+ pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags);
+ if (vfe31_ctrl->stop_ack_pending) {
+ irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+ irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1;
+ }
+ spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags);
+
+ CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+ irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+ qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+ qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+
+ spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+ list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q);
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+ tasklet_schedule(&vfe31_tasklet);
+
+ /* clear the pending interrupt of the same kind.*/
+ vfe_io_w(irq.vfeIrqStatus0, VFE_IRQ_CLEAR_0);
+ vfe_io_w(irq.vfeIrqStatus1, VFE_IRQ_CLEAR_1);
+
+ vfe_io_w(1, VFE_IRQ_CMD);
+
+ return IRQ_HANDLED;
+}
+
+static int vfe31_resource_init(struct msm_vfe_callback *presp,
+ struct platform_device *pdev, void *sdata)
+{
+ struct resource *vfemem, *vfeirq, *vfeio;
+ int rc;
+ struct msm_camera_sensor_info *s_info;
+ s_info = pdev->dev.platform_data;
+
+ pdev->resource = s_info->resource;
+ pdev->num_resources = s_info->num_resources;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ pr_err("%s: VFE region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
+ if (!vfe31_ctrl) {
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+
+ vfe31_ctrl->vfeirq = vfeirq->start;
+
+ vfe31_ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!vfe31_ctrl->vfebase) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto cmd_init_failed2;
+ }
+
+ rc = request_irq(vfe31_ctrl->vfeirq, vfe31_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+ if (rc < 0)
+ goto cmd_init_failed2;
+
+ if (presp && presp->vfe_resp)
+ vfe31_ctrl->resp = presp;
+ else {
+ rc = -EINVAL;
+ goto cmd_init_failed3;
+ }
+
+ vfe31_ctrl->extdata = kmalloc(sizeof(struct vfe31_frame_extra),
+ GFP_KERNEL);
+ if (!vfe31_ctrl->extdata) {
+ rc = -ENOMEM;
+ goto cmd_init_failed3;
+ }
+
+ vfe31_ctrl->extlen = sizeof(struct vfe31_frame_extra);
+
+ spin_lock_init(&vfe31_ctrl->stop_flag_lock);
+ spin_lock_init(&vfe31_ctrl->state_lock);
+ spin_lock_init(&vfe31_ctrl->update_ack_lock);
+ spin_lock_init(&vfe31_ctrl->tasklet_lock);
+
+ spin_lock_init(&vfe31_ctrl->aec_ack_lock);
+ spin_lock_init(&vfe31_ctrl->awb_ack_lock);
+ spin_lock_init(&vfe31_ctrl->af_ack_lock);
+ spin_lock_init(&vfe31_ctrl->ihist_ack_lock);
+ spin_lock_init(&vfe31_ctrl->rs_ack_lock);
+ spin_lock_init(&vfe31_ctrl->cs_ack_lock);
+ INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
+
+ vfe31_ctrl->syncdata = sdata;
+ vfe31_ctrl->vfemem = vfemem;
+ vfe31_ctrl->vfeio = vfeio;
+ return 0;
+
+cmd_init_failed3:
+ free_irq(vfe31_ctrl->vfeirq, 0);
+ iounmap(vfe31_ctrl->vfebase);
+cmd_init_failed2:
+ kfree(vfe31_ctrl);
+cmd_init_failed1:
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ return rc;
+}
+
+static int vfe31_init(struct msm_vfe_callback *presp,
+ struct platform_device *dev)
+{
+ int rc = 0;
+ rc = vfe31_resource_init(presp, dev, vfe_syncdata);
+ if (rc < 0)
+ return rc;
+
+ /* Bring up all the required GPIOs and Clocks */
+ rc = msm_camio_enable(dev);
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ fptr->vfe_init = vfe31_init;
+ fptr->vfe_enable = vfe31_enable;
+ fptr->vfe_config = vfe31_config;
+ fptr->vfe_disable = vfe31_disable;
+ fptr->vfe_release = vfe31_release;
+ vfe_syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe31.h b/drivers/media/video/msm/msm_vfe31.h
new file mode 100644
index 0000000..2a12ac4
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.h
@@ -0,0 +1,1052 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __MSM_VFE31_H__
+#define __MSM_VFE31_H__
+
+#define TRUE 1
+#define FALSE 0
+
+/* at start of camif, bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START 0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR 0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT 0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR 0x00000000
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD 0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD 0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 = halted, 0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go; bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO 0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO 0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples. JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK 0x00000001
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK 0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */
+
+/* imask for while waiting for stop ack, driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-31 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+ irq */
+#define VFE_IMASK_WHILE_STOPPING_0 0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1 0x00400000
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1 0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For BPC bit 1 and 2 are set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF9
+
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AWB_ENABLE_MASK 0x00000080 /* bit 7 */
+#define AF_ENABLE_MASK 0x00000040 /* bit 6 */
+#define AE_ENABLE_MASK 0x00000020 /* bit 5 */
+#define IHIST_ENABLE_MASK 0x00008000 /* bit 15 */
+#define RS_ENABLE_MASK 0x00000100 /* bit 8 */
+#define CS_ENABLE_MASK 0x00000200 /* bit 9 */
+
+
+#define VFE_REG_UPDATE_TRIGGER 1
+#define VFE_PM_BUF_MAX_CNT_MASK 0xFF
+#define VFE_DMI_CFG_DEFAULT 0x00000100
+#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32
+#define VFE_AE_PINGPONG_STATUS_BIT 0x80
+#define VFE_AF_PINGPONG_STATUS_BIT 0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT 0x200
+
+
+enum VFE31_DMI_RAM_SEL {
+ NO_MEM_SELECTED = 0,
+ ROLLOFF_RAM = 0x1,
+ RGBLUT_RAM_CH0_BANK0 = 0x2,
+ RGBLUT_RAM_CH0_BANK1 = 0x3,
+ RGBLUT_RAM_CH1_BANK0 = 0x4,
+ RGBLUT_RAM_CH1_BANK1 = 0x5,
+ RGBLUT_RAM_CH2_BANK0 = 0x6,
+ RGBLUT_RAM_CH2_BANK1 = 0x7,
+ STATS_HIST_CB_EVEN_RAM = 0x8,
+ STATS_HIST_CB_ODD_RAM = 0x9,
+ STATS_HIST_CR_EVEN_RAM = 0xa,
+ STATS_HIST_CR_ODD_RAM = 0xb,
+ RGBLUT_CHX_BANK0 = 0xc,
+ RGBLUT_CHX_BANK1 = 0xd,
+ LUMA_ADAPT_LUT_RAM_BANK0 = 0xe,
+ LUMA_ADAPT_LUT_RAM_BANK1 = 0xf
+
+};
+
+enum VFE_STATE {
+ VFE_STATE_IDLE,
+ VFE_STATE_ACTIVE
+};
+
+#define V31_DUMMY_0 0
+#define V31_SET_CLK 1
+#define V31_RESET 2
+#define V31_START 3
+#define V31_TEST_GEN_START 4
+#define V31_OPERATION_CFG 5
+#define V31_AXI_OUT_CFG 6
+#define V31_CAMIF_CFG 7
+#define V31_AXI_INPUT_CFG 8
+#define V31_BLACK_LEVEL_CFG 9
+#define V31_ROLL_OFF_CFG 10
+#define V31_DEMUX_CFG 11
+#define V31_DEMOSAIC_0_CFG 12 /* general */
+#define V31_DEMOSAIC_1_CFG 13 /* ABF */
+#define V31_DEMOSAIC_2_CFG 14 /* BPC */
+#define V31_FOV_CFG 15
+#define V31_MAIN_SCALER_CFG 16
+#define V31_WB_CFG 17
+#define V31_COLOR_COR_CFG 18
+#define V31_RGB_G_CFG 19
+#define V31_LA_CFG 20
+#define V31_CHROMA_EN_CFG 21
+#define V31_CHROMA_SUP_CFG 22
+#define V31_MCE_CFG 23
+#define V31_SK_ENHAN_CFG 24
+#define V31_ASF_CFG 25
+#define V31_S2Y_CFG 26
+#define V31_S2CbCr_CFG 27
+#define V31_CHROMA_SUBS_CFG 28
+#define V31_OUT_CLAMP_CFG 29
+#define V31_FRAME_SKIP_CFG 30
+#define V31_DUMMY_1 31
+#define V31_DUMMY_2 32
+#define V31_DUMMY_3 33
+#define V31_UPDATE 34
+#define V31_BL_LVL_UPDATE 35
+#define V31_DEMUX_UPDATE 36
+#define V31_DEMOSAIC_1_UPDATE 37 /* BPC */
+#define V31_DEMOSAIC_2_UPDATE 38 /* ABF */
+#define V31_FOV_UPDATE 39
+#define V31_MAIN_SCALER_UPDATE 40
+#define V31_WB_UPDATE 41
+#define V31_COLOR_COR_UPDATE 42
+#define V31_RGB_G_UPDATE 43
+#define V31_LA_UPDATE 44
+#define V31_CHROMA_EN_UPDATE 45
+#define V31_CHROMA_SUP_UPDATE 46
+#define V31_MCE_UPDATE 47
+#define V31_SK_ENHAN_UPDATE 48
+#define V31_S2CbCr_UPDATE 49
+#define V31_S2Y_UPDATE 50
+#define V31_ASF_UPDATE 51
+#define V31_FRAME_SKIP_UPDATE 52
+#define V31_CAMIF_FRAME_UPDATE 53
+#define V31_STATS_AF_UPDATE 54
+#define V31_STATS_AE_UPDATE 55
+#define V31_STATS_AWB_UPDATE 56
+#define V31_STATS_RS_UPDATE 57
+#define V31_STATS_CS_UPDATE 58
+#define V31_STATS_SKIN_UPDATE 59
+#define V31_STATS_IHIST_UPDATE 60
+#define V31_DUMMY_4 61
+#define V31_EPOCH1_ACK 62
+#define V31_EPOCH2_ACK 63
+#define V31_START_RECORDING 64
+#define V31_STOP_RECORDING 65
+#define V31_DUMMY_5 66
+#define V31_DUMMY_6 67
+#define V31_CAPTURE 68
+#define V31_DUMMY_7 69
+#define V31_STOP 70
+#define V31_GET_HW_VERSION 71
+#define V31_GET_FRAME_SKIP_COUNTS 72
+#define V31_OUTPUT1_BUFFER_ENQ 73
+#define V31_OUTPUT2_BUFFER_ENQ 74
+#define V31_OUTPUT3_BUFFER_ENQ 75
+#define V31_JPEG_OUT_BUF_ENQ 76
+#define V31_RAW_OUT_BUF_ENQ 77
+#define V31_RAW_IN_BUF_ENQ 78
+#define V31_STATS_AF_ENQ 79
+#define V31_STATS_AE_ENQ 80
+#define V31_STATS_AWB_ENQ 81
+#define V31_STATS_RS_ENQ 82
+#define V31_STATS_CS_ENQ 83
+#define V31_STATS_SKIN_ENQ 84
+#define V31_STATS_IHIST_ENQ 85
+#define V31_DUMMY_8 86
+#define V31_JPEG_ENC_CFG 87
+#define V31_DUMMY_9 88
+#define V31_STATS_AF_START 89
+#define V31_STATS_AF_STOP 90
+#define V31_STATS_AE_START 91
+#define V31_STATS_AE_STOP 92
+#define V31_STATS_AWB_START 93
+#define V31_STATS_AWB_STOP 94
+#define V31_STATS_RS_START 95
+#define V31_STATS_RS_STOP 96
+#define V31_STATS_CS_START 97
+#define V31_STATS_CS_STOP 98
+#define V31_STATS_SKIN_START 99
+#define V31_STATS_SKIN_STOP 100
+#define V31_STATS_IHIST_START 101
+#define V31_STATS_IHIST_STOP 102
+#define V31_DUMMY_10 103
+#define V31_SYNC_TIMER_SETTING 104
+#define V31_ASYNC_TIMER_SETTING 105
+#define V31_CAMIF_OFF 0x000001E4
+#define V31_CAMIF_LEN 32
+
+#define V31_DEMUX_OFF 0x00000284
+#define V31_DEMUX_LEN 20
+
+#define V31_DEMOSAIC_0_OFF 0x00000298
+#define V31_DEMOSAIC_0_LEN 4
+/* ABF */
+#define V31_DEMOSAIC_1_OFF 0x000002A4
+#define V31_DEMOSAIC_1_LEN 180
+/* BPC */
+#define V31_DEMOSAIC_2_OFF 0x0000029C
+#define V31_DEMOSAIC_2_LEN 8
+
+#define V31_OUT_CLAMP_OFF 0x00000524
+#define V31_OUT_CLAMP_LEN 8
+
+#define V31_OPERATION_CFG_LEN 28
+
+#define V31_AXI_OUT_OFF 0x00000038
+#define V31_AXI_OUT_LEN 188
+
+#define V31_FRAME_SKIP_OFF 0x00000504
+#define V31_FRAME_SKIP_LEN 32
+
+#define V31_CHROMA_SUBS_OFF 0x000004F8
+#define V31_CHROMA_SUBS_LEN 12
+
+#define V31_FOV_OFF 0x00000360
+#define V31_FOV_LEN 8
+
+#define V31_MAIN_SCALER_OFF 0x00000368
+#define V31_MAIN_SCALER_LEN 28
+
+#define V31_S2Y_OFF 0x000004D0
+#define V31_S2Y_LEN 20
+
+#define V31_S2CbCr_OFF 0x000004E4
+#define V31_S2CbCr_LEN 20
+
+#define V31_CHROMA_EN_OFF 0x000003C4
+#define V31_CHROMA_EN_LEN 36
+
+#define V31_BLACK_LEVEL_OFF 0x00000264
+#define V31_BLACK_LEVEL_LEN 16
+
+#define V31_ROLL_OFF_CFG_OFF 0x00000274
+#define V31_ROLL_OFF_CFG_LEN 16
+
+#define V31_COLOR_COR_OFF 0x00000388
+#define V31_COLOR_COR_LEN 52
+
+#define V31_WB_OFF 0x00000384
+#define V31_WB_LEN 4
+
+#define V31_RGB_G_OFF 0x000003BC
+#define V31_RGB_G_LEN 4
+
+#define V31_LA_OFF 0x000003C0
+#define V31_LA_LEN 4
+
+#define V31_CHROMA_SUP_OFF 0x000003E8
+#define V31_CHROMA_SUP_LEN 12
+
+#define V31_MCE_OFF 0x000003E8
+#define V31_MCE_LEN 36
+#define V31_STATS_AF_OFF 0x0000053c
+#define V31_STATS_AF_LEN 16
+
+#define V31_STATS_AE_OFF 0x00000534
+#define V31_STATS_AE_LEN 8
+
+#define V31_STATS_AWB_OFF 0x0000054c
+#define V31_STATS_AWB_LEN 32
+
+#define V31_STATS_IHIST_OFF 0x0000057c
+#define V31_STATS_IHIST_LEN 8
+
+#define V31_STATS_RS_OFF 0x0000056c
+#define V31_STATS_RS_LEN 8
+
+#define V31_STATS_CS_OFF 0x00000574
+#define V31_STATS_CS_LEN 8
+
+
+#define V31_ASF_OFF 0x000004A0
+#define V31_ASF_LEN 48
+#define V31_ASF_UPDATE_LEN 36
+
+#define V31_CAPTURE_LEN 4
+
+struct vfe_cmd_hw_version {
+ uint32_t minorVersion;
+ uint32_t majorVersion;
+ uint32_t coreVersion;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+ VFE_AXI_OUTPUT_MODE_Output1,
+ VFE_AXI_OUTPUT_MODE_Output2,
+ VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+ VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+ VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+ VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+ VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+ VFE_RAW_OUTPUT_DISABLED,
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+ VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+ VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH 4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3
+
+struct vfe_cmds_per_write_master {
+ uint16_t imageWidth;
+ uint16_t imageHeight;
+ uint16_t outRowCount;
+ uint16_t outRowIncrement;
+ uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+ [VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+ uint8_t fragmentCount;
+ struct vfe_cmds_per_write_master firstWM;
+ struct vfe_cmds_per_write_master secondWM;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+ VFE_AXI_BURST_LENGTH_IS_2 = 2,
+ VFE_AXI_BURST_LENGTH_IS_4 = 4,
+ VFE_AXI_BURST_LENGTH_IS_8 = 8,
+ VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+
+struct vfe_cmd_fov_crop_config {
+ uint8_t enable;
+ uint16_t firstPixel;
+ uint16_t lastPixel;
+ uint16_t firstLine;
+ uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+ uint16_t MNCounterInit;
+ uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+ uint8_t enable;
+ uint16_t inputSize;
+ uint16_t outputSize;
+ uint32_t phaseMultiplicationFactor;
+ uint8_t interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+ struct vfe_cmds_main_scaler_stripe_init MNInitH;
+ struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+
+struct vfe_cmd_frame_skip_update {
+ uint32_t output1Pattern;
+ uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+ uint8_t minCh0;
+ uint8_t minCh1;
+ uint8_t minCh2;
+ uint8_t maxCh0;
+ uint8_t maxCh1;
+ uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+ uint8_t enable;
+ uint8_t cropEnable;
+ uint8_t vsubSampleEnable;
+ uint8_t hsubSampleEnable;
+ uint8_t vCosited;
+ uint8_t hCosited;
+ uint8_t vCositedPhase;
+ uint8_t hCositedPhase;
+ uint16_t cropWidthFirstPixel;
+ uint16_t cropWidthLastPixel;
+ uint16_t cropHeightFirstLine;
+ uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_INPUT_SOURCE {
+ VFE_START_INPUT_SOURCE_CAMIF,
+ VFE_START_INPUT_SOURCE_TESTGEN,
+ VFE_START_INPUT_SOURCE_AXI,
+ VFE_START_INPUT_SOURCE_INVALID
+};
+
+enum VFE_START_OPERATION_MODE {
+ VFE_START_OPERATION_MODE_CONTINUOUS,
+ VFE_START_OPERATION_MODE_SNAPSHOT
+};
+
+enum VFE_START_PIXEL_PATTERN {
+ VFE_BAYER_RGRGRG,
+ VFE_BAYER_GRGRGR,
+ VFE_BAYER_BGBGBG,
+ VFE_BAYER_GBGBGB,
+ VFE_YUV_YCbYCr,
+ VFE_YUV_YCrYCb,
+ VFE_YUV_CbYCrY,
+ VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+ VFE_BAYER_RAW,
+ VFE_YUV_INTERLEAVED,
+ VFE_YUV_PSEUDO_PLANAR_Y,
+ VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+ VFE_YUV_COSITED,
+ VFE_YUV_INTERPOLATED
+};
+
+
+/* 13*1 */
+#define VFE31_ROLL_OFF_INIT_TABLE_SIZE 13
+/* 13*16 */
+#define VFE31_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+#define VFE31_GAMMA_NUM_ENTRIES 64
+
+#define VFE31_LA_TABLE_LENGTH 64
+
+struct vfe_cmds_demosaic_abf {
+ uint8_t enable;
+ uint8_t forceOn;
+ uint8_t shift;
+ uint16_t lpThreshold;
+ uint16_t max;
+ uint16_t min;
+ uint8_t ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+ uint8_t enable;
+ uint16_t fmaxThreshold;
+ uint16_t fminThreshold;
+ uint16_t redDiffThreshold;
+ uint16_t blueDiffThreshold;
+ uint16_t greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+ uint8_t enable;
+ uint8_t slopeShift;
+ struct vfe_cmds_demosaic_abf abfConfig;
+ struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+ struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+ struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+ uint8_t enable;
+ uint16_t ch2Gain;
+ uint16_t ch1Gain;
+ uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+ COEF_IS_Q7_SIGNED,
+ COEF_IS_Q8_SIGNED,
+ COEF_IS_Q9_SIGNED,
+ COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+ uint8_t enable;
+ enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+ int16_t C0;
+ int16_t C1;
+ int16_t C2;
+ int16_t C3;
+ int16_t C4;
+ int16_t C5;
+ int16_t C6;
+ int16_t C7;
+ int16_t C8;
+ int16_t K0;
+ int16_t K1;
+ int16_t K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 256
+struct vfe_cmd_la_config {
+ uint8_t enable;
+ int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+ RGB_GAMMA_CH0_SELECTED,
+ RGB_GAMMA_CH1_SELECTED,
+ RGB_GAMMA_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_SELECTED,
+ RGB_GAMMA_CH0_CH2_SELECTED,
+ RGB_GAMMA_CH1_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+ uint8_t enable;
+ enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+ int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+ uint8_t enable;
+ int16_t am;
+ int16_t ap;
+ int16_t bm;
+ int16_t bp;
+ int16_t cm;
+ int16_t cp;
+ int16_t dm;
+ int16_t dp;
+ int16_t kcr;
+ int16_t kcb;
+ int16_t RGBtoYConversionV0;
+ int16_t RGBtoYConversionV1;
+ int16_t RGBtoYConversionV2;
+ uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+ uint8_t enable;
+ uint8_t m1;
+ uint8_t m3;
+ uint8_t n1;
+ uint8_t n3;
+ uint8_t nn1;
+ uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+ uint16_t cropFirstPixel;
+ uint16_t cropLastPixel;
+ uint16_t cropFirstLine;
+ uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+ VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+ VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+
+struct vfe_cmd_bus_pm_start {
+ uint8_t output2YWrPmEnable;
+ uint8_t output2CbcrWrPmEnable;
+ uint8_t output1YWrPmEnable;
+ uint8_t output1CbcrWrPmEnable;
+};
+
+struct vfe_cmd_sync_timer_setting {
+ uint8_t whichSyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t hsyncCount;
+ uint32_t pclkCount;
+ uint32_t outputDuration;
+};
+
+struct vfe_cmd_async_timer_setting {
+ uint8_t whichAsyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t inactiveCount;
+ uint32_t activeCount;
+};
+
+struct vfe_frame_skip_counts {
+ uint32_t totalFrameCount;
+ uint32_t output1Count;
+ uint32_t output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+ VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+enum VFE31_MESSAGE_ID {
+ MSG_ID_RESET_ACK,
+ MSG_ID_START_ACK,
+ MSG_ID_STOP_ACK,
+ MSG_ID_UPDATE_ACK,
+ MSG_ID_OUTPUT_P,
+ MSG_ID_OUTPUT_T,
+ MSG_ID_OUTPUT_S,
+ MSG_ID_OUTPUT_V,
+ MSG_ID_SNAPSHOT_DONE,
+ MSG_ID_STATS_AEC,
+ MSG_ID_STATS_AF,
+ MSG_ID_STATS_AWB, /* 8 */
+ MSG_ID_STATS_RS,
+ MSG_ID_STATS_CS,
+ MSG_ID_STATS_IHIST,
+ MSG_ID_STATS_SKIN,
+ MSG_ID_EPOCH1,
+ MSG_ID_EPOCH2,
+ MSG_ID_SYNC_TIMER0_DONE,
+ MSG_ID_SYNC_TIMER1_DONE,
+ MSG_ID_SYNC_TIMER2_DONE,
+ MSG_ID_ASYNC_TIMER0_DONE,
+ MSG_ID_ASYNC_TIMER1_DONE,
+ MSG_ID_ASYNC_TIMER2_DONE,
+ MSG_ID_ASYNC_TIMER3_DONE,
+ MSG_ID_AE_OVERFLOW,
+ MSG_ID_AF_OVERFLOW,
+ MSG_ID_AWB_OVERFLOW,
+ MSG_ID_RS_OVERFLOW,
+ MSG_ID_CS_OVERFLOW,
+ MSG_ID_IHIST_OVERFLOW,
+ MSG_ID_SKIN_OVERFLOW,
+ MSG_ID_AXI_ERROR,
+ MSG_ID_CAMIF_OVERFLOW,
+ MSG_ID_VIOLATION,
+ MSG_ID_CAMIF_ERROR,
+ MSG_ID_BUS_OVERFLOW,
+};
+
+struct vfe_msg_stats{
+ uint32_t buffer;
+ uint32_t frameCounter;
+};
+
+
+struct vfe_frame_bpc_info {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+ uint8_t camifState;
+ uint32_t pixelCount;
+ uint32_t lineCount;
+};
+
+
+struct vfe31_irq_status {
+ uint32_t vfeIrqStatus0;
+ uint32_t vfeIrqStatus1;
+ uint32_t camifStatus;
+ uint32_t demosaicStatus;
+ uint32_t asfMaxEdge;
+};
+
+struct vfe_msg_output {
+ uint8_t output_id;
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+};
+
+struct vfe_message {
+ enum VFE31_MESSAGE_ID _d;
+ union {
+ struct vfe_msg_output msgOut;
+ struct vfe_msg_stats msgStats;
+ struct vfe_msg_camif_status msgCamifError;
+ } _u;
+};
+
+/* New one for 7x30 */
+struct msm_vfe31_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+#define V31_PREVIEW_AXI_FLAG 0x00000001
+#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe31_cmd_type {
+ uint16_t id;
+ uint32_t length;
+ uint32_t offset;
+ uint32_t flag;
+};
+
+struct vfe31_free_buf {
+ spinlock_t f_lock;
+ uint8_t available;
+ uint32_t paddr;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+};
+
+struct vfe31_output_ch {
+ struct vfe31_free_buf free_buf;
+ uint16_t output_fmt;
+ int8_t ch0;
+ int8_t ch1;
+ int8_t ch2;
+ uint32_t capture_cnt;
+ uint32_t frame_drop_cnt;
+};
+
+/* no error irq in mask 0 */
+#define VFE31_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE31_IMASK_ERROR_ONLY_1 0x003fffff
+
+struct vfe31_output_path {
+ uint16_t output_mode; /* bitmask */
+
+ struct vfe31_output_ch out0; /* preview and thumbnail */
+ struct vfe31_output_ch out1; /* snapshot */
+ struct vfe31_output_ch out2; /* video */
+};
+
+struct vfe31_frame_extra {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+
+ uint32_t yWrPmStats0;
+ uint32_t yWrPmStats1;
+ uint32_t cbcrWrPmStats0;
+ uint32_t cbcrWrPmStats1;
+
+ uint32_t frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS 0
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+
+#define VFE_VERSION 0x00000000
+#define VFE_GLOBAL_RESET 0x00000004
+#define VFE_CGC_OVERRIDE 0x0000000C
+#define VFE_MODULE_CFG 0x00000010
+#define VFE_CFG_OFF 0x00000014
+#define VFE_IRQ_CMD 0x00000018
+#define VFE_IRQ_MASK_0 0x0000001C
+#define VFE_IRQ_MASK_1 0x00000020
+#define VFE_IRQ_CLEAR_0 0x00000024
+#define VFE_IRQ_CLEAR_1 0x00000028
+#define VFE_IRQ_STATUS_0 0x0000002C
+#define VFE_IRQ_STATUS_1 0x00000030
+#define VFE_IRQ_COMP_MASK 0x00000034
+#define VFE_BUS_CMD 0x00000038
+#define VFE_AXI_OFFSET 0x00000050
+#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG 0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR 0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR 0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG 0x00000120
+#define VFE_BUS_STATS_CS_WR_PING_ADDR 0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR 0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG 0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144
+#define VFE_BUS_PING_PONG_STATUS 0x00000180
+#define VFE_AXI_CMD 0x000001D8
+#define VFE_AXI_STATUS 0x000001DC
+#define VFE_CAMIF_COMMAND 0x000001E0
+#define VFE_CAMIF_STATUS 0x00000204
+#define VFE_REG_UPDATE_CMD 0x00000260
+#define VFE_DEMUX_GAIN_0 0x00000288
+#define VFE_DEMUX_GAIN_1 0x0000028C
+#define VFE_CHROMA_UP 0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG 0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG 0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN 0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN 0x00000510
+#define VFE_FRAMEDROP_VIEW_Y 0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR 0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN 0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520
+#define VFE_CLAMP_MAX 0x00000524
+#define VFE_CLAMP_MIN 0x00000528
+#define VFE_REALIGN_BUF 0x0000052C
+#define VFE_STATS_CFG 0x00000530
+#define VFE_DMI_CFG 0x00000598
+#define VFE_DMI_ADDR 0x0000059C
+#define VFE_DMI_DATA_LO 0x000005A4
+
+struct vfe_stats_control {
+ uint8_t ackPending;
+ uint32_t nextFrameAddrBuf;
+ uint32_t droppedStatsFrameCount;
+ uint32_t bufToRender;
+};
+
+struct vfe31_ctrl_type {
+ uint16_t operation_mode; /* streaming or snapshot */
+ struct vfe31_output_path outpath;
+
+ uint32_t vfeImaskCompositePacked;
+
+ spinlock_t stop_flag_lock; /* protects stop_ack_pending */
+ spinlock_t update_ack_lock; /* protects update_ack_pending */
+ spinlock_t state_lock; /* protects vstate */
+ spinlock_t aec_ack_lock; /* protects aecStatsControl.ackPending */
+ spinlock_t awb_ack_lock; /* protects awbStatsControl.ackPending */
+ spinlock_t af_ack_lock; /* protects afStatsControl.ackPending */
+ spinlock_t ihist_ack_lock; /* protects ihistStatsControl.ackPending */
+ spinlock_t rs_ack_lock; /* protects rsStatsControl.ackPending */
+ spinlock_t cs_ack_lock; /* protects csStatsControl.ackPending */
+
+ struct msm_vfe_callback *resp;
+ uint32_t extlen;
+ void *extdata;
+
+ int8_t start_ack_pending;
+ int8_t stop_ack_pending;
+ int8_t reset_ack_pending;
+ int8_t update_ack_pending;
+ int8_t req_start_video_rec;
+ int8_t req_stop_video_rec;
+
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ int vfeirq;
+ void __iomem *vfebase;
+ void *syncdata;
+
+ struct resource *vfemem;
+ struct resource *vfeio;
+
+ uint32_t stats_comp;
+ uint8_t vstate;
+ uint32_t vfe_capture_count;
+
+ uint32_t vfeFrameId;
+ uint32_t output1Pattern;
+ uint32_t output1Period;
+ uint32_t output2Pattern;
+ uint32_t output2Period;
+ uint32_t vfeFrameSkipCount;
+ uint32_t vfeFrameSkipPeriod;
+ struct vfe_stats_control afStatsControl;
+ struct vfe_stats_control awbStatsControl;
+ struct vfe_stats_control aecStatsControl;
+ struct vfe_stats_control ihistStatsControl;
+ struct vfe_stats_control rsStatsControl;
+ struct vfe_stats_control csStatsControl;
+};
+
+#define statsAeNum 0
+#define statsAfNum 1
+#define statsAwbNum 2
+#define statsRsNum 3
+#define statsCsNum 4
+#define statsIhistNum 5
+#define statsSkinNum 6
+
+struct vfe_cmd_stats_ack{
+ uint32_t nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_buf{
+ uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+
+#define VFE31_OUTPUT_MODE_PT (0x1 << 0)
+#define VFE31_OUTPUT_MODE_S (0x1 << 1)
+#define VFE31_OUTPUT_MODE_V (0x1 << 2)
+
+#endif /* __MSM_VFE31_H__ */
diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c
new file mode 100644
index 0000000..0812bf5
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.c
@@ -0,0 +1,712 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/clk.h>
+#include <mach/msm_adsp.h>
+#include "msm_vfe7x.h"
+
+#define QDSP_CMDQUEUE QDSP_vfeCommandQueue
+
+#define VFE_RESET_CMD 0
+#define VFE_START_CMD 1
+#define VFE_STOP_CMD 2
+#define VFE_FRAME_ACK 20
+#define STATS_AF_ACK 21
+#define STATS_WE_ACK 22
+
+#define MSG_STOP_ACK 1
+#define MSG_SNAPSHOT 2
+#define MSG_OUTPUT1 6
+#define MSG_OUTPUT2 7
+#define MSG_STATS_AF 8
+#define MSG_STATS_WE 9
+
+static struct msm_adsp_module *qcam_mod;
+static struct msm_adsp_module *vfe_mod;
+static struct msm_vfe_callback *resp;
+static struct vfe_frame_extra *extdata;
+
+struct mutex vfe_lock;
+static void *vfe_syncdata;
+static uint8_t vfestopped;
+
+static struct stop_event stopevent;
+
+static struct clk *ebi1_clk;
+static const char *const ebi1_clk_name = "ebi1_clk";
+
+static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type,
+ void *data, void **ext, int *elen)
+{
+ switch (type) {
+ case VFE_MSG_OUTPUT1:
+ case VFE_MSG_OUTPUT2:{
+ pinfo->y_phy = ((struct vfe_endframe *)data)->y_address;
+ pinfo->cbcr_phy =
+ ((struct vfe_endframe *)data)->cbcr_address;
+
+ CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+
+ ((struct vfe_frame_extra *)extdata)->bl_evencol =
+ ((struct vfe_endframe *)data)->blacklevelevencolumn;
+
+ ((struct vfe_frame_extra *)extdata)->bl_oddcol =
+ ((struct vfe_endframe *)data)->blackleveloddcolumn;
+
+ ((struct vfe_frame_extra *)extdata)->g_def_p_cnt =
+ ((struct vfe_endframe *)data)->
+ greendefectpixelcount;
+
+ ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt =
+ ((struct vfe_endframe *)data)->
+ redbluedefectpixelcount;
+
+ *ext = extdata;
+ *elen = sizeof(*extdata);
+ }
+ break;
+
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_WE:
+ pinfo->sbuf_phy = *(uint32_t *) data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void vfe_7x_ops(void *driver_data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ uint32_t evt_buf[3];
+ struct msm_vfe_resp *rp;
+ void *data;
+
+ len = (id == (uint16_t)-1) ? 0 : len;
+ data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len,
+ vfe_syncdata,
+ GFP_ATOMIC);
+
+ if (!data) {
+ pr_err("rp: cannot allocate buffer\n");
+ return;
+ }
+ rp = (struct msm_vfe_resp *)data;
+ rp->evt_msg.len = len;
+
+ if (id == ((uint16_t)-1)) {
+ /* event */
+ rp->type = VFE_EVENT;
+ rp->evt_msg.type = MSM_CAMERA_EVT;
+ getevent(evt_buf, sizeof(evt_buf));
+ rp->evt_msg.msg_id = evt_buf[0];
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT,
+ vfe_syncdata,
+ GFP_ATOMIC);
+ } else {
+ /* messages */
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+ rp->evt_msg.data = rp + 1;
+ getevent(rp->evt_msg.data, len);
+
+ switch (rp->evt_msg.msg_id) {
+ case MSG_SNAPSHOT:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_OUTPUT1:
+ rp->type = VFE_MSG_OUTPUT1;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT1,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_OUTPUT2:
+ rp->type = VFE_MSG_OUTPUT2;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT2,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_STATS_AF:
+ rp->type = VFE_MSG_STATS_AF;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_STATS_WE:
+ rp->type = VFE_MSG_STATS_WE;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE,
+ rp->evt_msg.data, NULL, NULL);
+
+ CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy);
+ break;
+
+ case MSG_STOP_ACK:
+ rp->type = VFE_MSG_GENERAL;
+ stopevent.state = 1;
+ wake_up(&stopevent.wait);
+ break;
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata, GFP_ATOMIC);
+ }
+}
+
+static struct msm_adsp_ops vfe_7x_sync = {
+ .event = vfe_7x_ops,
+};
+
+static int vfe_7x_enable(struct camera_enable_cmd *enable)
+{
+ int rc = -EFAULT;
+
+ if (!strcmp(enable->name, "QCAMTASK"))
+ rc = msm_adsp_enable(qcam_mod);
+ else if (!strcmp(enable->name, "VFETASK"))
+ rc = msm_adsp_enable(vfe_mod);
+
+ return rc;
+}
+
+static int vfe_7x_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev __attribute__ ((unused)))
+{
+ int rc = -EFAULT;
+
+ if (!strcmp(enable->name, "QCAMTASK"))
+ rc = msm_adsp_disable(qcam_mod);
+ else if (!strcmp(enable->name, "VFETASK"))
+ rc = msm_adsp_disable(vfe_mod);
+
+ return rc;
+}
+
+static int vfe_7x_stop(void)
+{
+ int rc = 0;
+ uint32_t stopcmd = VFE_STOP_CMD;
+ rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE, &stopcmd, sizeof(uint32_t));
+ if (rc < 0) {
+ CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc);
+ return rc;
+ }
+
+ stopevent.state = 0;
+ rc = wait_event_timeout(stopevent.wait,
+ stopevent.state != 0,
+ msecs_to_jiffies(stopevent.timeout));
+
+ return rc;
+}
+
+static void vfe_7x_release(struct platform_device *pdev)
+{
+ struct msm_sensor_ctrl *sctrl =
+ &((struct msm_sync *)vfe_syncdata)->sctrl;
+
+ mutex_lock(&vfe_lock);
+ vfe_syncdata = NULL;
+ mutex_unlock(&vfe_lock);
+
+ if (!vfestopped) {
+ CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__);
+ vfe_7x_stop();
+ } else
+ vfestopped = 0;
+
+ msm_adsp_disable(qcam_mod);
+ msm_adsp_disable(vfe_mod);
+
+ if (sctrl)
+ sctrl->s_release();
+
+ msm_adsp_put(qcam_mod);
+ msm_adsp_put(vfe_mod);
+
+ msm_camio_disable(pdev);
+
+ if (ebi1_clk) {
+ clk_set_rate(ebi1_clk, 0);
+ clk_put(ebi1_clk);
+ ebi1_clk = 0;
+ }
+
+ kfree(extdata);
+ extdata = 0;
+}
+
+static int vfe_7x_init(struct msm_vfe_callback *presp,
+ struct platform_device *dev)
+{
+ int rc = 0;
+
+ init_waitqueue_head(&stopevent.wait);
+ stopevent.timeout = 200;
+ stopevent.state = 0;
+
+ if (presp && presp->vfe_resp)
+ resp = presp;
+ else
+ return -EIO;
+
+ ebi1_clk = clk_get(NULL, ebi1_clk_name);
+ if (!ebi1_clk) {
+ pr_err("%s: could not get %s\n", __func__, ebi1_clk_name);
+ return -EIO;
+ }
+
+ rc = clk_set_rate(ebi1_clk, 128000000);
+ if (rc < 0) {
+ pr_err("%s: clk_set_rate(%s) failed: %d\n", __func__,
+ ebi1_clk_name, rc);
+ return rc;
+ }
+
+ /* Bring up all the required GPIOs and Clocks */
+ rc = msm_camio_enable(dev);
+ if (rc < 0)
+ return rc;
+
+ msm_camio_camif_pad_reg_reset();
+
+ extdata = kmalloc(sizeof(struct vfe_frame_extra), GFP_ATOMIC);
+ if (!extdata) {
+ rc = -ENOMEM;
+ goto init_fail;
+ }
+
+ rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL);
+ if (rc) {
+ rc = -EBUSY;
+ goto get_qcam_fail;
+ }
+
+ rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL);
+ if (rc) {
+ rc = -EBUSY;
+ goto get_vfe_fail;
+ }
+
+ return 0;
+
+get_vfe_fail:
+ msm_adsp_put(qcam_mod);
+get_qcam_fail:
+ kfree(extdata);
+init_fail:
+ return rc;
+}
+
+static int vfe_7x_config_axi(int mode, struct axidata *ad, struct axiout *ao)
+{
+ struct msm_pmem_region *regptr;
+ unsigned long *bptr;
+ int cnt;
+
+ int rc = 0;
+
+ if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+ regptr = ad->region;
+
+ CDBG("bufnum1 = %d\n", ad->bufnum1);
+ CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off);
+
+ bptr = &ao->output1buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+ bptr++;
+ }
+ }
+ /* if OUTPUT1 or Both */
+ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+ regptr = &(ad->region[ad->bufnum1]);
+
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+ CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off);
+
+ bptr = &ao->output2buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+ bptr++;
+ }
+ }
+
+ return rc;
+}
+
+static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ int rc = 0;
+ int i;
+
+ struct msm_pmem_region *regptr = NULL;
+
+ struct vfe_stats_ack sack;
+ struct axidata *axid = NULL;
+
+ struct axiout axio;
+ void *cmd_data_alloc = NULL;
+ struct msm_vfe_command_7k vfecmd;
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(struct msm_vfe_command_7k))) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+ }
+
+ switch (cmd->cmd_type) {
+ case CMD_STATS_AEC_AWB_ENABLE:
+ case CMD_STATS_AXI_CFG:{
+ struct vfe_stats_we_cfg scfg;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ if (vfecmd.length != sizeof(typeof(scfg))) {
+ rc = -EIO;
+ pr_err
+ ("msm_camera: %s: cmd %d: "\
+ "user-space data size %d "\
+ "!= kernel data size %d\n", __func__,
+ cmd->cmd_type, vfecmd.length,
+ sizeof(typeof(scfg)));
+ goto config_error;
+ }
+
+ if (copy_from_user(&scfg,
+ (void __user *)(vfecmd.value),
+ vfecmd.length)) {
+
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n",
+ axid->bufnum1, scfg.wb_expstatsenable);
+
+ if (axid->bufnum1 > 0) {
+ regptr = axid->region;
+
+ for (i = 0; i < axid->bufnum1; i++) {
+
+ CDBG("STATS_ENABLE, phy = 0x%lx\n",
+ regptr->paddr);
+
+ scfg.wb_expstatoutputbuffer[i] =
+ (void *)regptr->paddr;
+ regptr++;
+ }
+
+ vfecmd.value = &scfg;
+
+ } else {
+ rc = -EINVAL;
+ goto config_error;
+ }
+ }
+ break;
+
+ case CMD_STATS_AF_ENABLE:
+ case CMD_STATS_AF_AXI_CFG:{
+ struct vfe_stats_af_cfg sfcfg;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ if (vfecmd.length > sizeof(typeof(sfcfg))) {
+ pr_err
+ ("msm_camera: %s: cmd %d: user-space "\
+ "data %d exceeds kernel buffer %d\n",
+ __func__, cmd->cmd_type, vfecmd.length,
+ sizeof(typeof(sfcfg)));
+ rc = -EIO;
+ goto config_error;
+ }
+
+ if (copy_from_user(&sfcfg,
+ (void __user *)(vfecmd.value),
+ vfecmd.length)) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n",
+ axid->bufnum1, sfcfg.af_enable);
+
+ if (axid->bufnum1 > 0) {
+ regptr = axid->region;
+
+ for (i = 0; i < axid->bufnum1; i++) {
+
+ CDBG("STATS_ENABLE, phy = 0x%lx\n",
+ regptr->paddr);
+
+ sfcfg.af_outbuf[i] =
+ (void *)regptr->paddr;
+
+ regptr++;
+ }
+
+ vfecmd.value = &sfcfg;
+
+ } else {
+ rc = -EINVAL;
+ goto config_error;
+ }
+ }
+ break;
+
+ case CMD_FRAME_BUF_RELEASE:{
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe_outputack fack;
+ if (!data) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ fack.header = VFE_FRAME_ACK;
+
+ fack.output2newybufferaddress = (void *)(p + b->y_off);
+
+ fack.output2newcbcrbufferaddress =
+ (void *)(p + b->cbcr_off);
+
+ vfecmd.queue = QDSP_CMDQUEUE;
+ vfecmd.length = sizeof(struct vfe_outputack);
+ vfecmd.value = &fack;
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+
+ case CMD_STATS_BUF_RELEASE:{
+ CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n");
+ if (!data) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ sack.header = STATS_WE_ACK;
+ sack.bufaddr = (void *)*(uint32_t *) data;
+
+ vfecmd.queue = QDSP_CMDQUEUE;
+ vfecmd.length = sizeof(struct vfe_stats_ack);
+ vfecmd.value = &sack;
+ }
+ break;
+
+ case CMD_STATS_AF_BUF_RELEASE:{
+ CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n");
+ if (!data) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ sack.header = STATS_AF_ACK;
+ sack.bufaddr = (void *)*(uint32_t *) data;
+
+ vfecmd.queue = QDSP_CMDQUEUE;
+ vfecmd.length = sizeof(struct vfe_stats_ack);
+ vfecmd.value = &sack;
+ }
+ break;
+
+ case CMD_GENERAL:
+ case CMD_STATS_DISABLE:{
+ uint8_t buf[256];
+ void *tmp = buf;
+ if (vfecmd.length > sizeof(buf)) {
+ cmd_data_alloc = tmp =
+ kmalloc(vfecmd.length, GFP_ATOMIC);
+ if (!cmd_data_alloc) {
+ rc = -ENOMEM;
+ goto config_error;
+ }
+ }
+
+ if (copy_from_user(tmp,
+ (void __user *)(vfecmd.value),
+ vfecmd.length)) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+ vfecmd.value = tmp;
+
+ if (vfecmd.queue == QDSP_CMDQUEUE) {
+ switch (*(uint32_t *) vfecmd.value) {
+ case VFE_RESET_CMD:
+ msm_camio_vfe_blk_reset();
+ msm_camio_camif_pad_reg_reset_2();
+ vfestopped = 0;
+ break;
+
+ case VFE_START_CMD:
+ msm_camio_camif_pad_reg_reset_2();
+ vfestopped = 0;
+ break;
+
+ case VFE_STOP_CMD:
+ vfestopped = 1;
+ goto config_send;
+
+ default:
+ break;
+ }
+ } /* QDSP_CMDQUEUE */
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT1:{
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ if (copy_from_user(&axio, (void *)(vfecmd.value),
+ sizeof(axio))) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ vfe_7x_config_axi(OUTPUT_1, axid, &axio);
+
+ vfecmd.value = &axio;
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT2:
+ case CMD_RAW_PICT_AXI_CFG:{
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ vfe_7x_config_axi(OUTPUT_2, axid, &axio);
+ vfecmd.value = &axio;
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP_O1_AND_O2:{
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ rc = -EFAULT;
+ goto config_error;
+ }
+
+ vfe_7x_config_axi(OUTPUT_1_AND_2, axid, &axio);
+
+ vfecmd.value = &axio;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ if (vfestopped)
+ goto config_error;
+
+config_send:
+ CDBG("send adsp command = %d\n", *(uint32_t *)vfecmd.value);
+ rc = msm_adsp_write(vfe_mod, vfecmd.queue, vfecmd.value, vfecmd.length);
+
+config_error:
+ kfree(cmd_data_alloc);
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ mutex_init(&vfe_lock);
+ fptr->vfe_init = vfe_7x_init;
+ fptr->vfe_enable = vfe_7x_enable;
+ fptr->vfe_config = vfe_7x_config;
+ fptr->vfe_disable = vfe_7x_disable;
+ fptr->vfe_release = vfe_7x_release;
+ vfe_syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe7x.h b/drivers/media/video/msm/msm_vfe7x.h
new file mode 100644
index 0000000..5e86ce0
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.h
@@ -0,0 +1,269 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __MSM_VFE7X_H__
+#define __MSM_VFE7X_H__
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+struct vfe_frame_extra {
+ uint32_t bl_evencol;
+ uint32_t bl_oddcol;
+ uint16_t g_def_p_cnt;
+ uint16_t r_b_def_p_cnt;
+};
+
+struct vfe_endframe {
+ uint32_t y_address;
+ uint32_t cbcr_address;
+
+ unsigned int blacklevelevencolumn:23;
+ uint16_t reserved1:9;
+ unsigned int blackleveloddcolumn:23;
+ uint16_t reserved2:9;
+
+ uint16_t greendefectpixelcount:8;
+ uint16_t reserved3:8;
+ uint16_t redbluedefectpixelcount:8;
+ uint16_t reserved4:8;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_outputack {
+ uint32_t header;
+ void *output2newybufferaddress;
+ void *output2newcbcrbufferaddress;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_stats_ack {
+ uint32_t header;
+ /* MUST BE 64 bit ALIGNED */
+ void *bufaddr;
+} __attribute__ ((packed, aligned(4)));
+
+/* AXI Output Config Command sent to DSP */
+struct axiout {
+ uint32_t cmdheader:32;
+ int outputmode:3;
+ uint8_t format:2;
+ uint32_t /* reserved */ : 27;
+
+ /* AXI Output 1 Y Configuration, Part 1 */
+ uint32_t out1yimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1yimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 Y Configuration, Part 2 */
+ uint8_t out1yburstlen:2;
+ uint32_t out1ynumrows:12;
+ uint32_t out1yrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 CbCr Configuration, Part 1 */
+ uint32_t out1cbcrimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1cbcrimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 CbCr Configuration, Part 2 */
+ uint8_t out1cbcrburstlen:2;
+ uint32_t out1cbcrnumrows:12;
+ uint32_t out1cbcrrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 Y Configuration, Part 1 */
+ uint32_t out2yimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2yimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 Y Configuration, Part 2 */
+ uint8_t out2yburstlen:2;
+ uint32_t out2ynumrows:12;
+ uint32_t out2yrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 CbCr Configuration, Part 1 */
+ uint32_t out2cbcrimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2cbcrimagewidtein64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 CbCr Configuration, Part 2 */
+ uint8_t out2cbcrburstlen:2;
+ uint32_t out2cbcrnumrows:12;
+ uint32_t out2cbcrrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* Address configuration:
+ * output1 phisycal address */
+ unsigned long output1buffer1_y_phy;
+ unsigned long output1buffer1_cbcr_phy;
+ unsigned long output1buffer2_y_phy;
+ unsigned long output1buffer2_cbcr_phy;
+ unsigned long output1buffer3_y_phy;
+ unsigned long output1buffer3_cbcr_phy;
+ unsigned long output1buffer4_y_phy;
+ unsigned long output1buffer4_cbcr_phy;
+ unsigned long output1buffer5_y_phy;
+ unsigned long output1buffer5_cbcr_phy;
+ unsigned long output1buffer6_y_phy;
+ unsigned long output1buffer6_cbcr_phy;
+ unsigned long output1buffer7_y_phy;
+ unsigned long output1buffer7_cbcr_phy;
+ unsigned long output1buffer8_y_phy;
+ unsigned long output1buffer8_cbcr_phy;
+
+ /* output2 phisycal address */
+ unsigned long output2buffer1_y_phy;
+ unsigned long output2buffer1_cbcr_phy;
+ unsigned long output2buffer2_y_phy;
+ unsigned long output2buffer2_cbcr_phy;
+ unsigned long output2buffer3_y_phy;
+ unsigned long output2buffer3_cbcr_phy;
+ unsigned long output2buffer4_y_phy;
+ unsigned long output2buffer4_cbcr_phy;
+ unsigned long output2buffer5_y_phy;
+ unsigned long output2buffer5_cbcr_phy;
+ unsigned long output2buffer6_y_phy;
+ unsigned long output2buffer6_cbcr_phy;
+ unsigned long output2buffer7_y_phy;
+ unsigned long output2buffer7_cbcr_phy;
+ unsigned long output2buffer8_y_phy;
+ unsigned long output2buffer8_cbcr_phy;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_stats_we_cfg {
+ uint32_t header;
+
+ /* White Balance/Exposure Statistic Selection */
+ uint8_t wb_expstatsenable:1;
+ uint8_t wb_expstatbuspriorityselection:1;
+ unsigned int wb_expstatbuspriorityvalue:4;
+ unsigned int /* reserved */ : 26;
+
+ /* White Balance/Exposure Statistic Configuration, Part 1 */
+ uint8_t exposurestatregions:1;
+ uint8_t exposurestatsubregions:1;
+ unsigned int /* reserved */ : 14;
+
+ unsigned int whitebalanceminimumy:8;
+ unsigned int whitebalancemaximumy:8;
+
+ /* White Balance/Exposure Statistic Configuration, Part 2 */
+ uint8_t
+ wb_expstatslopeofneutralregionline[NUM_WB_EXP_NEUTRAL_REGION_LINES];
+
+ /* White Balance/Exposure Statistic Configuration, Part 3 */
+ unsigned int wb_expstatcrinterceptofneutralregionline2:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int wb_expstatcbinterceptofneutralreginnline1:12;
+ unsigned int /* reserved */ : 4;
+
+ /* White Balance/Exposure Statistic Configuration, Part 4 */
+ unsigned int wb_expstatcrinterceptofneutralregionline4:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int wb_expstatcbinterceptofneutralregionline3:12;
+ unsigned int /* reserved */ : 4;
+
+ /* White Balance/Exposure Statistic Output Buffer Header */
+ unsigned int wb_expmetricheaderpattern:8;
+ unsigned int /* reserved */ : 24;
+
+ /* White Balance/Exposure Statistic Output Buffers-MUST
+ * BE 64 bit ALIGNED */
+ void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS];
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_stats_af_cfg {
+ uint32_t header;
+
+ /* Autofocus Statistic Selection */
+ uint8_t af_enable:1;
+ uint8_t af_busprioritysel:1;
+ unsigned int af_buspriorityval:4;
+ unsigned int /* reserved */ : 26;
+
+ /* Autofocus Statistic Configuration, Part 1 */
+ unsigned int af_singlewinvoffset:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int af_singlewinhoffset:12;
+ unsigned int /* reserved */ : 3;
+ uint8_t af_winmode:1;
+
+ /* Autofocus Statistic Configuration, Part 2 */
+ unsigned int af_singglewinvh:11;
+ unsigned int /* reserved */ : 5;
+ unsigned int af_singlewinhw:11;
+ unsigned int /* reserved */ : 5;
+
+ /* Autofocus Statistic Configuration, Parts 3-6 */
+ uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS];
+
+ /* Autofocus Statistic Configuration, Part 7 */
+ signed int af_metrichpfcoefa00:5;
+ signed int af_metrichpfcoefa04:5;
+ unsigned int af_metricmaxval:11;
+ uint8_t af_metricsel:1;
+ unsigned int /* reserved */ : 10;
+
+ /* Autofocus Statistic Configuration, Part 8 */
+ signed int af_metrichpfcoefa20:5;
+ signed int af_metrichpfcoefa21:5;
+ signed int af_metrichpfcoefa22:5;
+ signed int af_metrichpfcoefa23:5;
+ signed int af_metrichpfcoefa24:5;
+ unsigned int /* reserved */ : 7;
+
+ /* Autofocus Statistic Output Buffer Header */
+ unsigned int af_metrichp:8;
+ unsigned int /* reserved */ : 24;
+
+ /* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */
+ void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS];
+} __attribute__ ((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */
+
+struct msm_camera_frame_msg {
+ unsigned long output_y_address;
+ unsigned long output_cbcr_address;
+
+ unsigned int blacklevelevenColumn:23;
+ uint16_t reserved1:9;
+ unsigned int blackleveloddColumn:23;
+ uint16_t reserved2:9;
+
+ uint16_t greendefectpixelcount:8;
+ uint16_t reserved3:8;
+ uint16_t redbluedefectpixelcount:8;
+ uint16_t reserved4:8;
+} __attribute__ ((packed, aligned(4)));
+
+/* New one for 7k */
+struct msm_vfe_command_7k {
+ uint16_t queue;
+ uint16_t length;
+ void *value;
+};
+
+struct stop_event {
+ wait_queue_head_t wait;
+ int state;
+ int timeout;
+};
+
+#endif /* __MSM_VFE7X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c
new file mode 100644
index 0000000..b4943be
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.c
@@ -0,0 +1,727 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <mach/irqs.h>
+#include <linux/clk.h>
+#include "msm_vfe8x_proc.h"
+
+#define ON 1
+#define OFF 0
+
+static void *vfe_syncdata;
+static struct clk *ebi1_clk;
+static const char *const clk_name = "ebi1_clk";
+
+static int vfe_enable(struct camera_enable_cmd *enable)
+{
+ return 0;
+}
+
+static int vfe_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev)
+{
+ vfe_stop();
+ msm_camio_disable(dev);
+ return 0;
+}
+
+static int vfe_init(struct msm_vfe_callback *presp, struct platform_device *dev)
+{
+ int rc = 0;
+
+ ebi1_clk = clk_get(NULL, clk_name);
+ if (!ebi1_clk) {
+ pr_err("%s: could not get %s\n", __func__, clk_name);
+ return -EIO;
+ }
+
+ rc = clk_set_rate(ebi1_clk, 128000000);
+ if (rc < 0) {
+ pr_err("%s: clk_set_rate(%s) failed: %d\n", __func__,
+ clk_name, rc);
+ return rc;
+ }
+
+ rc = vfe_cmd_init(presp, dev, vfe_syncdata);
+ if (rc < 0)
+ return rc;
+
+ /* Bring up all the required GPIOs and Clocks */
+ return msm_camio_enable(dev);
+}
+
+static void vfe_release(struct platform_device *dev)
+{
+ struct msm_sensor_ctrl *sctrl =
+ &((struct msm_sync *)vfe_syncdata)->sctrl;
+
+ if (ebi1_clk) {
+ clk_set_rate(ebi1_clk, 0);
+ clk_put(ebi1_clk);
+ ebi1_clk = 0;
+ }
+
+ if (sctrl)
+ sctrl->s_release();
+
+ msm_camio_disable(dev);
+ vfe_cmd_release(dev);
+ vfe_syncdata = NULL;
+}
+
+static void vfe_config_axi(int mode,
+ struct axidata *ad,
+ struct vfe_cmd_axi_output_config *ao)
+{
+ struct msm_pmem_region *regptr;
+ int i, j;
+ uint32_t *p1, *p2;
+
+ if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+ regptr = ad->region;
+ for (i = 0; i < ad->bufnum1; i++) {
+
+ p1 = &(ao->output1.outputY.outFragments[i][0]);
+ p2 = &(ao->output1.outputCbcr.outFragments[i][0]);
+
+ for (j = 0; j < ao->output1.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->info.y_off;
+ p1++;
+
+ *p2 = regptr->paddr + regptr->info.cbcr_off;
+ p2++;
+ }
+ regptr++;
+ }
+ }
+ /* if OUTPUT1 or Both */
+ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+
+ regptr = &(ad->region[ad->bufnum1]);
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+
+ for (i = 0; i < ad->bufnum2; i++) {
+
+ p1 = &(ao->output2.outputY.outFragments[i][0]);
+ p2 = &(ao->output2.outputCbcr.outFragments[i][0]);
+
+ CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\
+ "cbcr_off = %d\n", regptr->paddr,
+ regptr->info.y_off, regptr->info.cbcr_off);
+
+ for (j = 0; j < ao->output2.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->info.y_off;
+ CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+ p1++;
+
+ *p2 = regptr->paddr + regptr->info.cbcr_off;
+ CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+ p2++;
+ }
+ regptr++;
+ }
+ }
+}
+
+#define ERR_COPY_FROM_USER() \
+ pr_err("%s(%d): copy from user\n", __func__, __LINE__)
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (cmd->length != sizeof(*(in))) { \
+ pr_err("msm_camera: %s:%d cmd %d: user data size %d " \
+ "!= kernel data size %d\n", \
+ __func__, __LINE__, \
+ cmd->id, cmd->length, sizeof(*(in))); \
+ rc = -EIO; \
+ break; \
+ } \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ sizeof(*(in)))) { \
+ ERR_COPY_FROM_USER(); \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+static int vfe_proc_general(struct msm_vfe_command_8k *cmd)
+{
+ int rc = 0;
+
+ CDBG("%s: cmdID = %d\n", __func__, cmd->id);
+
+ switch (cmd->id) {
+ case VFE_CMD_ID_RESET:
+ msm_camio_vfe_blk_reset();
+ msm_camio_camif_pad_reg_reset_2();
+ vfe_reset();
+ break;
+
+ case VFE_CMD_ID_START:{
+ struct vfe_cmd_start start;
+ CHECKED_COPY_FROM_USER(&start);
+
+ /* msm_camio_camif_pad_reg_reset_2(); */
+ msm_camio_camif_pad_reg_reset();
+ vfe_start(&start);
+ }
+ break;
+
+ case VFE_CMD_ID_CAMIF_CONFIG:{
+ struct vfe_cmd_camif_config camif;
+ CHECKED_COPY_FROM_USER(&camif);
+
+ vfe_camif_config(&camif);
+ }
+ break;
+
+ case VFE_CMD_ID_BLACK_LEVEL_CONFIG:{
+ struct vfe_cmd_black_level_config bl;
+ CHECKED_COPY_FROM_USER(&bl);
+
+ vfe_black_level_config(&bl);
+ }
+ break;
+
+ case VFE_CMD_ID_ROLL_OFF_CONFIG:{
+ /* rolloff is too big to be on the stack */
+ struct vfe_cmd_roll_off_config *rolloff =
+ kmalloc(sizeof(struct vfe_cmd_roll_off_config),
+ GFP_KERNEL);
+ if (!rolloff) {
+ pr_err("%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ break;
+ }
+ /* Wrap CHECKED_COPY_FROM_USER() in a do-while(0) loop
+ * to make sure we free rolloff when copy_from_user()
+ * fails.
+ */
+ do {
+ CHECKED_COPY_FROM_USER(rolloff);
+ vfe_roll_off_config(rolloff);
+ } while (0);
+ kfree(rolloff);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG:{
+ struct vfe_cmd_demux_channel_gain_config demuxc;
+ CHECKED_COPY_FROM_USER(&demuxc);
+
+ /* demux is always enabled. */
+ vfe_demux_channel_gain_config(&demuxc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_CONFIG:{
+ struct vfe_cmd_demosaic_config demosaic;
+ CHECKED_COPY_FROM_USER(&demosaic);
+
+ vfe_demosaic_config(&demosaic);
+ }
+ break;
+
+ case VFE_CMD_ID_FOV_CROP_CONFIG:
+ case VFE_CMD_ID_FOV_CROP_UPDATE:{
+ struct vfe_cmd_fov_crop_config fov;
+ CHECKED_COPY_FROM_USER(&fov);
+
+ vfe_fov_crop_config(&fov);
+ }
+ break;
+
+ case VFE_CMD_ID_MAIN_SCALER_CONFIG:
+ case VFE_CMD_ID_MAIN_SCALER_UPDATE:{
+ struct vfe_cmd_main_scaler_config mainds;
+ CHECKED_COPY_FROM_USER(&mainds);
+
+ vfe_main_scaler_config(&mainds);
+ }
+ break;
+
+ case VFE_CMD_ID_WHITE_BALANCE_CONFIG:
+ case VFE_CMD_ID_WHITE_BALANCE_UPDATE:{
+ struct vfe_cmd_white_balance_config wb;
+ CHECKED_COPY_FROM_USER(&wb);
+
+ vfe_white_balance_config(&wb);
+ }
+ break;
+
+ case VFE_CMD_ID_COLOR_CORRECTION_CONFIG:
+ case VFE_CMD_ID_COLOR_CORRECTION_UPDATE:{
+ struct vfe_cmd_color_correction_config cc;
+ CHECKED_COPY_FROM_USER(&cc);
+
+ vfe_color_correction_config(&cc);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_CONFIG:{
+ struct vfe_cmd_la_config la;
+ CHECKED_COPY_FROM_USER(&la);
+
+ vfe_la_config(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_CONFIG:{
+ struct vfe_cmd_rgb_gamma_config rgb;
+ CHECKED_COPY_FROM_USER(&rgb);
+
+ rc = vfe_rgb_gamma_config(&rgb);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_ENHAN_CONFIG:
+ case VFE_CMD_ID_CHROMA_ENHAN_UPDATE:{
+ struct vfe_cmd_chroma_enhan_config chrom;
+ CHECKED_COPY_FROM_USER(&chrom);
+
+ vfe_chroma_enhan_config(&chrom);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG:
+ case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE:{
+ struct vfe_cmd_chroma_suppression_config chromsup;
+ CHECKED_COPY_FROM_USER(&chromsup);
+
+ vfe_chroma_sup_config(&chromsup);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_CONFIG:{
+ struct vfe_cmd_asf_config asf;
+ CHECKED_COPY_FROM_USER(&asf);
+
+ vfe_asf_config(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2Y_CONFIG:
+ case VFE_CMD_ID_SCALER2Y_UPDATE:{
+ struct vfe_cmd_scaler2_config ds2y;
+ CHECKED_COPY_FROM_USER(&ds2y);
+
+ vfe_scaler2y_config(&ds2y);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2CbCr_CONFIG:
+ case VFE_CMD_ID_SCALER2CbCr_UPDATE:{
+ struct vfe_cmd_scaler2_config ds2cbcr;
+ CHECKED_COPY_FROM_USER(&ds2cbcr);
+
+ vfe_scaler2cbcr_config(&ds2cbcr);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG:{
+ struct vfe_cmd_chroma_subsample_config sub;
+ CHECKED_COPY_FROM_USER(&sub);
+
+ vfe_chroma_subsample_config(&sub);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_CONFIG:{
+ struct vfe_cmd_frame_skip_config fskip;
+ CHECKED_COPY_FROM_USER(&fskip);
+
+ vfe_frame_skip_config(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG:{
+ struct vfe_cmd_output_clamp_config clamp;
+ CHECKED_COPY_FROM_USER(&clamp);
+
+ vfe_output_clamp_config(&clamp);
+ }
+ break;
+
+ /* module update commands */
+ case VFE_CMD_ID_BLACK_LEVEL_UPDATE:{
+ struct vfe_cmd_black_level_config blk;
+ CHECKED_COPY_FROM_USER(&blk);
+
+ vfe_black_level_update(&blk);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE:{
+ struct vfe_cmd_demux_channel_gain_config dmu;
+ CHECKED_COPY_FROM_USER(&dmu);
+
+ vfe_demux_channel_gain_update(&dmu);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE:{
+ struct vfe_cmd_demosaic_bpc_update demo_bpc;
+ CHECKED_COPY_FROM_USER(&demo_bpc);
+
+ vfe_demosaic_bpc_update(&demo_bpc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE:{
+ struct vfe_cmd_demosaic_abf_update demo_abf;
+ CHECKED_COPY_FROM_USER(&demo_abf);
+
+ vfe_demosaic_abf_update(&demo_abf);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_UPDATE:{
+ struct vfe_cmd_la_config la;
+ CHECKED_COPY_FROM_USER(&la);
+
+ vfe_la_update(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_UPDATE:{
+ struct vfe_cmd_rgb_gamma_config rgb;
+ CHECKED_COPY_FROM_USER(&rgb);
+
+ rc = vfe_rgb_gamma_update(&rgb);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_UPDATE:{
+ struct vfe_cmd_asf_update asf;
+ CHECKED_COPY_FROM_USER(&asf);
+
+ vfe_asf_update(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_UPDATE:{
+ struct vfe_cmd_frame_skip_update fskip;
+ CHECKED_COPY_FROM_USER(&fskip);
+
+ vfe_frame_skip_update(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_CAMIF_FRAME_UPDATE:{
+ struct vfe_cmds_camif_frame fup;
+ CHECKED_COPY_FROM_USER(&fup);
+
+ vfe_camif_frame_update(&fup);
+ }
+ break;
+
+ /* stats update commands */
+ case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE:{
+ struct vfe_cmd_stats_af_update afup;
+ CHECKED_COPY_FROM_USER(&afup);
+
+ vfe_stats_update_af(&afup);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_UPDATE:{
+ struct vfe_cmd_stats_wb_exp_update wbexp;
+ CHECKED_COPY_FROM_USER(&wbexp);
+
+ vfe_stats_update_wb_exp(&wbexp);
+ }
+ break;
+
+ /* control of start, stop, update, etc... */
+ case VFE_CMD_ID_STOP:
+ vfe_stop();
+ break;
+
+ case VFE_CMD_ID_GET_HW_VERSION:
+ break;
+
+ /* stats */
+ case VFE_CMD_ID_STATS_SETTING:{
+ struct vfe_cmd_stats_setting stats;
+ CHECKED_COPY_FROM_USER(&stats);
+
+ vfe_stats_setting(&stats);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_AUTOFOCUS_START:{
+ struct vfe_cmd_stats_af_start af;
+ CHECKED_COPY_FROM_USER(&af);
+
+ vfe_stats_start_af(&af);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_AUTOFOCUS_STOP:
+ vfe_stats_af_stop();
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_START:{
+ struct vfe_cmd_stats_wb_exp_start awexp;
+ CHECKED_COPY_FROM_USER(&awexp);
+
+ vfe_stats_start_wb_exp(&awexp);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_STOP:
+ vfe_stats_wb_exp_stop();
+ break;
+
+ case VFE_CMD_ID_ASYNC_TIMER_SETTING:
+ break;
+
+ case VFE_CMD_ID_UPDATE:
+ vfe_update();
+ break;
+
+ /* test gen */
+ case VFE_CMD_ID_TEST_GEN_START:
+ break;
+
+ case VFE_CMD_ID_EPOCH1_CONFIG:{
+ struct vfe_cmds_camif_epoch epoch1;
+ CHECKED_COPY_FROM_USER(&epoch1);
+ vfe_epoch1_config(&epoch1);
+ }
+ break;
+
+/*
+ acknowledge from upper layer
+ these are not in general command.
+
+ case VFE_CMD_ID_OUTPUT1_ACK:
+ break;
+ case VFE_CMD_ID_OUTPUT2_ACK:
+ break;
+ case VFE_CMD_ID_EPOCH1_ACK:
+ break;
+ case VFE_CMD_ID_EPOCH2_ACK:
+ break;
+ case VFE_CMD_ID_STATS_AUTOFOCUS_ACK:
+ break;
+ case VFE_CMD_ID_STATS_WB_EXP_ACK:
+ break;
+*/
+
+ default:
+ pr_err("%s: invalid cmd id %d\n", __func__, cmd->id);
+ rc = -EINVAL;
+ break;
+ } /* switch */
+
+ return rc;
+}
+
+static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ struct msm_pmem_region *regptr;
+ struct msm_vfe_command_8k vfecmd;
+ struct vfe_cmd_axi_output_config axio;
+ struct axidata *axid = data;
+
+ int rc = 0;
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value), sizeof(vfecmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe_proc_general(&vfecmd);
+ break;
+
+ case CMD_STATS_ENABLE:
+ case CMD_STATS_AXI_CFG: {
+ int i;
+ struct vfe_cmd_stats_setting scfg;
+
+ BUG_ON(!axid);
+
+ if (vfecmd.length != sizeof(scfg)) {
+ pr_err
+ ("msm_camera: %s: cmd %d: user-space "\
+ "data size %d != kernel data size %d\n",
+ __func__,
+ cmd->cmd_type, vfecmd.length,
+ sizeof(scfg));
+ return -EIO;
+ }
+
+ if (copy_from_user(&scfg,
+ (void __user *)(vfecmd.value),
+ sizeof(scfg))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ regptr = axid->region;
+ if (axid->bufnum1 > 0) {
+ for (i = 0; i < axid->bufnum1; i++) {
+ scfg.awbBuffer[i] =
+ (uint32_t) (regptr->paddr);
+ regptr++;
+ }
+ }
+
+ if (axid->bufnum2 > 0) {
+ for (i = 0; i < axid->bufnum2; i++) {
+ scfg.afBuffer[i] =
+ (uint32_t) (regptr->paddr);
+ regptr++;
+ }
+ }
+
+ vfe_stats_setting(&scfg);
+ }
+ break;
+
+ case CMD_STATS_AF_AXI_CFG:
+ break;
+
+ case CMD_FRAME_BUF_RELEASE: {
+ /* preview buffer release */
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe_cmd_output_ack fack;
+
+ BUG_ON(!data);
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ fack.ybufaddr[0] = (uint32_t) (p + b->y_off);
+
+ fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off);
+
+ if (b->path == MSM_FRAME_PREV_1)
+ vfe_output1_ack(&fack);
+
+ if (b->path == MSM_FRAME_ENC ||
+ b->path == MSM_FRAME_PREV_2)
+ vfe_output2_ack(&fack);
+
+
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+
+ case CMD_STATS_BUF_RELEASE: {
+ struct vfe_cmd_stats_wb_exp_ack sack;
+
+ BUG_ON(!data);
+
+ sack.nextWbExpOutputBufferAddr = *(uint32_t *) data;
+ vfe_stats_wb_exp_ack(&sack);
+ }
+ break;
+
+ case CMD_STATS_AF_BUF_RELEASE: {
+ struct vfe_cmd_stats_af_ack ack;
+
+ BUG_ON(!data);
+
+ ack.nextAFOutputBufferAddr = *(uint32_t *) data;
+ vfe_stats_af_ack(&ack);
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT1: {
+
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_1, axid, &axio);
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT2:
+ case CMD_RAW_PICT_AXI_CFG: {
+
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_2, axid, &axio);
+
+ axio.outputDataSize = 0;
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_O1_AND_O2:
+ case CMD_AXI_CFG_SNAP_O1_AND_O2: {
+
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_1_AND_2, axid, &axio);
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ fptr->vfe_init = vfe_init;
+ fptr->vfe_enable = vfe_enable;
+ fptr->vfe_config = vfe_config;
+ fptr->vfe_disable = vfe_disable;
+ fptr->vfe_release = vfe_release;
+ vfe_syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h
new file mode 100644
index 0000000..09d5875
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.h
@@ -0,0 +1,913 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __MSM_VFE8X_H__
+#define __MSM_VFE8X_H__
+
+#define TRUE 1
+#define FALSE 0
+#define boolean uint8_t
+
+enum VFE_STATE {
+ VFE_STATE_IDLE,
+ VFE_STATE_ACTIVE
+};
+
+enum vfe_cmd_id {
+ /*
+ *Important! Command_ID are arranged in order.
+ *Don't change!*/
+ VFE_CMD_ID_START,
+ VFE_CMD_ID_RESET,
+
+ /* bus and camif config */
+ VFE_CMD_ID_AXI_INPUT_CONFIG,
+ VFE_CMD_ID_CAMIF_CONFIG,
+ VFE_CMD_ID_AXI_OUTPUT_CONFIG,
+
+ /* module config */
+ VFE_CMD_ID_BLACK_LEVEL_CONFIG,
+ VFE_CMD_ID_ROLL_OFF_CONFIG,
+ VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG,
+ VFE_CMD_ID_DEMOSAIC_CONFIG,
+ VFE_CMD_ID_FOV_CROP_CONFIG,
+ VFE_CMD_ID_MAIN_SCALER_CONFIG,
+ VFE_CMD_ID_WHITE_BALANCE_CONFIG,
+ VFE_CMD_ID_COLOR_CORRECTION_CONFIG,
+ VFE_CMD_ID_LA_CONFIG,
+ VFE_CMD_ID_RGB_GAMMA_CONFIG,
+ VFE_CMD_ID_CHROMA_ENHAN_CONFIG,
+ VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG,
+ VFE_CMD_ID_ASF_CONFIG,
+ VFE_CMD_ID_SCALER2Y_CONFIG,
+ VFE_CMD_ID_SCALER2CbCr_CONFIG,
+ VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG,
+ VFE_CMD_ID_FRAME_SKIP_CONFIG,
+ VFE_CMD_ID_OUTPUT_CLAMP_CONFIG,
+
+ /* test gen */
+ VFE_CMD_ID_TEST_GEN_START,
+
+ VFE_CMD_ID_UPDATE,
+
+ /* ackownledge from upper layer */
+ VFE_CMD_ID_OUTPUT1_ACK,
+ VFE_CMD_ID_OUTPUT2_ACK,
+ VFE_CMD_ID_EPOCH1_ACK,
+ VFE_CMD_ID_EPOCH2_ACK,
+ VFE_CMD_ID_STATS_AUTOFOCUS_ACK,
+ VFE_CMD_ID_STATS_WB_EXP_ACK,
+
+ /* module update commands */
+ VFE_CMD_ID_BLACK_LEVEL_UPDATE,
+ VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE,
+ VFE_CMD_ID_DEMOSAIC_BPC_UPDATE,
+ VFE_CMD_ID_DEMOSAIC_ABF_UPDATE,
+ VFE_CMD_ID_FOV_CROP_UPDATE,
+ VFE_CMD_ID_WHITE_BALANCE_UPDATE,
+ VFE_CMD_ID_COLOR_CORRECTION_UPDATE,
+ VFE_CMD_ID_LA_UPDATE,
+ VFE_CMD_ID_RGB_GAMMA_UPDATE,
+ VFE_CMD_ID_CHROMA_ENHAN_UPDATE,
+ VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE,
+ VFE_CMD_ID_MAIN_SCALER_UPDATE,
+ VFE_CMD_ID_SCALER2CbCr_UPDATE,
+ VFE_CMD_ID_SCALER2Y_UPDATE,
+ VFE_CMD_ID_ASF_UPDATE,
+ VFE_CMD_ID_FRAME_SKIP_UPDATE,
+ VFE_CMD_ID_CAMIF_FRAME_UPDATE,
+
+ /* stats update commands */
+ VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE,
+ VFE_CMD_ID_STATS_WB_EXP_UPDATE,
+
+ /* control of start, stop, update, etc... */
+ VFE_CMD_ID_STOP,
+ VFE_CMD_ID_GET_HW_VERSION,
+
+ /* stats */
+ VFE_CMD_ID_STATS_SETTING,
+ VFE_CMD_ID_STATS_AUTOFOCUS_START,
+ VFE_CMD_ID_STATS_AUTOFOCUS_STOP,
+ VFE_CMD_ID_STATS_WB_EXP_START,
+ VFE_CMD_ID_STATS_WB_EXP_STOP,
+
+ VFE_CMD_ID_ASYNC_TIMER_SETTING,
+
+ /* epoch1 */
+ VFE_CMD_ID_EPOCH1_CONFIG,
+
+ /* max id */
+ VFE_CMD_ID_MAX
+};
+
+struct vfe_cmd_hw_version {
+ uint32_t minorVersion;
+ uint32_t majorVersion;
+ uint32_t coreVersion;
+};
+
+enum VFE_CAMIF_SYNC_EDGE {
+ VFE_CAMIF_SYNC_EDGE_ActiveHigh,
+ VFE_CAMIF_SYNC_EDGE_ActiveLow
+};
+
+enum VFE_CAMIF_SYNC_MODE {
+ VFE_CAMIF_SYNC_MODE_APS,
+ VFE_CAMIF_SYNC_MODE_EFS,
+ VFE_CAMIF_SYNC_MODE_ELS,
+ VFE_CAMIF_SYNC_MODE_ILLEGAL
+};
+
+struct vfe_cmds_camif_efs {
+ uint8_t efsendofline;
+ uint8_t efsstartofline;
+ uint8_t efsendofframe;
+ uint8_t efsstartofframe;
+};
+
+struct vfe_cmds_camif_frame {
+ uint16_t pixelsPerLine;
+ uint16_t linesPerFrame;
+};
+
+struct vfe_cmds_camif_window {
+ uint16_t firstpixel;
+ uint16_t lastpixel;
+ uint16_t firstline;
+ uint16_t lastline;
+};
+
+enum CAMIF_SUBSAMPLE_FRAME_SKIP {
+ CAMIF_SUBSAMPLE_FRAME_SKIP_0,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_AllFrames,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_2Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_3Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_4Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_5Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_6Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_7Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_8Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_9Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_10Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_11Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_12Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_13Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_14Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_15Frame
+};
+
+struct vfe_cmds_camif_subsample {
+ uint16_t pixelskipmask;
+ uint16_t lineskipmask;
+ enum CAMIF_SUBSAMPLE_FRAME_SKIP frameskip;
+ uint8_t frameskipmode;
+ uint8_t pixelskipwrap;
+};
+
+struct vfe_cmds_camif_epoch {
+ uint8_t enable;
+ uint16_t lineindex;
+};
+
+struct vfe_cmds_camif_cfg {
+ enum VFE_CAMIF_SYNC_EDGE vSyncEdge;
+ enum VFE_CAMIF_SYNC_EDGE hSyncEdge;
+ enum VFE_CAMIF_SYNC_MODE syncMode;
+ uint8_t vfeSubSampleEnable;
+ uint8_t busSubSampleEnable;
+ uint8_t irqSubSampleEnable;
+ uint8_t binningEnable;
+ uint8_t misrEnable;
+};
+
+struct vfe_cmd_camif_config {
+ struct vfe_cmds_camif_cfg camifConfig;
+ struct vfe_cmds_camif_efs EFS;
+ struct vfe_cmds_camif_frame frame;
+ struct vfe_cmds_camif_window window;
+ struct vfe_cmds_camif_subsample subsample;
+ struct vfe_cmds_camif_epoch epoch1;
+ struct vfe_cmds_camif_epoch epoch2;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+ VFE_AXI_OUTPUT_MODE_Output1,
+ VFE_AXI_OUTPUT_MODE_Output2,
+ VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+ VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+ VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+ VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+ VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+ VFE_RAW_OUTPUT_DISABLED,
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+ VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+ VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+enum VFE_RAW_PIXEL_DATA_SIZE {
+ VFE_RAW_PIXEL_DATA_SIZE_8BIT,
+ VFE_RAW_PIXEL_DATA_SIZE_10BIT,
+ VFE_RAW_PIXEL_DATA_SIZE_12BIT,
+};
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH 4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3
+
+struct vfe_cmds_axi_out_per_component {
+ uint16_t imageWidth;
+ uint16_t imageHeight;
+ uint16_t outRowCount;
+ uint16_t outRowIncrement;
+ uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+ [VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+ uint8_t fragmentCount;
+ struct vfe_cmds_axi_out_per_component outputY;
+ struct vfe_cmds_axi_out_per_component outputCbcr;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+ VFE_AXI_BURST_LENGTH_IS_2 = 2,
+ VFE_AXI_BURST_LENGTH_IS_4 = 4,
+ VFE_AXI_BURST_LENGTH_IS_8 = 8,
+ VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+struct vfe_cmd_axi_output_config {
+ enum VFE_AXI_BURST_LENGTH burstLength;
+ enum VFE_AXI_OUTPUT_MODE outputMode;
+ enum VFE_RAW_PIXEL_DATA_SIZE outputDataSize;
+ struct vfe_cmds_axi_per_output_path output1;
+ struct vfe_cmds_axi_per_output_path output2;
+};
+
+struct vfe_cmd_fov_crop_config {
+ uint8_t enable;
+ uint16_t firstPixel;
+ uint16_t lastPixel;
+ uint16_t firstLine;
+ uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+ uint16_t MNCounterInit;
+ uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+ uint8_t enable;
+ uint16_t inputSize;
+ uint16_t outputSize;
+ uint32_t phaseMultiplicationFactor;
+ uint8_t interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+ struct vfe_cmds_main_scaler_stripe_init MNInitH;
+ struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+struct vfe_cmd_frame_skip_config {
+ uint8_t output1Period;
+ uint32_t output1Pattern;
+ uint8_t output2Period;
+ uint32_t output2Pattern;
+};
+
+struct vfe_cmd_frame_skip_update {
+ uint32_t output1Pattern;
+ uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+ uint8_t minCh0;
+ uint8_t minCh1;
+ uint8_t minCh2;
+ uint8_t maxCh0;
+ uint8_t maxCh1;
+ uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+ uint8_t enable;
+ uint8_t cropEnable;
+ uint8_t vsubSampleEnable;
+ uint8_t hsubSampleEnable;
+ uint8_t vCosited;
+ uint8_t hCosited;
+ uint8_t vCositedPhase;
+ uint8_t hCositedPhase;
+ uint16_t cropWidthFirstPixel;
+ uint16_t cropWidthLastPixel;
+ uint16_t cropHeightFirstLine;
+ uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_INPUT_SOURCE {
+ VFE_START_INPUT_SOURCE_CAMIF,
+ VFE_START_INPUT_SOURCE_TESTGEN,
+ VFE_START_INPUT_SOURCE_AXI,
+ VFE_START_INPUT_SOURCE_INVALID
+};
+
+enum VFE_START_OPERATION_MODE {
+ VFE_START_OPERATION_MODE_CONTINUOUS,
+ VFE_START_OPERATION_MODE_SNAPSHOT
+};
+
+enum VFE_START_PIXEL_PATTERN {
+ VFE_BAYER_RGRGRG,
+ VFE_BAYER_GRGRGR,
+ VFE_BAYER_BGBGBG,
+ VFE_BAYER_GBGBGB,
+ VFE_YUV_YCbYCr,
+ VFE_YUV_YCrYCb,
+ VFE_YUV_CbYCrY,
+ VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+ VFE_BAYER_RAW,
+ VFE_YUV_INTERLEAVED,
+ VFE_YUV_PSEUDO_PLANAR_Y,
+ VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+ VFE_YUV_COSITED,
+ VFE_YUV_INTERPOLATED
+};
+
+struct vfe_cmd_start {
+ enum VFE_START_INPUT_SOURCE inputSource;
+ enum VFE_START_OPERATION_MODE operationMode;
+ uint8_t snapshotCount;
+ enum VFE_START_PIXEL_PATTERN pixel;
+ enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode;
+};
+
+struct vfe_cmd_output_ack {
+ uint32_t ybufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+ uint32_t chromabufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_setting {
+ uint16_t frameHDimension;
+ uint16_t frameVDimension;
+ uint8_t afBusPrioritySelection;
+ uint8_t afBusPriority;
+ uint8_t awbBusPrioritySelection;
+ uint8_t awbBusPriority;
+ uint8_t histBusPrioritySelection;
+ uint8_t histBusPriority;
+ uint32_t afBuffer[VFE_STATS_BUFFER_COUNT];
+ uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT];
+ uint32_t histBuffer[VFE_STATS_BUFFER_COUNT];
+};
+
+struct vfe_cmd_stats_af_start {
+ uint8_t enable;
+ uint8_t windowMode;
+ uint16_t windowHOffset;
+ uint16_t windowVOffset;
+ uint16_t windowWidth;
+ uint16_t windowHeight;
+ uint8_t gridForMultiWindows[16];
+ uint8_t metricSelection;
+ int16_t metricMax;
+ int8_t highPassCoef[7];
+ int8_t bufferHeader;
+};
+
+struct vfe_cmd_stats_af_update {
+ uint8_t windowMode;
+ uint16_t windowHOffset;
+ uint16_t windowVOffset;
+ uint16_t windowWidth;
+ uint16_t windowHeight;
+};
+
+struct vfe_cmd_stats_wb_exp_start {
+ uint8_t enable;
+ uint8_t wbExpRegions;
+ uint8_t wbExpSubRegion;
+ uint8_t awbYMin;
+ uint8_t awbYMax;
+ int8_t awbMCFG[4];
+ int16_t awbCCFG[4];
+ int8_t axwHeader;
+};
+
+struct vfe_cmd_stats_wb_exp_update {
+ uint8_t wbExpRegions;
+ uint8_t wbExpSubRegion;
+ int8_t awbYMin;
+ int8_t awbYMax;
+ int8_t awbMCFG[4];
+ int16_t awbCCFG[4];
+};
+
+struct vfe_cmd_stats_af_ack {
+ uint32_t nextAFOutputBufferAddr;
+};
+
+struct vfe_cmd_stats_wb_exp_ack {
+ uint32_t nextWbExpOutputBufferAddr;
+};
+
+struct vfe_cmd_black_level_config {
+ uint8_t enable;
+ uint16_t evenEvenAdjustment;
+ uint16_t evenOddAdjustment;
+ uint16_t oddEvenAdjustment;
+ uint16_t oddOddAdjustment;
+};
+
+/* 13*1 */
+#define VFE_ROLL_OFF_INIT_TABLE_SIZE 13
+/* 13*16 */
+#define VFE_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+struct vfe_cmd_roll_off_config {
+ uint8_t enable;
+ uint16_t gridWidth;
+ uint16_t gridHeight;
+ uint16_t yDelta;
+ uint8_t gridXIndex;
+ uint8_t gridYIndex;
+ uint16_t gridPixelXIndex;
+ uint16_t gridPixelYIndex;
+ uint16_t yDeltaAccum;
+ uint16_t initTableR[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+};
+
+struct vfe_cmd_demux_channel_gain_config {
+ uint16_t ch0EvenGain;
+ uint16_t ch0OddGain;
+ uint16_t ch1Gain;
+ uint16_t ch2Gain;
+};
+
+struct vfe_cmds_demosaic_abf {
+ uint8_t enable;
+ uint8_t forceOn;
+ uint8_t shift;
+ uint16_t lpThreshold;
+ uint16_t max;
+ uint16_t min;
+ uint8_t ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+ uint8_t enable;
+ uint16_t fmaxThreshold;
+ uint16_t fminThreshold;
+ uint16_t redDiffThreshold;
+ uint16_t blueDiffThreshold;
+ uint16_t greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+ uint8_t enable;
+ uint8_t slopeShift;
+ struct vfe_cmds_demosaic_abf abfConfig;
+ struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+ struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+ struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+ uint8_t enable;
+ uint16_t ch2Gain;
+ uint16_t ch1Gain;
+ uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+ COEF_IS_Q7_SIGNED,
+ COEF_IS_Q8_SIGNED,
+ COEF_IS_Q9_SIGNED,
+ COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+ uint8_t enable;
+ enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+ int16_t C0;
+ int16_t C1;
+ int16_t C2;
+ int16_t C3;
+ int16_t C4;
+ int16_t C5;
+ int16_t C6;
+ int16_t C7;
+ int16_t C8;
+ int16_t K0;
+ int16_t K1;
+ int16_t K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 256
+struct vfe_cmd_la_config {
+ uint8_t enable;
+ int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+ RGB_GAMMA_CH0_SELECTED,
+ RGB_GAMMA_CH1_SELECTED,
+ RGB_GAMMA_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_SELECTED,
+ RGB_GAMMA_CH0_CH2_SELECTED,
+ RGB_GAMMA_CH1_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+ uint8_t enable;
+ enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+ int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+ uint8_t enable;
+ int16_t am;
+ int16_t ap;
+ int16_t bm;
+ int16_t bp;
+ int16_t cm;
+ int16_t cp;
+ int16_t dm;
+ int16_t dp;
+ int16_t kcr;
+ int16_t kcb;
+ int16_t RGBtoYConversionV0;
+ int16_t RGBtoYConversionV1;
+ int16_t RGBtoYConversionV2;
+ uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+ uint8_t enable;
+ uint8_t m1;
+ uint8_t m3;
+ uint8_t n1;
+ uint8_t n3;
+ uint8_t nn1;
+ uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+ uint16_t cropFirstPixel;
+ uint16_t cropLastPixel;
+ uint16_t cropFirstLine;
+ uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+ VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+ VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+struct vfe_cmd_test_gen_start {
+ uint8_t pixelDataSelect;
+ uint8_t systematicDataSelect;
+ enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge;
+ enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge;
+ uint16_t numFrame;
+ enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize;
+ uint16_t imageWidth;
+ uint16_t imageHeight;
+ uint32_t startOfFrameOffset;
+ uint32_t endOfFrameNOffset;
+ uint16_t startOfLineOffset;
+ uint16_t endOfLineNOffset;
+ uint16_t hbi;
+ uint8_t vblEnable;
+ uint16_t vbl;
+ uint8_t startOfFrameDummyLine;
+ uint8_t endOfFrameDummyLine;
+ uint8_t unicolorBarEnable;
+ uint8_t colorBarsSplitEnable;
+ uint8_t unicolorBarSelect;
+ enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern;
+ uint8_t colorBarsRotatePeriod;
+ uint16_t testGenRandomSeed;
+};
+
+struct vfe_cmd_bus_pm_start {
+ uint8_t output2YWrPmEnable;
+ uint8_t output2CbcrWrPmEnable;
+ uint8_t output1YWrPmEnable;
+ uint8_t output1CbcrWrPmEnable;
+};
+
+struct vfe_cmd_camif_frame_update {
+ struct vfe_cmds_camif_frame camifFrame;
+};
+
+struct vfe_cmd_sync_timer_setting {
+ uint8_t whichSyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t hsyncCount;
+ uint32_t pclkCount;
+ uint32_t outputDuration;
+};
+
+struct vfe_cmd_async_timer_setting {
+ uint8_t whichAsyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t inactiveCount;
+ uint32_t activeCount;
+};
+
+struct vfe_frame_skip_counts {
+ uint32_t totalFrameCount;
+ uint32_t output1Count;
+ uint32_t output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+ VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+struct vfe_cmd_axi_input_config {
+ uint32_t fragAddr[4];
+ uint8_t totalFragmentCount;
+ uint16_t ySize;
+ uint16_t xOffset;
+ uint16_t xSize;
+ uint16_t rowIncrement;
+ uint16_t numOfRows;
+ enum VFE_AXI_BURST_LENGTH burstLength;
+ uint8_t unpackPhase;
+ enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi;
+ enum VFE_RAW_PIXEL_DATA_SIZE pixelSize;
+ uint8_t padRepeatCountLeft;
+ uint8_t padRepeatCountRight;
+ uint8_t padRepeatCountTop;
+ uint8_t padRepeatCountBottom;
+ uint8_t padLeftComponentSelectCycle0;
+ uint8_t padLeftComponentSelectCycle1;
+ uint8_t padLeftComponentSelectCycle2;
+ uint8_t padLeftComponentSelectCycle3;
+ uint8_t padLeftStopCycle0;
+ uint8_t padLeftStopCycle1;
+ uint8_t padLeftStopCycle2;
+ uint8_t padLeftStopCycle3;
+ uint8_t padRightComponentSelectCycle0;
+ uint8_t padRightComponentSelectCycle1;
+ uint8_t padRightComponentSelectCycle2;
+ uint8_t padRightComponentSelectCycle3;
+ uint8_t padRightStopCycle0;
+ uint8_t padRightStopCycle1;
+ uint8_t padRightStopCycle2;
+ uint8_t padRightStopCycle3;
+ uint8_t padTopLineCount;
+ uint8_t padBottomLineCount;
+};
+
+struct vfe_interrupt_status {
+ uint8_t camifErrorIrq;
+ uint8_t camifSofIrq;
+ uint8_t camifEolIrq;
+ uint8_t camifEofIrq;
+ uint8_t camifEpoch1Irq;
+ uint8_t camifEpoch2Irq;
+ uint8_t camifOverflowIrq;
+ uint8_t ceIrq;
+ uint8_t regUpdateIrq;
+ uint8_t resetAckIrq;
+ uint8_t encYPingpongIrq;
+ uint8_t encCbcrPingpongIrq;
+ uint8_t viewYPingpongIrq;
+ uint8_t viewCbcrPingpongIrq;
+ uint8_t rdPingpongIrq;
+ uint8_t afPingpongIrq;
+ uint8_t awbPingpongIrq;
+ uint8_t histPingpongIrq;
+ uint8_t encIrq;
+ uint8_t viewIrq;
+ uint8_t busOverflowIrq;
+ uint8_t afOverflowIrq;
+ uint8_t awbOverflowIrq;
+ uint8_t syncTimer0Irq;
+ uint8_t syncTimer1Irq;
+ uint8_t syncTimer2Irq;
+ uint8_t asyncTimer0Irq;
+ uint8_t asyncTimer1Irq;
+ uint8_t asyncTimer2Irq;
+ uint8_t asyncTimer3Irq;
+ uint8_t axiErrorIrq;
+ uint8_t violationIrq;
+ uint8_t anyErrorIrqs;
+ uint8_t anyOutput1PathIrqs;
+ uint8_t anyOutput2PathIrqs;
+ uint8_t anyOutputPathIrqs;
+ uint8_t anyAsyncTimerIrqs;
+ uint8_t anySyncTimerIrqs;
+ uint8_t anyIrqForActiveStatesOnly;
+};
+
+enum VFE_MESSAGE_ID {
+ VFE_MSG_ID_RESET_ACK,
+ VFE_MSG_ID_START_ACK,
+ VFE_MSG_ID_STOP_ACK,
+ VFE_MSG_ID_UPDATE_ACK,
+ VFE_MSG_ID_OUTPUT1,
+ VFE_MSG_ID_OUTPUT2,
+ VFE_MSG_ID_SNAPSHOT_DONE,
+ VFE_MSG_ID_STATS_AUTOFOCUS,
+ VFE_MSG_ID_STATS_WB_EXP,
+ VFE_MSG_ID_EPOCH1,
+ VFE_MSG_ID_EPOCH2,
+ VFE_MSG_ID_SYNC_TIMER0_DONE,
+ VFE_MSG_ID_SYNC_TIMER1_DONE,
+ VFE_MSG_ID_SYNC_TIMER2_DONE,
+ VFE_MSG_ID_ASYNC_TIMER0_DONE,
+ VFE_MSG_ID_ASYNC_TIMER1_DONE,
+ VFE_MSG_ID_ASYNC_TIMER2_DONE,
+ VFE_MSG_ID_ASYNC_TIMER3_DONE,
+ VFE_MSG_ID_AF_OVERFLOW,
+ VFE_MSG_ID_AWB_OVERFLOW,
+ VFE_MSG_ID_AXI_ERROR,
+ VFE_MSG_ID_CAMIF_OVERFLOW,
+ VFE_MSG_ID_VIOLATION,
+ VFE_MSG_ID_CAMIF_ERROR,
+ VFE_MSG_ID_BUS_OVERFLOW,
+};
+
+struct vfe_msg_stats_autofocus {
+ uint32_t afBuffer;
+ uint32_t frameCounter;
+};
+
+struct vfe_msg_stats_wb_exp {
+ uint32_t awbBuffer;
+ uint32_t frameCounter;
+};
+
+struct vfe_frame_bpc_info {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+ uint8_t camifState;
+ uint32_t pixelCount;
+ uint32_t lineCount;
+};
+
+struct vfe_bus_pm_per_path {
+ uint32_t yWrPmStats0;
+ uint32_t yWrPmStats1;
+ uint32_t cbcrWrPmStats0;
+ uint32_t cbcrWrPmStats1;
+};
+
+struct vfe_bus_performance_monitor {
+ struct vfe_bus_pm_per_path encPathPmInfo;
+ struct vfe_bus_pm_per_path viewPathPmInfo;
+};
+
+struct vfe_irq_thread_msg {
+ uint32_t vfeIrqStatus;
+ uint32_t camifStatus;
+ uint32_t demosaicStatus;
+ uint32_t asfMaxEdge;
+ struct vfe_bus_performance_monitor pmInfo;
+};
+
+struct vfe_msg_output {
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+ struct vfe_bus_pm_per_path pmData;
+};
+
+struct vfe_message {
+ enum VFE_MESSAGE_ID _d;
+ union {
+ struct vfe_msg_output msgOutput1;
+ struct vfe_msg_output msgOutput2;
+ struct vfe_msg_stats_autofocus msgStatsAf;
+ struct vfe_msg_stats_wb_exp msgStatsWbExp;
+ struct vfe_msg_camif_status msgCamifError;
+ struct vfe_bus_performance_monitor msgBusOverflow;
+ } _u;
+};
+
+/* New one for 8k */
+struct msm_vfe_command_8k {
+ int id;
+ uint16_t length;
+ void *value;
+};
+
+struct vfe_frame_extra {
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+ struct vfe_bus_pm_per_path pmData;
+};
+#endif /* __MSM_VFE8X_H__ */
diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c
new file mode 100644
index 0000000..23ea197
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.c
@@ -0,0 +1,3827 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include "msm_vfe8x_proc.h"
+#include <media/msm_camera.h>
+#include <mach/board.h>
+
+struct isr_queue_cmd {
+ struct list_head list;
+ struct vfe_interrupt_status vfeInterruptStatus;
+ struct vfe_frame_asf_info vfeAsfFrameInfo;
+ struct vfe_frame_bpc_info vfeBpcFrameInfo;
+ struct vfe_msg_camif_status vfeCamifStatusLocal;
+ struct vfe_bus_performance_monitor vfePmData;
+};
+
+struct msm_vfe8x_ctrl {
+ /* bit 1:0 ENC_IRQ_MASK = 0x11:
+ * generate IRQ when both y and cbcr frame is ready. */
+
+ /* bit 1:0 VIEW_IRQ_MASK= 0x11:
+ * generate IRQ when both y and cbcr frame is ready. */
+ struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal;
+ struct vfe_module_enable vfeModuleEnableLocal;
+ struct vfe_camif_cfg_data vfeCamifConfigLocal;
+ struct vfe_cmds_camif_epoch vfeCamifEpoch1Local;
+ struct vfe_interrupt_mask vfeImaskLocal;
+ struct vfe_stats_cmd_data vfeStatsCmdLocal;
+ struct vfe_bus_cfg_data vfeBusConfigLocal;
+ struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal;
+ struct vfe_bus_cmd_data vfeBusCmdLocal;
+ enum vfe_interrupt_name vfeInterruptNameLocal;
+ uint32_t vfeLaBankSel;
+ struct vfe_gamma_lut_sel vfeGammaLutSel;
+
+ boolean vfeStartAckPendingFlag;
+ boolean vfeStopAckPending;
+ boolean vfeResetAckPending;
+ boolean vfeUpdateAckPending;
+
+ enum VFE_AXI_OUTPUT_MODE axiOutputMode;
+ enum VFE_START_OPERATION_MODE vfeOperationMode;
+
+ uint32_t vfeSnapShotCount;
+ uint32_t vfeRequestedSnapShotCount;
+ boolean vfeStatsPingPongReloadFlag;
+ uint32_t vfeFrameId;
+
+ struct vfe_cmd_frame_skip_config vfeFrameSkip;
+ uint32_t vfeFrameSkipPattern;
+ uint8_t vfeFrameSkipCount;
+ uint8_t vfeFrameSkipPeriod;
+
+ boolean vfeTestGenStartFlag;
+ uint32_t vfeImaskPacked;
+ uint32_t vfeImaskCompositePacked;
+ enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize;
+ struct vfe_irq_thread_msg vfeIrqThreadMsgLocal;
+
+ struct vfe_output_path_combo viewPath;
+ struct vfe_output_path_combo encPath;
+ struct vfe_frame_skip_counts vfeDroppedFrameCounts;
+ struct vfe_stats_control afStatsControl;
+ struct vfe_stats_control awbStatsControl;
+
+ enum VFE_STATE vstate;
+
+ struct msm_vfe_callback *resp;
+ struct vfe_frame_extra extdata;
+
+ struct isr_queue_cmd irqs[5];
+ spinlock_t irqs_lock;
+ int irq_get;
+ int irq_put;
+
+ int vfeirq;
+ void __iomem *vfebase;
+
+ void *syncdata;
+};
+
+static struct msm_vfe8x_ctrl *ctrl;
+
+static void vfe_prog_hw(uint8_t *hwreg, uint32_t *inptr, uint32_t regcnt)
+{
+ /* unsigned long flags; */
+ uint32_t i;
+ uint32_t *p;
+
+ p = (uint32_t *) (hwreg);
+ for (i = 0; i < (regcnt >> 2); i++)
+ writel(*inptr++, p++);
+ /* *p++ = *inptr++; */
+}
+
+static void
+vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath,
+ struct vfe_output_path_combo *epath)
+{
+ vpath->yPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR);
+ vpath->yPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR);
+ vpath->cbcrPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR);
+ vpath->cbcrPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR);
+
+ epath->yPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR);
+ epath->yPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR);
+ epath->cbcrPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR);
+ epath->cbcrPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR);
+}
+
+static void vfe_axi_output(struct vfe_cmd_axi_output_config *in,
+ struct vfe_output_path_combo *out1,
+ struct vfe_output_path_combo *out2, uint16_t out)
+{
+ struct vfe_axi_out_cfg cmd;
+
+ uint16_t temp;
+ uint32_t burstLength;
+
+ memset(&cmd, 0, sizeof(cmd));
+ /* force it to burst length 4, hardware does not support it. */
+ burstLength = 1;
+
+ /* AXI Output 2 Y Configuration */
+ /* VFE_BUS_ENC_Y_WR_PING_ADDR */
+ cmd.out2YPingAddr = out2->yPath.addressBuffer[0];
+
+ /* VFE_BUS_ENC_Y_WR_PONG_ADDR */
+ cmd.out2YPongAddr = out2->yPath.addressBuffer[1];
+
+ /* VFE_BUS_ENC_Y_WR_IMAGE_SIZE */
+ cmd.out2YImageHeight = in->output2.outputY.imageHeight;
+ /* convert the image width and row increment to be in
+ * unit of 64bit (8 bytes) */
+ temp = (in->output2.outputY.imageWidth + (out - 1)) / out;
+ cmd.out2YImageWidthin64bit = temp;
+
+ /* VFE_BUS_ENC_Y_WR_BUFFER_CFG */
+ cmd.out2YBurstLength = burstLength;
+ cmd.out2YNumRows = in->output2.outputY.outRowCount;
+ temp = (in->output2.outputY.outRowIncrement + (out - 1)) / out;
+ cmd.out2YRowIncrementIn64bit = temp;
+
+ /* AXI Output 2 Cbcr Configuration */
+ /* VFE_BUS_ENC_Cbcr_WR_PING_ADDR */
+ cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0];
+
+ /* VFE_BUS_ENC_Cbcr_WR_PONG_ADDR */
+ cmd.out2CbcrPongAddr = out2->cbcrPath.addressBuffer[1];
+
+ /* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */
+ cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight;
+ temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / out;
+ cmd.out2CbcrImageWidthIn64bit = temp;
+
+ /* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */
+ cmd.out2CbcrBurstLength = burstLength;
+ cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount;
+ temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / out;
+ cmd.out2CbcrRowIncrementIn64bit = temp;
+
+ /* AXI Output 1 Y Configuration */
+ /* VFE_BUS_VIEW_Y_WR_PING_ADDR */
+ cmd.out1YPingAddr = out1->yPath.addressBuffer[0];
+
+ /* VFE_BUS_VIEW_Y_WR_PONG_ADDR */
+ cmd.out1YPongAddr = out1->yPath.addressBuffer[1];
+
+ /* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */
+ cmd.out1YImageHeight = in->output1.outputY.imageHeight;
+ temp = (in->output1.outputY.imageWidth + (out - 1)) / out;
+ cmd.out1YImageWidthin64bit = temp;
+
+ /* VFE_BUS_VIEW_Y_WR_BUFFER_CFG */
+ cmd.out1YBurstLength = burstLength;
+ cmd.out1YNumRows = in->output1.outputY.outRowCount;
+
+ temp = (in->output1.outputY.outRowIncrement + (out - 1)) / out;
+ cmd.out1YRowIncrementIn64bit = temp;
+
+ /* AXI Output 1 Cbcr Configuration */
+ cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0];
+
+ /* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR */
+ cmd.out1CbcrPongAddr = out1->cbcrPath.addressBuffer[1];
+
+ /* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */
+ cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight;
+ temp = (in->output1.outputCbcr.imageWidth + (out - 1)) / out;
+ cmd.out1CbcrImageWidthIn64bit = temp;
+
+ cmd.out1CbcrBurstLength = burstLength;
+ cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount;
+ temp = (in->output1.outputCbcr.outRowIncrement + (out - 1)) / out;
+
+ cmd.out1CbcrRowIncrementIn64bit = temp;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in)
+{
+ struct vfe_axi_bus_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.stripeRdPathEn = in->stripeRdPathEn;
+ cmd.encYWrPathEn = in->encYWrPathEn;
+ cmd.encCbcrWrPathEn = in->encCbcrWrPathEn;
+ cmd.viewYWrPathEn = in->viewYWrPathEn;
+ cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn;
+ cmd.rawPixelDataSize = (uint32_t) in->rawPixelDataSize;
+ cmd.rawWritePathSelect = (uint32_t) in->rawWritePathSelect;
+
+ /* program vfe_bus_cfg */
+ writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG);
+}
+
+static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in)
+{
+ struct VFE_CAMIFConfigType cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.VSyncEdge = in->camifCfgFromCmd.vSyncEdge;
+
+ cfg.HSyncEdge = in->camifCfgFromCmd.hSyncEdge;
+
+ cfg.syncMode = in->camifCfgFromCmd.syncMode;
+
+ cfg.vfeSubsampleEnable = in->camifCfgFromCmd.vfeSubSampleEnable;
+
+ cfg.busSubsampleEnable = in->camifCfgFromCmd.busSubSampleEnable;
+
+ cfg.camif2vfeEnable = in->camif2OutputEnable;
+
+ cfg.camif2busEnable = in->camif2BusEnable;
+
+ cfg.irqSubsampleEnable = in->camifCfgFromCmd.irqSubSampleEnable;
+
+ cfg.binningEnable = in->camifCfgFromCmd.binningEnable;
+
+ cfg.misrEnable = in->camifCfgFromCmd.misrEnable;
+
+ /* program camif_config */
+ writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG);
+}
+
+static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in)
+{
+ struct vfe_buscmd cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.stripeReload = in->stripeReload;
+ cmd.busPingpongReload = in->busPingpongReload;
+ cmd.statsPingpongReload = in->statsPingpongReload;
+
+ writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD);
+
+ CDBG("bus command = 0x%x\n", (*((uint32_t *)&cmd)));
+
+ /* this is needed, as the control bits are pulse based.
+ * Don't want to reload bus pingpong again. */
+ in->busPingpongReload = 0;
+ in->statsPingpongReload = 0;
+ in->stripeReload = 0;
+}
+
+static void vfe_reg_module_cfg(struct vfe_module_enable *in)
+{
+ struct vfe_mod_enable ena;
+
+ memset(&ena, 0, sizeof(ena));
+
+ ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable;
+ ena.lensRollOffEnable = in->lensRollOffEnable;
+ ena.demuxEnable = in->demuxEnable;
+ ena.chromaUpsampleEnable = in->chromaUpsampleEnable;
+ ena.demosaicEnable = in->demosaicEnable;
+ ena.statsEnable = in->statsEnable;
+ ena.cropEnable = in->cropEnable;
+ ena.mainScalerEnable = in->mainScalerEnable;
+ ena.whiteBalanceEnable = in->whiteBalanceEnable;
+ ena.colorCorrectionEnable = in->colorCorrectionEnable;
+ ena.yHistEnable = in->yHistEnable;
+ ena.skinToneEnable = in->skinToneEnable;
+ ena.lumaAdaptationEnable = in->lumaAdaptationEnable;
+ ena.rgbLUTEnable = in->rgbLUTEnable;
+ ena.chromaEnhanEnable = in->chromaEnhanEnable;
+ ena.asfEnable = in->asfEnable;
+ ena.chromaSuppressionEnable = in->chromaSuppressionEnable;
+ ena.chromaSubsampleEnable = in->chromaSubsampleEnable;
+ ena.scaler2YEnable = in->scaler2YEnable;
+ ena.scaler2CbcrEnable = in->scaler2CbcrEnable;
+
+ writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG);
+}
+
+static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT;
+
+ value += (uint32_t) bankSel;
+ /* CDBG("dmi cfg input bank is 0x%x\n", bankSel); */
+
+ writel(value, ctrl->vfebase + VFE_DMI_CFG);
+ writel(0, ctrl->vfebase + VFE_DMI_ADDR);
+}
+
+static void vfe_write_lens_roll_off_table(struct vfe_cmd_roll_off_config *in)
+{
+ uint16_t i;
+ uint32_t data;
+
+ uint16_t *initGr = in->initTableGr;
+ uint16_t *initGb = in->initTableGb;
+ uint16_t *initB = in->initTableB;
+ uint16_t *initR = in->initTableR;
+
+ int16_t *pDeltaGr = in->deltaTableGr;
+ int16_t *pDeltaGb = in->deltaTableGb;
+ int16_t *pDeltaB = in->deltaTableB;
+ int16_t *pDeltaR = in->deltaTableR;
+
+ vfe_program_dmi_cfg(ROLLOFF_RAM);
+
+ /* first pack and write init table */
+ for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) {
+ data = (((uint32_t) (*initR)) & 0x0000FFFF) |
+ (((uint32_t) (*initGr)) << 16);
+ initR++;
+ initGr++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+ data = (((uint32_t) (*initB)) & 0x0000FFFF) |
+ (((uint32_t) (*initGr)) << 16);
+ initB++;
+ initGb++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+
+ /* there are gaps between the init table and delta table,
+ * set the offset for delta table. */
+ writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, ctrl->vfebase + VFE_DMI_ADDR);
+
+ /* pack and write delta table */
+ for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) {
+ data = *pDeltaR | (*pDeltaGr << 16);
+ pDeltaR++;
+ pDeltaGr++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+ data = *pDeltaB | (*pDeltaGb << 16);
+ pDeltaB++;
+ pDeltaGb++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+
+ /* After DMI transfer, to make it safe, need to set the
+ * DMI_CFG to unselect any SRAM
+ */
+ /* unselect the SRAM Bank. */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_set_default_reg_values(void)
+{
+ writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_0);
+ writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_1);
+ writel(0xFFFFF, ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+ /* default frame drop period and pattern */
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_CFG);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_CFG);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+ writel(0, ctrl->vfebase + VFE_CLAMP_MIN_CFG);
+ writel(0xFFFFFF, ctrl->vfebase + VFE_CLAMP_MAX_CFG);
+}
+
+static void vfe_config_demux(uint32_t period, uint32_t even, uint32_t odd)
+{
+ writel(period, ctrl->vfebase + VFE_DEMUX_CFG);
+ writel(even, ctrl->vfebase + VFE_DEMUX_EVEN_CFG);
+ writel(odd, ctrl->vfebase + VFE_DEMUX_ODD_CFG);
+}
+
+static void vfe_pm_stop(void)
+{
+ writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static void vfe_camif_stop_immediately(void)
+{
+ writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND);
+ writel(0, ctrl->vfebase + VFE_CGC_OVERRIDE);
+}
+
+static void vfe_program_reg_update_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD);
+}
+
+static void vfe_program_global_reset_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
+
+static void vfe_program_axi_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_AXI_CMD);
+}
+
+static void vfe_program_irq_composite_mask(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+}
+
+static inline void vfe_program_irq_mask(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_IRQ_MASK);
+}
+
+static uint32_t vfe_read_axi_status(void)
+{
+ return readl(ctrl->vfebase + VFE_AXI_STATUS);
+}
+
+static void
+vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl,
+ struct vfe_stats_control *awbControl)
+{
+ afControl->hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ afControl->hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+ awbControl->hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ awbControl->hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in)
+{
+ struct VFE_GammaLutSelect_ConfigCmdType cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0BankSelect = in->ch0BankSelect;
+ cmd.ch1BankSelect = in->ch1BankSelect;
+ cmd.ch2BankSelect = in->ch2BankSelect;
+ CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd));
+ vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in)
+{
+ struct VFE_StatsCmdType stats;
+ memset(&stats, 0, sizeof(stats));
+
+ stats.autoFocusEnable = in->autoFocusEnable;
+ stats.axwEnable = in->axwEnable;
+ stats.histEnable = in->histEnable;
+ stats.clearHistEnable = in->clearHistEnable;
+ stats.histAutoClearEnable = in->histAutoClearEnable;
+ stats.colorConversionEnable = in->colorConversionEnable;
+
+ writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD);
+}
+
+static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+ struct VFE_Bus_Pm_ConfigCmdType cmd;
+ memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType));
+
+ cmd.output2YWrPmEnable = in->output2YWrPmEnable;
+ cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable;
+ cmd.output1YWrPmEnable = in->output1YWrPmEnable;
+ cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+ in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn;
+ in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn;
+ in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn;
+ in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn;
+
+ if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable)
+ ctrl->viewPath.pmEnabled = TRUE;
+
+ if (in->output2CbcrWrPmEnable || in->output2YWrPmEnable)
+ ctrl->encPath.pmEnabled = TRUE;
+
+ vfe_pm_start(in);
+
+ writel(VFE_PERFORMANCE_MONITOR_GO, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data)
+{
+ struct vfe_irqenable packedData;
+
+ memset(&packedData, 0, sizeof(packedData));
+
+ packedData.camifErrorIrq = data.camifErrorIrq;
+ packedData.camifSofIrq = data.camifSofIrq;
+ packedData.camifEolIrq = data.camifEolIrq;
+ packedData.camifEofIrq = data.camifEofIrq;
+ packedData.camifEpoch1Irq = data.camifEpoch1Irq;
+ packedData.camifEpoch2Irq = data.camifEpoch2Irq;
+ packedData.camifOverflowIrq = data.camifOverflowIrq;
+ packedData.ceIrq = data.ceIrq;
+ packedData.regUpdateIrq = data.regUpdateIrq;
+ packedData.resetAckIrq = data.resetAckIrq;
+ packedData.encYPingpongIrq = data.encYPingpongIrq;
+ packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq;
+ packedData.viewYPingpongIrq = data.viewYPingpongIrq;
+ packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq;
+ packedData.rdPingpongIrq = data.rdPingpongIrq;
+ packedData.afPingpongIrq = data.afPingpongIrq;
+ packedData.awbPingpongIrq = data.awbPingpongIrq;
+ packedData.histPingpongIrq = data.histPingpongIrq;
+ packedData.encIrq = data.encIrq;
+ packedData.viewIrq = data.viewIrq;
+ packedData.busOverflowIrq = data.busOverflowIrq;
+ packedData.afOverflowIrq = data.afOverflowIrq;
+ packedData.awbOverflowIrq = data.awbOverflowIrq;
+ packedData.syncTimer0Irq = data.syncTimer0Irq;
+ packedData.syncTimer1Irq = data.syncTimer1Irq;
+ packedData.syncTimer2Irq = data.syncTimer2Irq;
+ packedData.asyncTimer0Irq = data.asyncTimer0Irq;
+ packedData.asyncTimer1Irq = data.asyncTimer1Irq;
+ packedData.asyncTimer2Irq = data.asyncTimer2Irq;
+ packedData.asyncTimer3Irq = data.asyncTimer3Irq;
+ packedData.axiErrorIrq = data.axiErrorIrq;
+ packedData.violationIrq = data.violationIrq;
+
+ return *((uint32_t *)&packedData);
+}
+
+static uint32_t
+vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data)
+{
+ struct VFE_Irq_Composite_MaskType packedData;
+
+ memset(&packedData, 0, sizeof(packedData));
+
+ packedData.encIrqComMaskBits = data.encIrqComMask;
+ packedData.viewIrqComMaskBits = data.viewIrqComMask;
+ packedData.ceDoneSelBits = data.ceDoneSel;
+
+ return *((uint32_t *)&packedData);
+}
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type, void *data, void **ext,
+ int *elen)
+{
+ switch (type) {
+ case VFE_MSG_OUTPUT1:{
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOutput1.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOutput1.
+ cbcrBuffer;
+
+ ctrl->extdata.bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo;
+
+ ctrl->extdata.asfInfo =
+ ((struct vfe_message *)data)->_u.msgOutput1.asfInfo;
+
+ ctrl->extdata.frameCounter =
+ ((struct vfe_message *)data)->_u.msgOutput1.
+ frameCounter;
+
+ ctrl->extdata.pmData =
+ ((struct vfe_message *)data)->_u.msgOutput1.pmData;
+
+ *ext = &ctrl->extdata;
+ *elen = sizeof(ctrl->extdata);
+ }
+ break;
+
+ case VFE_MSG_OUTPUT2:{
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.
+ cbcrBuffer;
+
+ CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n",
+ pinfo->y_phy);
+ CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n",
+ pinfo->cbcr_phy);
+
+ ctrl->extdata.bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo;
+
+ ctrl->extdata.asfInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.asfInfo;
+
+ ctrl->extdata.frameCounter =
+ ((struct vfe_message *)data)->_u.msgOutput2.
+ frameCounter;
+
+ ctrl->extdata.pmData =
+ ((struct vfe_message *)data)->_u.msgOutput2.pmData;
+
+ *ext = &ctrl->extdata;
+ *elen = sizeof(ctrl->extdata);
+ }
+ break;
+
+ case VFE_MSG_STATS_AF:
+ pinfo->sbuf_phy =
+ ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer;
+ break;
+
+ case VFE_MSG_STATS_WE:
+ pinfo->sbuf_phy =
+ ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer;
+ break;
+
+ default:
+ break;
+ } /* switch */
+}
+
+static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+
+static boolean invalid(struct msm_vfe_resp *rp,
+ struct vfe_message *_m, void *_d)
+{
+ BUG_ON(1); /* this function should not be called. */
+ return FALSE;
+}
+
+static struct {
+ boolean (*fn)(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data);
+ enum vfe_resp_msg rt; /* reponse type */
+} vfe_funcs[] = {
+ [VFE_MSG_ID_RESET_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_START_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_STOP_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_UPDATE_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_OUTPUT1] = { vfe_send_output1_msg, VFE_MSG_OUTPUT1 },
+ [VFE_MSG_ID_OUTPUT2] = { vfe_send_output2_msg, VFE_MSG_OUTPUT2 },
+ [VFE_MSG_ID_SNAPSHOT_DONE] = { NULL, VFE_MSG_SNAPSHOT },
+ [VFE_MSG_ID_STATS_AUTOFOCUS] = { vfe_send_af_stats_msg, VFE_MSG_STATS_AF },
+ [VFE_MSG_ID_STATS_WB_EXP] = { vfe_send_awb_stats_msg, VFE_MSG_STATS_WE },
+ [VFE_MSG_ID_EPOCH1] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_EPOCH2] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_SYNC_TIMER0_DONE] = { invalid },
+ [VFE_MSG_ID_SYNC_TIMER1_DONE] = { invalid },
+ [VFE_MSG_ID_SYNC_TIMER2_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER0_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER1_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER2_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER3_DONE] = { invalid },
+ [VFE_MSG_ID_AF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_AWB_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_AXI_ERROR] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_CAMIF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_VIOLATION] = { invalid },
+ [VFE_MSG_ID_CAMIF_ERROR] = { vfe_send_camif_error_msg, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_BUS_OVERFLOW] = { vfe_send_bus_overflow_msg, VFE_MSG_GENERAL },
+};
+
+static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data)
+{
+ struct msm_vfe_resp *rp;
+ struct vfe_message *msg;
+ struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata;
+
+ CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n",
+ ctrl->vfeOperationMode, id);
+
+ if (id >= ARRAY_SIZE(vfe_funcs) || vfe_funcs[id].fn == invalid) {
+ pr_err("%s: invalid VFE message id %d\n", __func__, id);
+ return;
+ }
+
+ /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before SNAPSHOT_DONE.
+ * We don't send such messages to the user. Note that we can do
+ * this in the vfe_func[] callback, but that would cause us to
+ * allocate and then immediately free the msm_vfe_resp structure,
+ * which is wasteful.
+ */
+ if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) &&
+ (id == VFE_MSG_ID_OUTPUT1 ||
+ id == VFE_MSG_ID_OUTPUT2))
+ return;
+
+ rp = ctrl->resp->vfe_alloc(sizeof(*rp) +
+ (vfe_funcs[id].fn ? sizeof(*msg) : 0),
+ ctrl->syncdata,
+ GFP_ATOMIC);
+ if (!rp) {
+ pr_err("%s: out of memory\n", __func__);
+ return;
+ }
+
+ rp->type = vfe_funcs[id].rt;
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+
+ /* Turn off the flash if epoch1 is enabled and snapshot is done. */
+ if (ctrl->vfeCamifEpoch1Local.enable &&
+ ctrl->vfeOperationMode ==
+ VFE_START_OPERATION_MODE_SNAPSHOT &&
+ id == VFE_MSG_ID_SNAPSHOT_DONE) {
+ ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_OFF);
+ ctrl->vfeCamifEpoch1Local.enable = 0;
+ }
+
+ if (!vfe_funcs[id].fn) {
+ rp->evt_msg.len = 0;
+ rp->evt_msg.data = 0;
+ } else {
+ /* populate the message accordingly */
+ if (vfe_funcs[id].fn)
+ rp->evt_msg.data = msg =
+ (struct vfe_message *)(rp + 1);
+ else
+ rp->evt_msg.data = msg = 0;
+ rp->evt_msg.len = sizeof(*msg);
+ msg->_d = id;
+ if (vfe_funcs[id].fn(rp, msg, data) == FALSE) {
+ pr_info("%s: freeing memory: handler for %d "
+ "returned false\n", __func__, id);
+ ctrl->resp->vfe_free(rp);
+ return;
+ }
+ }
+
+ ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata, GFP_ATOMIC);
+}
+
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg,
+ void *data)
+{
+ struct isr_queue_cmd *qcmd = data;
+ memcpy(&(msg->_u.msgBusOverflow),
+ &qcmd->vfePmData, sizeof(qcmd->vfePmData));
+ return TRUE;
+}
+
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg,
+ void *data)
+{
+ struct isr_queue_cmd *qcmd = data;
+ memcpy(&(msg->_u.msgCamifError),
+ &qcmd->vfeCamifStatusLocal, sizeof(qcmd->vfeCamifStatusLocal));
+ return TRUE;
+}
+
+static void vfe_process_error_irq(struct isr_queue_cmd *qcmd)
+{
+ struct vfe_interrupt_status *irqstatus = &qcmd->vfeInterruptStatus;
+
+ /* all possible error irq. Note error irqs are not enabled, it is
+ * checked only when other interrupts are present. */
+ if (irqstatus->afOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_AF_OVERFLOW, qcmd);
+
+ if (irqstatus->awbOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_AWB_OVERFLOW, qcmd);
+
+ if (irqstatus->axiErrorIrq)
+ vfe_proc_ops(VFE_MSG_ID_AXI_ERROR, qcmd);
+
+ if (irqstatus->busOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, qcmd);
+
+ if (irqstatus->camifErrorIrq)
+ vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, qcmd);
+
+ if (irqstatus->camifOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_CAMIF_OVERFLOW, qcmd);
+
+ if (irqstatus->violationIrq)
+ pr_err("%s: violation irq\n", __func__);
+}
+
+/* We use epoch1 interrupt to control flash timing. The purpose is to reduce the
+ * flash duration as much as possible. Userspace driver has no way to control
+ * the exactly timing like VFE. Currently we skip a frame during snapshot.
+ * We want to fire the flash in the middle of the first frame. Epoch1 interrupt
+ * allows us to set a line index and we will get an interrupt when VFE reaches
+ * the line. Userspace driver sets the line index in camif configuration. VFE
+ * will fire the flash in high mode when it gets the epoch1 interrupt. Flash
+ * will be turned off after snapshot is done.
+ */
+static void vfe_process_camif_epoch1_irq(void)
+{
+ /* Turn on the flash. */
+ struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata;
+ ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_HIGH);
+
+ /* Disable the epoch1 interrupt. */
+ ctrl->vfeImaskLocal.camifEpoch1Irq = FALSE;
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+}
+
+static void vfe_process_camif_sof_irq(void)
+{
+ /* increment the frame id number. */
+ ctrl->vfeFrameId++;
+
+ CDBG("camif_sof_irq, frameId = %d\n", ctrl->vfeFrameId);
+
+ /* In snapshot mode, if frame skip is programmed,
+ * need to check it accordingly to stop camif at
+ * correct frame boundary. For the dropped frames,
+ * there won't be any output path irqs, but there is
+ * still SOF irq, which can help us determine when
+ * to stop the camif.
+ */
+ if (ctrl->vfeOperationMode) {
+ if ((1 << ctrl->vfeFrameSkipCount)&ctrl->vfeFrameSkipPattern) {
+
+ ctrl->vfeSnapShotCount--;
+ if (ctrl->vfeSnapShotCount == 0)
+ /* terminate vfe pipeline at frame boundary. */
+ writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ ctrl->vfebase + CAMIF_COMMAND);
+ }
+
+ /* update frame skip counter for bit checking. */
+ ctrl->vfeFrameSkipCount++;
+ if (ctrl->vfeFrameSkipCount == (ctrl->vfeFrameSkipPeriod + 1))
+ ctrl->vfeFrameSkipCount = 0;
+ }
+}
+
+static boolean vfe_get_af_pingpong_status(void)
+{
+ uint32_t busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+ return !!(busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT);
+}
+
+static uint32_t vfe_read_af_buf_addr(boolean pipo)
+{
+ if (pipo == FALSE)
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ else
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static void vfe_update_af_buf_addr(boolean pipo, uint32_t addr)
+{
+ if (pipo == FALSE)
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ else
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ uint32_t afBufAddress = (uint32_t)data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ msg->_u.msgStatsAf.afBuffer = afBufAddress;
+ msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId;
+
+ ctrl->afStatsControl.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy), rp->type, msg, NULL, NULL);
+
+ return TRUE;
+}
+
+static void vfe_process_stats_af_irq(void)
+{
+ boolean bufferAvailable;
+
+ if (!(ctrl->afStatsControl.ackPending)) {
+
+ /* read hardware status. */
+ ctrl->afStatsControl.pingPongStatus =
+ vfe_get_af_pingpong_status();
+
+ bufferAvailable = (ctrl->afStatsControl.pingPongStatus) ^ 1;
+
+ ctrl->afStatsControl.bufToRender =
+ vfe_read_af_buf_addr(bufferAvailable);
+
+ /* update the same buffer address (ping or pong) */
+ vfe_update_af_buf_addr(bufferAvailable,
+ ctrl->afStatsControl.nextFrameAddrBuf);
+
+ vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS,
+ (void *)ctrl->afStatsControl.bufToRender);
+ } else
+ ctrl->afStatsControl.droppedStatsFrameCount++;
+}
+
+static boolean vfe_get_awb_pingpong_status(void)
+{
+ uint32_t busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+ return !!(busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT);
+}
+
+static uint32_t vfe_read_awb_buf_addr(boolean pingpong)
+{
+ if (pingpong == FALSE)
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ else
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_update_awb_buf_addr(boolean pingpong, uint32_t addr)
+{
+ if (pingpong == FALSE)
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ else
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ uint32_t awbBufAddress = (uint32_t)data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress;
+ msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId;
+
+ ctrl->awbStatsControl.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ NULL, NULL);
+
+ return TRUE;
+}
+
+static void vfe_process_stats_awb_irq(void)
+{
+ boolean bufferAvailable;
+
+ if (!(ctrl->awbStatsControl.ackPending)) {
+
+ ctrl->awbStatsControl.pingPongStatus =
+ vfe_get_awb_pingpong_status();
+
+ bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1;
+
+ ctrl->awbStatsControl.bufToRender =
+ vfe_read_awb_buf_addr(bufferAvailable);
+
+ vfe_update_awb_buf_addr(bufferAvailable,
+ ctrl->awbStatsControl.nextFrameAddrBuf);
+
+ vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP,
+ (void *)ctrl->awbStatsControl.bufToRender);
+
+ } else
+ ctrl->awbStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe_write_gamma_table(uint8_t channel,
+ boolean bank, int16_t *pTable)
+{
+ uint16_t i;
+
+ enum VFE_DMI_RAM_SEL dmiRamSel = NO_MEM_SELECTED;
+
+ switch (channel) {
+ case 0:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH0_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH0_BANK1;
+ break;
+
+ case 1:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH1_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH1_BANK1;
+ break;
+
+ case 2:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH2_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH2_BANK1;
+ break;
+
+ default:
+ break;
+ }
+
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) {
+ writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, need to set the DMI_CFG to unselect any SRAM
+ unselect the SRAM Bank. */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_prog_hw_testgen_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_HW_TESTGEN_CMD);
+}
+
+static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out)
+{
+ uint32_t *temp;
+
+ memset(out, 0, sizeof(struct vfe_irq_thread_msg));
+
+ temp = (uint32_t *) (ctrl->vfebase + VFE_IRQ_STATUS);
+ out->vfeIrqStatus = readl(temp);
+
+ temp = (uint32_t *) (ctrl->vfebase + CAMIF_STATUS);
+ out->camifStatus = readl(temp);
+#if 0 /*this for YUV performance tuning */
+ writel(0x7, ctrl->vfebase + CAMIF_COMMAND);
+ writel(0x3, ctrl->vfebase + CAMIF_COMMAND);
+ CDBG("camifStatus = 0x%x\n", out->camifStatus);
+#endif
+/*
+ temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS);
+ out->demosaicStatus = readl(temp);
+
+ temp = (uint32_t *)(ctrl->vfebase + VFE_ASF_MAX_EDGE);
+ out->asfMaxEdge = readl(temp);
+
+ temp = (uint32_t *)(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PM_STATS_0);
+*/
+
+#if 0
+ out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++);
+ out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++);
+ out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++);
+ out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp);
+#endif /* if 0 Jeff */
+}
+
+static void
+vfe_parse_interrupt_status(struct vfe_interrupt_status *ret, uint32_t irqStatusIn)
+{
+ struct vfe_irqenable hwstat;
+ boolean temp;
+
+ memset(&hwstat, 0, sizeof(hwstat));
+ memset(ret, 0, sizeof(*ret));
+
+ hwstat = *((struct vfe_irqenable *)(&irqStatusIn));
+
+ ret->camifErrorIrq = hwstat.camifErrorIrq;
+ ret->camifSofIrq = hwstat.camifSofIrq;
+ ret->camifEolIrq = hwstat.camifEolIrq;
+ ret->camifEofIrq = hwstat.camifEofIrq;
+ ret->camifEpoch1Irq = hwstat.camifEpoch1Irq;
+ ret->camifEpoch2Irq = hwstat.camifEpoch2Irq;
+ ret->camifOverflowIrq = hwstat.camifOverflowIrq;
+ ret->ceIrq = hwstat.ceIrq;
+ ret->regUpdateIrq = hwstat.regUpdateIrq;
+ ret->resetAckIrq = hwstat.resetAckIrq;
+ ret->encYPingpongIrq = hwstat.encYPingpongIrq;
+ ret->encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq;
+ ret->viewYPingpongIrq = hwstat.viewYPingpongIrq;
+ ret->viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq;
+ ret->rdPingpongIrq = hwstat.rdPingpongIrq;
+ ret->afPingpongIrq = hwstat.afPingpongIrq;
+ ret->awbPingpongIrq = hwstat.awbPingpongIrq;
+ ret->histPingpongIrq = hwstat.histPingpongIrq;
+ ret->encIrq = hwstat.encIrq;
+ ret->viewIrq = hwstat.viewIrq;
+ ret->busOverflowIrq = hwstat.busOverflowIrq;
+ ret->afOverflowIrq = hwstat.afOverflowIrq;
+ ret->awbOverflowIrq = hwstat.awbOverflowIrq;
+ ret->syncTimer0Irq = hwstat.syncTimer0Irq;
+ ret->syncTimer1Irq = hwstat.syncTimer1Irq;
+ ret->syncTimer2Irq = hwstat.syncTimer2Irq;
+ ret->asyncTimer0Irq = hwstat.asyncTimer0Irq;
+ ret->asyncTimer1Irq = hwstat.asyncTimer1Irq;
+ ret->asyncTimer2Irq = hwstat.asyncTimer2Irq;
+ ret->asyncTimer3Irq = hwstat.asyncTimer3Irq;
+ ret->axiErrorIrq = hwstat.axiErrorIrq;
+ ret->violationIrq = hwstat.violationIrq;
+
+ /* logic OR of any error bits
+ * although each irq corresponds to a bit, the data type here is a
+ * boolean already. hence use logic operation.
+ */
+ temp =
+ ret->camifErrorIrq ||
+ ret->camifOverflowIrq ||
+ ret->afOverflowIrq ||
+ ret->awbOverflowIrq ||
+ ret->awbPingpongIrq ||
+ ret->afPingpongIrq ||
+ ret->busOverflowIrq || ret->axiErrorIrq || ret->violationIrq;
+
+ ret->anyErrorIrqs = temp;
+
+ /* logic OR of any output path bits */
+ temp = ret->encYPingpongIrq || ret->encCbcrPingpongIrq || ret->encIrq;
+
+ ret->anyOutput2PathIrqs = temp;
+
+ temp = ret->viewYPingpongIrq || ret->viewCbcrPingpongIrq || ret->viewIrq;
+
+ ret->anyOutput1PathIrqs = temp;
+
+ ret->anyOutputPathIrqs =
+ ret->anyOutput1PathIrqs || ret->anyOutput2PathIrqs;
+
+ /* logic OR of any sync timer bits */
+ temp = ret->syncTimer0Irq || ret->syncTimer1Irq || ret->syncTimer2Irq;
+
+ ret->anySyncTimerIrqs = temp;
+
+ /* logic OR of any async timer bits */
+ temp =
+ ret->asyncTimer0Irq ||
+ ret->asyncTimer1Irq || ret->asyncTimer2Irq || ret->asyncTimer3Irq;
+
+ ret->anyAsyncTimerIrqs = temp;
+
+ /* bool for all interrupts that are not allowed in idle state */
+ temp =
+ ret->anyErrorIrqs ||
+ ret->anyOutputPathIrqs ||
+ ret->anySyncTimerIrqs ||
+ ret->regUpdateIrq ||
+ ret->awbPingpongIrq ||
+ ret->afPingpongIrq ||
+ ret->camifSofIrq || ret->camifEpoch2Irq || ret->camifEpoch1Irq;
+
+ ret->anyIrqForActiveStatesOnly = temp;
+}
+
+static void
+vfe_get_asf_frame_info(struct vfe_frame_asf_info *rc, struct vfe_irq_thread_msg *in)
+{
+ struct vfe_asf_info asfInfoTemp;
+
+ memset(rc, 0, sizeof(*rc));
+ memset(&asfInfoTemp, 0, sizeof(asfInfoTemp));
+
+ asfInfoTemp = *((struct vfe_asf_info *)(&(in->asfMaxEdge)));
+
+ rc->asfHbiCount = asfInfoTemp.HBICount;
+ rc->asfMaxEdge = asfInfoTemp.maxEdge;
+}
+
+static void
+vfe_get_demosaic_frame_info(struct vfe_frame_bpc_info *rc, struct vfe_irq_thread_msg *in)
+{
+ struct vfe_bps_info bpcInfoTemp;
+
+ memset(rc, 0, sizeof(*rc));
+ memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp));
+
+ bpcInfoTemp = *((struct vfe_bps_info *)(&(in->demosaicStatus)));
+
+ rc->greenDefectPixelCount = bpcInfoTemp.greenBadPixelCount;
+
+ rc->redBlueDefectPixelCount = bpcInfoTemp.RedBlueBadPixelCount;
+}
+
+static void
+vfe_get_camif_status(struct vfe_msg_camif_status *rc, struct vfe_irq_thread_msg *in)
+{
+ struct vfe_camif_stats camifStatusTemp;
+
+ memset(rc, 0, sizeof(*rc));
+ memset(&camifStatusTemp, 0, sizeof(camifStatusTemp));
+
+ camifStatusTemp = *((struct vfe_camif_stats *)(&(in->camifStatus)));
+
+ rc->camifState = (boolean) camifStatusTemp.camifHalt;
+ rc->lineCount = camifStatusTemp.lineCount;
+ rc->pixelCount = camifStatusTemp.pixelCount;
+}
+
+static void
+vfe_get_performance_monitor_data(struct vfe_bus_performance_monitor *rc,
+ struct vfe_irq_thread_msg *in)
+{
+ memset(rc, 0, sizeof(*rc));
+
+ rc->encPathPmInfo.yWrPmStats0 = in->pmInfo.encPathPmInfo.yWrPmStats0;
+ rc->encPathPmInfo.yWrPmStats1 = in->pmInfo.encPathPmInfo.yWrPmStats1;
+ rc->encPathPmInfo.cbcrWrPmStats0 =
+ in->pmInfo.encPathPmInfo.cbcrWrPmStats0;
+ rc->encPathPmInfo.cbcrWrPmStats1 =
+ in->pmInfo.encPathPmInfo.cbcrWrPmStats1;
+ rc->viewPathPmInfo.yWrPmStats0 = in->pmInfo.viewPathPmInfo.yWrPmStats0;
+ rc->viewPathPmInfo.yWrPmStats1 = in->pmInfo.viewPathPmInfo.yWrPmStats1;
+ rc->viewPathPmInfo.cbcrWrPmStats0 =
+ in->pmInfo.viewPathPmInfo.cbcrWrPmStats0;
+ rc->viewPathPmInfo.cbcrWrPmStats1 =
+ in->pmInfo.viewPathPmInfo.cbcrWrPmStats1;
+}
+
+static void vfe_process_reg_update_irq(void)
+{
+ CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n",
+ ctrl->vfeStartAckPendingFlag);
+ if (ctrl->vfeStartAckPendingFlag == TRUE) {
+ vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ } else
+ vfe_proc_ops(VFE_MSG_ID_UPDATE_ACK, NULL);
+}
+
+static void vfe_process_reset_irq(void)
+{
+ /* unsigned long flags; */
+
+ ctrl->vstate = VFE_STATE_IDLE;
+
+ if (ctrl->vfeStopAckPending == TRUE) {
+ ctrl->vfeStopAckPending = FALSE;
+ /* disable all irqs when got stop ack from VFE */
+ vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS);
+ vfe_proc_ops(VFE_MSG_ID_STOP_ACK, NULL);
+ } else {
+ vfe_set_default_reg_values();
+ vfe_proc_ops(VFE_MSG_ID_RESET_ACK, NULL);
+ }
+}
+
+static void vfe_process_pingpong_irq(struct vfe_output_path *in,
+ uint8_t fragmentCount)
+{
+ uint16_t circularIndex;
+ uint32_t nextFragmentAddr;
+
+ /* get next fragment address from circular buffer */
+ circularIndex = (in->fragIndex) % (2 * fragmentCount);
+ nextFragmentAddr = in->addressBuffer[circularIndex];
+
+ in->fragIndex = circularIndex + 1;
+
+ /* use next fragment to program hardware ping/pong address. */
+ if (in->hwCurrentFlag == ping) {
+ writel(nextFragmentAddr, in->hwRegPingAddress);
+ in->hwCurrentFlag = pong;
+
+ } else {
+ writel(nextFragmentAddr, in->hwRegPongAddress);
+ in->hwCurrentFlag = ping;
+ }
+}
+
+static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ memcpy(&(msg->_u.msgOutput2),
+ (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ ctrl->encPath.ackPending = TRUE;
+ rp->phy.output_id = MSM_FRAME_PREV_2;
+
+ if (!(ctrl->vfeRequestedSnapShotCount <= 3) &&
+ (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT))
+ ctrl->encPath.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+ return TRUE;
+}
+
+static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ ctrl->viewPath.ackPending = TRUE;
+ rp->phy.output_id = MSM_FRAME_PREV_1;
+ if (!(ctrl->vfeRequestedSnapShotCount <= 3) &&
+ (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT))
+ ctrl->viewPath.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+
+ return TRUE;
+}
+
+static void vfe_send_output_msg(boolean whichOutputPath,
+ uint32_t yPathAddr, uint32_t cbcrPathAddr)
+{
+ struct vfe_msg_output msgPayload;
+
+ msgPayload.yBuffer = yPathAddr;
+ msgPayload.cbcrBuffer = cbcrPathAddr;
+
+ /* asf info is common for both output1 and output2 */
+#if 0
+ msgPayload.asfInfo.asfHbiCount = ctrl->vfeAsfFrameInfo.asfHbiCount;
+ msgPayload.asfInfo.asfMaxEdge = ctrl->vfeAsfFrameInfo.asfMaxEdge;
+
+ /* demosaic info is common for both output1 and output2 */
+ msgPayload.bpcInfo.greenDefectPixelCount =
+ ctrl->vfeBpcFrameInfo.greenDefectPixelCount;
+ msgPayload.bpcInfo.redBlueDefectPixelCount =
+ ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount;
+#endif /* if 0 */
+
+ /* frame ID is common for both paths. */
+ msgPayload.frameCounter = ctrl->vfeFrameId;
+
+ if (whichOutputPath) {
+ /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT2, &msgPayload);
+ } else {
+ /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT1, &msgPayload);
+ }
+}
+
+static void vfe_process_frame_done_irq_multi_frag(struct vfe_output_path_combo
+ *in)
+{
+ uint32_t yAddress, cbcrAddress;
+ uint16_t idx;
+ uint32_t *ptrY;
+ uint32_t *ptrCbcr;
+ const uint32_t *ptrSrc;
+ uint8_t i;
+
+ if (!in->ackPending) {
+
+ idx = (in->currentFrame) * (in->fragCount);
+
+ /* Send output message. */
+ yAddress = in->yPath.addressBuffer[idx];
+ cbcrAddress = in->cbcrPath.addressBuffer[idx];
+
+ /* copy next frame to current frame. */
+ ptrSrc = in->nextFrameAddrBuf;
+ ptrY = (uint32_t *)&in->yPath.addressBuffer[idx];
+ ptrCbcr = (uint32_t *)&in->cbcrPath.addressBuffer[idx];
+
+ /* Copy Y address */
+ for (i = 0; i < in->fragCount; i++)
+ *ptrY++ = *ptrSrc++;
+
+ /* Copy Cbcr address */
+ for (i = 0; i < in->fragCount; i++)
+ *ptrCbcr++ = *ptrSrc++;
+
+ vfe_send_output_msg(in->whichOutputPath, yAddress, cbcrAddress);
+
+ } else {
+ if (in->whichOutputPath == 0)
+ ctrl->vfeDroppedFrameCounts.output1Count++;
+
+ if (in->whichOutputPath == 1)
+ ctrl->vfeDroppedFrameCounts.output2Count++;
+ }
+
+ /* toggle current frame. */
+ in->currentFrame = in->currentFrame ^ 1;
+
+ if (ctrl->vfeOperationMode)
+ in->snapshotPendingCount--;
+}
+
+static void vfe_process_frame_done_irq_no_frag_io(
+ struct vfe_output_path_combo *in,
+ uint32_t *pNextAddr,
+ uint32_t *pdestRenderAddr)
+{
+ uint32_t busPingPongStatus;
+ uint32_t tempAddress;
+
+ /* 1. read hw status register. */
+ busPingPongStatus = readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+ CDBG("hardware status is 0x%x\n", busPingPongStatus);
+
+ /* 2. determine ping or pong */
+ /* use cbcr status */
+ busPingPongStatus = busPingPongStatus & (1 << (in->cbcrStatusBit));
+
+ /* 3. read out address and update address */
+ if (busPingPongStatus == 0) {
+ /* hw is working on ping, render pong buffer */
+ /* a. read out pong address */
+ /* read out y address. */
+ tempAddress = readl(in->yPath.hwRegPongAddress);
+
+ CDBG("pong 1 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr++ = tempAddress;
+ /* read out cbcr address. */
+ tempAddress = readl(in->cbcrPath.hwRegPongAddress);
+
+ CDBG("pong 2 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr = tempAddress;
+
+ /* b. update pong address */
+ writel(*pNextAddr++, in->yPath.hwRegPongAddress);
+ writel(*pNextAddr, in->cbcrPath.hwRegPongAddress);
+ } else {
+ /* hw is working on pong, render ping buffer */
+
+ /* a. read out ping address */
+ tempAddress = readl(in->yPath.hwRegPingAddress);
+ CDBG("ping 1 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr++ = tempAddress;
+ tempAddress = readl(in->cbcrPath.hwRegPingAddress);
+
+ CDBG("ping 2 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr = tempAddress;
+
+ /* b. update ping address */
+ writel(*pNextAddr++, in->yPath.hwRegPingAddress);
+ CDBG("NextAddress = 0x%x\n", *pNextAddr);
+ writel(*pNextAddr, in->cbcrPath.hwRegPingAddress);
+ }
+}
+
+static void vfe_process_frame_done_irq_no_frag(struct vfe_output_path_combo *in)
+{
+ uint32_t addressToRender[2];
+
+ if (!in->ackPending) {
+ vfe_process_frame_done_irq_no_frag_io(in,
+ in->nextFrameAddrBuf,
+ addressToRender);
+
+ /* use addressToRender to send out message. */
+ vfe_send_output_msg(in->whichOutputPath,
+ addressToRender[0], addressToRender[1]);
+
+ } else {
+ /* ackPending is still there, accumulate dropped frame count.
+ * These count can be read through ioctrl command. */
+ CDBG("waiting frame ACK\n");
+
+ if (in->whichOutputPath == 0)
+ ctrl->vfeDroppedFrameCounts.output1Count++;
+
+ if (in->whichOutputPath == 1)
+ ctrl->vfeDroppedFrameCounts.output2Count++;
+ }
+
+ /* in case of multishot when upper layer did not ack, there will still
+ * be a snapshot done msg sent out, even though the number of frames
+ * sent out may be less than the desired number of frames. snapshot
+ * done msg would be helpful to indicate that vfe pipeline has stop,
+ * and in good known state.
+ */
+ if (ctrl->vfeOperationMode)
+ in->snapshotPendingCount--;
+}
+
+static void vfe_process_output_path_irq(struct vfe_interrupt_status *irqstatus)
+{
+ /* unsigned long flags; */
+
+ /* process the view path interrupts */
+ if (irqstatus->anyOutput1PathIrqs) {
+ if (ctrl->viewPath.multiFrag) {
+
+ if (irqstatus->viewCbcrPingpongIrq)
+ vfe_process_pingpong_irq(&
+ (ctrl->viewPath.
+ cbcrPath),
+ ctrl->viewPath.
+ fragCount);
+
+ if (irqstatus->viewYPingpongIrq)
+ vfe_process_pingpong_irq(&
+ (ctrl->viewPath.yPath),
+ ctrl->viewPath.
+ fragCount);
+
+ if (irqstatus->viewIrq)
+ vfe_process_frame_done_irq_multi_frag(&ctrl->
+ viewPath);
+
+ } else {
+ /* typical case for no fragment,
+ only frame done irq is enabled. */
+ if (irqstatus->viewIrq)
+ vfe_process_frame_done_irq_no_frag(&ctrl->
+ viewPath);
+ }
+ }
+
+ /* process the encoder path interrupts */
+ if (irqstatus->anyOutput2PathIrqs) {
+ if (ctrl->encPath.multiFrag) {
+ if (irqstatus->encCbcrPingpongIrq)
+ vfe_process_pingpong_irq(&
+ (ctrl->encPath.
+ cbcrPath),
+ ctrl->encPath.
+ fragCount);
+
+ if (irqstatus->encYPingpongIrq)
+ vfe_process_pingpong_irq(&(ctrl->encPath.yPath),
+ ctrl->encPath.
+ fragCount);
+
+ if (irqstatus->encIrq)
+ vfe_process_frame_done_irq_multi_frag(&ctrl->
+ encPath);
+
+ } else {
+ if (irqstatus->encIrq)
+ vfe_process_frame_done_irq_no_frag(&ctrl->
+ encPath);
+ }
+ }
+
+ if (ctrl->vfeOperationMode) {
+ if ((ctrl->encPath.snapshotPendingCount == 0) &&
+ (ctrl->viewPath.snapshotPendingCount == 0)) {
+
+ ctrl->vstate = VFE_STATE_IDLE;
+
+ vfe_proc_ops(VFE_MSG_ID_SNAPSHOT_DONE, NULL);
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+ vfe_pm_stop();
+ }
+ }
+}
+
+static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd)
+{
+ if (qcmd->vfeInterruptStatus.regUpdateIrq) {
+ CDBG("irq regUpdateIrq\n");
+ vfe_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus.resetAckIrq) {
+ CDBG("%s: process resetAckIrq\n", __func__);
+ vfe_process_reset_irq();
+ }
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return;
+
+ if (qcmd->vfeInterruptStatus.camifEpoch1Irq) {
+ vfe_process_camif_epoch1_irq();
+ }
+
+#if 0
+ if (qcmd->vfeInterruptStatus.camifEpoch2Irq)
+ vfe_proc_ops(VFE_MSG_ID_EPOCH2);
+#endif
+
+ /* next, check output path related interrupts. */
+ if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) {
+ CDBG("irq: anyOutputPathIrqs\n");
+ vfe_process_output_path_irq(&qcmd->vfeInterruptStatus);
+ }
+
+ if (qcmd->vfeInterruptStatus.afPingpongIrq)
+ vfe_process_stats_af_irq();
+
+ if (qcmd->vfeInterruptStatus.awbPingpongIrq)
+ vfe_process_stats_awb_irq();
+
+ /* any error irqs */
+ if (qcmd->vfeInterruptStatus.anyErrorIrqs)
+ vfe_process_error_irq(qcmd);
+
+#if 0
+ if (qcmd->vfeInterruptStatus.anySyncTimerIrqs)
+ vfe_process_sync_timer_irq();
+
+ if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs)
+ vfe_process_async_timer_irq();
+#endif
+
+ if (qcmd->vfeInterruptStatus.camifSofIrq) {
+ CDBG("irq: camifSofIrq\n");
+ vfe_process_camif_sof_irq();
+ }
+}
+
+static struct isr_queue_cmd *get_irq_cmd_nosync(void)
+{
+ int old_get = ctrl->irq_get++;
+ ctrl->irq_get = ctrl->irq_get % ARRAY_SIZE(ctrl->irqs);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ pr_err("%s: out of irq command packets\n", __func__);
+ ctrl->irq_get = old_get;
+ return NULL;
+ }
+
+ return ctrl->irqs + old_get;
+}
+
+static struct isr_queue_cmd *next_irq_cmd(void)
+{
+ unsigned long flags;
+ struct isr_queue_cmd *cmd;
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return NULL; /* already empty */
+ }
+ cmd = ctrl->irqs + ctrl->irq_put;
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return cmd;
+}
+
+static void put_irq_cmd(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return; /* already empty */
+ }
+ ctrl->irq_put++;
+ ctrl->irq_put %= ARRAY_SIZE(ctrl->irqs);
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+}
+
+static void vfe_do_tasklet(unsigned long data)
+{
+ int cnt = 0;
+ struct isr_queue_cmd *qcmd = NULL;
+
+ CDBG("%s\n", __func__);
+
+ while ((qcmd = next_irq_cmd())) {
+ __vfe_do_tasklet(qcmd);
+ put_irq_cmd();
+ cnt++;
+ }
+
+ if (cnt > 1)
+ pr_info("%s: serviced %d vfe interrupts\n", __func__, cnt);
+}
+
+DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0);
+
+static irqreturn_t vfe_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t irqStatusLocal;
+ struct vfe_irq_thread_msg irq;
+ struct isr_queue_cmd *qcmd;
+
+ CDBG("vfe_parse_irq\n");
+
+ vfe_read_irq_status(&irq);
+
+ if (irq.vfeIrqStatus == 0) {
+ CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n");
+ return IRQ_HANDLED;
+ }
+
+ if (ctrl->vfeStopAckPending)
+ irqStatusLocal = (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus);
+ else
+ irqStatusLocal =
+ ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) &
+ irq.vfeIrqStatus);
+
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ qcmd = get_irq_cmd_nosync();
+ if (!qcmd) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ goto done;
+ }
+ /* parse the interrupt status to local data structures. */
+ vfe_parse_interrupt_status(&qcmd->vfeInterruptStatus, irqStatusLocal);
+ vfe_get_asf_frame_info(&qcmd->vfeAsfFrameInfo, &irq);
+ vfe_get_demosaic_frame_info(&qcmd->vfeBpcFrameInfo, &irq);
+ vfe_get_camif_status(&qcmd->vfeCamifStatusLocal, &irq);
+ vfe_get_performance_monitor_data(&qcmd->vfePmData, &irq);
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ tasklet_schedule(&vfe_tasklet);
+
+done:
+ /* clear the pending interrupt of the same kind. */
+ writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR);
+ return IRQ_HANDLED;
+}
+
+int vfe_cmd_init(struct msm_vfe_callback *presp,
+ struct platform_device *pdev, void *sdata)
+{
+ struct resource *vfemem, *vfeirq, *vfeio;
+ int rc;
+ struct msm_camera_sensor_info *s_info;
+ s_info = pdev->dev.platform_data;
+
+ pdev->resource = s_info->resource;
+ pdev->num_resources = s_info->num_resources;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ pr_err("%s: no mem resource\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ pr_err("%s: no irq resource\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ pr_err("%s: VFE region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ ctrl = kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ pr_err("%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+
+ spin_lock_init(&ctrl->irqs_lock);
+
+ ctrl->vfeirq = vfeirq->start;
+
+ ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!ctrl->vfebase) {
+ pr_err("%s: ioremap failed\n", __func__);
+ rc = -ENOMEM;
+ goto cmd_init_failed2;
+ }
+
+ rc = request_irq(ctrl->vfeirq, vfe_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+ if (rc < 0) {
+ pr_err("%s: request_irq(%d) failed\n", __func__, ctrl->vfeirq);
+ goto cmd_init_failed2;
+ }
+
+ if (presp && presp->vfe_resp)
+ ctrl->resp = presp;
+ else {
+ pr_err("%s: no vfe_resp function\n", __func__);
+ rc = -EIO;
+ goto cmd_init_failed3;
+ }
+
+ ctrl->syncdata = sdata;
+ return 0;
+
+cmd_init_failed3:
+ disable_irq(ctrl->vfeirq);
+ free_irq(ctrl->vfeirq, 0);
+ iounmap(ctrl->vfebase);
+cmd_init_failed2:
+ kfree(ctrl);
+cmd_init_failed1:
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ return rc;
+}
+
+void vfe_cmd_release(struct platform_device *dev)
+{
+ struct resource *mem;
+
+ disable_irq(ctrl->vfeirq);
+ free_irq(ctrl->vfeirq, 0);
+
+ iounmap(ctrl->vfebase);
+ mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ kfree(ctrl);
+ ctrl = 0;
+}
+
+void vfe_stats_af_stop(void)
+{
+ ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE;
+ ctrl->vfeImaskLocal.afPingpongIrq = FALSE;
+}
+
+void vfe_stop(void)
+{
+ int spin_cnt = 0;
+ uint32_t vfeAxiStauts;
+
+ /* for reset hw modules, and send msg when reset_irq comes. */
+ ctrl->vfeStopAckPending = TRUE;
+
+ ctrl->vfeStatsPingPongReloadFlag = FALSE;
+ vfe_pm_stop();
+
+ /* disable all interrupts. */
+ vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS);
+
+ /* in either continuous or snapshot mode, stop command can be issued
+ * at any time.
+ */
+ vfe_camif_stop_immediately();
+ vfe_program_axi_cmd(AXI_HALT);
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+
+ do {
+ vfeAxiStauts = vfe_read_axi_status();
+ spin_cnt++;
+ } while (!(vfeAxiStauts & AXI_STATUS_BUSY_MASK));
+ if (spin_cnt > 1)
+ pr_warning("%s: spin_cnt %d\n", __func__, spin_cnt);
+
+ vfe_program_axi_cmd(AXI_HALT_CLEAR);
+
+ /* clear all pending interrupts */
+ writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* enable reset_ack and async timer interrupt only while stopping
+ * the pipeline.
+ */
+ vfe_program_irq_mask(VFE_IMASK_WHILE_STOPPING);
+
+ vfe_program_global_reset_cmd(VFE_RESET_UPON_STOP_CMD);
+}
+
+void vfe_update(void)
+{
+ ctrl->vfeModuleEnableLocal.statsEnable =
+ ctrl->vfeStatsCmdLocal.autoFocusEnable |
+ ctrl->vfeStatsCmdLocal.axwEnable;
+
+ vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+ vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+ if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) &&
+ (ctrl->vfeStatsPingPongReloadFlag == FALSE)) {
+ ctrl->vfeStatsPingPongReloadFlag = TRUE;
+
+ ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+ vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+ }
+
+ vfe_program_reg_update_cmd(VFE_REG_UPDATE_TRIGGER);
+}
+
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in)
+{
+ int rc = 0;
+
+ ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+ switch (in->channelSelect) {
+ case RGB_GAMMA_CH0_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ vfe_write_gamma_table(0,
+ ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH1_SELECTED:
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ vfe_write_gamma_table(1,
+ ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(2,
+ ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH1_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ default:
+ pr_err("%s: invalid gamma channel %d\n", __func__, in->channelSelect);
+ return -EINVAL;
+ } /* switch */
+
+ /* update the gammaLutSel register. */
+ vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+ return rc;
+}
+
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in)
+{
+ int rc = 0;
+
+ ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+ switch (in->channelSelect) {
+ case RGB_GAMMA_CH0_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH1_SELECTED:
+ vfe_write_gamma_table(1, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH2_SELECTED:
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(1, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH2_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH1_CH2_SELECTED:
+ vfe_write_gamma_table(1, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(1, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ default:
+ pr_err("%s: invalid gamma channel %d\n", __func__, in->channelSelect);
+ rc = -EINVAL;
+ break;
+ } /* switch */
+
+ return rc;
+}
+
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *in)
+{
+ ctrl->afStatsControl.nextFrameAddrBuf = in->nextAFOutputBufferAddr;
+ ctrl->afStatsControl.ackPending = FALSE;
+}
+
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in)
+{
+ ctrl->awbStatsControl.nextFrameAddrBuf = in->nextWbExpOutputBufferAddr;
+ ctrl->awbStatsControl.ackPending = FALSE;
+}
+
+void vfe_output2_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ CDBG("output2_ack: ack addr = 0x%x\n", in->ybufaddr[0]);
+
+ psrc = in->ybufaddr;
+ for (i = 0; i < ctrl->encPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ psrc = in->chromabufaddr;
+ for (i = 0; i < ctrl->encPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->encPath.ackPending = FALSE;
+}
+
+void vfe_output1_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ pdest = ctrl->viewPath.nextFrameAddrBuf;
+
+ psrc = in->ybufaddr;
+ for (i = 0; i < ctrl->viewPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ psrc = in->chromabufaddr;
+ for (i = 0; i < ctrl->viewPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->viewPath.ackPending = FALSE;
+}
+
+void vfe_start(struct vfe_cmd_start *in)
+{
+ uint32_t pmstatus = 0;
+ boolean rawmode;
+ uint32_t demperiod = 0;
+ uint32_t demeven = 0;
+ uint32_t demodd = 0;
+
+ /* derived from other commands. (camif config, axi output config,
+ * etc)
+ */
+ struct vfe_cfg hwcfg;
+ struct vfe_upsample_cfg chromupcfg;
+
+ CDBG("vfe_start operationMode = %d\n", in->operationMode);
+
+ memset(&hwcfg, 0, sizeof(hwcfg));
+ memset(&chromupcfg, 0, sizeof(chromupcfg));
+
+ switch (in->pixel) {
+ case VFE_BAYER_RGRGRG:
+ demperiod = 1;
+ demeven = 0xC9;
+ demodd = 0xAC;
+ break;
+
+ case VFE_BAYER_GRGRGR:
+ demperiod = 1;
+ demeven = 0x9C;
+ demodd = 0xCA;
+ break;
+
+ case VFE_BAYER_BGBGBG:
+ demperiod = 1;
+ demeven = 0xCA;
+ demodd = 0x9C;
+ break;
+
+ case VFE_BAYER_GBGBGB:
+ demperiod = 1;
+ demeven = 0xAC;
+ demodd = 0xC9;
+ break;
+
+ case VFE_YUV_YCbYCr:
+ demperiod = 3;
+ demeven = 0x9CAC;
+ demodd = 0x9CAC;
+ break;
+
+ case VFE_YUV_YCrYCb:
+ demperiod = 3;
+ demeven = 0xAC9C;
+ demodd = 0xAC9C;
+ break;
+
+ case VFE_YUV_CbYCrY:
+ demperiod = 3;
+ demeven = 0xC9CA;
+ demodd = 0xC9CA;
+ break;
+
+ case VFE_YUV_CrYCbY:
+ demperiod = 3;
+ demeven = 0xCAC9;
+ demodd = 0xCAC9;
+ break;
+
+ default:
+ return;
+ }
+
+ vfe_config_demux(demperiod, demeven, demodd);
+
+ vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+ /* save variables to local. */
+ ctrl->vfeOperationMode = in->operationMode;
+ if (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) {
+ /* in snapshot mode, initialize snapshot count */
+ ctrl->vfeSnapShotCount = in->snapshotCount;
+
+ /* save the requested count, this is temporarily done, to
+ help with HJR / multishot. */
+ ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount;
+
+ CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount);
+
+ /* Assumption is to have the same pattern and period for both
+ paths, if both paths are used. */
+ if (ctrl->viewPath.pathEnabled) {
+ ctrl->viewPath.snapshotPendingCount = in->snapshotCount;
+
+ ctrl->vfeFrameSkipPattern =
+ ctrl->vfeFrameSkip.output1Pattern;
+ ctrl->vfeFrameSkipPeriod =
+ ctrl->vfeFrameSkip.output1Period;
+ }
+
+ if (ctrl->encPath.pathEnabled) {
+ ctrl->encPath.snapshotPendingCount = in->snapshotCount;
+
+ ctrl->vfeFrameSkipPattern =
+ ctrl->vfeFrameSkip.output2Pattern;
+ ctrl->vfeFrameSkipPeriod =
+ ctrl->vfeFrameSkip.output2Period;
+ }
+ }
+
+ /* enable color conversion for bayer sensor
+ if stats enabled, need to do color conversion. */
+ if (in->pixel <= VFE_BAYER_GBGBGB)
+ ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE;
+
+ vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+ if (in->pixel >= VFE_YUV_YCbYCr)
+ ctrl->vfeModuleEnableLocal.chromaUpsampleEnable = TRUE;
+
+ ctrl->vfeModuleEnableLocal.demuxEnable = TRUE;
+
+ /* if any stats module is enabled, the main bit is enabled. */
+ ctrl->vfeModuleEnableLocal.statsEnable =
+ ctrl->vfeStatsCmdLocal.autoFocusEnable |
+ ctrl->vfeStatsCmdLocal.axwEnable;
+
+ vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+ /* in case of offline processing, do not need to config camif. Having
+ * bus output enabled in camif_config register might confuse the
+ * hardware?
+ */
+ if (in->inputSource != VFE_START_INPUT_SOURCE_AXI) {
+ vfe_reg_camif_config(&ctrl->vfeCamifConfigLocal);
+ } else {
+ /* offline processing, enable axi read */
+ ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE;
+ ctrl->vfeBusCmdLocal.stripeReload = TRUE;
+ ctrl->vfeBusConfigLocal.rawPixelDataSize =
+ ctrl->axiInputDataSize;
+ }
+
+ vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal);
+
+ /* directly from start command */
+ hwcfg.pixelPattern = in->pixel;
+ hwcfg.inputSource = in->inputSource;
+ writel(*(uint32_t *)&hwcfg, ctrl->vfebase + VFE_CFG);
+
+ /* regardless module enabled or not, it does not hurt
+ * to program the cositing mode. */
+ chromupcfg.chromaCositingForYCbCrInputs = in->yuvInputCositingMode;
+
+ writel(*(uint32_t *)&chromupcfg,
+ ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG);
+
+ /* MISR to monitor the axi read. */
+ writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0);
+
+ /* clear all pending interrupts. */
+ writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* define how composite interrupt work. */
+ ctrl->vfeImaskCompositePacked =
+ vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal);
+
+ vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked);
+
+ /* enable all necessary interrupts. */
+ ctrl->vfeImaskLocal.camifSofIrq = TRUE;
+ ctrl->vfeImaskLocal.regUpdateIrq = TRUE;
+ ctrl->vfeImaskLocal.resetAckIrq = TRUE;
+
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+ /* enable bus performance monitor */
+ vfe_8k_pm_start(&ctrl->vfeBusPmConfigLocal);
+
+ /* trigger vfe reg update */
+ ctrl->vfeStartAckPendingFlag = TRUE;
+
+ /* write bus command to trigger reload of ping pong buffer. */
+ ctrl->vfeBusCmdLocal.busPingpongReload = TRUE;
+
+ if (ctrl->vfeModuleEnableLocal.statsEnable == TRUE) {
+ ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+ ctrl->vfeStatsPingPongReloadFlag = TRUE;
+ }
+
+ writel(VFE_REG_UPDATE_TRIGGER, ctrl->vfebase + VFE_REG_UPDATE_CMD);
+
+ /* program later than the reg update. */
+ vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+
+ if ((in->inputSource ==
+ VFE_START_INPUT_SOURCE_CAMIF) ||
+ (in->inputSource == VFE_START_INPUT_SOURCE_TESTGEN))
+ writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND);
+
+ /* start test gen if it is enabled */
+ if (ctrl->vfeTestGenStartFlag == TRUE) {
+ ctrl->vfeTestGenStartFlag = FALSE;
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_GO);
+ }
+
+ CDBG("ctrl->axiOutputMode = %d\n", ctrl->axiOutputMode);
+ if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2) {
+ /* raw dump mode */
+ rawmode = TRUE;
+
+ while (rawmode) {
+ pmstatus =
+ readl(ctrl->vfebase +
+ VFE_BUS_ENC_CBCR_WR_PM_STATS_1);
+
+ if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0)
+ rawmode = FALSE;
+ }
+
+ vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ }
+
+ ctrl->vstate = VFE_STATE_ACTIVE;
+}
+
+void vfe_la_update(struct vfe_cmd_la_config *in)
+{
+ int16_t *pTable;
+ enum VFE_DMI_RAM_SEL dmiRamSel;
+ int i;
+
+ pTable = in->table;
+ ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+ /* toggle the bank to be used. */
+ ctrl->vfeLaBankSel ^= 1;
+
+ if (ctrl->vfeLaBankSel == 0)
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+ else
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+ /* configure the DMI_CFG to select right sram */
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+ writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, to make it safe, need to set
+ * the DMI_CFG to unselect any SRAM */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+ writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+}
+
+void vfe_la_config(struct vfe_cmd_la_config *in)
+{
+ uint16_t i;
+ int16_t *pTable;
+ enum VFE_DMI_RAM_SEL dmiRamSel;
+
+ pTable = in->table;
+ ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+ if (ctrl->vfeLaBankSel == 0)
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+ else
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+ /* configure the DMI_CFG to select right sram */
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+ writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, to make it safe, need to set the
+ * DMI_CFG to unselect any SRAM */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+
+ /* can only be bank 0 or bank 1 for now. */
+ writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+ CDBG("VFE Luma adaptation bank selection is 0x%x\n",
+ *(uint32_t *)&ctrl->vfeLaBankSel);
+}
+
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in)
+{
+ struct VFE_TestGen_ConfigCmdType cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.numFrame = in->numFrame;
+ cmd.pixelDataSelect = in->pixelDataSelect;
+ cmd.systematicDataSelect = in->systematicDataSelect;
+ cmd.pixelDataSize = (uint32_t) in->pixelDataSize;
+ cmd.hsyncEdge = (uint32_t) in->hsyncEdge;
+ cmd.vsyncEdge = (uint32_t) in->vsyncEdge;
+ cmd.imageWidth = in->imageWidth;
+ cmd.imageHeight = in->imageHeight;
+ cmd.sofOffset = in->startOfFrameOffset;
+ cmd.eofNOffset = in->endOfFrameNOffset;
+ cmd.solOffset = in->startOfLineOffset;
+ cmd.eolNOffset = in->endOfLineNOffset;
+ cmd.hBlankInterval = in->hbi;
+ cmd.vBlankInterval = in->vbl;
+ cmd.vBlankIntervalEnable = in->vblEnable;
+ cmd.sofDummy = in->startOfFrameDummyLine;
+ cmd.eofDummy = in->endOfFrameDummyLine;
+ cmd.unicolorBarSelect = in->unicolorBarSelect;
+ cmd.unicolorBarEnable = in->unicolorBarEnable;
+ cmd.splitEnable = in->colorBarsSplitEnable;
+ cmd.pixelPattern = (uint32_t) in->colorBarsPixelPattern;
+ cmd.rotatePeriod = in->colorBarsRotatePeriod;
+ cmd.randomSeed = in->testGenRandomSeed;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in)
+{
+ struct VFE_FRAME_SKIP_UpdateCmdType cmd;
+
+ cmd.yPattern = in->output1Pattern;
+ cmd.cbcrPattern = in->output1Pattern;
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd.yPattern = in->output2Pattern;
+ cmd.cbcrPattern = in->output2Pattern;
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in)
+{
+ struct vfe_frame_skip_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeFrameSkip = *in;
+
+ cmd.output2YPeriod = in->output2Period;
+ cmd.output2CbCrPeriod = in->output2Period;
+ cmd.output2YPattern = in->output2Pattern;
+ cmd.output2CbCrPattern = in->output2Pattern;
+ cmd.output1YPeriod = in->output1Period;
+ cmd.output1CbCrPeriod = in->output1Period;
+ cmd.output1YPattern = in->output1Pattern;
+ cmd.output1CbCrPattern = in->output1Pattern;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in)
+{
+ struct vfe_output_clamp_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.yChanMax = in->maxCh0;
+ cmd.cbChanMax = in->maxCh1;
+ cmd.crChanMax = in->maxCh2;
+
+ cmd.yChanMin = in->minCh0;
+ cmd.cbChanMin = in->minCh1;
+ cmd.crChanMin = in->minCh2;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd,
+ sizeof(cmd));
+}
+
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in)
+{
+ struct vfe_camifframe_update cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.pixelsPerLine = in->pixelsPerLine;
+ cmd.linesPerFrame = in->linesPerFrame;
+
+ vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd,
+ sizeof(cmd));
+}
+
+void vfe_color_correction_config(struct vfe_cmd_color_correction_config *in)
+{
+ struct vfe_color_correction_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ctrl->vfeModuleEnableLocal.colorCorrectionEnable = in->enable;
+
+ cmd.c0 = in->C0;
+ cmd.c1 = in->C1;
+ cmd.c2 = in->C2;
+ cmd.c3 = in->C3;
+ cmd.c4 = in->C4;
+ cmd.c5 = in->C5;
+ cmd.c6 = in->C6;
+ cmd.c7 = in->C7;
+ cmd.c8 = in->C8;
+
+ cmd.k0 = in->K0;
+ cmd.k1 = in->K1;
+ cmd.k2 = in->K2;
+
+ cmd.coefQFactor = in->coefQFactor;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in)
+{
+ struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_abf_cfg cmdabf;
+ uint32_t temp;
+
+ memset(&cmd, 0, sizeof(cmd));
+ temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+ cmd = *((struct vfe_demosaic_cfg *)(&temp));
+ cmd.abfEnable = in->abfUpdate.enable;
+ cmd.forceAbfOn = in->abfUpdate.forceOn;
+ cmd.abfShift = in->abfUpdate.shift;
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmdabf.lpThreshold = in->abfUpdate.lpThreshold;
+ cmdabf.ratio = in->abfUpdate.ratio;
+ cmdabf.minValue = in->abfUpdate.min;
+ cmdabf.maxValue = in->abfUpdate.max;
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+ (uint32_t *)&cmdabf, sizeof(cmdabf));
+}
+
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in)
+{
+ struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_bpc_cfg cmdbpc;
+ uint32_t temp;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+ cmd = *((struct vfe_demosaic_cfg *)(&temp));
+ cmd.badPixelCorrEnable = in->bpcUpdate.enable;
+ cmd.fminThreshold = in->bpcUpdate.fminThreshold;
+ cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold;
+ cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold;
+ cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+ (uint32_t *)&cmdbpc, sizeof(cmdbpc));
+}
+
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in)
+{
+ struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_bpc_cfg cmd_bpc;
+ struct vfe_demosaic_abf_cfg cmd_abf;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd_bpc, 0, sizeof(cmd_bpc));
+ memset(&cmd_abf, 0, sizeof(cmd_abf));
+
+ ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable;
+
+ cmd.abfEnable = in->abfConfig.enable;
+ cmd.badPixelCorrEnable = in->bpcConfig.enable;
+ cmd.forceAbfOn = in->abfConfig.forceOn;
+ cmd.abfShift = in->abfConfig.shift;
+ cmd.fminThreshold = in->bpcConfig.fminThreshold;
+ cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold;
+ cmd.slopeShift = in->slopeShift;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd_abf.lpThreshold = in->abfConfig.lpThreshold;
+ cmd_abf.ratio = in->abfConfig.ratio;
+ cmd_abf.minValue = in->abfConfig.min;
+ cmd_abf.maxValue = in->abfConfig.max;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+ (uint32_t *)&cmd_abf, sizeof(cmd_abf));
+
+ cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold;
+ cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold;
+ cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+ (uint32_t *)&cmd_bpc, sizeof(cmd_bpc));
+}
+
+void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *in)
+{
+ struct vfe_demux_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0EvenGain = in->ch0EvenGain;
+ cmd.ch0OddGain = in->ch0OddGain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *in)
+{
+ struct vfe_demux_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0EvenGain = in->ch0EvenGain;
+ cmd.ch0OddGain = in->ch0OddGain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_update(struct vfe_cmd_black_level_config *in)
+{
+ struct vfe_blacklevel_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+ cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+ cmd.evenOddAdjustment = in->evenOddAdjustment;
+ cmd.oddEvenAdjustment = in->oddEvenAdjustment;
+ cmd.oddOddAdjustment = in->oddOddAdjustment;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_config(struct vfe_cmd_black_level_config *in)
+{
+ struct vfe_blacklevel_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+ cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+ cmd.evenOddAdjustment = in->evenOddAdjustment;
+ cmd.oddEvenAdjustment = in->oddEvenAdjustment;
+ cmd.oddOddAdjustment = in->oddOddAdjustment;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_update(struct vfe_cmd_asf_update *in)
+{
+ struct vfe_asf_update cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+ cmd.smoothEnable = in->smoothFilterEnabled;
+ cmd.sharpMode = in->sharpMode;
+ cmd.smoothCoeff0 = in->smoothCoefCenter;
+ cmd.smoothCoeff1 = in->smoothCoefSurr;
+ cmd.cropEnable = in->cropEnable;
+ cmd.sharpThresholdE1 = in->sharpThreshE1;
+ cmd.sharpDegreeK1 = in->sharpK1;
+ cmd.sharpDegreeK2 = in->sharpK2;
+ cmd.normalizeFactor = in->normalizeFactor;
+ cmd.sharpThresholdE2 = in->sharpThreshE2;
+ cmd.sharpThresholdE3 = in->sharpThreshE3;
+ cmd.sharpThresholdE4 = in->sharpThreshE4;
+ cmd.sharpThresholdE5 = in->sharpThreshE5;
+ cmd.F1Coeff0 = in->filter1Coefficients[0];
+ cmd.F1Coeff1 = in->filter1Coefficients[1];
+ cmd.F1Coeff2 = in->filter1Coefficients[2];
+ cmd.F1Coeff3 = in->filter1Coefficients[3];
+ cmd.F1Coeff4 = in->filter1Coefficients[4];
+ cmd.F1Coeff5 = in->filter1Coefficients[5];
+ cmd.F1Coeff6 = in->filter1Coefficients[6];
+ cmd.F1Coeff7 = in->filter1Coefficients[7];
+ cmd.F1Coeff8 = in->filter1Coefficients[8];
+ cmd.F2Coeff0 = in->filter2Coefficients[0];
+ cmd.F2Coeff1 = in->filter2Coefficients[1];
+ cmd.F2Coeff2 = in->filter2Coefficients[2];
+ cmd.F2Coeff3 = in->filter2Coefficients[3];
+ cmd.F2Coeff4 = in->filter2Coefficients[4];
+ cmd.F2Coeff5 = in->filter2Coefficients[5];
+ cmd.F2Coeff6 = in->filter2Coefficients[6];
+ cmd.F2Coeff7 = in->filter2Coefficients[7];
+ cmd.F2Coeff8 = in->filter2Coefficients[8];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_config(struct vfe_cmd_asf_config *in)
+{
+ struct vfe_asf_update cmd;
+ struct vfe_asfcrop_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+ cmd.smoothEnable = in->smoothFilterEnabled;
+ cmd.sharpMode = in->sharpMode;
+ cmd.smoothCoeff0 = in->smoothCoefCenter;
+ cmd.smoothCoeff1 = in->smoothCoefSurr;
+ cmd.cropEnable = in->cropEnable;
+ cmd.sharpThresholdE1 = in->sharpThreshE1;
+ cmd.sharpDegreeK1 = in->sharpK1;
+ cmd.sharpDegreeK2 = in->sharpK2;
+ cmd.normalizeFactor = in->normalizeFactor;
+ cmd.sharpThresholdE2 = in->sharpThreshE2;
+ cmd.sharpThresholdE3 = in->sharpThreshE3;
+ cmd.sharpThresholdE4 = in->sharpThreshE4;
+ cmd.sharpThresholdE5 = in->sharpThreshE5;
+ cmd.F1Coeff0 = in->filter1Coefficients[0];
+ cmd.F1Coeff1 = in->filter1Coefficients[1];
+ cmd.F1Coeff2 = in->filter1Coefficients[2];
+ cmd.F1Coeff3 = in->filter1Coefficients[3];
+ cmd.F1Coeff4 = in->filter1Coefficients[4];
+ cmd.F1Coeff5 = in->filter1Coefficients[5];
+ cmd.F1Coeff6 = in->filter1Coefficients[6];
+ cmd.F1Coeff7 = in->filter1Coefficients[7];
+ cmd.F1Coeff8 = in->filter1Coefficients[8];
+ cmd.F2Coeff0 = in->filter2Coefficients[0];
+ cmd.F2Coeff1 = in->filter2Coefficients[1];
+ cmd.F2Coeff2 = in->filter2Coefficients[2];
+ cmd.F2Coeff3 = in->filter2Coefficients[3];
+ cmd.F2Coeff4 = in->filter2Coefficients[4];
+ cmd.F2Coeff5 = in->filter2Coefficients[5];
+ cmd.F2Coeff6 = in->filter2Coefficients[6];
+ cmd.F2Coeff7 = in->filter2Coefficients[7];
+ cmd.F2Coeff8 = in->filter2Coefficients[8];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.firstLine = in->cropFirstLine;
+ cmd2.lastLine = in->cropLastLine;
+ cmd2.firstPixel = in->cropFirstPixel;
+ cmd2.lastPixel = in->cropLastPixel;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in)
+{
+ struct vfe_wb_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.whiteBalanceEnable = in->enable;
+
+ cmd.ch0Gain = in->ch0Gain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in)
+{
+ struct vfe_chroma_suppress_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable;
+
+ cmd.m1 = in->m1;
+ cmd.m3 = in->m3;
+ cmd.n1 = in->n1;
+ cmd.n3 = in->n3;
+ cmd.mm1 = in->mm1;
+ cmd.nn1 = in->nn1;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in)
+{
+ struct vfe_rolloff_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable;
+
+ cmd.gridWidth = in->gridWidth;
+ cmd.gridHeight = in->gridHeight;
+ cmd.yDelta = in->yDelta;
+ cmd.gridX = in->gridXIndex;
+ cmd.gridY = in->gridYIndex;
+ cmd.pixelX = in->gridPixelXIndex;
+ cmd.pixelY = in->gridPixelYIndex;
+ cmd.yDeltaAccum = in->yDeltaAccum;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ vfe_write_lens_roll_off_table(in);
+}
+
+void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *in)
+{
+ struct vfe_chromasubsample_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable;
+
+ cmd.hCositedPhase = in->hCositedPhase;
+ cmd.vCositedPhase = in->vCositedPhase;
+ cmd.hCosited = in->hCosited;
+ cmd.vCosited = in->vCosited;
+ cmd.hsubSampleEnable = in->hsubSampleEnable;
+ cmd.vsubSampleEnable = in->vsubSampleEnable;
+ cmd.cropEnable = in->cropEnable;
+ cmd.cropWidthLastPixel = in->cropWidthLastPixel;
+ cmd.cropWidthFirstPixel = in->cropWidthFirstPixel;
+ cmd.cropHeightLastLine = in->cropHeightLastLine;
+ cmd.cropHeightFirstLine = in->cropHeightFirstLine;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in)
+{
+ struct vfe_chroma_enhance_cfg cmd;
+ struct vfe_color_convert_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable;
+
+ cmd.ap = in->ap;
+ cmd.am = in->am;
+ cmd.bp = in->bp;
+ cmd.bm = in->bm;
+ cmd.cp = in->cp;
+ cmd.cm = in->cm;
+ cmd.dp = in->dp;
+ cmd.dm = in->dm;
+ cmd.kcb = in->kcb;
+ cmd.kcr = in->kcr;
+
+ cmd2.v0 = in->RGBtoYConversionV0;
+ cmd2.v1 = in->RGBtoYConversionV1;
+ cmd2.v2 = in->RGBtoYConversionV2;
+ cmd2.ConvertOffset = in->RGBtoYConversionOffset;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in)
+{
+ struct vfe_scaler2_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in)
+{
+ struct vfe_scaler2_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in)
+{
+ struct vfe_main_scaler_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.horizMNInit = in->MNInitH.MNCounterInit;
+ cmd.horizPhaseInit = in->MNInitH.phaseInit;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+ cmd.vertMNInit = in->MNInitV.MNCounterInit;
+ cmd.vertPhaseInit = in->MNInitV.phaseInit;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_wb_exp_stop(void)
+{
+ ctrl->vfeStatsCmdLocal.axwEnable = FALSE;
+ ctrl->vfeImaskLocal.awbPingpongIrq = FALSE;
+}
+
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in)
+{
+ struct vfe_statsawb_update cmd;
+ struct vfe_statsawbae_update cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ cmd.m1 = in->awbMCFG[0];
+ cmd.m2 = in->awbMCFG[1];
+ cmd.m3 = in->awbMCFG[2];
+ cmd.m4 = in->awbMCFG[3];
+ cmd.c1 = in->awbCCFG[0];
+ cmd.c2 = in->awbCCFG[1];
+ cmd.c3 = in->awbCCFG[2];
+ cmd.c4 = in->awbCCFG[3];
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.aeRegionCfg = in->wbExpRegions;
+ cmd2.aeSubregionCfg = in->wbExpSubRegion;
+ cmd2.awbYMin = in->awbYMin;
+ cmd2.awbYMax = in->awbYMax;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in)
+{
+ struct vfe_statsaf_update cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.windowVOffset = in->windowVOffset;
+ cmd.windowHOffset = in->windowHOffset;
+ cmd.windowMode = in->windowMode;
+ cmd.windowHeight = in->windowHeight;
+ cmd.windowWidth = in->windowWidth;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in)
+{
+ struct vfe_statsawb_update cmd;
+ struct vfe_statsawbae_update cmd2;
+ struct vfe_statsaxw_hdr_cfg cmd3;
+
+ ctrl->vfeStatsCmdLocal.axwEnable = in->enable;
+ ctrl->vfeImaskLocal.awbPingpongIrq = TRUE;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+ memset(&cmd3, 0, sizeof(cmd3));
+
+ cmd.m1 = in->awbMCFG[0];
+ cmd.m2 = in->awbMCFG[1];
+ cmd.m3 = in->awbMCFG[2];
+ cmd.m4 = in->awbMCFG[3];
+ cmd.c1 = in->awbCCFG[0];
+ cmd.c2 = in->awbCCFG[1];
+ cmd.c3 = in->awbCCFG[2];
+ cmd.c4 = in->awbCCFG[3];
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.aeRegionCfg = in->wbExpRegions;
+ cmd2.aeSubregionCfg = in->wbExpSubRegion;
+ cmd2.awbYMin = in->awbYMin;
+ cmd2.awbYMax = in->awbYMax;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+
+ cmd3.axwHeader = in->axwHeader;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER,
+ (uint32_t *)&cmd3, sizeof(cmd3));
+}
+
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in)
+{
+ struct vfe_statsaf_update cmd;
+ struct vfe_statsaf_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable;
+ ctrl->vfeImaskLocal.afPingpongIrq = TRUE;
+
+ cmd.windowVOffset = in->windowVOffset;
+ cmd.windowHOffset = in->windowHOffset;
+ cmd.windowMode = in->windowMode;
+ cmd.windowHeight = in->windowHeight;
+ cmd.windowWidth = in->windowWidth;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.a00 = in->highPassCoef[0];
+ cmd2.a04 = in->highPassCoef[1];
+ cmd2.a20 = in->highPassCoef[2];
+ cmd2.a21 = in->highPassCoef[3];
+ cmd2.a22 = in->highPassCoef[4];
+ cmd2.a23 = in->highPassCoef[5];
+ cmd2.a24 = in->highPassCoef[6];
+ cmd2.fvMax = in->metricMax;
+ cmd2.fvMetric = in->metricSelection;
+ cmd2.afHeader = in->bufferHeader;
+ cmd2.entry00 = in->gridForMultiWindows[0];
+ cmd2.entry01 = in->gridForMultiWindows[1];
+ cmd2.entry02 = in->gridForMultiWindows[2];
+ cmd2.entry03 = in->gridForMultiWindows[3];
+ cmd2.entry10 = in->gridForMultiWindows[4];
+ cmd2.entry11 = in->gridForMultiWindows[5];
+ cmd2.entry12 = in->gridForMultiWindows[6];
+ cmd2.entry13 = in->gridForMultiWindows[7];
+ cmd2.entry20 = in->gridForMultiWindows[8];
+ cmd2.entry21 = in->gridForMultiWindows[9];
+ cmd2.entry22 = in->gridForMultiWindows[10];
+ cmd2.entry23 = in->gridForMultiWindows[11];
+ cmd2.entry30 = in->gridForMultiWindows[12];
+ cmd2.entry31 = in->gridForMultiWindows[13];
+ cmd2.entry32 = in->gridForMultiWindows[14];
+ cmd2.entry33 = in->gridForMultiWindows[15];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_setting(struct vfe_cmd_stats_setting *in)
+{
+ struct vfe_statsframe cmd1;
+ struct vfe_busstats_wrprio cmd2;
+
+ memset(&cmd1, 0, sizeof(cmd1));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0];
+ ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1];
+ ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2];
+
+ ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0];
+ ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1];
+ ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2];
+
+ cmd1.lastPixel = in->frameHDimension;
+ cmd1.lastLine = in->frameVDimension;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE,
+ (uint32_t *)&cmd1, sizeof(cmd1));
+
+ cmd2.afBusPriority = in->afBusPriority;
+ cmd2.awbBusPriority = in->awbBusPriority;
+ cmd2.histBusPriority = in->histBusPriority;
+ cmd2.afBusPriorityEn = in->afBusPrioritySelection;
+ cmd2.awbBusPriorityEn = in->awbBusPrioritySelection;
+ cmd2.histBusPriorityEn = in->histBusPrioritySelection;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+
+ /* Program the bus ping pong address for statistics modules. */
+ writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+ writel(in->awbBuffer[0],
+ ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ writel(in->awbBuffer[1],
+ ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+ writel(in->histBuffer[0],
+ ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+ writel(in->histBuffer[1],
+ ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+}
+
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in)
+{
+ struct VFE_AxiInputCmdType cmd;
+ uint32_t xSizeWord, axiRdUnpackPattern;
+ uint8_t axiInputPpw;
+ uint32_t busPingpongRdIrqEnable;
+
+ ctrl->vfeImaskLocal.rdPingpongIrq = TRUE;
+
+ switch (in->pixelSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_10BIT;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_12BIT;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ default:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_8BIT;
+ break;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ switch (in->pixelSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ axiInputPpw = 6;
+ axiRdUnpackPattern = 0xD43210;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ axiInputPpw = 5;
+ axiRdUnpackPattern = 0xC3210;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ default:
+ axiInputPpw = 8;
+ axiRdUnpackPattern = 0xF6543210;
+ break;
+ }
+
+ xSizeWord =
+ ((((in->xOffset % axiInputPpw) + in->xSize) +
+ (axiInputPpw - 1)) / axiInputPpw) - 1;
+
+ cmd.stripeStartAddr0 = in->fragAddr[0];
+ cmd.stripeStartAddr1 = in->fragAddr[1];
+ cmd.stripeStartAddr2 = in->fragAddr[2];
+ cmd.stripeStartAddr3 = in->fragAddr[3];
+ cmd.ySize = in->ySize;
+ cmd.yOffsetDelta = 0;
+ cmd.xSizeWord = xSizeWord;
+ cmd.burstLength = 1;
+ cmd.NumOfRows = in->numOfRows;
+ cmd.RowIncrement = (in->rowIncrement + (axiInputPpw - 1)) / axiInputPpw;
+ cmd.mainUnpackHeight = in->ySize;
+ cmd.mainUnpackWidth = in->xSize - 1;
+ cmd.mainUnpackHbiSel = (uint32_t) in->unpackHbi;
+ cmd.mainUnpackPhase = in->unpackPhase;
+ cmd.unpackPattern = axiRdUnpackPattern;
+ cmd.padLeft = in->padRepeatCountLeft;
+ cmd.padRight = in->padRepeatCountRight;
+ cmd.padTop = in->padRepeatCountTop;
+ cmd.padBottom = in->padRepeatCountBottom;
+ cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0;
+ cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1;
+ cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2;
+ cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3;
+ cmd.leftUnpackStop0 = in->padLeftStopCycle0;
+ cmd.leftUnpackStop1 = in->padLeftStopCycle1;
+ cmd.leftUnpackStop2 = in->padLeftStopCycle2;
+ cmd.leftUnpackStop3 = in->padLeftStopCycle3;
+ cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0;
+ cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1;
+ cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2;
+ cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3;
+ cmd.rightUnpackStop0 = in->padRightStopCycle0;
+ cmd.rightUnpackStop1 = in->padRightStopCycle1;
+ cmd.rightUnpackStop2 = in->padRightStopCycle2;
+ cmd.rightUnpackStop3 = in->padRightStopCycle3;
+ cmd.topUnapckPattern = in->padTopLineCount;
+ cmd.bottomUnapckPattern = in->padBottomLineCount;
+
+ /* program vfe_bus_cfg */
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ /* hacking code, put it to default value */
+ busPingpongRdIrqEnable = 0xf;
+
+ writel(busPingpongRdIrqEnable, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN);
+}
+
+void vfe_axi_output_config(struct vfe_cmd_axi_output_config *in)
+{
+ /* local variable */
+ uint32_t *pcircle;
+ uint32_t *pdest;
+ uint32_t *psrc;
+ uint8_t i;
+ uint8_t fcnt;
+ uint16_t axioutpw = 8;
+
+ /* parameters check, condition and usage mode check */
+ ctrl->encPath.fragCount = in->output2.fragmentCount;
+ if (ctrl->encPath.fragCount > 1)
+ ctrl->encPath.multiFrag = TRUE;
+
+ ctrl->viewPath.fragCount = in->output1.fragmentCount;
+ if (ctrl->viewPath.fragCount > 1)
+ ctrl->viewPath.multiFrag = TRUE;
+
+ /* VFE_BUS_CFG. raw data size */
+ ctrl->vfeBusConfigLocal.rawPixelDataSize = in->outputDataSize;
+
+ switch (in->outputDataSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ axioutpw = 8;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ axioutpw = 6;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ axioutpw = 5;
+ break;
+ }
+
+ ctrl->axiOutputMode = in->outputMode;
+
+ CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode);
+
+ switch (ctrl->axiOutputMode) {
+ case VFE_AXI_OUTPUT_MODE_Output1:{
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.encIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE;
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output2:{
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.viewIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output1AndOutput2:{
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2:{
+ /* For raw snapshot, we need both ping and pong buffer
+ * initialized to the same address. Otherwise, if we
+ * leave the pong buffer to NULL, there will be
+ * axi_error.
+ * Note that ideally we should deal with this at upper
+ * layer, which is in msm_vfe8x.c */
+ if (!in->output2.outputCbcr.outFragments[1][0]) {
+ in->output2.outputCbcr.outFragments[1][0] =
+ in->output2.outputCbcr.outFragments[0][0];
+ }
+
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.viewIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1:{
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_VIEW_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2:{
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */
+ break;
+
+ case VFE_AXI_LAST_OUTPUT_MODE_ENUM:
+ break;
+ } /* switch */
+
+ /* Save the addresses for each path. */
+ /* output2 path */
+ fcnt = ctrl->encPath.fragCount;
+
+ pcircle = ctrl->encPath.yPath.addressBuffer;
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ psrc = &(in->output2.outputY.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputY.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputY.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ pcircle = ctrl->encPath.cbcrPath.addressBuffer;
+
+ psrc = &(in->output2.outputCbcr.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputCbcr.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputCbcr.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ vfe_set_bus_pipo_addr(&ctrl->viewPath, &ctrl->encPath);
+
+ ctrl->encPath.ackPending = FALSE;
+ ctrl->encPath.currentFrame = ping;
+ ctrl->encPath.whichOutputPath = 1;
+ ctrl->encPath.yPath.fragIndex = 2;
+ ctrl->encPath.cbcrPath.fragIndex = 2;
+ ctrl->encPath.yPath.hwCurrentFlag = ping;
+ ctrl->encPath.cbcrPath.hwCurrentFlag = ping;
+
+ /* output1 path */
+ pcircle = ctrl->viewPath.yPath.addressBuffer;
+ pdest = ctrl->viewPath.nextFrameAddrBuf;
+ fcnt = ctrl->viewPath.fragCount;
+
+ psrc = &(in->output1.outputY.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputY.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputY.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ pcircle = ctrl->viewPath.cbcrPath.addressBuffer;
+
+ psrc = &(in->output1.outputCbcr.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputCbcr.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputCbcr.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->viewPath.ackPending = FALSE;
+ ctrl->viewPath.currentFrame = ping;
+ ctrl->viewPath.whichOutputPath = 0;
+ ctrl->viewPath.yPath.fragIndex = 2;
+ ctrl->viewPath.cbcrPath.fragIndex = 2;
+ ctrl->viewPath.yPath.hwCurrentFlag = ping;
+ ctrl->viewPath.cbcrPath.hwCurrentFlag = ping;
+
+ /* call to program the registers. */
+ vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw);
+}
+
+void vfe_epoch1_config(struct vfe_cmds_camif_epoch *in)
+{
+ struct vfe_epoch1cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ /* determine if epoch interrupt needs to be enabled. */
+ if (in->enable == TRUE) {
+ cmd.epoch1Line = in->lineindex;
+ vfe_prog_hw(ctrl->vfebase + CAMIF_EPOCH_IRQ, (uint32_t *)&cmd,
+ sizeof(cmd));
+ }
+
+ /* Set the epoch1 interrupt mask. */
+ ctrl->vfeImaskLocal.camifEpoch1Irq = in->enable;
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+ /* Store the epoch1 data. */
+ ctrl->vfeCamifEpoch1Local.enable = in->enable;
+ ctrl->vfeCamifEpoch1Local.lineindex = in->lineindex;
+}
+
+void vfe_camif_config(struct vfe_cmd_camif_config *in)
+{
+ struct vfe_camifcfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine);
+ CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame);
+ CDBG("camif.window firstpixel = %d\n", in->window.firstpixel);
+ CDBG("camif.window lastpixel = %d\n", in->window.lastpixel);
+ CDBG("camif.window firstline = %d\n", in->window.firstline);
+ CDBG("camif.window lastline = %d\n", in->window.lastline);
+
+ /* determine if epoch interrupt needs to be enabled. */
+ if ((in->epoch1.enable == TRUE) &&
+ (in->epoch1.lineindex <= in->frame.linesPerFrame))
+ ctrl->vfeImaskLocal.camifEpoch1Irq = 1;
+
+ if ((in->epoch2.enable == TRUE) &&
+ (in->epoch2.lineindex <= in->frame.linesPerFrame)) {
+ ctrl->vfeImaskLocal.camifEpoch2Irq = 1;
+ }
+
+ /* save the content to program CAMIF_CONFIG seperately. */
+ ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig;
+
+ /* EFS_Config */
+ cmd.efsEndOfLine = in->EFS.efsendofline;
+ cmd.efsStartOfLine = in->EFS.efsstartofline;
+ cmd.efsEndOfFrame = in->EFS.efsendofframe;
+ cmd.efsStartOfFrame = in->EFS.efsstartofframe;
+
+ /* Frame Config */
+ cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine;
+ cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame;
+
+ /* Window Width Config */
+ cmd.windowWidthCfgLastPixel = in->window.lastpixel;
+ cmd.windowWidthCfgFirstPixel = in->window.firstpixel;
+
+ /* Window Height Config */
+ cmd.windowHeightCfglastLine = in->window.lastline;
+ cmd.windowHeightCfgfirstLine = in->window.firstline;
+
+ /* Subsample 1 Config */
+ cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask;
+ cmd.subsample1CfgLineSkip = in->subsample.lineskipmask;
+
+ /* Subsample 2 Config */
+ cmd.subsample2CfgFrameSkip = in->subsample.frameskip;
+ cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode;
+ cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap;
+
+ /* Epoch Interrupt */
+ cmd.epoch1Line = in->epoch1.lineindex;
+ cmd.epoch2Line = in->epoch2.lineindex;
+
+ vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in)
+{
+ struct vfe_fov_crop_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.cropEnable = in->enable;
+
+ /* FOV Corp, Part 1 */
+ cmd.lastPixel = in->lastPixel;
+ cmd.firstPixel = in->firstPixel;
+
+ /* FOV Corp, Part 2 */
+ cmd.lastLine = in->lastLine;
+ cmd.firstLine = in->firstLine;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_get_hw_version(struct vfe_cmd_hw_version *out)
+{
+ uint32_t vfeHwVersionPacked;
+ struct vfe_hw_ver ver;
+
+ vfeHwVersionPacked = readl(ctrl->vfebase + VFE_HW_VERSION);
+
+ ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked);
+
+ out->coreVersion = ver.coreVersion;
+ out->minorVersion = ver.minorVersion;
+ out->majorVersion = ver.majorVersion;
+}
+
+static void vfe_reset_internal_variables(void)
+{
+ /* local variables to program the hardware. */
+ ctrl->vfeImaskPacked = 0;
+ ctrl->vfeImaskCompositePacked = 0;
+
+ /* FALSE = disable, 1 = enable. */
+ memset(&ctrl->vfeModuleEnableLocal, 0,
+ sizeof(ctrl->vfeModuleEnableLocal));
+
+ /* 0 = disable, 1 = enable */
+ memset(&ctrl->vfeCamifConfigLocal, 0,
+ sizeof(ctrl->vfeCamifConfigLocal));
+ /* 0 = disable, 1 = enable */
+ memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal));
+ memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal));
+ memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal));
+ memset(&ctrl->vfeBusPmConfigLocal, 0,
+ sizeof(ctrl->vfeBusPmConfigLocal));
+ memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal));
+ memset(&ctrl->vfeInterruptNameLocal, 0,
+ sizeof(ctrl->vfeInterruptNameLocal));
+ memset(&ctrl->vfeDroppedFrameCounts, 0,
+ sizeof(ctrl->vfeDroppedFrameCounts));
+ memset(&ctrl->vfeIrqThreadMsgLocal, 0,
+ sizeof(ctrl->vfeIrqThreadMsgLocal));
+
+ /* state control variables */
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ ctrl->vfeStopAckPending = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = VFE_COMP_IRQ_BOTH_Y_CBCR;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vstate = VFE_STATE_IDLE;
+
+ ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM;
+ /* 0 for continuous mode, 1 for snapshot mode */
+ ctrl->vfeOperationMode = VFE_START_OPERATION_MODE_CONTINUOUS;
+ ctrl->vfeSnapShotCount = 0;
+ ctrl->vfeStatsPingPongReloadFlag = FALSE;
+ /* this is unsigned 32 bit integer. */
+ ctrl->vfeFrameId = 0;
+ ctrl->vfeFrameSkip.output1Pattern = 0xffffffff;
+ ctrl->vfeFrameSkip.output1Period = 31;
+ ctrl->vfeFrameSkip.output2Pattern = 0xffffffff;
+ ctrl->vfeFrameSkip.output2Period = 31;
+ ctrl->vfeFrameSkipPattern = 0xffffffff;
+ ctrl->vfeFrameSkipCount = 0;
+ ctrl->vfeFrameSkipPeriod = 31;
+
+ memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath));
+ memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath));
+
+ ctrl->encPath.whichOutputPath = 1;
+ ctrl->encPath.cbcrStatusBit = 5;
+ ctrl->viewPath.whichOutputPath = 0;
+ ctrl->viewPath.cbcrStatusBit = 7;
+
+ ctrl->vfeTestGenStartFlag = FALSE;
+
+ /* default to bank 0. */
+ ctrl->vfeLaBankSel = 0;
+
+ /* default to bank 0 for all channels. */
+ memset(&ctrl->vfeGammaLutSel, 0, sizeof(ctrl->vfeGammaLutSel));
+
+ /* Stats control variables. */
+ memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl));
+ memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl));
+ vfe_set_stats_pingpong_address(&ctrl->afStatsControl,
+ &ctrl->awbStatsControl);
+}
+
+void vfe_reset(void)
+{
+ vfe_reset_internal_variables();
+
+ ctrl->vfeImaskLocal.resetAckIrq = TRUE;
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+
+ /* disable all interrupts. */
+ writel(VFE_DISABLE_ALL_IRQS, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+
+ /* clear all pending interrupts */
+ writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* enable reset_ack interrupt. */
+ writel(ctrl->vfeImaskPacked, ctrl->vfebase + VFE_IRQ_MASK);
+
+ writel(VFE_RESET_UPON_RESET_CMD, ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
diff --git a/drivers/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h
new file mode 100644
index 0000000..aef7eca
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.h
@@ -0,0 +1,1570 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __MSM_VFE8X_REG_H__
+#define __MSM_VFE8X_REG_H__
+
+#include <mach/msm_iomap.h>
+#include <mach/camera.h>
+#include "msm_vfe8x.h"
+
+/* at start of camif, bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START 0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR 0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT 0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR 0x00000000
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD 0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD 0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 = halted, 0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go; bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO 0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO 0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples. JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+/* imask for while waiting for stop ack, driver has already
+ * requested stop, waiting for reset irq,
+ * bit 29,28,27,26 for async timer, bit 9 for reset */
+#define VFE_IMASK_WHILE_STOPPING 0x3c000200
+
+/* when normal case, don't want to block error status.
+ * bit 0,6,20,21,22,30,31 */
+#define VFE_IMASK_ERROR_ONLY 0xC0700041
+#define VFE_REG_UPDATE_TRIGGER 1
+#define VFE_PM_BUF_MAX_CNT_MASK 0xFF
+#define VFE_DMI_CFG_DEFAULT 0x00000100
+#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32
+#define VFE_AF_PINGPONG_STATUS_BIT 0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT 0x200
+
+/* VFE I/O registers */
+enum {
+ VFE_HW_VERSION = 0x00000000,
+ VFE_GLOBAL_RESET_CMD = 0x00000004,
+ VFE_MODULE_RESET = 0x00000008,
+ VFE_CGC_OVERRIDE = 0x0000000C,
+ VFE_MODULE_CFG = 0x00000010,
+ VFE_CFG = 0x00000014,
+ VFE_IRQ_MASK = 0x00000018,
+ VFE_IRQ_CLEAR = 0x0000001C,
+ VFE_IRQ_STATUS = 0x00000020,
+ VFE_IRQ_COMPOSITE_MASK = 0x00000024,
+ VFE_BUS_CMD = 0x00000028,
+ VFE_BUS_CFG = 0x0000002C,
+ VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030,
+ VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034,
+ VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038,
+ VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C,
+ VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040,
+ VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044,
+ VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048,
+ VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C,
+ VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050,
+ VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054,
+ VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058,
+ VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C,
+ VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060,
+ VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064,
+ VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068,
+ VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C,
+ VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070,
+ VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074,
+ VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078,
+ VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C,
+ VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080,
+ VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084,
+ VFE_BUS_STATS_WR_PRIORITY = 0x00000088,
+ VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C,
+ VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090,
+ VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094,
+ VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098,
+ VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C,
+ VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0,
+ VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4,
+ VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8,
+ VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC,
+ VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0,
+ VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4,
+ VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8,
+ VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC,
+ VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0,
+ VFE_BUS_PINGPONG_STATUS = 0x000000C4,
+ VFE_BUS_PM_CMD = 0x000000C8,
+ VFE_BUS_PM_CFG = 0x000000CC,
+ VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0,
+ VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4,
+ VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8,
+ VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC,
+ VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0,
+ VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4,
+ VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8,
+ VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC,
+ VFE_BUS_MISR_CFG = 0x000000F4,
+ VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8,
+ VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC,
+ VFE_BUS_MISR_RD_VAL = 0x00000100,
+ VFE_AXI_CMD = 0x00000104,
+ VFE_AXI_CFG = 0x00000108,
+ VFE_AXI_STATUS = 0x0000010C,
+ CAMIF_COMMAND = 0x00000110,
+ CAMIF_CONFIG = 0x00000114,
+ CAMIF_EFS_CONFIG = 0x00000118,
+ CAMIF_FRAME_CONFIG = 0x0000011C,
+ CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120,
+ CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124,
+ CAMIF_SUBSAMPLE1_CONFIG = 0x00000128,
+ CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C,
+ CAMIF_EPOCH_IRQ = 0x00000130,
+ CAMIF_STATUS = 0x00000134,
+ CAMIF_MISR = 0x00000138,
+ VFE_SYNC_TIMER_CMD = 0x0000013C,
+ VFE_SYNC_TIMER0_LINE_START = 0x00000140,
+ VFE_SYNC_TIMER0_PIXEL_START = 0x00000144,
+ VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148,
+ VFE_SYNC_TIMER1_LINE_START = 0x0000014C,
+ VFE_SYNC_TIMER1_PIXEL_START = 0x00000150,
+ VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154,
+ VFE_SYNC_TIMER2_LINE_START = 0x00000158,
+ VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C,
+ VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160,
+ VFE_SYNC_TIMER_POLARITY = 0x00000164,
+ VFE_ASYNC_TIMER_CMD = 0x00000168,
+ VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C,
+ VFE_ASYNC_TIMER0_CFG_1 = 0x00000170,
+ VFE_ASYNC_TIMER1_CFG_0 = 0x00000174,
+ VFE_ASYNC_TIMER1_CFG_1 = 0x00000178,
+ VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C,
+ VFE_ASYNC_TIMER2_CFG_1 = 0x00000180,
+ VFE_ASYNC_TIMER3_CFG_0 = 0x00000184,
+ VFE_ASYNC_TIMER3_CFG_1 = 0x00000188,
+ VFE_TIMER_SEL = 0x0000018C,
+ VFE_REG_UPDATE_CMD = 0x00000190,
+ VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194,
+ VFE_BLACK_EVEN_ODD_VALUE = 0x00000198,
+ VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C,
+ VFE_BLACK_ODD_ODD_VALUE = 0x000001A0,
+ VFE_ROLLOFF_CFG_0 = 0x000001A4,
+ VFE_ROLLOFF_CFG_1 = 0x000001A8,
+ VFE_ROLLOFF_CFG_2 = 0x000001AC,
+ VFE_DEMUX_CFG = 0x000001B0,
+ VFE_DEMUX_GAIN_0 = 0x000001B4,
+ VFE_DEMUX_GAIN_1 = 0x000001B8,
+ VFE_DEMUX_EVEN_CFG = 0x000001BC,
+ VFE_DEMUX_ODD_CFG = 0x000001C0,
+ VFE_DEMOSAIC_CFG = 0x000001C4,
+ VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8,
+ VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC,
+ VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0,
+ VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4,
+ VFE_DEMOSAIC_STATUS = 0x000001D8,
+ VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC,
+ VFE_CROP_WIDTH_CFG = 0x000001E0,
+ VFE_CROP_HEIGHT_CFG = 0x000001E4,
+ VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8,
+ VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC,
+ VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0,
+ VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4,
+ VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8,
+ VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC,
+ VFE_COLOR_CORRECT_COEFF_6 = 0x00000200,
+ VFE_COLOR_CORRECT_COEFF_7 = 0x00000204,
+ VFE_COLOR_CORRECT_COEFF_8 = 0x00000208,
+ VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C,
+ VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210,
+ VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214,
+ VFE_COLOR_CORRECT_COEFF_Q = 0x00000218,
+ VFE_LA_CFG = 0x0000021C,
+ VFE_LUT_BANK_SEL = 0x00000220,
+ VFE_CHROMA_ENHAN_A = 0x00000224,
+ VFE_CHROMA_ENHAN_B = 0x00000228,
+ VFE_CHROMA_ENHAN_C = 0x0000022C,
+ VFE_CHROMA_ENHAN_D = 0x00000230,
+ VFE_CHROMA_ENHAN_K = 0x00000234,
+ VFE_COLOR_CONVERT_COEFF_0 = 0x00000238,
+ VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C,
+ VFE_COLOR_CONVERT_COEFF_2 = 0x00000240,
+ VFE_COLOR_CONVERT_OFFSET = 0x00000244,
+ VFE_ASF_CFG = 0x00000248,
+ VFE_ASF_SHARP_CFG_0 = 0x0000024C,
+ VFE_ASF_SHARP_CFG_1 = 0x00000250,
+ VFE_ASF_SHARP_COEFF_0 = 0x00000254,
+ VFE_ASF_SHARP_COEFF_1 = 0x00000258,
+ VFE_ASF_SHARP_COEFF_2 = 0x0000025C,
+ VFE_ASF_SHARP_COEFF_3 = 0x00000260,
+ VFE_ASF_MAX_EDGE = 0x00000264,
+ VFE_ASF_CROP_WIDTH_CFG = 0x00000268,
+ VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C,
+ VFE_SCALE_CFG = 0x00000270,
+ VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274,
+ VFE_SCALE_H_PHASE_CFG = 0x00000278,
+ VFE_SCALE_H_STRIPE_CFG = 0x0000027C,
+ VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280,
+ VFE_SCALE_V_PHASE_CFG = 0x00000284,
+ VFE_SCALE_V_STRIPE_CFG = 0x00000288,
+ VFE_SCALE_Y_CFG = 0x0000028C,
+ VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290,
+ VFE_SCALE_Y_H_PHASE_CFG = 0x00000294,
+ VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298,
+ VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C,
+ VFE_SCALE_CBCR_CFG = 0x000002A0,
+ VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4,
+ VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8,
+ VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC,
+ VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0,
+ VFE_WB_CFG = 0x000002B4,
+ VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8,
+ VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC,
+ VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0,
+ VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4,
+ VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8,
+ VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC,
+ VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0,
+ VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4,
+ VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8,
+ VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC,
+ VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0,
+ VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4,
+ VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8,
+ VFE_CLAMP_MAX_CFG = 0x000002EC,
+ VFE_CLAMP_MIN_CFG = 0x000002F0,
+ VFE_STATS_CMD = 0x000002F4,
+ VFE_STATS_AF_CFG = 0x000002F8,
+ VFE_STATS_AF_DIM = 0x000002FC,
+ VFE_STATS_AF_GRID_0 = 0x00000300,
+ VFE_STATS_AF_GRID_1 = 0x00000304,
+ VFE_STATS_AF_GRID_2 = 0x00000308,
+ VFE_STATS_AF_GRID_3 = 0x0000030C,
+ VFE_STATS_AF_HEADER = 0x00000310,
+ VFE_STATS_AF_COEF0 = 0x00000314,
+ VFE_STATS_AF_COEF1 = 0x00000318,
+ VFE_STATS_AWBAE_CFG = 0x0000031C,
+ VFE_STATS_AXW_HEADER = 0x00000320,
+ VFE_STATS_AWB_MCFG = 0x00000324,
+ VFE_STATS_AWB_CCFG1 = 0x00000328,
+ VFE_STATS_AWB_CCFG2 = 0x0000032C,
+ VFE_STATS_HIST_HEADER = 0x00000330,
+ VFE_STATS_HIST_INNER_OFFSET = 0x00000334,
+ VFE_STATS_HIST_INNER_DIM = 0x00000338,
+ VFE_STATS_FRAME_SIZE = 0x0000033C,
+ VFE_DMI_CFG = 0x00000340,
+ VFE_DMI_ADDR = 0x00000344,
+ VFE_DMI_DATA_HI = 0x00000348,
+ VFE_DMI_DATA_LO = 0x0000034C,
+ VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350,
+ VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354,
+ VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358,
+ VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C,
+ VFE_TESTBUS_SEL = 0x00000360,
+ VFE_TESTGEN_CFG = 0x00000364,
+ VFE_SW_TESTGEN_CMD = 0x00000368,
+ VFE_HW_TESTGEN_CMD = 0x0000036C,
+ VFE_HW_TESTGEN_CFG = 0x00000370,
+ VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374,
+ VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378,
+ VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C,
+ VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380,
+ VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384,
+ VFE_HW_TESTGEN_HBI_CFG = 0x00000388,
+ VFE_HW_TESTGEN_VBL_CFG = 0x0000038C,
+ VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390,
+ VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394,
+ VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398,
+ VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C,
+ VFE_SPARE = 0x000003A0,
+};
+
+#define ping 0x0
+#define pong 0x1
+
+struct vfe_bus_cfg_data {
+ boolean stripeRdPathEn;
+ boolean encYWrPathEn;
+ boolean encCbcrWrPathEn;
+ boolean viewYWrPathEn;
+ boolean viewCbcrWrPathEn;
+ enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize;
+ enum VFE_RAW_WR_PATH_SEL rawWritePathSelect;
+};
+
+struct vfe_camif_cfg_data {
+ boolean camif2OutputEnable;
+ boolean camif2BusEnable;
+ struct vfe_cmds_camif_cfg camifCfgFromCmd;
+};
+
+struct vfe_irq_composite_mask_config {
+ uint8_t encIrqComMask;
+ uint8_t viewIrqComMask;
+ uint8_t ceDoneSel;
+};
+
+/* define a structure for each output path.*/
+struct vfe_output_path {
+ uint32_t addressBuffer[8];
+ uint16_t fragIndex;
+ boolean hwCurrentFlag;
+ uint8_t *hwRegPingAddress;
+ uint8_t *hwRegPongAddress;
+};
+
+struct vfe_output_path_combo {
+ boolean whichOutputPath;
+ boolean pathEnabled;
+ boolean multiFrag;
+ uint8_t fragCount;
+ boolean ackPending;
+ uint8_t currentFrame;
+ uint32_t nextFrameAddrBuf[8];
+ struct vfe_output_path yPath;
+ struct vfe_output_path cbcrPath;
+ uint8_t snapshotPendingCount;
+ boolean pmEnabled;
+ uint8_t cbcrStatusBit;
+};
+
+struct vfe_stats_control {
+ boolean ackPending;
+ uint32_t addressBuffer[2];
+ uint32_t nextFrameAddrBuf;
+ boolean pingPongStatus;
+ uint8_t *hwRegPingAddress;
+ uint8_t *hwRegPongAddress;
+ uint32_t droppedStatsFrameCount;
+ uint32_t bufToRender;
+};
+
+struct vfe_gamma_lut_sel {
+ boolean ch0BankSelect;
+ boolean ch1BankSelect;
+ boolean ch2BankSelect;
+};
+
+struct vfe_interrupt_mask {
+ boolean camifErrorIrq;
+ boolean camifSofIrq;
+ boolean camifEolIrq;
+ boolean camifEofIrq;
+ boolean camifEpoch1Irq;
+ boolean camifEpoch2Irq;
+ boolean camifOverflowIrq;
+ boolean ceIrq;
+ boolean regUpdateIrq;
+ boolean resetAckIrq;
+ boolean encYPingpongIrq;
+ boolean encCbcrPingpongIrq;
+ boolean viewYPingpongIrq;
+ boolean viewCbcrPingpongIrq;
+ boolean rdPingpongIrq;
+ boolean afPingpongIrq;
+ boolean awbPingpongIrq;
+ boolean histPingpongIrq;
+ boolean encIrq;
+ boolean viewIrq;
+ boolean busOverflowIrq;
+ boolean afOverflowIrq;
+ boolean awbOverflowIrq;
+ boolean syncTimer0Irq;
+ boolean syncTimer1Irq;
+ boolean syncTimer2Irq;
+ boolean asyncTimer0Irq;
+ boolean asyncTimer1Irq;
+ boolean asyncTimer2Irq;
+ boolean asyncTimer3Irq;
+ boolean axiErrorIrq;
+ boolean violationIrq;
+};
+
+enum vfe_interrupt_name {
+ CAMIF_ERROR_IRQ,
+ CAMIF_SOF_IRQ,
+ CAMIF_EOL_IRQ,
+ CAMIF_EOF_IRQ,
+ CAMIF_EPOCH1_IRQ,
+ CAMIF_EPOCH2_IRQ,
+ CAMIF_OVERFLOW_IRQ,
+ CE_IRQ,
+ REG_UPDATE_IRQ,
+ RESET_ACK_IRQ,
+ ENC_Y_PINGPONG_IRQ,
+ ENC_CBCR_PINGPONG_IRQ,
+ VIEW_Y_PINGPONG_IRQ,
+ VIEW_CBCR_PINGPONG_IRQ,
+ RD_PINGPONG_IRQ,
+ AF_PINGPONG_IRQ,
+ AWB_PINGPONG_IRQ,
+ HIST_PINGPONG_IRQ,
+ ENC_IRQ,
+ VIEW_IRQ,
+ BUS_OVERFLOW_IRQ,
+ AF_OVERFLOW_IRQ,
+ AWB_OVERFLOW_IRQ,
+ SYNC_TIMER0_IRQ,
+ SYNC_TIMER1_IRQ,
+ SYNC_TIMER2_IRQ,
+ ASYNC_TIMER0_IRQ,
+ ASYNC_TIMER1_IRQ,
+ ASYNC_TIMER2_IRQ,
+ ASYNC_TIMER3_IRQ,
+ AXI_ERROR_IRQ,
+ VIOLATION_IRQ
+};
+
+enum VFE_DMI_RAM_SEL {
+ NO_MEM_SELECTED = 0,
+ ROLLOFF_RAM = 0x1,
+ RGBLUT_RAM_CH0_BANK0 = 0x2,
+ RGBLUT_RAM_CH0_BANK1 = 0x3,
+ RGBLUT_RAM_CH1_BANK0 = 0x4,
+ RGBLUT_RAM_CH1_BANK1 = 0x5,
+ RGBLUT_RAM_CH2_BANK0 = 0x6,
+ RGBLUT_RAM_CH2_BANK1 = 0x7,
+ STATS_HIST_CB_EVEN_RAM = 0x8,
+ STATS_HIST_CB_ODD_RAM = 0x9,
+ STATS_HIST_CR_EVEN_RAM = 0xa,
+ STATS_HIST_CR_ODD_RAM = 0xb,
+ RGBLUT_CHX_BANK0 = 0xc,
+ RGBLUT_CHX_BANK1 = 0xd,
+ LUMA_ADAPT_LUT_RAM_BANK0 = 0xe,
+ LUMA_ADAPT_LUT_RAM_BANK1 = 0xf
+};
+
+struct vfe_module_enable {
+ boolean blackLevelCorrectionEnable;
+ boolean lensRollOffEnable;
+ boolean demuxEnable;
+ boolean chromaUpsampleEnable;
+ boolean demosaicEnable;
+ boolean statsEnable;
+ boolean cropEnable;
+ boolean mainScalerEnable;
+ boolean whiteBalanceEnable;
+ boolean colorCorrectionEnable;
+ boolean yHistEnable;
+ boolean skinToneEnable;
+ boolean lumaAdaptationEnable;
+ boolean rgbLUTEnable;
+ boolean chromaEnhanEnable;
+ boolean asfEnable;
+ boolean chromaSuppressionEnable;
+ boolean chromaSubsampleEnable;
+ boolean scaler2YEnable;
+ boolean scaler2CbcrEnable;
+};
+
+struct vfe_bus_cmd_data {
+ boolean stripeReload;
+ boolean busPingpongReload;
+ boolean statsPingpongReload;
+};
+
+struct vfe_stats_cmd_data {
+ boolean autoFocusEnable;
+ boolean axwEnable;
+ boolean histEnable;
+ boolean clearHistEnable;
+ boolean histAutoClearEnable;
+ boolean colorConversionEnable;
+};
+
+struct vfe_hw_ver {
+ uint32_t minorVersion:8;
+ uint32_t majorVersion:8;
+ uint32_t coreVersion:4;
+ uint32_t /* reserved */ : 12;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_cfg {
+ uint32_t pixelPattern:3;
+ uint32_t /* reserved */ : 13;
+ uint32_t inputSource:2;
+ uint32_t /* reserved */ : 14;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_buscmd {
+ uint32_t stripeReload:1;
+ uint32_t /* reserved */ : 3;
+ uint32_t busPingpongReload:1;
+ uint32_t statsPingpongReload:1;
+ uint32_t /* reserved */ : 26;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_Irq_Composite_MaskType {
+ uint32_t encIrqComMaskBits:2;
+ uint32_t viewIrqComMaskBits:2;
+ uint32_t ceDoneSelBits:5;
+ uint32_t /* reserved */ : 23;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_mod_enable {
+ uint32_t blackLevelCorrectionEnable:1;
+ uint32_t lensRollOffEnable:1;
+ uint32_t demuxEnable:1;
+ uint32_t chromaUpsampleEnable:1;
+ uint32_t demosaicEnable:1;
+ uint32_t statsEnable:1;
+ uint32_t cropEnable:1;
+ uint32_t mainScalerEnable:1;
+ uint32_t whiteBalanceEnable:1;
+ uint32_t colorCorrectionEnable:1;
+ uint32_t yHistEnable:1;
+ uint32_t skinToneEnable:1;
+ uint32_t lumaAdaptationEnable:1;
+ uint32_t rgbLUTEnable:1;
+ uint32_t chromaEnhanEnable:1;
+ uint32_t asfEnable:1;
+ uint32_t chromaSuppressionEnable:1;
+ uint32_t chromaSubsampleEnable:1;
+ uint32_t scaler2YEnable:1;
+ uint32_t scaler2CbcrEnable:1;
+ uint32_t /* reserved */ : 14;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_irqenable {
+ uint32_t camifErrorIrq:1;
+ uint32_t camifSofIrq:1;
+ uint32_t camifEolIrq:1;
+ uint32_t camifEofIrq:1;
+ uint32_t camifEpoch1Irq:1;
+ uint32_t camifEpoch2Irq:1;
+ uint32_t camifOverflowIrq:1;
+ uint32_t ceIrq:1;
+ uint32_t regUpdateIrq:1;
+ uint32_t resetAckIrq:1;
+ uint32_t encYPingpongIrq:1;
+ uint32_t encCbcrPingpongIrq:1;
+ uint32_t viewYPingpongIrq:1;
+ uint32_t viewCbcrPingpongIrq:1;
+ uint32_t rdPingpongIrq:1;
+ uint32_t afPingpongIrq:1;
+ uint32_t awbPingpongIrq:1;
+ uint32_t histPingpongIrq:1;
+ uint32_t encIrq:1;
+ uint32_t viewIrq:1;
+ uint32_t busOverflowIrq:1;
+ uint32_t afOverflowIrq:1;
+ uint32_t awbOverflowIrq:1;
+ uint32_t syncTimer0Irq:1;
+ uint32_t syncTimer1Irq:1;
+ uint32_t syncTimer2Irq:1;
+ uint32_t asyncTimer0Irq:1;
+ uint32_t asyncTimer1Irq:1;
+ uint32_t asyncTimer2Irq:1;
+ uint32_t asyncTimer3Irq:1;
+ uint32_t axiErrorIrq:1;
+ uint32_t violationIrq:1;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_upsample_cfg {
+ uint32_t chromaCositingForYCbCrInputs:1;
+ uint32_t /* reserved */ : 31;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_CAMIFConfigType {
+ /* CAMIF Config */
+ uint32_t /* reserved */ : 1;
+ uint32_t VSyncEdge:1;
+ uint32_t HSyncEdge:1;
+ uint32_t syncMode:2;
+ uint32_t vfeSubsampleEnable:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t busSubsampleEnable:1;
+ uint32_t camif2vfeEnable:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t camif2busEnable:1;
+ uint32_t irqSubsampleEnable:1;
+ uint32_t binningEnable:1;
+ uint32_t /* reserved */ : 18;
+ uint32_t misrEnable:1;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_camifcfg {
+ /* EFS_Config */
+ uint32_t efsEndOfLine:8;
+ uint32_t efsStartOfLine:8;
+ uint32_t efsEndOfFrame:8;
+ uint32_t efsStartOfFrame:8;
+ /* Frame Config */
+ uint32_t frameConfigPixelsPerLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t frameConfigLinesPerFrame:14;
+ uint32_t /* reserved */ : 2;
+ /* Window Width Config */
+ uint32_t windowWidthCfgLastPixel:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t windowWidthCfgFirstPixel:14;
+ uint32_t /* reserved */ : 2;
+ /* Window Height Config */
+ uint32_t windowHeightCfglastLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t windowHeightCfgfirstLine:14;
+ uint32_t /* reserved */ : 2;
+ /* Subsample 1 Config */
+ uint32_t subsample1CfgPixelSkip:16;
+ uint32_t subsample1CfgLineSkip:16;
+ /* Subsample 2 Config */
+ uint32_t subsample2CfgFrameSkip:4;
+ uint32_t subsample2CfgFrameSkipMode:1;
+ uint32_t subsample2CfgPixelSkipWrap:1;
+ uint32_t /* reserved */ : 26;
+ /* Epoch Interrupt */
+ uint32_t epoch1Line:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t epoch2Line:14;
+ uint32_t /* reserved */ : 2;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_epoch1cfg {
+ /* Epoch Interrupt */
+ uint32_t epoch1Line:14;
+ uint32_t /* reserved */ : 2;
+} __attribute__ ((packed, aligned(4)));
+
+
+struct vfe_camifframe_update {
+ uint32_t pixelsPerLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t linesPerFrame:14;
+ uint32_t /* reserved */ : 2;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_axi_bus_cfg {
+ uint32_t stripeRdPathEn:1;
+ uint32_t /* reserved */ : 3;
+ uint32_t encYWrPathEn:1;
+ uint32_t encCbcrWrPathEn:1;
+ uint32_t viewYWrPathEn:1;
+ uint32_t viewCbcrWrPathEn:1;
+ uint32_t rawPixelDataSize:2;
+ uint32_t rawWritePathSelect:2;
+ uint32_t /* reserved */ : 20;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_axi_out_cfg {
+ uint32_t out2YPingAddr:32;
+ uint32_t out2YPongAddr:32;
+ uint32_t out2YImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2YImageWidthin64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out2YBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out2YNumRows:12;
+ uint32_t out2YRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2CbcrPingAddr:32;
+ uint32_t out2CbcrPongAddr:32;
+ uint32_t out2CbcrImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2CbcrImageWidthIn64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out2CbcrBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out2CbcrNumRows:12;
+ uint32_t out2CbcrRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1YPingAddr:32;
+ uint32_t out1YPongAddr:32;
+ uint32_t out1YImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1YImageWidthin64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out1YBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out1YNumRows:12;
+ uint32_t out1YRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1CbcrPingAddr:32;
+ uint32_t out1CbcrPongAddr:32;
+ uint32_t out1CbcrImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1CbcrImageWidthIn64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out1CbcrBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out1CbcrNumRows:12;
+ uint32_t out1CbcrRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_output_clamp_cfg {
+ /* Output Clamp Maximums */
+ uint32_t yChanMax:8;
+ uint32_t cbChanMax:8;
+ uint32_t crChanMax:8;
+ uint32_t /* reserved */ : 8;
+ /* Output Clamp Minimums */
+ uint32_t yChanMin:8;
+ uint32_t cbChanMin:8;
+ uint32_t crChanMin:8;
+ uint32_t /* reserved */ : 8;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_fov_crop_cfg {
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstPixel:12;
+ uint32_t /* reserved */ : 4;
+
+ /* FOV Corp, Part 2 */
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_FRAME_SKIP_UpdateCmdType {
+ uint32_t yPattern:32;
+ uint32_t cbcrPattern:32;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_frame_skip_cfg {
+ /* Frame Drop Enc (output2) */
+ uint32_t output2YPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output2CbCrPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output2YPattern:32;
+ uint32_t output2CbCrPattern:32;
+ /* Frame Drop View (output1) */
+ uint32_t output1YPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output1CbCrPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output1YPattern:32;
+ uint32_t output1CbCrPattern:32;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_main_scaler_cfg {
+ /* Scaler Enable Config */
+ uint32_t hEnable:1;
+ uint32_t vEnable:1;
+ uint32_t /* reserved */ : 30;
+ /* Scale H Image Size Config */
+ uint32_t inWidth:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outWidth:12;
+ uint32_t /* reserved */ : 4;
+ /* Scale H Phase Config */
+ uint32_t horizPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t horizInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scale H Stripe Config */
+ uint32_t horizMNInit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t horizPhaseInit:15;
+ uint32_t /* reserved */ : 1;
+ /* Scale V Image Size Config */
+ uint32_t inHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outHeight:12;
+ uint32_t /* reserved */ : 4;
+ /* Scale V Phase Config */
+ uint32_t vertPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t vertInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scale V Stripe Config */
+ uint32_t vertMNInit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t vertPhaseInit:15;
+ uint32_t /* reserved */ : 1;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_scaler2_cfg {
+ /* Scaler Enable Config */
+ uint32_t hEnable:1;
+ uint32_t vEnable:1;
+ uint32_t /* reserved */ : 30;
+ /* Scaler H Image Size Config */
+ uint32_t inWidth:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outWidth:12;
+ uint32_t /* reserved */ : 4;
+ /* Scaler H Phase Config */
+ uint32_t horizPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t horizInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scaler V Image Size Config */
+ uint32_t inHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outHeight:12;
+ uint32_t /* reserved */ : 4;
+ /* Scaler V Phase Config */
+ uint32_t vertPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t vertInterResolution:2;
+ uint32_t /* reserved */ : 10;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_rolloff_cfg {
+ /* Rolloff 0 Config */
+ uint32_t gridWidth:9;
+ uint32_t gridHeight:9;
+ uint32_t yDelta:9;
+ uint32_t /* reserved */ : 5;
+ /* Rolloff 1 Config */
+ uint32_t gridX:4;
+ uint32_t gridY:4;
+ uint32_t pixelX:9;
+ uint32_t /* reserved */ : 3;
+ uint32_t pixelY:9;
+ uint32_t /* reserved */ : 3;
+ /* Rolloff 2 Config */
+ uint32_t yDeltaAccum:12;
+ uint32_t /* reserved */ : 20;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_asf_update {
+ /* ASF Config Command */
+ uint32_t smoothEnable:1;
+ uint32_t sharpMode:2;
+ uint32_t /* reserved */ : 1;
+ uint32_t smoothCoeff1:4;
+ uint32_t smoothCoeff0:8;
+ uint32_t pipeFlushCount:12;
+ uint32_t pipeFlushOvd:1;
+ uint32_t flushHaltOvd:1;
+ uint32_t cropEnable:1;
+ uint32_t /* reserved */ : 1;
+ /* Sharpening Config 0 */
+ uint32_t sharpThresholdE1:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t sharpDegreeK1:5;
+ uint32_t /* reserved */ : 3;
+ uint32_t sharpDegreeK2:5;
+ uint32_t /* reserved */ : 3;
+ uint32_t normalizeFactor:7;
+ uint32_t /* reserved */ : 1;
+ /* Sharpening Config 1 */
+ uint32_t sharpThresholdE2:8;
+ uint32_t sharpThresholdE3:8;
+ uint32_t sharpThresholdE4:8;
+ uint32_t sharpThresholdE5:8;
+ /* Sharpening Coefficients 0 */
+ uint32_t F1Coeff0:6;
+ uint32_t F1Coeff1:6;
+ uint32_t F1Coeff2:6;
+ uint32_t F1Coeff3:6;
+ uint32_t F1Coeff4:6;
+ uint32_t /* reserved */ : 2;
+ /* Sharpening Coefficients 1 */
+ uint32_t F1Coeff5:6;
+ uint32_t F1Coeff6:6;
+ uint32_t F1Coeff7:6;
+ uint32_t F1Coeff8:7;
+ uint32_t /* reserved */ : 7;
+ /* Sharpening Coefficients 2 */
+ uint32_t F2Coeff0:6;
+ uint32_t F2Coeff1:6;
+ uint32_t F2Coeff2:6;
+ uint32_t F2Coeff3:6;
+ uint32_t F2Coeff4:6;
+ uint32_t /* reserved */ : 2;
+ /* Sharpening Coefficients 3 */
+ uint32_t F2Coeff5:6;
+ uint32_t F2Coeff6:6;
+ uint32_t F2Coeff7:6;
+ uint32_t F2Coeff8:7;
+ uint32_t /* reserved */ : 7;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_asfcrop_cfg {
+ /* ASF Crop Width Config */
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstPixel:12;
+ uint32_t /* reserved */ : 4;
+ /* ASP Crop Height Config */
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_chroma_suppress_cfg {
+ /* Chroma Suppress 0 Config */
+ uint32_t m1:8;
+ uint32_t m3:8;
+ uint32_t n1:3;
+ uint32_t /* reserved */ : 1;
+ uint32_t n3:3;
+ uint32_t /* reserved */ : 9;
+ /* Chroma Suppress 1 Config */
+ uint32_t mm1:8;
+ uint32_t nn1:3;
+ uint32_t /* reserved */ : 21;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_chromasubsample_cfg {
+ /* Chroma Subsample Selection */
+ uint32_t hCositedPhase:1;
+ uint32_t vCositedPhase:1;
+ uint32_t hCosited:1;
+ uint32_t vCosited:1;
+ uint32_t hsubSampleEnable:1;
+ uint32_t vsubSampleEnable:1;
+ uint32_t cropEnable:1;
+ uint32_t /* reserved */ : 25;
+ uint32_t cropWidthLastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropWidthFirstPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropHeightLastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropHeightFirstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_blacklevel_cfg {
+ /* Black Even-Even Value Config */
+ uint32_t evenEvenAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Even-Odd Value Config */
+ uint32_t evenOddAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Odd-Even Value Config */
+ uint32_t oddEvenAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Odd-Odd Value Config */
+ uint32_t oddOddAdjustment:9;
+ uint32_t /* reserved */ : 23;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_demux_cfg {
+ /* Demux Gain 0 Config */
+ uint32_t ch0EvenGain:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t ch0OddGain:10;
+ uint32_t /* reserved */ : 6;
+ /* Demux Gain 1 Config */
+ uint32_t ch1Gain:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t ch2Gain:10;
+ uint32_t /* reserved */ : 6;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_bps_info {
+ uint32_t greenBadPixelCount:8;
+ uint32_t /* reserved */ : 8;
+ uint32_t RedBlueBadPixelCount:8;
+ uint32_t /* reserved */ : 8;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_demosaic_cfg {
+ /* Demosaic Config */
+ uint32_t abfEnable:1;
+ uint32_t badPixelCorrEnable:1;
+ uint32_t forceAbfOn:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t abfShift:4;
+ uint32_t fminThreshold:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t fmaxThreshold:7;
+ uint32_t /* reserved */ : 5;
+ uint32_t slopeShift:3;
+ uint32_t /* reserved */ : 1;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_demosaic_bpc_cfg {
+ /* Demosaic BPC Config 0 */
+ uint32_t blueDiffThreshold:12;
+ uint32_t redDiffThreshold:12;
+ uint32_t /* reserved */ : 8;
+ /* Demosaic BPC Config 1 */
+ uint32_t greenDiffThreshold:12;
+ uint32_t /* reserved */ : 20;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_demosaic_abf_cfg {
+ /* Demosaic ABF Config 0 */
+ uint32_t lpThreshold:10;
+ uint32_t /* reserved */ : 22;
+ /* Demosaic ABF Config 1 */
+ uint32_t ratio:4;
+ uint32_t minValue:10;
+ uint32_t /* reserved */ : 2;
+ uint32_t maxValue:10;
+ uint32_t /* reserved */ : 6;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_color_correction_cfg {
+ /* Color Corr. Coefficient 0 Config */
+ uint32_t c0:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 1 Config */
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 2 Config */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 3 Config */
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 4 Config */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 5 Config */
+ uint32_t c5:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 6 Config */
+ uint32_t c6:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 7 Config */
+ uint32_t c7:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 8 Config */
+ uint32_t c8:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Offset 0 Config */
+ uint32_t k0:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Offset 1 Config */
+ uint32_t k1:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Offset 2 Config */
+ uint32_t k2:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Coefficient Q Config */
+ uint32_t coefQFactor:2;
+ uint32_t /* reserved */ : 30;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_LumaAdaptation_ConfigCmdType {
+ /* LA Config */
+ uint32_t lutBankSelect:1;
+ uint32_t /* reserved */ : 31;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_wb_cfg {
+ /* WB Config */
+ uint32_t ch0Gain:9;
+ uint32_t ch1Gain:9;
+ uint32_t ch2Gain:9;
+ uint32_t /* reserved */ : 5;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_GammaLutSelect_ConfigCmdType {
+ /* LUT Bank Select Config */
+ uint32_t ch0BankSelect:1;
+ uint32_t ch1BankSelect:1;
+ uint32_t ch2BankSelect:1;
+ uint32_t /* reserved */ : 29;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_chroma_enhance_cfg {
+ /* Chroma Enhance A Config */
+ uint32_t ap:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t am:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance B Config */
+ uint32_t bp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t bm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance C Config */
+ uint32_t cp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t cm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance D Config */
+ uint32_t dp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t dm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance K Config */
+ uint32_t kcb:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t kcr:11;
+ uint32_t /* reserved */ : 5;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_color_convert_cfg {
+ /* Conversion Coefficient 0 */
+ uint32_t v0:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Coefficient 1 */
+ uint32_t v1:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Coefficient 2 */
+ uint32_t v2:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Offset */
+ uint32_t ConvertOffset:8;
+ uint32_t /* reserved */ : 24;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_SyncTimer_ConfigCmdType {
+ /* Timer Line Start Config */
+ uint32_t timerLineStart:12;
+ uint32_t /* reserved */ : 20;
+ /* Timer Pixel Start Config */
+ uint32_t timerPixelStart:18;
+ uint32_t /* reserved */ : 14;
+ /* Timer Pixel Duration Config */
+ uint32_t timerPixelDuration:28;
+ uint32_t /* reserved */ : 4;
+ /* Sync Timer Polarity Config */
+ uint32_t timer0Polarity:1;
+ uint32_t timer1Polarity:1;
+ uint32_t timer2Polarity:1;
+ uint32_t /* reserved */ : 29;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_AsyncTimer_ConfigCmdType {
+ /* Async Timer Config 0 */
+ uint32_t inactiveLength:20;
+ uint32_t numRepetition:10;
+ uint32_t /* reserved */ : 1;
+ uint32_t polarity:1;
+ /* Async Timer Config 1 */
+ uint32_t activeLength:20;
+ uint32_t /* reserved */ : 12;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_AWBAEStatistics_ConfigCmdType {
+ /* AWB autoexposure Config */
+ uint32_t aeRegionConfig:1;
+ uint32_t aeSubregionConfig:1;
+ uint32_t /* reserved */ : 14;
+ uint32_t awbYMin:8;
+ uint32_t awbYMax:8;
+ /* AXW Header */
+ uint32_t axwHeader:8;
+ uint32_t /* reserved */ : 24;
+ /* AWB Mconfig */
+ uint32_t m4:8;
+ uint32_t m3:8;
+ uint32_t m2:8;
+ uint32_t m1:8;
+ /* AWB Cconfig */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 4;
+ /* AWB Cconfig 2 */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_TestGen_ConfigCmdType {
+ /* HW Test Gen Config */
+ uint32_t numFrame:10;
+ uint32_t /* reserved */ : 2;
+ uint32_t pixelDataSelect:1;
+ uint32_t systematicDataSelect:1;
+ uint32_t /* reserved */ : 2;
+ uint32_t pixelDataSize:2;
+ uint32_t hsyncEdge:1;
+ uint32_t vsyncEdge:1;
+ uint32_t /* reserved */ : 12;
+ /* HW Test Gen Image Config */
+ uint32_t imageWidth:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t imageHeight:14;
+ uint32_t /* reserved */ : 2;
+ /* SOF Offset Config */
+ uint32_t sofOffset:24;
+ uint32_t /* reserved */ : 8;
+ /* EOF NOffset Config */
+ uint32_t eofNOffset:24;
+ uint32_t /* reserved */ : 8;
+ /* SOL Offset Config */
+ uint32_t solOffset:9;
+ uint32_t /* reserved */ : 23;
+ /* EOL NOffset Config */
+ uint32_t eolNOffset:9;
+ uint32_t /* reserved */ : 23;
+ /* HBI Config */
+ uint32_t hBlankInterval:14;
+ uint32_t /* reserved */ : 18;
+ /* VBL Config */
+ uint32_t vBlankInterval:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t vBlankIntervalEnable:1;
+ uint32_t /* reserved */ : 15;
+ /* SOF Dummy Line Config */
+ uint32_t sofDummy:8;
+ uint32_t /* reserved */ : 24;
+ /* EOF Dummy Line Config */
+ uint32_t eofDummy:8;
+ uint32_t /* reserved */ : 24;
+ /* Color Bars Config */
+ uint32_t unicolorBarSelect:3;
+ uint32_t /* reserved */ : 1;
+ uint32_t unicolorBarEnable:1;
+ uint32_t splitEnable:1;
+ uint32_t pixelPattern:2;
+ uint32_t rotatePeriod:6;
+ uint32_t /* reserved */ : 18;
+ /* Random Config */
+ uint32_t randomSeed:16;
+ uint32_t /* reserved */ : 16;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_Bus_Pm_ConfigCmdType {
+ /* VFE Bus Performance Monitor Config */
+ uint32_t output2YWrPmEnable:1;
+ uint32_t output2CbcrWrPmEnable:1;
+ uint32_t output1YWrPmEnable:1;
+ uint32_t output1CbcrWrPmEnable:1;
+ uint32_t /* reserved */ : 28;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_asf_info {
+ /* asf max edge */
+ uint32_t maxEdge:13;
+ uint32_t /* reserved */ : 3;
+ /* HBi count */
+ uint32_t HBICount:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_camif_stats {
+ uint32_t pixelCount:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t lineCount:14;
+ uint32_t /* reserved */ : 1;
+ uint32_t camifHalt:1;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_StatsCmdType {
+ uint32_t autoFocusEnable:1;
+ uint32_t axwEnable:1;
+ uint32_t histEnable:1;
+ uint32_t clearHistEnable:1;
+ uint32_t histAutoClearEnable:1;
+ uint32_t colorConversionEnable:1;
+ uint32_t /* reserved */ : 26;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsframe {
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_busstats_wrprio {
+ uint32_t afBusPriority:4;
+ uint32_t awbBusPriority:4;
+ uint32_t histBusPriority:4;
+ uint32_t afBusPriorityEn:1;
+ uint32_t awbBusPriorityEn:1;
+ uint32_t histBusPriorityEn:1;
+ uint32_t /* reserved */ : 17;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsaf_update {
+ /* VFE_STATS_AF_CFG */
+ uint32_t windowVOffset:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t windowHOffset:12;
+ uint32_t /* reserved */ : 3;
+ uint32_t windowMode:1;
+
+ /* VFE_STATS_AF_DIM */
+ uint32_t windowHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t windowWidth:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsaf_cfg {
+ /* VFE_STATS_AF_GRID_0 */
+ uint32_t entry00:8;
+ uint32_t entry01:8;
+ uint32_t entry02:8;
+ uint32_t entry03:8;
+
+ /* VFE_STATS_AF_GRID_1 */
+ uint32_t entry10:8;
+ uint32_t entry11:8;
+ uint32_t entry12:8;
+ uint32_t entry13:8;
+
+ /* VFE_STATS_AF_GRID_2 */
+ uint32_t entry20:8;
+ uint32_t entry21:8;
+ uint32_t entry22:8;
+ uint32_t entry23:8;
+
+ /* VFE_STATS_AF_GRID_3 */
+ uint32_t entry30:8;
+ uint32_t entry31:8;
+ uint32_t entry32:8;
+ uint32_t entry33:8;
+
+ /* VFE_STATS_AF_HEADER */
+ uint32_t afHeader:8;
+ uint32_t /* reserved */ : 24;
+ /* VFE_STATS_AF_COEF0 */
+ uint32_t a00:5;
+ uint32_t a04:5;
+ uint32_t fvMax:11;
+ uint32_t fvMetric:1;
+ uint32_t /* reserved */ : 10;
+
+ /* VFE_STATS_AF_COEF1 */
+ uint32_t a20:5;
+ uint32_t a21:5;
+ uint32_t a22:5;
+ uint32_t a23:5;
+ uint32_t a24:5;
+ uint32_t /* reserved */ : 7;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsawbae_update {
+ uint32_t aeRegionCfg:1;
+ uint32_t aeSubregionCfg:1;
+ uint32_t /* reserved */ : 14;
+ uint32_t awbYMin:8;
+ uint32_t awbYMax:8;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsaxw_hdr_cfg {
+ /* Stats AXW Header Config */
+ uint32_t axwHeader:8;
+ uint32_t /* reserved */ : 24;
+} __attribute__ ((packed, aligned(4)));
+
+struct vfe_statsawb_update {
+ /* AWB MConfig */
+ uint32_t m4:8;
+ uint32_t m3:8;
+ uint32_t m2:8;
+ uint32_t m1:8;
+
+ /* AWB CConfig1 */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 4;
+
+ /* AWB CConfig2 */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_SyncTimerCmdType {
+ uint32_t hsyncCount:12;
+ uint32_t /* reserved */ : 20;
+ uint32_t pclkCount:18;
+ uint32_t /* reserved */ : 14;
+ uint32_t outputDuration:28;
+ uint32_t /* reserved */ : 4;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_AsyncTimerCmdType {
+ /* config 0 */
+ uint32_t inactiveCount:20;
+ uint32_t repeatCount:10;
+ uint32_t /* reserved */ : 1;
+ uint32_t polarity:1;
+ /* config 1 */
+ uint32_t activeCount:20;
+ uint32_t /* reserved */ : 12;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_AxiInputCmdType {
+ uint32_t stripeStartAddr0:32;
+ uint32_t stripeStartAddr1:32;
+ uint32_t stripeStartAddr2:32;
+ uint32_t stripeStartAddr3:32;
+
+ uint32_t ySize:12;
+ uint32_t yOffsetDelta:12;
+ uint32_t /* reserved */ : 8;
+
+ /* bus_stripe_rd_hSize */
+ uint32_t /* reserved */ : 16;
+ uint32_t xSizeWord:10;
+ uint32_t /* reserved */ : 6;
+
+ /* bus_stripe_rd_buffer_cfg */
+ uint32_t burstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t NumOfRows:12;
+ uint32_t RowIncrement:12;
+ uint32_t /* reserved */ : 4;
+
+ /* bus_stripe_rd_unpack_cfg */
+ uint32_t mainUnpackHeight:12;
+ uint32_t mainUnpackWidth:13;
+ uint32_t mainUnpackHbiSel:3;
+ uint32_t mainUnpackPhase:3;
+ uint32_t /* reserved */ : 1;
+
+ /* bus_stripe_rd_unpack */
+ uint32_t unpackPattern:32;
+
+ /* bus_stripe_rd_pad_size */
+ uint32_t padLeft:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padRight:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padTop:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padBottom:7;
+ uint32_t /* reserved */ : 1;
+
+ /* bus_stripe_rd_pad_L_unpack */
+ uint32_t leftUnpackPattern0:4;
+ uint32_t leftUnpackPattern1:4;
+ uint32_t leftUnpackPattern2:4;
+ uint32_t leftUnpackPattern3:4;
+ uint32_t leftUnpackStop0:1;
+ uint32_t leftUnpackStop1:1;
+ uint32_t leftUnpackStop2:1;
+ uint32_t leftUnpackStop3:1;
+ uint32_t /* reserved */ : 12;
+
+ /* bus_stripe_rd_pad_R_unpack */
+ uint32_t rightUnpackPattern0:4;
+ uint32_t rightUnpackPattern1:4;
+ uint32_t rightUnpackPattern2:4;
+ uint32_t rightUnpackPattern3:4;
+ uint32_t rightUnpackStop0:1;
+ uint32_t rightUnpackStop1:1;
+ uint32_t rightUnpackStop2:1;
+ uint32_t rightUnpackStop3:1;
+ uint32_t /* reserved */ : 12;
+
+ /* bus_stripe_rd_pad_tb_unpack */
+ uint32_t topUnapckPattern:4;
+ uint32_t /* reserved */ : 12;
+ uint32_t bottomUnapckPattern:4;
+ uint32_t /* reserved */ : 12;
+} __attribute__ ((packed, aligned(4)));
+
+struct VFE_AxiRdFragIrqEnable {
+ uint32_t stripeRdFragirq0Enable:1;
+ uint32_t stripeRdFragirq1Enable:1;
+ uint32_t stripeRdFragirq2Enable:1;
+ uint32_t stripeRdFragirq3Enable:1;
+ uint32_t /* reserved */ : 28;
+} __attribute__ ((packed, aligned(4)));
+
+int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *);
+void vfe_stats_af_stop(void);
+void vfe_stop(void);
+void vfe_update(void);
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *);
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *);
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *);
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *);
+void vfe_start(struct vfe_cmd_start *);
+void vfe_la_update(struct vfe_cmd_la_config *);
+void vfe_la_config(struct vfe_cmd_la_config *);
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *);
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *);
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *);
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *);
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *);
+void vfe_color_correction_config(struct vfe_cmd_color_correction_config *);
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *);
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *);
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *);
+void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_black_level_update(struct vfe_cmd_black_level_config *);
+void vfe_black_level_config(struct vfe_cmd_black_level_config *);
+void vfe_asf_update(struct vfe_cmd_asf_update *);
+void vfe_asf_config(struct vfe_cmd_asf_config *);
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *);
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *);
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *);
+void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *);
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *);
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *);
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *);
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *);
+void vfe_stats_wb_exp_stop(void);
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *);
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *);
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *);
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *);
+void vfe_stats_setting(struct vfe_cmd_stats_setting *);
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *);
+void vfe_stats_config(struct vfe_cmd_stats_setting *);
+void vfe_axi_output_config(struct vfe_cmd_axi_output_config *);
+void vfe_camif_config(struct vfe_cmd_camif_config *);
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *);
+void vfe_get_hw_version(struct vfe_cmd_hw_version *);
+void vfe_reset(void);
+void vfe_cmd_release(struct platform_device *);
+void vfe_output1_ack(struct vfe_cmd_output_ack *);
+void vfe_output2_ack(struct vfe_cmd_output_ack *);
+void vfe_epoch1_config(struct vfe_cmds_camif_epoch *);
+#endif /* __MSM_VFE8X_REG_H__ */
diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c
new file mode 100644
index 0000000..5a6a8db
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.c
@@ -0,0 +1,736 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include "mt9d112.h"
+
+/* Micron MT9D112 Registers and their values */
+/* Sensor Core Registers */
+#define REG_MT9D112_MODEL_ID 0x3000
+#define MT9D112_MODEL_ID 0x1580
+
+/* SOC Registers Page 1 */
+#define REG_MT9D112_SENSOR_RESET 0x301A
+#define REG_MT9D112_STANDBY_CONTROL 0x3202
+#define REG_MT9D112_MCU_BOOT 0x3386
+
+struct mt9d112_work {
+ struct work_struct work;
+};
+
+static struct mt9d112_work *mt9d112_sensorw;
+static struct i2c_client *mt9d112_client;
+
+struct mt9d112_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+};
+
+static struct mt9d112_ctrl *mt9d112_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue);
+
+static int mt9d112_reset(const struct msm_camera_sensor_info *dev)
+{
+ int rc = 0;
+
+ rc = gpio_request(dev->sensor_reset, "mt9d112");
+
+ if (!rc) {
+ rc = gpio_direction_output(dev->sensor_reset, 0);
+ mdelay(20);
+ rc = gpio_direction_output(dev->sensor_reset, 1);
+ }
+
+ gpio_free(dev->sensor_reset);
+ return rc;
+}
+
+static int mt9d112_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) {
+ CDBG("mt9d112_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9d112_i2c_write(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata,
+ enum mt9d112_width width)
+{
+ int rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ switch (width) {
+ case WORD_LEN:{
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00) >> 8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9d112_i2c_txdata(saddr, buf, 4);
+ }
+ break;
+
+ case BYTE_LEN:{
+ buf[0] = waddr;
+ buf[1] = wdata;
+ rc = mt9d112_i2c_txdata(saddr, buf, 2);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (rc < 0)
+ CDBG("i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int mt9d112_i2c_write_table(struct mt9d112_i2c_reg_conf const
+ *reg_conf_tbl, int num_of_items_in_table)
+{
+ int i;
+ int rc = -EIO;
+
+ for (i = 0; i < num_of_items_in_table; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ reg_conf_tbl->waddr, reg_conf_tbl->wdata,
+ reg_conf_tbl->width);
+ if (rc < 0)
+ break;
+ if (reg_conf_tbl->mdelay_time != 0)
+ mdelay(reg_conf_tbl->mdelay_time);
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int mt9d112_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9d112_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9d112_i2c_read(unsigned short saddr,
+ unsigned short raddr, unsigned short *rdata,
+ enum mt9d112_width width)
+{
+ int rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ switch (width) {
+ case WORD_LEN:{
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9d112_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (rc < 0)
+ CDBG("mt9d112_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int mt9d112_set_lens_roll_off(void)
+{
+ int rc = 0;
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0],
+ mt9d112_regs.rftbl_size);
+ return rc;
+}
+
+static int mt9d112_reg_init(void)
+{
+ int array_length;
+ int i;
+ int rc;
+
+ /* PLL Setup Start */
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0],
+ mt9d112_regs.plltbl_size);
+
+ if (rc < 0)
+ return rc;
+ /* PLL Setup End */
+
+ array_length = mt9d112_regs.prev_snap_reg_settings_size;
+
+ /* Configure sensor for Preview mode and Snapshot mode */
+ for (i = 0; i < array_length; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ mt9d112_regs.prev_snap_reg_settings[i].
+ register_address,
+ mt9d112_regs.prev_snap_reg_settings[i].
+ register_value, WORD_LEN);
+
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Configure for Noise Reduction, Saturation and Aperture Correction */
+ array_length = mt9d112_regs.noise_reduction_reg_settings_size;
+
+ for (i = 0; i < array_length; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ mt9d112_regs.
+ noise_reduction_reg_settings[i].
+ register_address,
+ mt9d112_regs.
+ noise_reduction_reg_settings[i].
+ register_value, WORD_LEN);
+
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Set Color Kill Saturation point to optimum value */
+ rc = mt9d112_i2c_write(mt9d112_client->addr, 0x35A4, 0x0593, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0],
+ mt9d112_regs.stbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_set_lens_roll_off();
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int mt9d112_set_sensor_mode(int mode)
+{
+ uint16_t clock;
+ int rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA20C, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0004, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA215, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0004, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA20B, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0000, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ clock = 0x0250;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x341C, clock, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0001, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ /* Switch to lower fps for Snapshot */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x341C, 0x0120, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA120, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt9d112_set_effect(int mode, int effect)
+{
+ uint16_t reg_addr;
+ uint16_t reg_val;
+ int rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ /* Context A Special Effects */
+ reg_addr = 0x2799;
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ /* Context B Special Effects */
+ reg_addr = 0x279B;
+ break;
+
+ default:
+ reg_addr = 0x2799;
+ break;
+ }
+
+ switch (effect) {
+ case CAMERA_EFFECT_OFF:{
+ reg_val = 0x6440;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_MONO:{
+ reg_val = 0x6441;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_NEGATIVE:{
+ reg_val = 0x6443;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_SOLARIZE:{
+ reg_val = 0x6445;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_SEPIA:{
+ reg_val = 0x6442;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_PASTEL:
+ case CAMERA_EFFECT_MOSAIC:
+ case CAMERA_EFFECT_RESIZE:
+ return -EINVAL;
+
+ default:{
+ reg_val = 0x6440;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ return -EINVAL;
+ }
+ }
+
+ /* Refresh Sequencer */
+ rc = mt9d112_i2c_write(mt9d112_client->addr, 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr, 0x3390, 0x0005, WORD_LEN);
+
+ return rc;
+}
+
+static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data)
+{
+ uint16_t model_id = 0;
+ int rc = 0;
+
+ CDBG("init entry \n");
+ rc = mt9d112_reset(data);
+ if (rc < 0) {
+ CDBG("reset failed!\n");
+ goto init_probe_fail;
+ }
+
+ mdelay(5);
+
+ /* Micron suggested Power up block Start:
+ * Put MCU into Reset - Stop MCU */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* Pull MCU from Reset - Start MCU */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(5);
+
+ /* Micron Suggested - Power up block */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* FUSED_DEFECT_CORRECTION */
+ rc = mt9d112_i2c_write(mt9d112_client->addr, 0x33F4, 0x031D, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(5);
+
+ /* Micron suggested Power up block End */
+ /* Read the Model ID of the sensor */
+ rc = mt9d112_i2c_read(mt9d112_client->addr,
+ REG_MT9D112_MODEL_ID, &model_id, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ CDBG("mt9d112 model_id = 0x%x\n", model_id);
+
+ /* Check if it matches it with the value in Datasheet */
+ if (model_id != MT9D112_MODEL_ID) {
+ rc = -EINVAL;
+ goto init_probe_fail;
+ }
+
+ rc = mt9d112_reg_init();
+ if (rc < 0)
+ goto init_probe_fail;
+
+ return rc;
+
+init_probe_fail:
+ return rc;
+}
+
+int mt9d112_sensor_init(const struct msm_camera_sensor_info *data)
+{
+ int rc = 0;
+
+ mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL);
+ if (!mt9d112_ctrl) {
+ CDBG("mt9d112_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ if (data)
+ mt9d112_ctrl->sensordata = data;
+
+ /* Input MCLK = 24MHz */
+ msm_camio_clk_rate_set(24000000);
+ mdelay(5);
+
+ msm_camio_camif_pad_reg_reset();
+
+ rc = mt9d112_sensor_init_probe(data);
+ if (rc < 0) {
+ CDBG("mt9d112_sensor_init failed!\n");
+ goto init_fail;
+ }
+
+init_done:
+ return rc;
+
+init_fail:
+ kfree(mt9d112_ctrl);
+ return rc;
+}
+
+static int mt9d112_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9d112_wait_queue);
+ return 0;
+}
+
+int mt9d112_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cfg_data;
+ long rc = 0;
+
+ if (copy_from_user(&cfg_data,
+ (void *)argp, sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n",
+ cfg_data.cfgtype, cfg_data.mode);
+
+ switch (cfg_data.cfgtype) {
+ case CFG_SET_MODE:
+ rc = mt9d112_set_sensor_mode(cfg_data.mode);
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = mt9d112_set_effect(cfg_data.mode, cfg_data.cfg.effect);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9d112_sensor_release(void)
+{
+ int rc = 0;
+
+ kfree(mt9d112_ctrl);
+
+ return rc;
+}
+
+static int mt9d112_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ rc = -ENOTSUPP;
+ goto probe_failure;
+ }
+
+ mt9d112_sensorw = kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL);
+
+ if (!mt9d112_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9d112_sensorw);
+ mt9d112_init_client(client);
+ mt9d112_client = client;
+
+ CDBG("mt9d112_probe succeeded!\n");
+
+ return 0;
+
+probe_failure:
+ kfree(mt9d112_sensorw);
+ mt9d112_sensorw = NULL;
+ CDBG("mt9d112_probe failed!\n");
+ return rc;
+}
+
+static const struct i2c_device_id mt9d112_i2c_id[] = {
+ {"mt9d112", 0},
+ {},
+};
+
+static struct i2c_driver mt9d112_i2c_driver = {
+ .id_table = mt9d112_i2c_id,
+ .probe = mt9d112_i2c_probe,
+ .remove = __exit_p(mt9d112_i2c_remove),
+ .driver = {
+ .name = "mt9d112",
+ },
+};
+
+static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&mt9d112_i2c_driver);
+ if (rc < 0 || mt9d112_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ /* Input MCLK = 24MHz */
+ msm_camio_clk_rate_set(24000000);
+ mdelay(5);
+
+ rc = mt9d112_sensor_init_probe(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = mt9d112_sensor_init;
+ s->s_release = mt9d112_sensor_release;
+ s->s_config = mt9d112_sensor_config;
+
+probe_done:
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ return rc;
+}
+
+static int __mt9d112_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9d112_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9d112_probe,
+ .driver = {
+ .name = "msm_camera_mt9d112",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9d112_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9d112_init);
diff --git a/drivers/media/video/msm/mt9d112.h b/drivers/media/video/msm/mt9d112.h
new file mode 100644
index 0000000..39777c7
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef MT9D112_H
+#define MT9D112_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+enum mt9d112_width {
+ WORD_LEN,
+ BYTE_LEN
+};
+
+struct mt9d112_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+ enum mt9d112_width width;
+ unsigned short mdelay_time;
+};
+
+struct mt9d112_reg {
+ const struct register_address_value_pair *prev_snap_reg_settings;
+ uint16_t prev_snap_reg_settings_size;
+ const struct register_address_value_pair *noise_reduction_reg_settings;
+ uint16_t noise_reduction_reg_settings_size;
+ const struct mt9d112_i2c_reg_conf *plltbl;
+ uint16_t plltbl_size;
+ const struct mt9d112_i2c_reg_conf *stbl;
+ uint16_t stbl_size;
+ const struct mt9d112_i2c_reg_conf *rftbl;
+ uint16_t rftbl_size;
+};
+
+extern struct mt9d112_reg mt9d112_regs;
+
+#endif /* MT9D112_H */
diff --git a/drivers/media/video/msm/mt9d112_reg.c b/drivers/media/video/msm/mt9d112_reg.c
new file mode 100644
index 0000000..f2ae0c7
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112_reg.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "mt9d112.h"
+
+struct register_address_value_pair
+ preview_snapshot_mode_reg_settings_array[] = {
+ {0x338C, 0x2703},
+ {0x3390, 800}, /* Output Width (P) = 640 */
+ {0x338C, 0x2705},
+ {0x3390, 600}, /* Output Height (P) = 480 */
+ {0x338C, 0x2707},
+ {0x3390, 0x0640}, /* Output Width (S) = 1600 */
+ {0x338C, 0x2709},
+ {0x3390, 0x04B0}, /* Output Height (S) = 1200 */
+ {0x338C, 0x270D},
+ {0x3390, 0x0000}, /* Row Start (P) = 0 */
+ {0x338C, 0x270F},
+ {0x3390, 0x0000}, /* Column Start (P) = 0 */
+ {0x338C, 0x2711},
+ {0x3390, 0x04BD}, /* Row End (P) = 1213 */
+ {0x338C, 0x2713},
+ {0x3390, 0x064D}, /* Column End (P) = 1613 */
+ {0x338C, 0x2715},
+ {0x3390, 0x0000}, /* Extra Delay (P) = 0 */
+ {0x338C, 0x2717},
+ {0x3390, 0x2111}, /* Row Speed (P) = 8465 */
+ {0x338C, 0x2719},
+ {0x3390, 0x046C}, /* Read Mode (P) = 1132 */
+ {0x338C, 0x271B},
+ {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */
+ {0x338C, 0x271D},
+ {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */
+ {0x338C, 0x271F},
+ {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */
+ {0x338C, 0x2721},
+ {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */
+ {0x338C, 0x2723},
+ {0x3390, 659}, /* Frame Lines (P) = 679 */
+ {0x338C, 0x2725},
+ {0x3390, 0x0824}, /* Line Length (P) = 2084 */
+ {0x338C, 0x2727},
+ {0x3390, 0x2020},
+ {0x338C, 0x2729},
+ {0x3390, 0x2020},
+ {0x338C, 0x272B},
+ {0x3390, 0x1020},
+ {0x338C, 0x272D},
+ {0x3390, 0x2007},
+ {0x338C, 0x272F},
+ {0x3390, 0x0004}, /* Row Start(S) = 4 */
+ {0x338C, 0x2731},
+ {0x3390, 0x0004}, /* Column Start(S) = 4 */
+ {0x338C, 0x2733},
+ {0x3390, 0x04BB}, /* Row End(S) = 1211 */
+ {0x338C, 0x2735},
+ {0x3390, 0x064B}, /* Column End(S) = 1611 */
+ {0x338C, 0x2737},
+ {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */
+ {0x338C, 0x2739},
+ {0x3390, 0x2111}, /* Row Speed(S) = 8465 */
+ {0x338C, 0x273B},
+ {0x3390, 0x0024}, /* Read Mode(S) = 36 */
+ {0x338C, 0x273D},
+ {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */
+ {0x338C, 0x2741},
+ {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */
+ {0x338C, 0x2745},
+ {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */
+ {0x338C, 0x2747},
+ {0x3390, 0x0824}, /* Line Length(S) = 2084 */
+ {0x338C, 0x2751},
+ {0x3390, 0x0000}, /* Crop_X0(P) = 0 */
+ {0x338C, 0x2753},
+ {0x3390, 0x0320}, /* Crop_X1(P) = 800 */
+ {0x338C, 0x2755},
+ {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */
+ {0x338C, 0x2757},
+ {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */
+ {0x338C, 0x275F},
+ {0x3390, 0x0000}, /* Crop_X0(S) = 0 */
+ {0x338C, 0x2761},
+ {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */
+ {0x338C, 0x2763},
+ {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */
+ {0x338C, 0x2765},
+ {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */
+ {0x338C, 0x222E},
+ {0x3390, 0x00A0}, /* R9 Step = 160 */
+ {0x338C, 0xA408},
+ {0x3390, 0x001F},
+ {0x338C, 0xA409},
+ {0x3390, 0x0021},
+ {0x338C, 0xA40A},
+ {0x3390, 0x0025},
+ {0x338C, 0xA40B},
+ {0x3390, 0x0027},
+ {0x338C, 0x2411},
+ {0x3390, 0x00A0},
+ {0x338C, 0x2413},
+ {0x3390, 0x00C0},
+ {0x338C, 0x2415},
+ {0x3390, 0x00A0},
+ {0x338C, 0x2417},
+ {0x3390, 0x00C0},
+ {0x338C, 0x2799},
+ {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */
+ {0x338C, 0x279B},
+ {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */
+};
+
+static struct register_address_value_pair
+ noise_reduction_reg_settings_array[] = {
+ {0x338C, 0xA76D},
+ {0x3390, 0x0003},
+ {0x338C, 0xA76E},
+ {0x3390, 0x0003},
+ {0x338C, 0xA76F},
+ {0x3390, 0},
+ {0x338C, 0xA770},
+ {0x3390, 21},
+ {0x338C, 0xA771},
+ {0x3390, 37},
+ {0x338C, 0xA772},
+ {0x3390, 63},
+ {0x338C, 0xA773},
+ {0x3390, 100},
+ {0x338C, 0xA774},
+ {0x3390, 128},
+ {0x338C, 0xA775},
+ {0x3390, 151},
+ {0x338C, 0xA776},
+ {0x3390, 169},
+ {0x338C, 0xA777},
+ {0x3390, 186},
+ {0x338C, 0xA778},
+ {0x3390, 199},
+ {0x338C, 0xA779},
+ {0x3390, 210},
+ {0x338C, 0xA77A},
+ {0x3390, 220},
+ {0x338C, 0xA77B},
+ {0x3390, 228},
+ {0x338C, 0xA77C},
+ {0x3390, 234},
+ {0x338C, 0xA77D},
+ {0x3390, 240},
+ {0x338C, 0xA77E},
+ {0x3390, 244},
+ {0x338C, 0xA77F},
+ {0x3390, 248},
+ {0x338C, 0xA780},
+ {0x3390, 252},
+ {0x338C, 0xA781},
+ {0x3390, 255},
+ {0x338C, 0xA782},
+ {0x3390, 0},
+ {0x338C, 0xA783},
+ {0x3390, 21},
+ {0x338C, 0xA784},
+ {0x3390, 37},
+ {0x338C, 0xA785},
+ {0x3390, 63},
+ {0x338C, 0xA786},
+ {0x3390, 100},
+ {0x338C, 0xA787},
+ {0x3390, 128},
+ {0x338C, 0xA788},
+ {0x3390, 151},
+ {0x338C, 0xA789},
+ {0x3390, 169},
+ {0x338C, 0xA78A},
+ {0x3390, 186},
+ {0x338C, 0xA78B},
+ {0x3390, 199},
+ {0x338C, 0xA78C},
+ {0x3390, 210},
+ {0x338C, 0xA78D},
+ {0x3390, 220},
+ {0x338C, 0xA78E},
+ {0x3390, 228},
+ {0x338C, 0xA78F},
+ {0x3390, 234},
+ {0x338C, 0xA790},
+ {0x3390, 240},
+ {0x338C, 0xA791},
+ {0x3390, 244},
+ {0x338C, 0xA793},
+ {0x3390, 252},
+ {0x338C, 0xA794},
+ {0x3390, 255},
+ {0x338C, 0xA103},
+ {0x3390, 6},
+};
+
+static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = {
+ {0x34CE, 0x81A0, WORD_LEN, 0},
+ {0x34D0, 0x6331, WORD_LEN, 0},
+ {0x34D2, 0x3394, WORD_LEN, 0},
+ {0x34D4, 0x9966, WORD_LEN, 0},
+ {0x34D6, 0x4B25, WORD_LEN, 0},
+ {0x34D8, 0x2670, WORD_LEN, 0},
+ {0x34DA, 0x724C, WORD_LEN, 0},
+ {0x34DC, 0xFFFD, WORD_LEN, 0},
+ {0x34DE, 0x00CA, WORD_LEN, 0},
+ {0x34E6, 0x00AC, WORD_LEN, 0},
+ {0x34EE, 0x0EE1, WORD_LEN, 0},
+ {0x34F6, 0x0D87, WORD_LEN, 0},
+ {0x3500, 0xE1F7, WORD_LEN, 0},
+ {0x3508, 0x1CF4, WORD_LEN, 0},
+ {0x3510, 0x1D28, WORD_LEN, 0},
+ {0x3518, 0x1F26, WORD_LEN, 0},
+ {0x3520, 0x2220, WORD_LEN, 0},
+ {0x3528, 0x333D, WORD_LEN, 0},
+ {0x3530, 0x15D9, WORD_LEN, 0},
+ {0x3538, 0xCFB8, WORD_LEN, 0},
+ {0x354C, 0x05FE, WORD_LEN, 0},
+ {0x3544, 0x05F8, WORD_LEN, 0},
+ {0x355C, 0x0596, WORD_LEN, 0},
+ {0x3554, 0x0611, WORD_LEN, 0},
+ {0x34E0, 0x00F2, WORD_LEN, 0},
+ {0x34E8, 0x00A8, WORD_LEN, 0},
+ {0x34F0, 0x0F7B, WORD_LEN, 0},
+ {0x34F8, 0x0CD7, WORD_LEN, 0},
+ {0x3502, 0xFEDB, WORD_LEN, 0},
+ {0x350A, 0x13E4, WORD_LEN, 0},
+ {0x3512, 0x1F2C, WORD_LEN, 0},
+ {0x351A, 0x1D20, WORD_LEN, 0},
+ {0x3522, 0x2422, WORD_LEN, 0},
+ {0x352A, 0x2925, WORD_LEN, 0},
+ {0x3532, 0x1D04, WORD_LEN, 0},
+ {0x353A, 0xFBF2, WORD_LEN, 0},
+ {0x354E, 0x0616, WORD_LEN, 0},
+ {0x3546, 0x0597, WORD_LEN, 0},
+ {0x355E, 0x05CD, WORD_LEN, 0},
+ {0x3556, 0x0529, WORD_LEN, 0},
+ {0x34E4, 0x00B2, WORD_LEN, 0},
+ {0x34EC, 0x005E, WORD_LEN, 0},
+ {0x34F4, 0x0F43, WORD_LEN, 0},
+ {0x34FC, 0x0E2F, WORD_LEN, 0},
+ {0x3506, 0xF9FC, WORD_LEN, 0},
+ {0x350E, 0x0CE4, WORD_LEN, 0},
+ {0x3516, 0x1E1E, WORD_LEN, 0},
+ {0x351E, 0x1B19, WORD_LEN, 0},
+ {0x3526, 0x151B, WORD_LEN, 0},
+ {0x352E, 0x1416, WORD_LEN, 0},
+ {0x3536, 0x10FC, WORD_LEN, 0},
+ {0x353E, 0xC018, WORD_LEN, 0},
+ {0x3552, 0x06B4, WORD_LEN, 0},
+ {0x354A, 0x0506, WORD_LEN, 0},
+ {0x3562, 0x06AB, WORD_LEN, 0},
+ {0x355A, 0x063A, WORD_LEN, 0},
+ {0x34E2, 0x00E5, WORD_LEN, 0},
+ {0x34EA, 0x008B, WORD_LEN, 0},
+ {0x34F2, 0x0E4C, WORD_LEN, 0},
+ {0x34FA, 0x0CA3, WORD_LEN, 0},
+ {0x3504, 0x0907, WORD_LEN, 0},
+ {0x350C, 0x1DFD, WORD_LEN, 0},
+ {0x3514, 0x1E24, WORD_LEN, 0},
+ {0x351C, 0x2529, WORD_LEN, 0},
+ {0x3524, 0x1D20, WORD_LEN, 0},
+ {0x352C, 0x2332, WORD_LEN, 0},
+ {0x3534, 0x10E9, WORD_LEN, 0},
+ {0x353C, 0x0BCB, WORD_LEN, 0},
+ {0x3550, 0x04EF, WORD_LEN, 0},
+ {0x3548, 0x0609, WORD_LEN, 0},
+ {0x3560, 0x0580, WORD_LEN, 0},
+ {0x3558, 0x05DD, WORD_LEN, 0},
+ {0x3540, 0x0000, WORD_LEN, 0},
+ {0x3542, 0x0000, WORD_LEN, 0}
+};
+
+static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = {
+ {0x341E, 0x8F09, WORD_LEN, 0},
+ {0x341C, 0x0250, WORD_LEN, 0},
+ {0x341E, 0x8F09, WORD_LEN, 5},
+ {0x341E, 0x8F08, WORD_LEN, 0}
+};
+
+/* Refresh Sequencer */
+static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = {
+ {0x338C, 0x2799, WORD_LEN, 0},
+ {0x3390, 0x6440, WORD_LEN, 5},
+ {0x338C, 0x279B, WORD_LEN, 0},
+ {0x3390, 0x6440, WORD_LEN, 5},
+ {0x338C, 0xA103, WORD_LEN, 0},
+ {0x3390, 0x0005, WORD_LEN, 5},
+ {0x338C, 0xA103, WORD_LEN, 0},
+ {0x3390, 0x0006, WORD_LEN, 5}
+};
+
+struct mt9d112_reg mt9d112_regs = {
+ .prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0],
+ .prev_snap_reg_settings_size =
+ ARRAY_SIZE(preview_snapshot_mode_reg_settings_array),
+ .noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0],
+ .noise_reduction_reg_settings_size =
+ ARRAY_SIZE(noise_reduction_reg_settings_array),
+ .plltbl = pll_setup_tbl,
+ .plltbl_size = ARRAY_SIZE(pll_setup_tbl),
+ .stbl = sequencer_tbl,
+ .stbl_size = ARRAY_SIZE(sequencer_tbl),
+ .rftbl = lens_roll_off_tbl,
+ .rftbl_size = ARRAY_SIZE(lens_roll_off_tbl)
+};
diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h
new file mode 100644
index 0000000..4c05f79
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef MT9T012_H
+#define MT9T012_H
+
+#include <linux/types.h>
+
+struct reg_struct {
+ uint16_t vt_pix_clk_div; /* 0x0300 */
+ uint16_t vt_sys_clk_div; /* 0x0302 */
+ uint16_t pre_pll_clk_div; /* 0x0304 */
+ uint16_t pll_multiplier; /* 0x0306 */
+ uint16_t op_pix_clk_div; /* 0x0308 */
+ uint16_t op_sys_clk_div; /* 0x030A */
+ uint16_t scale_m; /* 0x0404 */
+ uint16_t row_speed; /* 0x3016 */
+ uint16_t x_addr_start; /* 0x3004 */
+ uint16_t x_addr_end; /* 0x3008 */
+ uint16_t y_addr_start; /* 0x3002 */
+ uint16_t y_addr_end; /* 0x3006 */
+ uint16_t read_mode; /* 0x3040 */
+ uint16_t x_output_size; /* 0x034C */
+ uint16_t y_output_size; /* 0x034E */
+ uint16_t line_length_pck; /* 0x300C */
+ uint16_t frame_length_lines; /* 0x300A */
+ uint16_t coarse_int_time; /* 0x3012 */
+ uint16_t fine_int_time; /* 0x3014 */
+};
+
+struct mt9p012_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct mt9p012_reg {
+ struct reg_struct *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9p012_i2c_reg_conf *ttbl;
+ uint16_t ttbl_size;
+ struct mt9p012_i2c_reg_conf *lctbl;
+ uint16_t lctbl_size;
+ struct mt9p012_i2c_reg_conf *rftbl;
+ uint16_t rftbl_size;
+};
+
+extern struct mt9p012_reg mt9p012_regs;
+
+#endif /* MT9T012_H */
diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c
new file mode 100644
index 0000000..f2ff40a
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_fox.c
@@ -0,0 +1,1328 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9p012.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9P012_REG_MODEL_ID 0x0000
+#define MT9P012_MODEL_ID 0x2801
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x0100
+#define GROUPED_PARAMETER_UPDATE 0x0000
+#define REG_COARSE_INT_TIME 0x3012
+#define REG_VT_PIX_CLK_DIV 0x0300
+#define REG_VT_SYS_CLK_DIV 0x0302
+#define REG_PRE_PLL_CLK_DIV 0x0304
+#define REG_PLL_MULTIPLIER 0x0306
+#define REG_OP_PIX_CLK_DIV 0x0308
+#define REG_OP_SYS_CLK_DIV 0x030A
+#define REG_SCALE_M 0x0404
+#define REG_FRAME_LENGTH_LINES 0x300A
+#define REG_LINE_LENGTH_PCK 0x300C
+#define REG_X_ADDR_START 0x3004
+#define REG_Y_ADDR_START 0x3002
+#define REG_X_ADDR_END 0x3008
+#define REG_Y_ADDR_END 0x3006
+#define REG_X_OUTPUT_SIZE 0x034C
+#define REG_Y_OUTPUT_SIZE 0x034E
+#define REG_FINE_INTEGRATION_TIME 0x3014
+#define REG_ROW_SPEED 0x3016
+#define MT9P012_REG_RESET_REGISTER 0x301A
+#define MT9P012_RESET_REGISTER_PWON 0x10CC
+#define MT9P012_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+#define MT9P012_REV_7
+
+enum mt9p012_test_mode {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9p012_resolution {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9p012_reg_update {
+ /* Sensor egisters that need to be updated during initialization */
+ REG_INIT,
+ /* Sensor egisters that needs periodic I2C writes */
+ UPDATE_PERIODIC,
+ /* All the sensor Registers will be updated */
+ UPDATE_ALL,
+ /* Not valid update */
+ UPDATE_INVALID
+};
+
+enum mt9p012_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9P012_AF_I2C_ADDR 0x18
+
+/* AF Total steps parameters */
+#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF 32
+#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR 32
+
+#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0
+#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES 0
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_RESET_DELAY_MSECS 66
+
+/* for 20 fps preview */
+#define MT9P012_DEFAULT_CLOCK_RATE 24000000
+#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */
+
+struct mt9p012_work {
+ struct work_struct work;
+};
+static struct mt9p012_work *mt9p012_sensorw;
+static struct i2c_client *mt9p012_client;
+
+struct mt9p012_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum mt9p012_resolution prev_res;
+ enum mt9p012_resolution pict_res;
+ enum mt9p012_resolution curr_res;
+ enum mt9p012_test_mode set_test;
+};
+
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9p012_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr,
+ unsigned short *rdata)
+{
+ int rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9p012_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ CDBG("mt9p012_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata,
+ int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) {
+ CDBG("mt9p012_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr,
+ unsigned short bdata)
+{
+ int rc = -EIO;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ rc = mt9p012_i2c_txdata(saddr, buf, 2);
+
+ if (rc < 0)
+ CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n",
+ saddr, baddr, bdata);
+
+ return rc;
+}
+
+static int mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr,
+ unsigned short wdata)
+{
+ int rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00) >> 8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9p012_i2c_txdata(saddr, buf, 4);
+
+ if (rc < 0)
+ CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int mt9p012_i2c_write_w_table(struct mt9p012_i2c_reg_conf
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int rc = -EIO;
+
+ for (i = 0; i < num; i++) {
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int mt9p012_test(enum mt9p012_test_mode mo)
+{
+ int rc = 0;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ if (mo == TEST_OFF)
+ return 0;
+ else {
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl,
+ mt9p012_regs.ttbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t) mo);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int mt9p012_lens_shading_enable(uint8_t is_enable)
+{
+ int rc = 0;
+
+ CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780,
+ ((uint16_t) is_enable) << 15);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ CDBG("%s: exiting. rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int mt9p012_set_lc(void)
+{
+ int rc;
+
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl,
+ mt9p012_regs.lctbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl,
+ mt9p012_regs.rftbl_size);
+
+ return rc;
+}
+
+static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /*Q10 */
+ uint32_t pclk_mult; /*Q10 */
+
+ if (mt9p012_ctrl->prev_res == QTR_SIZE) {
+ divider = (uint32_t)
+ (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) *
+ 0x00000400) /
+ (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines *
+ mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck));
+
+ pclk_mult =
+ (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].
+ pll_multiplier * 0x00000400) /
+ (mt9p012_regs.reg_pat[RES_PREVIEW].
+ pll_multiplier));
+ } else {
+ /* full size resolution used for preview. */
+ divider = 0x00000400; /*1.0 */
+ pclk_mult = 0x00000400; /*1.0 */
+ }
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 /
+ 0x00000400);
+}
+
+static uint16_t mt9p012_get_prev_lines_pf(void)
+{
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+ else
+ return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_prev_pixels_pl(void)
+{
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck;
+ else
+ return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_get_pict_lines_pf(void)
+{
+ return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_pict_pixels_pl(void)
+{
+ return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (mt9p012_ctrl->pict_res == QTR_SIZE)
+ snapshot_lines_per_frame =
+ mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+ else
+ snapshot_lines_per_frame =
+ mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+ return snapshot_lines_per_frame * 24;
+}
+
+static int mt9p012_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q10 format */
+ int rc = 0;
+
+ mt9p012_ctrl->fps_divider = fps->fps_div;
+ mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return -EBUSY;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_LINE_LENGTH_PCK,
+ (mt9p012_regs.reg_pat[RES_PREVIEW].
+ line_length_pck * fps->f_mult / 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ return rc;
+}
+
+static int mt9p012_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0x01FF;
+ uint32_t line_length_ratio = 0x00000400;
+ enum mt9p012_setting setting;
+ int rc = 0;
+
+ CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__);
+
+ if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ mt9p012_ctrl->my_reg_gain = gain;
+ mt9p012_ctrl->my_reg_line_count = (uint16_t) line;
+ }
+
+ if (gain > max_legal_gain) {
+ CDBG("Max legal gain Line:%d \n", __LINE__);
+ gain = max_legal_gain;
+ }
+
+ /* Verify no overflow */
+ if (mt9p012_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ line = (uint32_t) (line * mt9p012_ctrl->fps_divider /
+ 0x00000400);
+ setting = RES_PREVIEW;
+ } else {
+ line = (uint32_t) (line * mt9p012_ctrl->pict_fps_divider /
+ 0x00000400);
+ setting = RES_CAPTURE;
+ }
+
+ /* Set digital gain to 1 */
+#ifdef MT9P012_REV_7
+ gain |= 0x1000;
+#else
+ gain |= 0x0200;
+#endif
+
+ if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+ line_length_ratio = (uint32_t) (line * 0x00000400) /
+ (mt9p012_regs.reg_pat[setting].frame_length_lines - 1);
+ } else
+ line_length_ratio = 0x00000400;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, REG_GLOBAL_GAIN, gain);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_COARSE_INT_TIME, line);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+
+ return rc;
+}
+
+static int mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int rc = 0;
+
+ CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__);
+
+ rc = mt9p012_write_exp_gain(gain, line);
+ if (rc < 0) {
+ CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n",
+ __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0002);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ mdelay(5);
+
+ /* camera_timed_wait(snapshot_wait*exposure_ratio); */
+ return rc;
+}
+
+static int mt9p012_setting(enum mt9p012_reg_update rupdate,
+ enum mt9p012_setting rt)
+{
+ int rc = 0;
+
+ switch (rupdate) {
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+ struct mt9p012_i2c_reg_conf ppc_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_ROW_SPEED,
+ mt9p012_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START,
+ mt9p012_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END,
+ mt9p012_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START,
+ mt9p012_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END,
+ mt9p012_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE,
+ mt9p012_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE,
+ mt9p012_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE,
+ mt9p012_regs.reg_pat[rt].y_output_size},
+
+ {REG_LINE_LENGTH_PCK,
+ mt9p012_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ (mt9p012_regs.reg_pat[rt].frame_length_lines *
+ mt9p012_ctrl->fps_divider / 0x00000400)},
+ {REG_COARSE_INT_TIME,
+ mt9p012_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME,
+ mt9p012_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE},
+ };
+
+ rc = mt9p012_i2c_write_w_table(&ppc_tbl[0],
+ ARRAY_SIZE(ppc_tbl));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_test(mt9p012_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWON |
+ 0x0002);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5); /* 15? wait for sensor to transition */
+
+ return rc;
+ }
+ break; /* UPDATE_PERIODIC */
+
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct mt9p012_i2c_reg_conf ipc_tbl1[] = {
+ {MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWOFF},
+ {REG_VT_PIX_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ mt9p012_regs.reg_pat[rt].pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+ {0x30B0, 0x0001},
+ {0x308E, 0xE060},
+ {0x3092, 0x0A52},
+ {0x3094, 0x4656},
+ {0x3096, 0x5652},
+ {0x30CA, 0x8006},
+ {0x312A, 0xDD02},
+ {0x312C, 0x00E4},
+ {0x3170, 0x299A},
+#endif
+ /* optimized settings for noise */
+ {0x3088, 0x6FF6},
+ {0x3154, 0x0282},
+ {0x3156, 0x0381},
+ {0x3162, 0x04CE},
+ {0x0204, 0x0010},
+ {0x0206, 0x0010},
+ {0x0208, 0x0010},
+ {0x020A, 0x0010},
+ {0x020C, 0x0010},
+ {MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWON},
+ };
+
+ struct mt9p012_i2c_reg_conf ipc_tbl2[] = {
+ {MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWOFF},
+ {REG_VT_PIX_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ mt9p012_regs.reg_pat[rt].pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV,
+ mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+ {0x30B0, 0x0001},
+ {0x308E, 0xE060},
+ {0x3092, 0x0A52},
+ {0x3094, 0x4656},
+ {0x3096, 0x5652},
+ {0x30CA, 0x8006},
+ {0x312A, 0xDD02},
+ {0x312C, 0x00E4},
+ {0x3170, 0x299A},
+#endif
+ /* optimized settings for noise */
+ {0x3088, 0x6FF6},
+ {0x3154, 0x0282},
+ {0x3156, 0x0381},
+ {0x3162, 0x04CE},
+ {0x0204, 0x0010},
+ {0x0206, 0x0010},
+ {0x0208, 0x0010},
+ {0x020A, 0x0010},
+ {0x020C, 0x0010},
+ {MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWON},
+ };
+
+ struct mt9p012_i2c_reg_conf ipc_tbl3[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ /* Set preview or snapshot mode */
+ {REG_ROW_SPEED,
+ mt9p012_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START,
+ mt9p012_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END,
+ mt9p012_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START,
+ mt9p012_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END,
+ mt9p012_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE,
+ mt9p012_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE,
+ mt9p012_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE,
+ mt9p012_regs.reg_pat[rt].y_output_size},
+ {REG_LINE_LENGTH_PCK,
+ mt9p012_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ mt9p012_regs.reg_pat[rt].frame_length_lines},
+ {REG_COARSE_INT_TIME,
+ mt9p012_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME,
+ mt9p012_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE},
+ };
+
+ /* reset fps_divider */
+ mt9p012_ctrl->fps_divider = 1 * 0x0400;
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0],
+ ARRAY_SIZE(ipc_tbl1));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0],
+ ARRAY_SIZE(ipc_tbl2));
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0],
+ ARRAY_SIZE(ipc_tbl3));
+ if (rc < 0)
+ return rc;
+
+ /* load lens shading */
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_set_lc();
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ if (rc < 0)
+ return rc;
+ }
+ break; /* case REG_INIT: */
+
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int mt9p012_video_config(int mode, int res)
+{
+ int rc;
+
+ switch (res) {
+ case QTR_SIZE:
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+
+ CDBG("mt9p012 sensor configuration done!\n");
+ break;
+
+ case FULL_SIZE:
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ default:
+ return 0;
+ } /* switch */
+
+ mt9p012_ctrl->prev_res = res;
+ mt9p012_ctrl->curr_res = res;
+ mt9p012_ctrl->sensormode = mode;
+
+ rc = mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain,
+ mt9p012_ctrl->my_reg_line_count);
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER, 0x10cc | 0x0002);
+
+ return rc;
+}
+
+static int mt9p012_snapshot_config(int mode)
+{
+ int rc = 0;
+
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+ mt9p012_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int mt9p012_raw_snapshot_config(int mode)
+{
+ int rc = 0;
+
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+ mt9p012_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int mt9p012_power_down(void)
+{
+ int rc = 0;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWOFF);
+
+ mdelay(5);
+ return rc;
+}
+
+static int mt9p012_move_focus(int direction, int num_steps)
+{
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ uint8_t code_val_msb, code_val_lsb;
+
+ if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (num_steps == 0) {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ if (direction == MOVE_NEAR)
+ step_direction = 16; /* 10bit */
+ else if (direction == MOVE_FAR)
+ step_direction = -16; /* 10 bit */
+ else {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos)
+ mt9p012_ctrl->curr_lens_pos = mt9p012_ctrl->init_curr_lens_pos;
+
+ actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+ next_position = (int16_t) (mt9p012_ctrl->curr_lens_pos + actual_step);
+
+ if (next_position > 1023)
+ next_position = 1023;
+ else if (next_position < 0)
+ next_position = 0;
+
+ code_val_msb = next_position >> 4;
+ code_val_lsb = (next_position & 0x000F) << 4;
+ /* code_val_lsb |= mode_mask; */
+
+ /* Writing the digital code for current to the actuator */
+ if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb) < 0) {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+
+ /* Storing the current lens Position */
+ mt9p012_ctrl->curr_lens_pos = next_position;
+
+ return 0;
+}
+
+static int mt9p012_set_default_focus(void)
+{
+ int rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+
+ code_val_msb = 0x00;
+ code_val_lsb = 0x00;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb);
+
+ mt9p012_ctrl->curr_lens_pos = 0;
+ mt9p012_ctrl->init_curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+ uint16_t chipid;
+
+ rc = gpio_request(data->sensor_reset, "mt9p012");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ mdelay(20);
+
+ /* RESET the sensor image part via I2C command */
+ CDBG("mt9p012_sensor_init(): reseting sensor.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0001);
+ if (rc < 0) {
+ CDBG("sensor reset failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ mdelay(MT9P012_RESET_DELAY_MSECS);
+
+ /* 3. Read sensor Model ID: */
+ rc = mt9p012_i2c_read_w(mt9p012_client->addr,
+ MT9P012_REG_MODEL_ID, &chipid);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* 4. Compare sensor ID to MT9T012VC ID: */
+ if (chipid != MT9P012_MODEL_ID) {
+ CDBG("mt9p012 wrong model_id = 0x%x\n", chipid);
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000);
+ if (rc < 0) {
+ CDBG("REV_7 write failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ /* RESET_REGISTER, enable parallel interface and disable serialiser */
+ CDBG("mt9p012_sensor_init(): enabling parallel interface.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC);
+ if (rc < 0) {
+ CDBG("enable parallel interface failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ /* To disable the 2 extra lines */
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3064, 0x0805);
+
+ if (rc < 0) {
+ CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ mdelay(MT9P012_RESET_DELAY_MSECS);
+ goto init_probe_done;
+
+init_probe_fail:
+ mt9p012_probe_init_done(data);
+init_probe_done:
+ return rc;
+}
+
+static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+
+ mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL);
+ if (!mt9p012_ctrl) {
+ CDBG("mt9p012_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ mt9p012_ctrl->fps_divider = 1 * 0x00000400;
+ mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9p012_ctrl->set_test = TEST_OFF;
+ mt9p012_ctrl->prev_res = QTR_SIZE;
+ mt9p012_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9p012_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = mt9p012_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail1;
+
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ rc = mt9p012_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = mt9p012_setting(REG_INIT, RES_CAPTURE);
+
+ if (rc < 0) {
+ CDBG("mt9p012_setting failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* sensor : output enable */
+ CDBG("mt9p012_sensor_open_init(): enabling output.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWON);
+ if (rc < 0) {
+ CDBG("sensor output enable failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* TODO: enable AF actuator */
+#if 0
+ CDBG("enable AF actuator, gpio = %d\n",
+ mt9p012_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012");
+ if (!rc)
+ gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1);
+ else {
+ CDBG("mt9p012_ctrl gpio request failed!\n");
+ goto init_fail1;
+ }
+ mdelay(20);
+
+ rc = mt9p012_set_default_focus();
+#endif
+ if (rc >= 0)
+ goto init_done;
+
+ /* TODO:
+ * gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+ * gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); */
+init_fail1:
+ mt9p012_probe_init_done(data);
+ kfree(mt9p012_ctrl);
+init_done:
+ return rc;
+}
+
+static int mt9p012_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9p012_wait_queue);
+ return 0;
+}
+
+static int mt9p012_set_sensor_mode(int mode, int res)
+{
+ int rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9p012_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = mt9p012_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = mt9p012_raw_snapshot_config(mode);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9p012_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ int rc = 0;
+
+ if (copy_from_user(&cdata,
+ (void *)argp, sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+
+ if (copy_to_user((void *)argp, &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc = mt9p012_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = mt9p012_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+ rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9p012_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d "
+ "cdata.cfg.focus.steps=%d\n",
+ cdata.cfg.focus.dir, cdata.cfg.focus.steps);
+ rc = mt9p012_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = mt9p012_set_default_focus();
+ break;
+
+ case CFG_SET_LENS_SHADING:
+ CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+ rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF;
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mt9p012_power_down();
+
+ gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, 0);
+ gpio_free(mt9p012_ctrl->sensordata->sensor_reset);
+
+ gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+
+ kfree(mt9p012_ctrl);
+ mt9p012_ctrl = NULL;
+
+ CDBG("mt9p012_release completed\n");
+
+ return rc;
+}
+
+static int mt9p012_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("mt9p012_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL);
+ if (!mt9p012_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9p012_sensorw);
+ mt9p012_init_client(client);
+ mt9p012_client = client;
+
+ mdelay(50);
+
+ CDBG("mt9p012_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("mt9p012_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id mt9p012_i2c_id[] = {
+ {"mt9p012", 0},
+ {}
+};
+
+static struct i2c_driver mt9p012_i2c_driver = {
+ .id_table = mt9p012_i2c_id,
+ .probe = mt9p012_i2c_probe,
+ .remove = __exit_p(mt9p012_i2c_remove),
+ .driver = {
+ .name = "mt9p012",
+ },
+};
+
+static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&mt9p012_i2c_driver);
+ if (rc < 0 || mt9p012_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = mt9p012_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = mt9p012_sensor_open_init;
+ s->s_release = mt9p012_sensor_release;
+ s->s_config = mt9p012_sensor_config;
+ mt9p012_probe_init_done(info);
+
+probe_done:
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ return rc;
+}
+
+static int __mt9p012_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9p012_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9p012_probe,
+ .driver = {
+ .name = "msm_camera_mt9p012",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9p012_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_init);
diff --git a/drivers/media/video/msm/mt9p012_reg.c b/drivers/media/video/msm/mt9p012_reg.c
new file mode 100644
index 0000000..713a061
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_reg.c
@@ -0,0 +1,580 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "mt9p012.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct mt9p012_reg_pat[2] = {
+ { /* Preview */
+ /* vt_pix_clk_div REG=0x0300 */
+ 6, /* 5 */
+
+ /* vt_sys_clk_div REG=0x0302 */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 */
+ 2,
+
+ /* pll_multiplier REG=0x0306 */
+ 60,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8, /* 10 */
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2597,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1949,
+
+ /* read_mode REG=0x3040
+ * Preview 2x2 skipping */
+ 0x00C3,
+
+ /* x_output_size REG=0x034C */
+ 1296,
+
+ /* y_output_size REG=0x034E */
+ 972,
+
+ /* line_length_pck REG=0x300C */
+ 3784,
+
+ /* frame_length_lines REG=0x300A */
+ 1057,
+
+ /* coarse_integration_time REG=0x3012 */
+ 16,
+
+ /* fine_integration_time REG=0x3014 */
+ 1764},
+ { /*Snapshot */
+ /* vt_pix_clk_div REG=0x0300 */
+ 6,
+
+ /* vt_sys_clk_div REG=0x0302 */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 */
+ 2,
+
+ /* pll_multiplier REG=0x0306
+ * 60 for 10fps snapshot */
+ 60,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2615,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1967,
+
+ /* read_mode REG=0x3040 */
+ 0x0041,
+
+ /* x_output_size REG=0x034C */
+ 2608,
+
+ /* y_output_size REG=0x034E */
+ 1960,
+
+ /* line_length_pck REG=0x300C */
+ 3911,
+
+ /* frame_length_lines REG=0x300A //10 fps snapshot */
+ 2045,
+
+ /* coarse_integration_time REG=0x3012 */
+ 16,
+
+ /* fine_integration_time REG=0x3014 */
+ 882}
+};
+
+struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = {
+ {0x3044, 0x0544 & 0xFBFF},
+ {0x30CA, 0x0004 | 0x0001},
+ {0x30D4, 0x9020 & 0x7FFF},
+ {0x31E0, 0x0003 & 0xFFFE},
+ {0x3180, 0x91FF & 0x7FFF},
+ {0x301A, (0x10CC | 0x8000) & 0xFFF7},
+ {0x301E, 0x0000},
+ {0x3780, 0x0000},
+};
+
+struct mt9p012_i2c_reg_conf mt9p012_lc_tbl[] = {
+ /* [Lens shading 85 Percent TL84] */
+ /* P_RD_P0Q0 */
+ {0x360A, 0x7FEF},
+ /* P_RD_P0Q1 */
+ {0x360C, 0x232C},
+ /* P_RD_P0Q2 */
+ {0x360E, 0x7050},
+ /* P_RD_P0Q3 */
+ {0x3610, 0xF3CC},
+ /* P_RD_P0Q4 */
+ {0x3612, 0x89D1},
+ /* P_RD_P1Q0 */
+ {0x364A, 0xBE0D},
+ /* P_RD_P1Q1 */
+ {0x364C, 0x9ACB},
+ /* P_RD_P1Q2 */
+ {0x364E, 0x2150},
+ /* P_RD_P1Q3 */
+ {0x3650, 0xB26B},
+ /* P_RD_P1Q4 */
+ {0x3652, 0x9511},
+ /* P_RD_P2Q0 */
+ {0x368A, 0x2151},
+ /* P_RD_P2Q1 */
+ {0x368C, 0x00AD},
+ /* P_RD_P2Q2 */
+ {0x368E, 0x8334},
+ /* P_RD_P2Q3 */
+ {0x3690, 0x478E},
+ /* P_RD_P2Q4 */
+ {0x3692, 0x0515},
+ /* P_RD_P3Q0 */
+ {0x36CA, 0x0710},
+ /* P_RD_P3Q1 */
+ {0x36CC, 0x452D},
+ /* P_RD_P3Q2 */
+ {0x36CE, 0xF352},
+ /* P_RD_P3Q3 */
+ {0x36D0, 0x190F},
+ /* P_RD_P3Q4 */
+ {0x36D2, 0x4413},
+ /* P_RD_P4Q0 */
+ {0x370A, 0xD112},
+ /* P_RD_P4Q1 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q2 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q3 */
+ {0x3710, 0xDC11},
+ /* P_RD_P4Q4 */
+ {0x3712, 0xD776},
+ /* P_GR_P0Q0 */
+ {0x3600, 0x1750},
+ /* P_GR_P0Q1 */
+ {0x3602, 0xF0AC},
+ /* P_GR_P0Q2 */
+ {0x3604, 0x4711},
+ /* P_GR_P0Q3 */
+ {0x3606, 0x07CE},
+ /* P_GR_P0Q4 */
+ {0x3608, 0x96B2},
+ /* P_GR_P1Q0 */
+ {0x3640, 0xA9AE},
+ /* P_GR_P1Q1 */
+ {0x3642, 0xF9AC},
+ /* P_GR_P1Q2 */
+ {0x3644, 0x39F1},
+ /* P_GR_P1Q3 */
+ {0x3646, 0x016F},
+ /* P_GR_P1Q4 */
+ {0x3648, 0x8AB2},
+ /* P_GR_P2Q0 */
+ {0x3680, 0x1752},
+ /* P_GR_P2Q1 */
+ {0x3682, 0x70F0},
+ /* P_GR_P2Q2 */
+ {0x3684, 0x83F5},
+ /* P_GR_P2Q3 */
+ {0x3686, 0x8392},
+ /* P_GR_P2Q4 */
+ {0x3688, 0x1FD6},
+ /* P_GR_P3Q0 */
+ {0x36C0, 0x1131},
+ /* P_GR_P3Q1 */
+ {0x36C2, 0x3DAF},
+ /* P_GR_P3Q2 */
+ {0x36C4, 0x89B4},
+ /* P_GR_P3Q3 */
+ {0x36C6, 0xA391},
+ /* P_GR_P3Q4 */
+ {0x36C8, 0x1334},
+ /* P_GR_P4Q0 */
+ {0x3700, 0xDC13},
+ /* P_GR_P4Q1 */
+ {0x3702, 0xD052},
+ /* P_GR_P4Q2 */
+ {0x3704, 0x5156},
+ /* P_GR_P4Q3 */
+ {0x3706, 0x1F13},
+ /* P_GR_P4Q4 */
+ {0x3708, 0x8C38},
+ /* P_BL_P0Q0 */
+ {0x3614, 0x0050},
+ /* P_BL_P0Q1 */
+ {0x3616, 0xBD4C},
+ /* P_BL_P0Q2 */
+ {0x3618, 0x41B0},
+ /* P_BL_P0Q3 */
+ {0x361A, 0x660D},
+ /* P_BL_P0Q4 */
+ {0x361C, 0xC590},
+ /* P_BL_P1Q0 */
+ {0x3654, 0x87EC},
+ /* P_BL_P1Q1 */
+ {0x3656, 0xE44C},
+ /* P_BL_P1Q2 */
+ {0x3658, 0x302E},
+ /* P_BL_P1Q3 */
+ {0x365A, 0x106E},
+ /* P_BL_P1Q4 */
+ {0x365C, 0xB58E},
+ /* P_BL_P2Q0 */
+ {0x3694, 0x0DD1},
+ /* P_BL_P2Q1 */
+ {0x3696, 0x2A50},
+ /* P_BL_P2Q2 */
+ {0x3698, 0xC793},
+ /* P_BL_P2Q3 */
+ {0x369A, 0xE8F1},
+ /* P_BL_P2Q4 */
+ {0x369C, 0x4174},
+ /* P_BL_P3Q0 */
+ {0x36D4, 0x01EF},
+ /* P_BL_P3Q1 */
+ {0x36D6, 0x06CF},
+ /* P_BL_P3Q2 */
+ {0x36D8, 0x8D91},
+ /* P_BL_P3Q3 */
+ {0x36DA, 0x91F0},
+ /* P_BL_P3Q4 */
+ {0x36DC, 0x52EF},
+ /* P_BL_P4Q0 */
+ {0x3714, 0xA6D2},
+ /* P_BL_P4Q1 */
+ {0x3716, 0xA312},
+ /* P_BL_P4Q2 */
+ {0x3718, 0x2695},
+ /* P_BL_P4Q3 */
+ {0x371A, 0x3953},
+ /* P_BL_P4Q4 */
+ {0x371C, 0x9356},
+ /* P_GB_P0Q0 */
+ {0x361E, 0x7EAF},
+ /* P_GB_P0Q1 */
+ {0x3620, 0x2A4C},
+ /* P_GB_P0Q2 */
+ {0x3622, 0x49F0},
+ {0x3624, 0xF1EC},
+ /* P_GB_P0Q4 */
+ {0x3626, 0xC670},
+ /* P_GB_P1Q0 */
+ {0x365E, 0x8E0C},
+ /* P_GB_P1Q1 */
+ {0x3660, 0xC2A9},
+ /* P_GB_P1Q2 */
+ {0x3662, 0x274F},
+ /* P_GB_P1Q3 */
+ {0x3664, 0xADAB},
+ /* P_GB_P1Q4 */
+ {0x3666, 0x8EF0},
+ /* P_GB_P2Q0 */
+ {0x369E, 0x09B1},
+ /* P_GB_P2Q1 */
+ {0x36A0, 0xAA2E},
+ /* P_GB_P2Q2 */
+ {0x36A2, 0xC3D3},
+ /* P_GB_P2Q3 */
+ {0x36A4, 0x7FAF},
+ /* P_GB_P2Q4 */
+ {0x36A6, 0x3F34},
+ /* P_GB_P3Q0 */
+ {0x36DE, 0x4C8F},
+ /* P_GB_P3Q1 */
+ {0x36E0, 0x886E},
+ /* P_GB_P3Q2 */
+ {0x36E2, 0xE831},
+ /* P_GB_P3Q3 */
+ {0x36E4, 0x1FD0},
+ /* P_GB_P3Q4 */
+ {0x36E6, 0x1192},
+ /* P_GB_P4Q0 */
+ {0x371E, 0xB952},
+ /* P_GB_P4Q1 */
+ {0x3720, 0x6DCF},
+ /* P_GB_P4Q2 */
+ {0x3722, 0x1B55},
+ /* P_GB_P4Q3 */
+ {0x3724, 0xA112},
+ /* P_GB_P4Q4 */
+ {0x3726, 0x82F6},
+ /* POLY_ORIGIN_C */
+ {0x3782, 0x0510},
+ /* POLY_ORIGIN_R */
+ {0x3784, 0x0390},
+ /* POLY_SC_ENABLE */
+ {0x3780, 0x8000},
+};
+
+/* rolloff table for illuminant A */
+struct mt9p012_i2c_reg_conf mt9p012_rolloff_tbl[] = {
+ /* P_RD_P0Q0 */
+ {0x360A, 0x7FEF},
+ /* P_RD_P0Q1 */
+ {0x360C, 0x232C},
+ /* P_RD_P0Q2 */
+ {0x360E, 0x7050},
+ /* P_RD_P0Q3 */
+ {0x3610, 0xF3CC},
+ /* P_RD_P0Q4 */
+ {0x3612, 0x89D1},
+ /* P_RD_P1Q0 */
+ {0x364A, 0xBE0D},
+ /* P_RD_P1Q1 */
+ {0x364C, 0x9ACB},
+ /* P_RD_P1Q2 */
+ {0x364E, 0x2150},
+ /* P_RD_P1Q3 */
+ {0x3650, 0xB26B},
+ /* P_RD_P1Q4 */
+ {0x3652, 0x9511},
+ /* P_RD_P2Q0 */
+ {0x368A, 0x2151},
+ /* P_RD_P2Q1 */
+ {0x368C, 0x00AD},
+ /* P_RD_P2Q2 */
+ {0x368E, 0x8334},
+ /* P_RD_P2Q3 */
+ {0x3690, 0x478E},
+ /* P_RD_P2Q4 */
+ {0x3692, 0x0515},
+ /* P_RD_P3Q0 */
+ {0x36CA, 0x0710},
+ /* P_RD_P3Q1 */
+ {0x36CC, 0x452D},
+ /* P_RD_P3Q2 */
+ {0x36CE, 0xF352},
+ /* P_RD_P3Q3 */
+ {0x36D0, 0x190F},
+ /* P_RD_P3Q4 */
+ {0x36D2, 0x4413},
+ /* P_RD_P4Q0 */
+ {0x370A, 0xD112},
+ /* P_RD_P4Q1 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q2 */
+ {0x370E, 0x6375},
+ /* P_RD_P4Q3 */
+ {0x3710, 0xDC11},
+ /* P_RD_P4Q4 */
+ {0x3712, 0xD776},
+ /* P_GR_P0Q0 */
+ {0x3600, 0x1750},
+ /* P_GR_P0Q1 */
+ {0x3602, 0xF0AC},
+ /* P_GR_P0Q2 */
+ {0x3604, 0x4711},
+ /* P_GR_P0Q3 */
+ {0x3606, 0x07CE},
+ /* P_GR_P0Q4 */
+ {0x3608, 0x96B2},
+ /* P_GR_P1Q0 */
+ {0x3640, 0xA9AE},
+ /* P_GR_P1Q1 */
+ {0x3642, 0xF9AC},
+ /* P_GR_P1Q2 */
+ {0x3644, 0x39F1},
+ /* P_GR_P1Q3 */
+ {0x3646, 0x016F},
+ /* P_GR_P1Q4 */
+ {0x3648, 0x8AB2},
+ /* P_GR_P2Q0 */
+ {0x3680, 0x1752},
+ /* P_GR_P2Q1 */
+ {0x3682, 0x70F0},
+ /* P_GR_P2Q2 */
+ {0x3684, 0x83F5},
+ /* P_GR_P2Q3 */
+ {0x3686, 0x8392},
+ /* P_GR_P2Q4 */
+ {0x3688, 0x1FD6},
+ /* P_GR_P3Q0 */
+ {0x36C0, 0x1131},
+ /* P_GR_P3Q1 */
+ {0x36C2, 0x3DAF},
+ /* P_GR_P3Q2 */
+ {0x36C4, 0x89B4},
+ /* P_GR_P3Q3 */
+ {0x36C6, 0xA391},
+ /* P_GR_P3Q4 */
+ {0x36C8, 0x1334},
+ /* P_GR_P4Q0 */
+ {0x3700, 0xDC13},
+ /* P_GR_P4Q1 */
+ {0x3702, 0xD052},
+ /* P_GR_P4Q2 */
+ {0x3704, 0x5156},
+ /* P_GR_P4Q3 */
+ {0x3706, 0x1F13},
+ /* P_GR_P4Q4 */
+ {0x3708, 0x8C38},
+ /* P_BL_P0Q0 */
+ {0x3614, 0x0050},
+ /* P_BL_P0Q1 */
+ {0x3616, 0xBD4C},
+ /* P_BL_P0Q2 */
+ {0x3618, 0x41B0},
+ /* P_BL_P0Q3 */
+ {0x361A, 0x660D},
+ /* P_BL_P0Q4 */
+ {0x361C, 0xC590},
+ /* P_BL_P1Q0 */
+ {0x3654, 0x87EC},
+ /* P_BL_P1Q1 */
+ {0x3656, 0xE44C},
+ /* P_BL_P1Q2 */
+ {0x3658, 0x302E},
+ /* P_BL_P1Q3 */
+ {0x365A, 0x106E},
+ /* P_BL_P1Q4 */
+ {0x365C, 0xB58E},
+ /* P_BL_P2Q0 */
+ {0x3694, 0x0DD1},
+ /* P_BL_P2Q1 */
+ {0x3696, 0x2A50},
+ /* P_BL_P2Q2 */
+ {0x3698, 0xC793},
+ /* P_BL_P2Q3 */
+ {0x369A, 0xE8F1},
+ /* P_BL_P2Q4 */
+ {0x369C, 0x4174},
+ /* P_BL_P3Q0 */
+ {0x36D4, 0x01EF},
+ /* P_BL_P3Q1 */
+ {0x36D6, 0x06CF},
+ /* P_BL_P3Q2 */
+ {0x36D8, 0x8D91},
+ /* P_BL_P3Q3 */
+ {0x36DA, 0x91F0},
+ /* P_BL_P3Q4 */
+ {0x36DC, 0x52EF},
+ /* P_BL_P4Q0 */
+ {0x3714, 0xA6D2},
+ /* P_BL_P4Q1 */
+ {0x3716, 0xA312},
+ /* P_BL_P4Q2 */
+ {0x3718, 0x2695},
+ /* P_BL_P4Q3 */
+ {0x371A, 0x3953},
+ /* P_BL_P4Q4 */
+ {0x371C, 0x9356},
+ /* P_GB_P0Q0 */
+ {0x361E, 0x7EAF},
+ /* P_GB_P0Q1 */
+ {0x3620, 0x2A4C},
+ /* P_GB_P0Q2 */
+ {0x3622, 0x49F0},
+ {0x3624, 0xF1EC},
+ /* P_GB_P0Q4 */
+ {0x3626, 0xC670},
+ /* P_GB_P1Q0 */
+ {0x365E, 0x8E0C},
+ /* P_GB_P1Q1 */
+ {0x3660, 0xC2A9},
+ /* P_GB_P1Q2 */
+ {0x3662, 0x274F},
+ /* P_GB_P1Q3 */
+ {0x3664, 0xADAB},
+ /* P_GB_P1Q4 */
+ {0x3666, 0x8EF0},
+ /* P_GB_P2Q0 */
+ {0x369E, 0x09B1},
+ /* P_GB_P2Q1 */
+ {0x36A0, 0xAA2E},
+ /* P_GB_P2Q2 */
+ {0x36A2, 0xC3D3},
+ /* P_GB_P2Q3 */
+ {0x36A4, 0x7FAF},
+ /* P_GB_P2Q4 */
+ {0x36A6, 0x3F34},
+ /* P_GB_P3Q0 */
+ {0x36DE, 0x4C8F},
+ /* P_GB_P3Q1 */
+ {0x36E0, 0x886E},
+ /* P_GB_P3Q2 */
+ {0x36E2, 0xE831},
+ /* P_GB_P3Q3 */
+ {0x36E4, 0x1FD0},
+ /* P_GB_P3Q4 */
+ {0x36E6, 0x1192},
+ /* P_GB_P4Q0 */
+ {0x371E, 0xB952},
+ /* P_GB_P4Q1 */
+ {0x3720, 0x6DCF},
+ /* P_GB_P4Q2 */
+ {0x3722, 0x1B55},
+ /* P_GB_P4Q3 */
+ {0x3724, 0xA112},
+ /* P_GB_P4Q4 */
+ {0x3726, 0x82F6},
+ /* POLY_ORIGIN_C */
+ {0x3782, 0x0510},
+ /* POLY_ORIGIN_R */
+ {0x3784, 0x0390},
+ /* POLY_SC_ENABLE */
+ {0x3780, 0x8000},
+};
+
+struct mt9p012_reg mt9p012_regs = {
+ .reg_pat = &mt9p012_reg_pat[0],
+ .reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat),
+ .ttbl = &mt9p012_test_tbl[0],
+ .ttbl_size = ARRAY_SIZE(mt9p012_test_tbl),
+ .lctbl = &mt9p012_lc_tbl[0],
+ .lctbl_size = ARRAY_SIZE(mt9p012_lc_tbl),
+ .rftbl = &mt9p012_rolloff_tbl[0],
+ .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl)
+};
diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c
new file mode 100644
index 0000000..e77904e
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.c
@@ -0,0 +1,1506 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "mt9t013.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9T013_REG_MODEL_ID 0x0000
+#define MT9T013_MODEL_ID 0x2600
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x0100
+#define GROUPED_PARAMETER_UPDATE 0x0000
+#define REG_COARSE_INT_TIME 0x3012
+#define REG_VT_PIX_CLK_DIV 0x0300
+#define REG_VT_SYS_CLK_DIV 0x0302
+#define REG_PRE_PLL_CLK_DIV 0x0304
+#define REG_PLL_MULTIPLIER 0x0306
+#define REG_OP_PIX_CLK_DIV 0x0308
+#define REG_OP_SYS_CLK_DIV 0x030A
+#define REG_SCALE_M 0x0404
+#define REG_FRAME_LENGTH_LINES 0x300A
+#define REG_LINE_LENGTH_PCK 0x300C
+#define REG_X_ADDR_START 0x3004
+#define REG_Y_ADDR_START 0x3002
+#define REG_X_ADDR_END 0x3008
+#define REG_Y_ADDR_END 0x3006
+#define REG_X_OUTPUT_SIZE 0x034C
+#define REG_Y_OUTPUT_SIZE 0x034E
+#define REG_FINE_INT_TIME 0x3014
+#define REG_ROW_SPEED 0x3016
+#define MT9T013_REG_RESET_REGISTER 0x301A
+#define MT9T013_RESET_REGISTER_PWON 0x10CC
+#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming */
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+enum mt9t013_test_mode {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9t013_resolution {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9t013_reg_update {
+ REG_INIT, /* registers that need to be updated during initialization */
+ UPDATE_PERIODIC, /* registers that needs periodic I2C writes */
+ UPDATE_ALL, /* all registers will be updated */
+ UPDATE_INVALID
+};
+
+enum mt9t013_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9T013_AF_I2C_ADDR 0x18
+
+/*
+* AF Total steps parameters
+*/
+#define MT9T013_TOTAL_STEPS_NEAR_TO_FAR 30
+
+/*
+ * Time in milisecs for waiting for the sensor to reset.
+ */
+#define MT9T013_RESET_DELAY_MSECS 66
+
+/* for 30 fps preview */
+#define MT9T013_DEFAULT_CLOCK_RATE 24000000
+#define MT9T013_DEFAULT_MAX_FPS 26
+
+/* FIXME: Changes from here */
+struct mt9t013_work {
+ struct work_struct work;
+};
+
+static struct mt9t013_work *mt9t013_sensorw;
+static struct i2c_client *mt9t013_client;
+
+struct mt9t013_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum mt9t013_resolution prev_res;
+ enum mt9t013_resolution pict_res;
+ enum mt9t013_resolution curr_res;
+ enum mt9t013_test_mode set_test;
+
+ unsigned short imgaddr;
+};
+
+static struct mt9t013_ctrl *mt9t013_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue);
+
+static int mt9t013_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) {
+ pr_err("mt9t013_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9t013_i2c_read_w(unsigned short saddr,
+ unsigned short raddr, unsigned short *rdata)
+{
+ int rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9t013_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ pr_err("mt9t013_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int mt9t013_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) {
+ pr_err("mt9t013_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt9t013_i2c_write_b(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata)
+{
+ int rc = -EIO;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = waddr;
+ buf[1] = wdata;
+ rc = mt9t013_i2c_txdata(saddr, buf, 2);
+
+ if (rc < 0)
+ pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int mt9t013_i2c_write_w(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata)
+{
+ int rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00) >> 8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9t013_i2c_txdata(saddr, buf, 4);
+
+ if (rc < 0)
+ pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int mt9t013_i2c_write_w_table(struct mt9t013_i2c_reg_conf
+ *reg_conf_tbl,
+ int num_of_items_in_table)
+{
+ int i;
+ int rc = -EIO;
+
+ for (i = 0; i < num_of_items_in_table; i++) {
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int mt9t013_test(enum mt9t013_test_mode mo)
+{
+ int rc = 0;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ if (mo == TEST_OFF)
+ return 0;
+ else {
+ rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl,
+ mt9t013_regs.ttbl_size);
+ if (rc < 0)
+ return rc;
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t) mo);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int mt9t013_set_lc(void)
+{
+ int rc;
+
+ rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl,
+ mt9t013_regs.lctbl_size);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int mt9t013_set_default_focus(uint8_t af_step)
+{
+ int rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+ code_val_msb = 0x01;
+ code_val_lsb = af_step;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb);
+
+ mt9t013_ctrl->curr_lens_pos = 0;
+ mt9t013_ctrl->init_curr_lens_pos = 0;
+ return rc;
+}
+
+static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /*Q10 */
+ uint32_t pclk_mult; /*Q10 */
+
+ if (mt9t013_ctrl->prev_res == QTR_SIZE) {
+ divider =
+ (uint32_t) (((mt9t013_regs.reg_pat[RES_PREVIEW].
+ frame_length_lines *
+ mt9t013_regs.reg_pat[RES_PREVIEW].
+ line_length_pck) * 0x00000400) /
+ (mt9t013_regs.reg_pat[RES_CAPTURE].
+ frame_length_lines *
+ mt9t013_regs.reg_pat[RES_CAPTURE].
+ line_length_pck));
+
+ pclk_mult =
+ (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].
+ pll_multiplier * 0x00000400) /
+ (mt9t013_regs.reg_pat[RES_PREVIEW].
+ pll_multiplier));
+
+ } else {
+ /* full size resolution used for preview. */
+ divider = 0x00000400; /*1.0 */
+ pclk_mult = 0x00000400; /*1.0 */
+ }
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps =
+ (uint16_t) (fps * divider * pclk_mult / 0x00000400 / 0x00000400);
+}
+
+static uint16_t mt9t013_get_prev_lines_pf(void)
+{
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+ else
+ return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_prev_pixels_pl(void)
+{
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck;
+ else
+ return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9t013_get_pict_lines_pf(void)
+{
+ return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_pict_pixels_pl(void)
+{
+ return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9t013_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (mt9t013_ctrl->pict_res == QTR_SIZE) {
+ snapshot_lines_per_frame =
+ mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+ } else {
+ snapshot_lines_per_frame =
+ mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+ }
+
+ return snapshot_lines_per_frame * 24;
+}
+
+static int mt9t013_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q8 format */
+ int rc = 0;
+
+ mt9t013_ctrl->fps_divider = fps->fps_div;
+ mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return -EBUSY;
+
+ CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n",
+ fps->fps_div,
+ (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW].
+ frame_length_lines * fps->fps_div / 0x00000400));
+
+ CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n",
+ fps->f_mult,
+ (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW].
+ line_length_pck * fps->f_mult / 0x00000400));
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW].
+ line_length_pck * fps->f_mult /
+ 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int mt9t013_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ const uint16_t max_legal_gain = 0x01FF;
+ uint32_t line_length_ratio = 0x00000400;
+ enum mt9t013_setting setting;
+ int rc = 0;
+
+ if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ mt9t013_ctrl->my_reg_gain = gain;
+ mt9t013_ctrl->my_reg_line_count = (uint16_t) line;
+ }
+
+ if (gain > max_legal_gain)
+ gain = max_legal_gain;
+
+ /* Verify no overflow */
+ if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ line = (uint32_t) (line * mt9t013_ctrl->fps_divider /
+ 0x00000400);
+
+ setting = RES_PREVIEW;
+
+ } else {
+ line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider /
+ 0x00000400);
+
+ setting = RES_CAPTURE;
+ }
+
+ /*Set digital gain to 1 */
+ gain |= 0x0200;
+
+ if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+
+ line_length_ratio =
+ (uint32_t) (line * 0x00000400) /
+ (mt9t013_regs.reg_pat[setting].frame_length_lines - 1);
+ } else
+ line_length_ratio = 0x00000400;
+
+ /* There used to be PARAMETER_HOLD register write before and
+ * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes
+ * aec oscillation. Hence removed. */
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ (uint16_t) ((uint32_t) line));
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int rc = 0;
+
+ rc = mt9t013_write_exp_gain(gain, line);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER, 0x10CC | 0x0002);
+
+ mdelay(5);
+
+ /* camera_timed_wait(snapshot_wait*exposure_ratio); */
+ return rc;
+}
+
+static int mt9t013_setting(enum mt9t013_reg_update rupdate,
+ enum mt9t013_setting rt)
+{
+ int rc = 0;
+
+ switch (rupdate) {
+ case UPDATE_PERIODIC:{
+
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+#if 0
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc < 0)
+ return rc;
+#endif
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_PIX_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ vt_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_SYS_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ vt_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PRE_PLL_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ pre_pll_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PLL_MULTIPLIER,
+ mt9t013_regs.
+ reg_pat[rt].
+ pll_multiplier);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_PIX_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ op_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_SYS_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ op_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_ROW_SPEED,
+ mt9t013_regs.
+ reg_pat[rt].row_speed);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_START,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_END,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_addr_end);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_START,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_END,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_addr_end);
+ if (rc < 0)
+ return rc;
+
+ if (machine_is_sapphire()) {
+ if (rt == 0)
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE, 0x046F);
+ else
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE, 0x0027);
+ } else
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE,
+ mt9t013_regs.reg_pat[rt].
+ read_mode);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_SCALE_M,
+ mt9t013_regs.
+ reg_pat[rt].scale_m);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_OUTPUT_SIZE,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_OUTPUT_SIZE,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ mt9t013_regs.
+ reg_pat[rt].
+ line_length_pck);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ (mt9t013_regs.
+ reg_pat[rt].
+ frame_length_lines *
+ mt9t013_ctrl->
+ fps_divider /
+ 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ mt9t013_regs.
+ reg_pat[rt].
+ coarse_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FINE_INT_TIME,
+ mt9t013_regs.
+ reg_pat[rt].
+ fine_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_test(mt9t013_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ return rc;
+ }
+ }
+ break;
+
+ /*CAMSENSOR_REG_UPDATE_PERIODIC */
+ case REG_INIT:{
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc < 0)
+ /* MODE_SELECT, stop streaming */
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_PIX_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ vt_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_SYS_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ vt_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PRE_PLL_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ pre_pll_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PLL_MULTIPLIER,
+ mt9t013_regs.
+ reg_pat[rt].
+ pll_multiplier);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_PIX_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ op_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_SYS_CLK_DIV,
+ mt9t013_regs.
+ reg_pat[rt].
+ op_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ /* additional power saving mode ok around
+ * 38.2MHz
+ */
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3084, 0x2409);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3092, 0x0A49);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3094, 0x4949);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3096, 0x4949);
+ if (rc < 0)
+ return rc;
+
+ /* Set preview or snapshot mode */
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_ROW_SPEED,
+ mt9t013_regs.
+ reg_pat[rt].row_speed);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_START,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_END,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_addr_end);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_START,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_END,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_addr_end);
+ if (rc < 0)
+ return rc;
+
+ if (machine_is_sapphire()) {
+ if (rt == 0)
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE, 0x046F);
+ else
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE, 0x0027);
+ } else
+ rc = mt9t013_i2c_write_w
+ (mt9t013_client->addr,
+ REG_READ_MODE,
+ mt9t013_regs.reg_pat[rt].
+ read_mode);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_SCALE_M,
+ mt9t013_regs.
+ reg_pat[rt].scale_m);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_OUTPUT_SIZE,
+ mt9t013_regs.
+ reg_pat[rt].
+ x_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_OUTPUT_SIZE,
+ mt9t013_regs.
+ reg_pat[rt].
+ y_output_size);
+ if (rc < 0)
+ return 0;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ mt9t013_regs.
+ reg_pat[rt].
+ line_length_pck);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ mt9t013_regs.
+ reg_pat[rt].
+ frame_length_lines);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ mt9t013_regs.
+ reg_pat[rt].
+ coarse_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FINE_INT_TIME,
+ mt9t013_regs.
+ reg_pat[rt].
+ fine_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ /* load lens shading */
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ /* most likely needs to be written only once. */
+ rc = mt9t013_set_lc();
+ if (rc < 0)
+ return -EBUSY;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_test(mt9t013_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+ if (rc < 0)
+ /* MODE_SELECT, stop streaming */
+ return rc;
+
+ CDBG("!!! mt9t013 !!! PowerOn is done!\n");
+ mdelay(5);
+ return rc;
+ }
+ } /* case CAMSENSOR_REG_INIT: */
+ break;
+
+ /*CAMSENSOR_REG_INIT */
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int mt9t013_video_config(int mode, int res)
+{
+ int rc;
+
+ switch (res) {
+ case QTR_SIZE:
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+ CDBG("sensor configuration done!\n");
+ break;
+
+ case FULL_SIZE:
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ mt9t013_ctrl->prev_res = res;
+ mt9t013_ctrl->curr_res = res;
+ mt9t013_ctrl->sensormode = mode;
+
+ return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain,
+ mt9t013_ctrl->my_reg_line_count);
+}
+
+static int mt9t013_snapshot_config(int mode)
+{
+ int rc = 0;
+
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+ mt9t013_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int mt9t013_raw_snapshot_config(int mode)
+{
+ int rc = 0;
+
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+ mt9t013_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int mt9t013_power_down(void)
+{
+ int rc = 0;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc >= 0)
+ mdelay(5);
+ return rc;
+}
+
+static int mt9t013_move_focus(int direction, int num_steps)
+{
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ int16_t break_steps[4];
+ uint8_t code_val_msb, code_val_lsb;
+ int16_t i;
+
+ if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (num_steps == 0)
+ return -EINVAL;
+
+ if (direction == MOVE_NEAR)
+ step_direction = 4;
+ else if (direction == MOVE_FAR)
+ step_direction = -4;
+ else
+ return -EINVAL;
+
+ if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos)
+ mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos;
+
+ actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+
+ for (i = 0; i < 4; i++)
+ break_steps[i] =
+ actual_step / 4 * (i + 1) - actual_step / 4 * i;
+
+ for (i = 0; i < 4; i++) {
+ next_position = (int16_t)
+ (mt9t013_ctrl->curr_lens_pos + break_steps[i]);
+
+ if (next_position > 255)
+ next_position = 255;
+ else if (next_position < 0)
+ next_position = 0;
+
+ code_val_msb =
+ ((next_position >> 4) << 2) | ((next_position << 4) >> 6);
+
+ code_val_lsb = ((next_position & 0x03) << 6);
+
+ /* Writing the digital code for current to the actuator */
+ if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb) < 0)
+ return -EBUSY;
+
+ /* Storing the current lens Position */
+ mt9t013_ctrl->curr_lens_pos = next_position;
+
+ if (i < 3)
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+ uint16_t chipid;
+
+ rc = gpio_request(data->sensor_reset, "mt9t013");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ mdelay(20);
+
+ /* RESET the sensor image part via I2C command */
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER, 0x1009);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* 3. Read sensor Model ID: */
+ rc = mt9t013_i2c_read_w(mt9t013_client->addr,
+ MT9T013_REG_MODEL_ID, &chipid);
+
+ if (rc < 0)
+ goto init_probe_fail;
+
+ CDBG("mt9t013 model_id = 0x%x\n", chipid);
+
+ /* 4. Compare sensor ID to MT9T012VC ID: */
+ if (chipid != MT9T013_MODEL_ID) {
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr, 0x3064, 0x0805);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(MT9T013_RESET_DELAY_MSECS);
+
+ goto init_probe_done;
+
+ /* sensor: output enable */
+#if 0
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+
+ /* if this fails, the sensor is not the MT9T013 */
+ rc = mt9t013_set_default_focus(0);
+#endif
+
+init_probe_fail:
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+init_probe_done:
+ return rc;
+}
+
+static int mt9t013_poweron_af(void)
+{
+ int rc = 0;
+
+ /* enable AF actuator */
+ CDBG("enable AF actuator, gpio = %d\n",
+ mt9t013_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013");
+ if (!rc) {
+ gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0);
+ mdelay(20);
+ rc = mt9t013_set_default_focus(0);
+ } else
+ pr_err("%s, gpio_request failed (%d)!\n", __func__, rc);
+ return rc;
+}
+
+static void mt9t013_poweroff_af(void)
+{
+ gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1);
+ gpio_free(mt9t013_ctrl->sensordata->vcm_pwd);
+}
+
+int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+
+ mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL);
+ if (!mt9t013_ctrl) {
+ pr_err("mt9t013_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ mt9t013_ctrl->fps_divider = 1 * 0x00000400;
+ mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9t013_ctrl->set_test = TEST_OFF;
+ mt9t013_ctrl->prev_res = QTR_SIZE;
+ mt9t013_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9t013_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = mt9t013_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ rc = mt9t013_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = mt9t013_setting(REG_INIT, RES_CAPTURE);
+
+ if (rc >= 0)
+ rc = mt9t013_poweron_af();
+
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+
+init_fail:
+ kfree(mt9t013_ctrl);
+init_done:
+ return rc;
+}
+
+static int mt9t013_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9t013_wait_queue);
+ return 0;
+}
+
+static int mt9t013_set_sensor_mode(int mode, int res)
+{
+ int rc = 0;
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9t013_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = mt9t013_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = mt9t013_raw_snapshot_config(mode);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* FIXME: what should we do if rc < 0? */
+ if (rc >= 0)
+ return mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ return rc;
+}
+
+int mt9t013_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ long rc = 0;
+
+ if (copy_from_user(&cdata, (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf();
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl();
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf();
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl = mt9t013_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc = mt9t013_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = mt9t013_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9t013_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = mt9t013_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = mt9t013_set_default_focus(cdata.cfg.focus.steps);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9t013_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mt9t013_poweroff_af();
+ mt9t013_power_down();
+
+ gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, 0);
+ gpio_free(mt9t013_ctrl->sensordata->sensor_reset);
+
+ kfree(mt9t013_ctrl);
+
+ CDBG("mt9t013_release completed!\n");
+ return rc;
+}
+
+static int mt9t013_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ rc = -ENOTSUPP;
+ goto probe_failure;
+ }
+
+ mt9t013_sensorw = kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL);
+
+ if (!mt9t013_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9t013_sensorw);
+ mt9t013_init_client(client);
+ mt9t013_client = client;
+ mt9t013_client->addr = mt9t013_client->addr >> 1;
+ mdelay(50);
+
+ CDBG("i2c probe ok\n");
+ return 0;
+
+probe_failure:
+ kfree(mt9t013_sensorw);
+ mt9t013_sensorw = NULL;
+ pr_err("i2c probe failure %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id mt9t013_i2c_id[] = {
+ {"mt9t013", 0},
+ {}
+};
+
+static struct i2c_driver mt9t013_i2c_driver = {
+ .id_table = mt9t013_i2c_id,
+ .probe = mt9t013_i2c_probe,
+ .remove = __exit_p(mt9t013_i2c_remove),
+ .driver = {
+ .name = "mt9t013",
+ },
+};
+
+static int mt9t013_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ /* We expect this driver to match with the i2c device registered
+ * in the board file immediately. */
+ int rc = i2c_add_driver(&mt9t013_i2c_driver);
+ if (rc < 0 || mt9t013_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = mt9t013_probe_init_sensor(info);
+ if (rc < 0) {
+ i2c_del_driver(&mt9t013_i2c_driver);
+ goto probe_done;
+ }
+
+ s->s_init = mt9t013_sensor_open_init;
+ s->s_release = mt9t013_sensor_release;
+ s->s_config = mt9t013_sensor_config;
+ mt9t013_sensor_init_done(info);
+
+probe_done:
+ return rc;
+}
+
+static int __mt9t013_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9t013_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9t013_probe,
+ .driver = {
+ .name = "msm_camera_mt9t013",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9t013_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9t013_init);
diff --git a/drivers/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h
new file mode 100644
index 0000000..67c4d9c
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef MT9T013_H
+#define MT9T013_H
+
+#include <linux/types.h>
+
+struct reg_struct {
+ uint16_t vt_pix_clk_div; /* 0x0300 */
+ uint16_t vt_sys_clk_div; /* 0x0302 */
+ uint16_t pre_pll_clk_div; /* 0x0304 */
+ uint16_t pll_multiplier; /* 0x0306 */
+ uint16_t op_pix_clk_div; /* 0x0308 */
+ uint16_t op_sys_clk_div; /* 0x030A */
+ uint16_t scale_m; /* 0x0404 */
+ uint16_t row_speed; /* 0x3016 */
+ uint16_t x_addr_start; /* 0x3004 */
+ uint16_t x_addr_end; /* 0x3008 */
+ uint16_t y_addr_start; /* 0x3002 */
+ uint16_t y_addr_end; /* 0x3006 */
+ uint16_t read_mode; /* 0x3040 */
+ uint16_t x_output_size; /* 0x034C */
+ uint16_t y_output_size; /* 0x034E */
+ uint16_t line_length_pck; /* 0x300C */
+ uint16_t frame_length_lines; /* 0x300A */
+ uint16_t coarse_int_time; /* 0x3012 */
+ uint16_t fine_int_time; /* 0x3014 */
+};
+
+struct mt9t013_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct mt9t013_reg {
+ struct reg_struct *reg_pat;
+ int reg_pat_size;
+ struct mt9t013_i2c_reg_conf *ttbl;
+ int ttbl_size;
+ struct mt9t013_i2c_reg_conf *lctbl;
+ int lctbl_size;
+ struct mt9t013_i2c_reg_conf *rftbl;
+ int rftbl_size;
+};
+
+extern struct mt9t013_reg mt9t013_regs;
+
+#endif /* #define MT9T013_H */
diff --git a/drivers/media/video/msm/mt9t013_reg.c b/drivers/media/video/msm/mt9t013_reg.c
new file mode 100644
index 0000000..aac81c7
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013_reg.c
@@ -0,0 +1,277 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "mt9t013.h"
+#include <linux/kernel.h>
+
+struct reg_struct mt9t013_reg_pat[2] = {
+ {/* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */
+ /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps
+ * if this change */
+ 10,
+
+ /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps
+ * if this change */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps
+ * if this change */
+ 3,
+
+ /* pll_multiplier REG=0x0306 60 for 30fps preview, 40
+ * for 20fps preview
+ * 46 for 30fps preview, try 47/48 to increase further */
+ 80,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 10,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2053,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1541,
+
+ /* read_mode REG=0x3040 */
+ 0x046C,
+
+ /* x_output_size REG=0x034C */
+ 1024,
+
+ /* y_output_size REG=0x034E */
+ 768,
+
+ /* line_length_pck REG=0x300C */
+ 3540,
+
+ /* frame_length_lines REG=0x300A */
+ 861,
+
+ /* coarse_int_time REG=0x3012 */
+ 16,
+
+ /* fine_int_time REG=0x3014 */
+ 1461},
+
+ { /*Snapshot */
+ /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps
+ * if this change */
+ 10,
+
+ /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps
+ * if this change */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps
+ * if this change */
+ 3,
+
+ /* pll_multiplier REG=0x0306 50 for 15fps snapshot,
+ * 40 for 10fps snapshot
+ * 46 for 30fps snapshot, try 47/48 to increase further */
+ 80,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 10,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2063,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1551,
+
+ /* read_mode REG=0x3040 */
+ 0x0024,
+
+ /* x_output_size REG=0x034C */
+ 2064,
+
+ /* y_output_size REG=0x034E */
+ 1544,
+
+ /* line_length_pck REG=0x300C */
+ 4800,
+
+ /* frame_length_lines REG=0x300A */
+ 1629,
+
+ /* coarse_int_time REG=0x3012 */
+ 16,
+
+ /* fine_int_time REG=0x3014 */
+ 733}
+};
+
+struct mt9t013_i2c_reg_conf mt9t013_test_tbl[] = {
+ {0x3044, 0x0544 & 0xFBFF},
+ {0x30CA, 0x0004 | 0x0001},
+ {0x30D4, 0x9020 & 0x7FFF},
+ {0x31E0, 0x0003 & 0xFFFE},
+ {0x3180, 0x91FF & 0x7FFF},
+ {0x301A, (0x10CC | 0x8000) & 0xFFF7},
+ {0x301E, 0x0000},
+ {0x3780, 0x0000},
+};
+
+/* [Lens shading 85 Percent TL84] */
+struct mt9t013_i2c_reg_conf mt9t013_lc_tbl[] = {
+ {0x360A, 0x0290}, /* P_RD_P0Q0 */
+ {0x360C, 0xC92D}, /* P_RD_P0Q1 */
+ {0x360E, 0x0771}, /* P_RD_P0Q2 */
+ {0x3610, 0xE38C}, /* P_RD_P0Q3 */
+ {0x3612, 0xD74F}, /* P_RD_P0Q4 */
+ {0x364A, 0x168C}, /* P_RD_P1Q0 */
+ {0x364C, 0xCACB}, /* P_RD_P1Q1 */
+ {0x364E, 0x8C4C}, /* P_RD_P1Q2 */
+ {0x3650, 0x0BEA}, /* P_RD_P1Q3 */
+ {0x3652, 0xDC0F}, /* P_RD_P1Q4 */
+ {0x368A, 0x70B0}, /* P_RD_P2Q0 */
+ {0x368C, 0x200B}, /* P_RD_P2Q1 */
+ {0x368E, 0x30B2}, /* P_RD_P2Q2 */
+ {0x3690, 0xD04F}, /* P_RD_P2Q3 */
+ {0x3692, 0xACF5}, /* P_RD_P2Q4 */
+ {0x36CA, 0xF7C9}, /* P_RD_P3Q0 */
+ {0x36CC, 0x2AED}, /* P_RD_P3Q1 */
+ {0x36CE, 0xA652}, /* P_RD_P3Q2 */
+ {0x36D0, 0x8192}, /* P_RD_P3Q3 */
+ {0x36D2, 0x3A15}, /* P_RD_P3Q4 */
+ {0x370A, 0xDA30}, /* P_RD_P4Q0 */
+ {0x370C, 0x2E2F}, /* P_RD_P4Q1 */
+ {0x370E, 0xBB56}, /* P_RD_P4Q2 */
+ {0x3710, 0x8195}, /* P_RD_P4Q3 */
+ {0x3712, 0x02F9}, /* P_RD_P4Q4 */
+ {0x3600, 0x0230}, /* P_GR_P0Q0 */
+ {0x3602, 0x58AD}, /* P_GR_P0Q1 */
+ {0x3604, 0x18D1}, /* P_GR_P0Q2 */
+ {0x3606, 0x260D}, /* P_GR_P0Q3 */
+ {0x3608, 0xF530}, /* P_GR_P0Q4 */
+ {0x3640, 0x17EB}, /* P_GR_P1Q0 */
+ {0x3642, 0x3CAB}, /* P_GR_P1Q1 */
+ {0x3644, 0x87CE}, /* P_GR_P1Q2 */
+ {0x3646, 0xC02E}, /* P_GR_P1Q3 */
+ {0x3648, 0xF48F}, /* P_GR_P1Q4 */
+ {0x3680, 0x5350}, /* P_GR_P2Q0 */
+ {0x3682, 0x7EAF}, /* P_GR_P2Q1 */
+ {0x3684, 0x4312}, /* P_GR_P2Q2 */
+ {0x3686, 0xC652}, /* P_GR_P2Q3 */
+ {0x3688, 0xBC15}, /* P_GR_P2Q4 */
+ {0x36C0, 0xB8AD}, /* P_GR_P3Q0 */
+ {0x36C2, 0xBDCD}, /* P_GR_P3Q1 */
+ {0x36C4, 0xE4B2}, /* P_GR_P3Q2 */
+ {0x36C6, 0xB50F}, /* P_GR_P3Q3 */
+ {0x36C8, 0x5B95}, /* P_GR_P3Q4 */
+ {0x3700, 0xFC90}, /* P_GR_P4Q0 */
+ {0x3702, 0x8C51}, /* P_GR_P4Q1 */
+ {0x3704, 0xCED6}, /* P_GR_P4Q2 */
+ {0x3706, 0xB594}, /* P_GR_P4Q3 */
+ {0x3708, 0x0A39}, /* P_GR_P4Q4 */
+ {0x3614, 0x0230}, /* P_BL_P0Q0 */
+ {0x3616, 0x160D}, /* P_BL_P0Q1 */
+ {0x3618, 0x08D1}, /* P_BL_P0Q2 */
+ {0x361A, 0x98AB}, /* P_BL_P0Q3 */
+ {0x361C, 0xEA50}, /* P_BL_P0Q4 */
+ {0x3654, 0xB4EA}, /* P_BL_P1Q0 */
+ {0x3656, 0xEA6C}, /* P_BL_P1Q1 */
+ {0x3658, 0xFE08}, /* P_BL_P1Q2 */
+ {0x365A, 0x2C6E}, /* P_BL_P1Q3 */
+ {0x365C, 0xEB0E}, /* P_BL_P1Q4 */
+ {0x3694, 0x6DF0}, /* P_BL_P2Q0 */
+ {0x3696, 0x3ACF}, /* P_BL_P2Q1 */
+ {0x3698, 0x3E0F}, /* P_BL_P2Q2 */
+ {0x369A, 0xB2B1}, /* P_BL_P2Q3 */
+ {0x369C, 0xC374}, /* P_BL_P2Q4 */
+ {0x36D4, 0xF2AA}, /* P_BL_P3Q0 */
+ {0x36D6, 0x8CCC}, /* P_BL_P3Q1 */
+ {0x36D8, 0xDEF2}, /* P_BL_P3Q2 */
+ {0x36DA, 0xFA11}, /* P_BL_P3Q3 */
+ {0x36DC, 0x42F5}, /* P_BL_P3Q4 */
+ {0x3714, 0xF4F1}, /* P_BL_P4Q0 */
+ {0x3716, 0xF6F0}, /* P_BL_P4Q1 */
+ {0x3718, 0x8FD6}, /* P_BL_P4Q2 */
+ {0x371A, 0xEA14}, /* P_BL_P4Q3 */
+ {0x371C, 0x6338}, /* P_BL_P4Q4 */
+ {0x361E, 0x0350}, /* P_GB_P0Q0 */
+ {0x3620, 0x91AE}, /* P_GB_P0Q1 */
+ {0x3622, 0x0571}, /* P_GB_P0Q2 */
+ {0x3624, 0x100D}, /* P_GB_P0Q3 */
+ {0x3626, 0xCA70}, /* P_GB_P0Q4 */
+ {0x365E, 0xE6CB}, /* P_GB_P1Q0 */
+ {0x3660, 0x50ED}, /* P_GB_P1Q1 */
+ {0x3662, 0x3DAE}, /* P_GB_P1Q2 */
+ {0x3664, 0xAA4F}, /* P_GB_P1Q3 */
+ {0x3666, 0xDC50}, /* P_GB_P1Q4 */
+ {0x369E, 0x5470}, /* P_GB_P2Q0 */
+ {0x36A0, 0x1F6E}, /* P_GB_P2Q1 */
+ {0x36A2, 0x6671}, /* P_GB_P2Q2 */
+ {0x36A4, 0xC010}, /* P_GB_P2Q3 */
+ {0x36A6, 0x8DF5}, /* P_GB_P2Q4 */
+ {0x36DE, 0x0B0C}, /* P_GB_P3Q0 */
+ {0x36E0, 0x84CE}, /* P_GB_P3Q1 */
+ {0x36E2, 0x8493}, /* P_GB_P3Q2 */
+ {0x36E4, 0xA610}, /* P_GB_P3Q3 */
+ {0x36E6, 0x50B5}, /* P_GB_P3Q4 */
+ {0x371E, 0x9651}, /* P_GB_P4Q0 */
+ {0x3720, 0x1EAB}, /* P_GB_P4Q1 */
+ {0x3722, 0xAF76}, /* P_GB_P4Q2 */
+ {0x3724, 0xE4F4}, /* P_GB_P4Q3 */
+ {0x3726, 0x79F8}, /* P_GB_P4Q4 */
+ {0x3782, 0x0410}, /* POLY_ORIGIN_C */
+ {0x3784, 0x0320}, /* POLY_ORIGIN_R */
+ {0x3780, 0x8000} /* POLY_SC_ENABLE */
+};
+
+struct mt9t013_reg mt9t013_regs = {
+ .reg_pat = &mt9t013_reg_pat[0],
+ .reg_pat_size = ARRAY_SIZE(mt9t013_reg_pat),
+ .ttbl = &mt9t013_test_tbl[0],
+ .ttbl_size = ARRAY_SIZE(mt9t013_test_tbl),
+ .lctbl = &mt9t013_lc_tbl[0],
+ .lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl),
+ .rftbl = &mt9t013_lc_tbl[0],
+ .rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl)
+};
diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c
new file mode 100644
index 0000000..a2ab9ba
--- /dev/null
+++ b/drivers/media/video/msm/s5k3e2fx.c
@@ -0,0 +1,3104 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <linux/clk.h>
+#include <linux/wakelock.h>
+
+static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */
+
+/* prepare for modify PCLK*/
+#define REG_PLL_MULTIPLIER_LSB_VALUE 0x90
+/* 0xA0 for PCLK=80MHz */
+/* 0x90 for PCLK=72MHz */
+
+/* prepare for modify initial gain*/
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE 0x80
+
+#define S5K3E2FX_REG_MODEL_ID 0x0000
+#define S5K3E2FX_MODEL_ID 0x3E2F
+
+#define S5K3E2FX_REG_MODULE_VER 0x0002
+
+#define S5K3E2FX_DEF_MCLK 24000000
+
+#define S5K3E2FX_QTR_SIZE_WIDTH 1296
+#define S5K3E2FX_QTR_SIZE_HEIGHT 972
+
+#define S5K3E2FX_FULL_SIZE_WIDTH 2608
+#define S5K3E2FX_FULL_SIZE_HEIGHT 1960
+
+/* AEC_FLASHING */
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x01
+#define GROUPED_PARAMETER_UPDATE 0x00
+
+/* Greenish in low light */
+#define REG_MASK_CORRUPTED_FRAMES 0x0105
+#define MASK 0x01
+#define NO_MASK 0x00
+
+/* PLL Registers */
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLL_MULTIPLIER_MSB 0x0306
+#define REG_PLL_MULTIPLIER_LSB 0x0307
+#define REG_VT_PIX_CLK_DIV 0x0301
+#define REG_VT_SYS_CLK_DIV 0x0303
+#define REG_OP_PIX_CLK_DIV 0x0309
+#define REG_OP_SYS_CLK_DIV 0x030B
+
+/* Data Format Registers */
+#define REG_CCP_DATA_FORMAT_MSB 0x0112
+#define REG_CCP_DATA_FORMAT_LSB 0x0113
+
+/* Output Size */
+#define REG_X_OUTPUT_SIZE_MSB 0x034C
+#define REG_X_OUTPUT_SIZE_LSB 0x034D
+#define REG_Y_OUTPUT_SIZE_MSB 0x034E
+#define REG_Y_OUTPUT_SIZE_LSB 0x034F
+
+/* Binning */
+#define REG_X_EVEN_INC 0x0381
+#define REG_X_ODD_INC 0x0383
+#define REG_Y_EVEN_INC 0x0385
+#define REG_Y_ODD_INC 0x0387
+/*Reserved register */
+#define REG_BINNING_ENABLE 0x3014
+
+/* Frame Fotmat */
+#define REG_FRAME_LENGTH_LINES_MSB 0x0340
+#define REG_FRAME_LENGTH_LINES_LSB 0x0341
+#define REG_LINE_LENGTH_PCK_MSB 0x0342
+#define REG_LINE_LENGTH_PCK_LSB 0x0343
+
+/* MSR setting */
+/* Reserved registers */
+#define REG_SHADE_CLK_ENABLE 0x30AC
+#define REG_SEL_CCP 0x30C4
+#define REG_VPIX 0x3024
+#define REG_CLAMP_ON 0x3015
+#define REG_OFFSET 0x307E
+
+/* CDS timing settings */
+/* Reserved registers */
+#define REG_LD_START 0x3000
+#define REG_LD_END 0x3001
+#define REG_SL_START 0x3002
+#define REG_SL_END 0x3003
+#define REG_RX_START 0x3004
+#define REG_S1_START 0x3005
+#define REG_S1_END 0x3006
+#define REG_S1S_START 0x3007
+#define REG_S1S_END 0x3008
+#define REG_S3_START 0x3009
+#define REG_S3_END 0x300A
+#define REG_CMP_EN_START 0x300B
+#define REG_CLP_SL_START 0x300C
+#define REG_CLP_SL_END 0x300D
+#define REG_OFF_START 0x300E
+#define REG_RMP_EN_START 0x300F
+#define REG_TX_START 0x3010
+#define REG_TX_END 0x3011
+#define REG_STX_WIDTH 0x3012
+#define REG_TYPE1_AF_ENABLE 0x3130
+#define DRIVER_ENABLED 0x0001
+#define AUTO_START_ENABLED 0x0010
+#define REG_NEW_POSITION 0x3131
+#define REG_3152_RESERVED 0x3152
+#define REG_315A_RESERVED 0x315A
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205
+#define REG_FINE_INTEGRATION_TIME 0x0200
+#define REG_COARSE_INTEGRATION_TIME 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203
+
+/* Mode select register */
+#define S5K3E2FX_REG_MODE_SELECT 0x0100
+#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */
+#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */
+#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103
+#define S5K3E2FX_SOFTWARE_RESET 0x01
+#define REG_TEST_PATTERN_MODE 0x0601
+
+/* Samsung other MSR setting*/
+#define REG_301D_RESERVED 0x301D
+#define REG_3028_RESERVED 0x3028
+#define REG_3070_RESERVED 0x3070
+#define REG_3072_RESERVED 0x3072
+#define REG_301B_RESERVED 0x301B
+#define REG_30BD_RESERVED 0x30BD
+#define REG_30C2_RESERVED 0x30C2
+#define REG_3151_RESERVED 0x3151
+#define REG_3029_RESERVED 0x3029
+#define REG_30BF_RESERVED 0x30BF
+#define REG_3022_RESERVED 0x3022
+#define REG_3019_RESERVED 0x3019
+#define REG_3150_RESERVED 0x3150
+#define REG_3157_RESERVED 0x3157
+#define REG_3159_RESERVED 0x3159
+/* LC Preview/Snapshot difference register */
+#define REG_SH4CH_BLK_WIDTH_R 0x309E
+#define REG_SH4CH_BLK_HEIGHT_R 0x309F
+#define REG_SH4CH_STEP_X_R_MSB 0x30A0
+#define REG_SH4CH_STEP_X_R_LSB 0x30A1
+#define REG_SH4CH_STEP_Y_R_MSB 0x30A2
+#define REG_SH4CH_STEP_Y_R_LSB 0x30A3
+#define REG_SH4CH_START_BLK_CNT_X_R 0x30A4
+#define REG_SH4CH_START_BLK_INT_X_R 0x30A5
+#define REG_SH4CH_START_FRAC_X_R_MSB 0x30A6
+#define REG_SH4CH_START_FRAC_X_R_LSB 0x30A7
+#define REG_SH4CH_START_BLK_CNT_Y_R 0x30A8
+#define REG_SH4CH_START_BLK_INT_Y_R 0x30A9
+#define REG_SH4CH_START_FRAC_Y_R_MSB 0x30AA
+#define REG_SH4CH_START_FRAC_Y_R_LSB 0x30AB
+#define REG_X_ADDR_START_MSB 0x0344
+#define REG_X_ADDR_START_LSB 0x0345
+#define REG_Y_ADDR_START_MSB 0x0346
+#define REG_Y_ADDR_START_LSB 0x0347
+#define REG_X_ADDR_END_MSB 0x0348
+#define REG_X_ADDR_END_LSB 0x0349
+#define REG_Y_ADDR_END_MSB 0x034A
+#define REG_Y_ADDR_END_LSB 0x034B
+
+#define NUM_INIT_REG 94
+#define NUM_LC_REG 434
+
+struct s5k3e2fx_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned char bdata;
+};
+
+/* Separate the EVT4/EVT5 sensor init and LC setting start */
+struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = {
+/*EVT4 */
+ {
+ {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */
+ {REG_PLL_MULTIPLIER_MSB, 0x00},
+ {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE},
+ {REG_VT_PIX_CLK_DIV, 0x08},
+ {REG_VT_SYS_CLK_DIV, 0x01},
+ {REG_OP_PIX_CLK_DIV, 0x08},
+ {REG_OP_SYS_CLK_DIV, 0x01},
+/* Data Format */
+ {REG_CCP_DATA_FORMAT_MSB, 0x0a},
+ {REG_CCP_DATA_FORMAT_LSB, 0x0a},
+/* Preview Output Size */
+ {REG_X_OUTPUT_SIZE_MSB, 0x05},
+ {REG_X_OUTPUT_SIZE_LSB, 0x10},
+ {REG_Y_OUTPUT_SIZE_MSB, 0x03},
+ {REG_Y_OUTPUT_SIZE_LSB, 0xcc},
+ {REG_X_ADDR_START_MSB, 0x00},
+ {REG_X_ADDR_START_LSB, 0x08},
+ {REG_Y_ADDR_START_MSB, 0x00},
+ {REG_Y_ADDR_START_LSB, 0x08},
+ {REG_X_ADDR_END_MSB, 0x0a},
+ {REG_X_ADDR_END_LSB, 0x27},
+ {REG_Y_ADDR_END_MSB, 0x07},
+ {REG_Y_ADDR_END_LSB, 0x9f},
+/* Frame format */
+ {REG_FRAME_LENGTH_LINES_MSB, 0x03},
+ {REG_FRAME_LENGTH_LINES_LSB, 0xe2},
+ {REG_LINE_LENGTH_PCK_MSB, 0x0a},
+ {REG_LINE_LENGTH_PCK_LSB, 0xac},
+/* Preview Binning */
+ {REG_X_EVEN_INC, 0x01},
+ {REG_X_ODD_INC, 0x01},
+ {REG_Y_EVEN_INC, 0x01},
+ {REG_Y_ODD_INC, 0x03},
+ {REG_BINNING_ENABLE, 0x06},
+/* Samsung MSR Setting */
+ {REG_SEL_CCP, 0x01},
+ {REG_LD_START, 0x03},
+/* Add EVT5 sensor Samsung MSR setting, Start */
+ {REG_LD_END, 0x94},
+ {REG_SL_START, 0x02},
+ {REG_SL_END, 0x95},
+ {REG_RX_START, 0x0f},
+ {REG_S1_START, 0x05},
+ {REG_S1_END, 0x3c},
+ {REG_S1S_START, 0x8c},
+ {REG_S1S_END, 0x93},
+ {REG_S3_START, 0x05},
+ {REG_S3_END, 0x3a},
+ {REG_CMP_EN_START, 0x10},
+ {REG_CLP_SL_START, 0x02},
+ {REG_CLP_SL_END, 0x3e},
+ {REG_OFF_START, 0x02},
+ {REG_RMP_EN_START, 0x0e},
+ {REG_TX_START, 0x46},
+ {REG_TX_END, 0x64},
+ {REG_STX_WIDTH, 0x1e},
+ {REG_CLAMP_ON, 0x00},
+ {REG_301D_RESERVED, 0x3f},
+ {REG_VPIX, 0x04},
+ {REG_3028_RESERVED, 0x40},
+ {REG_3070_RESERVED, 0xdf},
+ {REG_3072_RESERVED, 0x20},
+ {REG_301B_RESERVED, 0x73},
+ {REG_OFFSET, 0x02},
+ {REG_30BD_RESERVED, 0x06},
+ {REG_30C2_RESERVED, 0x0b},
+ {REG_SHADE_CLK_ENABLE, 0x81},
+ {REG_3151_RESERVED, 0xe6},
+ {REG_3029_RESERVED, 0x02},
+ {REG_30BF_RESERVED, 0x00},
+ {REG_3022_RESERVED, 0x87},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3019_RESERVED, 0x60},
+ {REG_3152_RESERVED, 0x08},
+ {REG_3150_RESERVED, 0x50}, /* from 0x40 to 0x50 for PCLK=80MHz */
+/* Inverse PCLK = 0x50 */
+ {REG_3157_RESERVED, 0x04}, /* from 0x00 to 0x04 for PCLK=80MHz */
+/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */
+ {REG_3159_RESERVED, 0x0f}, /* from 0x00 to 0x0f for PCLK=80MHz */
+/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+ {REG_315A_RESERVED, 0xf0}, /* from 0x10 to 0xf0 for PCLK=80MHz */
+/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA
+ * 10:6mA, 11:8mA
+ */
+/* AEC Setting */
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE},
+ {REG_FINE_INTEGRATION_TIME, 0x02},
+ {REG_COARSE_INTEGRATION_TIME, 0x03},
+/* Preview LC config Setting */
+ {REG_SH4CH_BLK_WIDTH_R, 0x52},
+ {REG_SH4CH_BLK_HEIGHT_R, 0x3e},
+ {REG_SH4CH_STEP_X_R_MSB, 0x03},
+ {REG_SH4CH_STEP_X_R_LSB, 0x1f},
+ {REG_SH4CH_STEP_Y_R_MSB, 0x04},
+ {REG_SH4CH_STEP_Y_R_LSB, 0x21},
+ {REG_SH4CH_START_BLK_CNT_X_R, 0x04},
+ {REG_SH4CH_START_BLK_INT_X_R, 0x00},
+ {REG_SH4CH_START_FRAC_X_R_MSB, 0x0c},
+ {REG_SH4CH_START_FRAC_X_R_LSB, 0x7c},
+ {REG_SH4CH_START_BLK_CNT_Y_R, 0x04},
+ {REG_SH4CH_START_BLK_INT_Y_R, 0x00},
+ {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10},
+ {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84},
+ },
+
+/* EVT5 */
+ {
+ {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */
+ {REG_PLL_MULTIPLIER_MSB, 0x00},
+ {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE},
+ {REG_VT_PIX_CLK_DIV, 0x08},
+ {REG_VT_SYS_CLK_DIV, 0x01},
+ {REG_OP_PIX_CLK_DIV, 0x08},
+ {REG_OP_SYS_CLK_DIV, 0x01},
+/* Data Format */
+ {REG_CCP_DATA_FORMAT_MSB, 0x0a},
+ {REG_CCP_DATA_FORMAT_LSB, 0x0a},
+/* Preview Output Size */
+ {REG_X_OUTPUT_SIZE_MSB, 0x05},
+ {REG_X_OUTPUT_SIZE_LSB, 0x10},
+ {REG_Y_OUTPUT_SIZE_MSB, 0x03},
+ {REG_Y_OUTPUT_SIZE_LSB, 0xcc},
+ {REG_X_ADDR_START_MSB, 0x00},
+ {REG_X_ADDR_START_LSB, 0x08},
+ {REG_Y_ADDR_START_MSB, 0x00},
+ {REG_Y_ADDR_START_LSB, 0x08},
+ {REG_X_ADDR_END_MSB, 0x0a},
+ {REG_X_ADDR_END_LSB, 0x27},
+ {REG_Y_ADDR_END_MSB, 0x07},
+ {REG_Y_ADDR_END_LSB, 0x9f},
+/* Frame format */
+ {REG_FRAME_LENGTH_LINES_MSB, 0x03},
+ {REG_FRAME_LENGTH_LINES_LSB, 0xe2},
+ {REG_LINE_LENGTH_PCK_MSB, 0x0a},
+ {REG_LINE_LENGTH_PCK_LSB, 0xac},
+/* Preview Binning */
+ {REG_X_EVEN_INC, 0x01},
+ {REG_X_ODD_INC, 0x01},
+ {REG_Y_EVEN_INC, 0x01},
+ {REG_Y_ODD_INC, 0x03},
+ {REG_BINNING_ENABLE, 0x06},
+/* Samsung MSR Setting */
+ {REG_SEL_CCP, 0x01},
+ {REG_LD_START, 0x03},
+/* EVT5 sensor Samsung MSR setting */
+ {REG_LD_END, 0x99},
+ {REG_SL_START, 0x02},
+ {REG_SL_END, 0x9A},
+ {REG_RX_START, 0x0f},
+ {REG_S1_START, 0x05},
+ {REG_S1_END, 0x3c},
+ {REG_S1S_START, 0x8c},
+ {REG_S1S_END, 0x26},
+ {REG_S3_START, 0x05},
+ {REG_S3_END, 0x3a},
+ {REG_CMP_EN_START, 0x10},
+ {REG_CLP_SL_START, 0x02},
+ {REG_CLP_SL_END, 0x3e},
+ {REG_OFF_START, 0x02},
+ {REG_RMP_EN_START, 0x0e},
+ {REG_TX_START, 0x46},
+ {REG_TX_END, 0x64},
+ {REG_STX_WIDTH, 0x1e},
+ {REG_CLAMP_ON, 0x00},
+ {REG_301D_RESERVED, 0x3f},
+ {REG_VPIX, 0x04},
+ {REG_3028_RESERVED, 0x40},
+ {REG_3070_RESERVED, 0xdf},
+ {REG_3072_RESERVED, 0x20},
+ {REG_301B_RESERVED, 0x73},
+ {REG_OFFSET, 0x02},
+ {REG_30BD_RESERVED, 0x06},
+ {REG_30C2_RESERVED, 0x0b},
+ {REG_SHADE_CLK_ENABLE, 0x81},
+ {REG_3151_RESERVED, 0xe6},
+ {REG_3029_RESERVED, 0x02},
+ {REG_30BF_RESERVED, 0x00},
+ {REG_3022_RESERVED, 0x87},
+ {REG_3019_RESERVED, 0x60},
+ {0x3060, 0x03},
+ {0x3061, 0x6C},
+ {0x3062, 0x00},
+ {0x3063, 0xD6},
+ {0x3023, 0x0C},
+ {REG_3152_RESERVED, 0x08},
+ {REG_3150_RESERVED, 0x50}, /* from 0x40 to 0x50 for PCLK=80MHz */
+/* Inverse PCLK = 0x50 */
+ {REG_3157_RESERVED, 0x04}, /* from 0x00 to 0x04 for PCLK=80MHz */
+/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */
+ {REG_3159_RESERVED, 0x0f}, /* from 0x00 to 0x0f for PCLK=80MHz */
+/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+ {REG_315A_RESERVED, 0xf0}, /* from 0x10 to 0xf0 for PCLK=80MHz */
+/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA,
+ * 10:6mA, 11:8mA
+ */
+/* AEC Setting */
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE},
+ {REG_FINE_INTEGRATION_TIME, 0x02},
+ {REG_COARSE_INTEGRATION_TIME, 0x03},
+/* Preview LC config Setting */
+ {REG_SH4CH_BLK_WIDTH_R, 0x52},
+ {REG_SH4CH_BLK_HEIGHT_R, 0x3e},
+ {REG_SH4CH_STEP_X_R_MSB, 0x03},
+ {REG_SH4CH_STEP_X_R_LSB, 0x1f},
+ {REG_SH4CH_STEP_Y_R_MSB, 0x04},
+ {REG_SH4CH_STEP_Y_R_LSB, 0x21},
+ {REG_SH4CH_START_BLK_CNT_X_R, 0x04},
+ {REG_SH4CH_START_BLK_INT_X_R, 0x00},
+ {REG_SH4CH_START_FRAC_X_R_MSB, 0x0c},
+ {REG_SH4CH_START_FRAC_X_R_LSB, 0x7c},
+ {REG_SH4CH_START_BLK_CNT_Y_R, 0x04},
+ {REG_SH4CH_START_BLK_INT_Y_R, 0x00},
+ {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10},
+ {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84},
+ }
+};
+
+struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = {
+/*EVT4 */
+ {
+ /*EVT4 */ /* 100108 Modify LC setting DNP light source t75-r73*/
+ {0x3200, 0x00},
+ {0x3201, 0x99},
+ {0x3202, 0xc1},
+ {0x3203, 0x0f},
+ {0x3204, 0xd0},
+ {0x3205, 0x1b},
+ {0x3206, 0x00},
+ {0x3207, 0x24},
+ {0x3208, 0x8d},
+ {0x3209, 0x0f},
+ {0x320a, 0xee},
+ {0x320b, 0x0f},
+ {0x320c, 0x00},
+ {0x320d, 0x04},
+ {0x320e, 0x5c},
+ {0x320f, 0x00},
+ {0x3210, 0x07},
+ {0x3211, 0x68},
+ {0x3212, 0x0f},
+ {0x3213, 0xc2},
+ {0x3214, 0x82},
+ {0x3215, 0x00},
+ {0x3216, 0x29},
+ {0x3217, 0x3e},
+ {0x3218, 0x0f},
+ {0x3219, 0xd3},
+ {0x321a, 0x63},
+ {0x321b, 0x00},
+ {0x321c, 0x22},
+ {0x321d, 0x6c},
+ {0x321e, 0x0f},
+ {0x321f, 0xf8},
+ {0x3220, 0xce},
+ {0x3221, 0x0f},
+ {0x3222, 0xed},
+ {0x3223, 0x30},
+ {0x3224, 0x00},
+ {0x3225, 0x37},
+ {0x3226, 0x87},
+ {0x3227, 0x0f},
+ {0x3228, 0xc2},
+ {0x3229, 0x87},
+ {0x322a, 0x00},
+ {0x322b, 0x2a},
+ {0x322c, 0xc6},
+ {0x322d, 0x0f},
+ {0x322e, 0xf3},
+ {0x322f, 0xd9},
+ {0x3230, 0x0f},
+ {0x3231, 0xea},
+ {0x3232, 0x1a},
+ {0x3233, 0x00},
+ {0x3234, 0x2d},
+ {0x3235, 0x9f},
+ {0x3236, 0x0f},
+ {0x3237, 0xde},
+ {0x3238, 0x7d},
+ {0x3239, 0x00},
+ {0x323a, 0x37},
+ {0x323b, 0x1e},
+ {0x323c, 0x0f},
+ {0x323d, 0xed},
+ {0x323e, 0x9c},
+ {0x323f, 0x0f},
+ {0x3240, 0xf6},
+ {0x3241, 0xfd},
+ {0x3242, 0x00},
+ {0x3243, 0x15},
+ {0x3244, 0xeb},
+ {0x3245, 0x0f},
+ {0x3246, 0xd3},
+ {0x3247, 0xca},
+ {0x3248, 0x00},
+ {0x3249, 0x08},
+ {0x324a, 0xe6},
+ {0x324b, 0x0f},
+ {0x324c, 0xf4},
+ {0x324d, 0x7a},
+ {0x324e, 0x0f},
+ {0x324f, 0xed},
+ {0x3250, 0x1e},
+ {0x3251, 0x00},
+ {0x3252, 0x0d},
+ {0x3253, 0x46},
+ {0x3254, 0x00},
+ {0x3255, 0x0c},
+ {0x3256, 0x3e},
+ {0x3257, 0x00},
+ {0x3258, 0x09},
+ {0x3259, 0xcf},
+ {0x325a, 0x00},
+ {0x325b, 0x09},
+ {0x325c, 0xb5},
+ {0x325d, 0x0f},
+ {0x325e, 0xec},
+ {0x325f, 0x47},
+ {0x3260, 0x00},
+ {0x3261, 0x1d},
+ {0x3262, 0xd8},
+ {0x3263, 0x0f},
+ {0x3264, 0xf7},
+ {0x3265, 0x11},
+ {0x3266, 0x0f},
+ {0x3267, 0xea},
+ {0x3268, 0x3d},
+ {0x3269, 0x00},
+ {0x326a, 0x09},
+ {0x326b, 0xcc},
+ {0x326c, 0x00},
+ {0x326d, 0x9b},
+ {0x326e, 0x73},
+ {0x326f, 0x0f},
+ {0x3270, 0xd4},
+ {0x3271, 0x9e},
+ {0x3272, 0x00},
+ {0x3273, 0x1a},
+ {0x3274, 0x87},
+ {0x3275, 0x0f},
+ {0x3276, 0xfd},
+ {0x3277, 0xeb},
+ {0x3278, 0x0f},
+ {0x3279, 0xf5},
+ {0x327a, 0xb4},
+ {0x327b, 0x00},
+ {0x327c, 0x0d},
+ {0x327d, 0x8c},
+ {0x327e, 0x0f},
+ {0x327f, 0xc9},
+ {0x3280, 0x4d},
+ {0x3281, 0x00},
+ {0x3282, 0x1d},
+ {0x3283, 0x2d},
+ {0x3284, 0x0f},
+ {0x3285, 0xea},
+ {0x3286, 0x5b},
+ {0x3287, 0x00},
+ {0x3288, 0x04},
+ {0x3289, 0x76},
+ {0x328a, 0x00},
+ {0x328b, 0x10},
+ {0x328c, 0x2d},
+ {0x328d, 0x0f},
+ {0x328e, 0xe6},
+ {0x328f, 0xde},
+ {0x3290, 0x00},
+ {0x3291, 0x26},
+ {0x3292, 0x85},
+ {0x3293, 0x0f},
+ {0x3294, 0xcf},
+ {0x3295, 0x12},
+ {0x3296, 0x00},
+ {0x3297, 0x14},
+ {0x3298, 0x0f},
+ {0x3299, 0x00},
+ {0x329a, 0x0b},
+ {0x329b, 0x36},
+ {0x329c, 0x0f},
+ {0x329d, 0xe4},
+ {0x329e, 0xa4},
+ {0x329f, 0x00},
+ {0x32a0, 0x21},
+ {0x32a1, 0x1f},
+ {0x32a2, 0x0f},
+ {0x32a3, 0xf3},
+ {0x32a4, 0x99},
+ {0x32a5, 0x00},
+ {0x32a6, 0x30},
+ {0x32a7, 0x8f},
+ {0x32a8, 0x0f},
+ {0x32a9, 0xf9},
+ {0x32aa, 0x35},
+ {0x32ab, 0x0f},
+ {0x32ac, 0xee},
+ {0x32ad, 0x6e},
+ {0x32ae, 0x00},
+ {0x32af, 0x09},
+ {0x32b0, 0x19},
+ {0x32b1, 0x0f},
+ {0x32b2, 0xf0},
+ {0x32b3, 0x57},
+ {0x32b4, 0x00},
+ {0x32b5, 0x01},
+ {0x32b6, 0xcc},
+ {0x32b7, 0x0f},
+ {0x32b8, 0xf1},
+ {0x32b9, 0x0b},
+ {0x32ba, 0x0f},
+ {0x32bb, 0xee},
+ {0x32bc, 0x99},
+ {0x32bd, 0x00},
+ {0x32be, 0x11},
+ {0x32bf, 0x3d},
+ {0x32c0, 0x00},
+ {0x32c1, 0x10},
+ {0x32c2, 0x64},
+ {0x32c3, 0x0f},
+ {0x32c4, 0xf6},
+ {0x32c5, 0xab},
+ {0x32c6, 0x00},
+ {0x32c7, 0x03},
+ {0x32c8, 0x19},
+ {0x32c9, 0x0f},
+ {0x32ca, 0xf3},
+ {0x32cb, 0xc9},
+ {0x32cc, 0x00},
+ {0x32cd, 0x17},
+ {0x32ce, 0xb3},
+ {0x32cf, 0x0f},
+ {0x32d0, 0xf2},
+ {0x32d1, 0x3d},
+ {0x32d2, 0x0f},
+ {0x32d3, 0xf4},
+ {0x32d4, 0x7e},
+ {0x32d5, 0x00},
+ {0x32d6, 0x09},
+ {0x32d7, 0x46},
+ {0x32d8, 0x00},
+ {0x32d9, 0x7c},
+ {0x32da, 0x79},
+ {0x32db, 0x0f},
+ {0x32dc, 0xde},
+ {0x32dd, 0x19},
+ {0x32de, 0x00},
+ {0x32df, 0x19},
+ {0x32e0, 0xe8},
+ {0x32e1, 0x0f},
+ {0x32e2, 0xf3},
+ {0x32e3, 0x41},
+ {0x32e4, 0x00},
+ {0x32e5, 0x03},
+ {0x32e6, 0x4c},
+ {0x32e7, 0x00},
+ {0x32e8, 0x05},
+ {0x32e9, 0x73},
+ {0x32ea, 0x0f},
+ {0x32eb, 0xd6},
+ {0x32ec, 0xa5},
+ {0x32ed, 0x00},
+ {0x32ee, 0x1f},
+ {0x32ef, 0x81},
+ {0x32f0, 0x0f},
+ {0x32f1, 0xdc},
+ {0x32f2, 0xe6},
+ {0x32f3, 0x00},
+ {0x32f4, 0x18},
+ {0x32f5, 0x65},
+ {0x32f6, 0x00},
+ {0x32f7, 0x00},
+ {0x32f8, 0x11},
+ {0x32f9, 0x0f},
+ {0x32fa, 0xed},
+ {0x32fb, 0x65},
+ {0x32fc, 0x00},
+ {0x32fd, 0x23},
+ {0x32fe, 0x12},
+ {0x32ff, 0x0f},
+ {0x3300, 0xcf},
+ {0x3301, 0x28},
+ {0x3302, 0x00},
+ {0x3303, 0x2b},
+ {0x3304, 0xda},
+ {0x3305, 0x0f},
+ {0x3306, 0xef},
+ {0x3307, 0xae},
+ {0x3308, 0x0f},
+ {0x3309, 0xeb},
+ {0x330a, 0x13},
+ {0x330b, 0x00},
+ {0x330c, 0x27},
+ {0x330d, 0xb8},
+ {0x330e, 0x0f},
+ {0x330f, 0xec},
+ {0x3310, 0x69},
+ {0x3311, 0x00},
+ {0x3312, 0x2f},
+ {0x3313, 0x5f},
+ {0x3314, 0x0f},
+ {0x3315, 0xdf},
+ {0x3316, 0x4f},
+ {0x3317, 0x00},
+ {0x3318, 0x05},
+ {0x3319, 0x70},
+ {0x331a, 0x00},
+ {0x331b, 0x0f},
+ {0x331c, 0xd2},
+ {0x331d, 0x0f},
+ {0x331e, 0xe1},
+ {0x331f, 0xd8},
+ {0x3320, 0x00},
+ {0x3321, 0x09},
+ {0x3322, 0xcf},
+ {0x3323, 0x0f},
+ {0x3324, 0xf2},
+ {0x3325, 0x6e},
+ {0x3326, 0x0f},
+ {0x3327, 0xf6},
+ {0x3328, 0xb4},
+ {0x3329, 0x00},
+ {0x332a, 0x0d},
+ {0x332b, 0x87},
+ {0x332c, 0x00},
+ {0x332d, 0x08},
+ {0x332e, 0x1e},
+ {0x332f, 0x0f},
+ {0x3330, 0xfa},
+ {0x3331, 0x6e},
+ {0x3332, 0x0f},
+ {0x3333, 0xff},
+ {0x3334, 0xaa},
+ {0x3335, 0x0f},
+ {0x3336, 0xf2},
+ {0x3337, 0xc0},
+ {0x3338, 0x00},
+ {0x3339, 0x1d},
+ {0x333a, 0x18},
+ {0x333b, 0x0f},
+ {0x333c, 0xef},
+ {0x333d, 0xed},
+ {0x333e, 0x0f},
+ {0x333f, 0xec},
+ {0x3340, 0xf6},
+ {0x3341, 0x00},
+ {0x3342, 0x16},
+ {0x3343, 0x8e},
+ {0x3344, 0x00},
+ {0x3345, 0x9c},
+ {0x3346, 0x52},
+ {0x3347, 0x0f},
+ {0x3348, 0xcf},
+ {0x3349, 0xb9},
+ {0x334a, 0x00},
+ {0x334b, 0x29},
+ {0x334c, 0xe9},
+ {0x334d, 0x0f},
+ {0x334e, 0xe2},
+ {0x334f, 0x83},
+ {0x3350, 0x00},
+ {0x3351, 0x11},
+ {0x3352, 0xcc},
+ {0x3353, 0x0f},
+ {0x3354, 0xff},
+ {0x3355, 0xf4},
+ {0x3356, 0x0f},
+ {0x3357, 0xc1},
+ {0x3358, 0xa4},
+ {0x3359, 0x00},
+ {0x335a, 0x2f},
+ {0x335b, 0xce},
+ {0x335c, 0x0f},
+ {0x335d, 0xc5},
+ {0x335e, 0xbb},
+ {0x335f, 0x00},
+ {0x3360, 0x35},
+ {0x3361, 0x2a},
+ {0x3362, 0x0f},
+ {0x3363, 0xe6},
+ {0x3364, 0x2a},
+ {0x3365, 0x0f},
+ {0x3366, 0xf7},
+ {0x3367, 0x44},
+ {0x3368, 0x00},
+ {0x3369, 0x31},
+ {0x336a, 0xfe},
+ {0x336b, 0x0f},
+ {0x336c, 0xb6},
+ {0x336d, 0x84},
+ {0x336e, 0x00},
+ {0x336f, 0x3c},
+ {0x3370, 0x71},
+ {0x3371, 0x0f},
+ {0x3372, 0xe5},
+ {0x3373, 0xfe},
+ {0x3374, 0x0f},
+ {0x3375, 0xf2},
+ {0x3376, 0x87},
+ {0x3377, 0x00},
+ {0x3378, 0x29},
+ {0x3379, 0x2b},
+ {0x337a, 0x0f},
+ {0x337b, 0xe5},
+ {0x337c, 0x3f},
+ {0x337d, 0x00},
+ {0x337e, 0x45},
+ {0x337f, 0xc6},
+ {0x3380, 0x0f},
+ {0x3381, 0xdf},
+ {0x3382, 0xe6},
+ {0x3383, 0x0f},
+ {0x3384, 0xfb},
+ {0x3385, 0x0f},
+ {0x3386, 0x00},
+ {0x3387, 0x0f},
+ {0x3388, 0xf4},
+ {0x3389, 0x0f},
+ {0x338a, 0xdf},
+ {0x338b, 0x72},
+ {0x338c, 0x00},
+ {0x338d, 0x0e},
+ {0x338e, 0xaf},
+ {0x338f, 0x0f},
+ {0x3390, 0xed},
+ {0x3391, 0x7a},
+ {0x3392, 0x0f},
+ {0x3393, 0xe5},
+ {0x3394, 0xab},
+ {0x3395, 0x00},
+ {0x3396, 0x18},
+ {0x3397, 0x43},
+ {0x3398, 0x00},
+ {0x3399, 0x1b},
+ {0x339a, 0x41},
+ {0x339b, 0x0f},
+ {0x339c, 0xea},
+ {0x339d, 0x84},
+ {0x339e, 0x0f},
+ {0x339f, 0xfd},
+ {0x33a0, 0xdb},
+ {0x33a1, 0x0f},
+ {0x33a2, 0xe9},
+ {0x33a3, 0xbd},
+ {0x33a4, 0x00},
+ {0x33a5, 0x30},
+ {0x33a6, 0x77},
+ {0x33a7, 0x0f},
+ {0x33a8, 0xe9},
+ {0x33a9, 0x93},
+ {0x33aa, 0x0f},
+ {0x33ab, 0xd7},
+ {0x33ac, 0xde},
+ {0x33ad, 0x00},
+ {0x33ae, 0x2a},
+ {0x33af, 0x14},
+ {0x309D, 0x62},
+ {0x309d, 0x22},
+
+/* LC setting End */
+ },
+/*EVT5 */
+ {
+/* LC setting Start */
+ {0x3200, 0x00}, /* 100108 Modify LC setting DNP light source t75-r73*/
+ {0x3201, 0x99},
+ {0x3202, 0xc1},
+ {0x3203, 0x0f},
+ {0x3204, 0xd0},
+ {0x3205, 0x1b},
+ {0x3206, 0x00},
+ {0x3207, 0x24},
+ {0x3208, 0x8d},
+ {0x3209, 0x0f},
+ {0x320a, 0xee},
+ {0x320b, 0x0f},
+ {0x320c, 0x00},
+ {0x320d, 0x04},
+ {0x320e, 0x5c},
+ {0x320f, 0x00},
+ {0x3210, 0x07},
+ {0x3211, 0x68},
+ {0x3212, 0x0f},
+ {0x3213, 0xc2},
+ {0x3214, 0x82},
+ {0x3215, 0x00},
+ {0x3216, 0x29},
+ {0x3217, 0x3e},
+ {0x3218, 0x0f},
+ {0x3219, 0xd3},
+ {0x321a, 0x63},
+ {0x321b, 0x00},
+ {0x321c, 0x22},
+ {0x321d, 0x6c},
+ {0x321e, 0x0f},
+ {0x321f, 0xf8},
+ {0x3220, 0xce},
+ {0x3221, 0x0f},
+ {0x3222, 0xed},
+ {0x3223, 0x30},
+ {0x3224, 0x00},
+ {0x3225, 0x37},
+ {0x3226, 0x87},
+ {0x3227, 0x0f},
+ {0x3228, 0xc2},
+ {0x3229, 0x87},
+ {0x322a, 0x00},
+ {0x322b, 0x2a},
+ {0x322c, 0xc6},
+ {0x322d, 0x0f},
+ {0x322e, 0xf3},
+ {0x322f, 0xd9},
+ {0x3230, 0x0f},
+ {0x3231, 0xea},
+ {0x3232, 0x1a},
+ {0x3233, 0x00},
+ {0x3234, 0x2d},
+ {0x3235, 0x9f},
+ {0x3236, 0x0f},
+ {0x3237, 0xde},
+ {0x3238, 0x7d},
+ {0x3239, 0x00},
+ {0x323a, 0x37},
+ {0x323b, 0x1e},
+ {0x323c, 0x0f},
+ {0x323d, 0xed},
+ {0x323e, 0x9c},
+ {0x323f, 0x0f},
+ {0x3240, 0xf6},
+ {0x3241, 0xfd},
+ {0x3242, 0x00},
+ {0x3243, 0x15},
+ {0x3244, 0xeb},
+ {0x3245, 0x0f},
+ {0x3246, 0xd3},
+ {0x3247, 0xca},
+ {0x3248, 0x00},
+ {0x3249, 0x08},
+ {0x324a, 0xe6},
+ {0x324b, 0x0f},
+ {0x324c, 0xf4},
+ {0x324d, 0x7a},
+ {0x324e, 0x0f},
+ {0x324f, 0xed},
+ {0x3250, 0x1e},
+ {0x3251, 0x00},
+ {0x3252, 0x0d},
+ {0x3253, 0x46},
+ {0x3254, 0x00},
+ {0x3255, 0x0c},
+ {0x3256, 0x3e},
+ {0x3257, 0x00},
+ {0x3258, 0x09},
+ {0x3259, 0xcf},
+ {0x325a, 0x00},
+ {0x325b, 0x09},
+ {0x325c, 0xb5},
+ {0x325d, 0x0f},
+ {0x325e, 0xec},
+ {0x325f, 0x47},
+ {0x3260, 0x00},
+ {0x3261, 0x1d},
+ {0x3262, 0xd8},
+ {0x3263, 0x0f},
+ {0x3264, 0xf7},
+ {0x3265, 0x11},
+ {0x3266, 0x0f},
+ {0x3267, 0xea},
+ {0x3268, 0x3d},
+ {0x3269, 0x00},
+ {0x326a, 0x09},
+ {0x326b, 0xcc},
+ {0x326c, 0x00},
+ {0x326d, 0x99},
+ {0x326e, 0x45},
+ {0x326f, 0x0f},
+ {0x3270, 0xd3},
+ {0x3271, 0x80},
+ {0x3272, 0x00},
+ {0x3273, 0x20},
+ {0x3274, 0xf7},
+ {0x3275, 0x0f},
+ {0x3276, 0xef},
+ {0x3277, 0x0d},
+ {0x3278, 0x00},
+ {0x3279, 0x09},
+ {0x327a, 0x3c},
+ {0x327b, 0x00},
+ {0x327c, 0x01},
+ {0x327d, 0x16},
+ {0x327e, 0x0f},
+ {0x327f, 0xc9},
+ {0x3280, 0x36},
+ {0x3281, 0x00},
+ {0x3282, 0x21},
+ {0x3283, 0xff},
+ {0x3284, 0x0f},
+ {0x3285, 0xdc},
+ {0x3286, 0xc2},
+ {0x3287, 0x00},
+ {0x3288, 0x1e},
+ {0x3289, 0xc0},
+ {0x328a, 0x0f},
+ {0x328b, 0xf0},
+ {0x328c, 0xa7},
+ {0x328d, 0x0f},
+ {0x328e, 0xf9},
+ {0x328f, 0x2a},
+ {0x3290, 0x00},
+ {0x3291, 0x29},
+ {0x3292, 0x5c},
+ {0x3293, 0x0f},
+ {0x3294, 0xc9},
+ {0x3295, 0x2a},
+ {0x3296, 0x00},
+ {0x3297, 0x1f},
+ {0x3298, 0x5c},
+ {0x3299, 0x0f},
+ {0x329a, 0xfa},
+ {0x329b, 0x0c},
+ {0x329c, 0x0f},
+ {0x329d, 0xf3},
+ {0x329e, 0x94},
+ {0x329f, 0x00},
+ {0x32a0, 0x1c},
+ {0x32a1, 0xce},
+ {0x32a2, 0x0f},
+ {0x32a3, 0xed},
+ {0x32a4, 0xb7},
+ {0x32a5, 0x00},
+ {0x32a6, 0x34},
+ {0x32a7, 0x51},
+ {0x32a8, 0x0f},
+ {0x32a9, 0xfa},
+ {0x32aa, 0x7d},
+ {0x32ab, 0x0f},
+ {0x32ac, 0xe6},
+ {0x32ad, 0xbf},
+ {0x32ae, 0x00},
+ {0x32af, 0x18},
+ {0x32b0, 0xc6},
+ {0x32b1, 0x0f},
+ {0x32b2, 0xe0},
+ {0x32b3, 0x72},
+ {0x32b4, 0x00},
+ {0x32b5, 0x08},
+ {0x32b6, 0x23},
+ {0x32b7, 0x0f},
+ {0x32b8, 0xf1},
+ {0x32b9, 0x54},
+ {0x32ba, 0x0f},
+ {0x32bb, 0xe1},
+ {0x32bc, 0x84},
+ {0x32bd, 0x00},
+ {0x32be, 0x26},
+ {0x32bf, 0xb1},
+ {0x32c0, 0x0f},
+ {0x32c1, 0xfa},
+ {0x32c2, 0xc2},
+ {0x32c3, 0x00},
+ {0x32c4, 0x05},
+ {0x32c5, 0x3d},
+ {0x32c6, 0x0f},
+ {0x32c7, 0xff},
+ {0x32c8, 0xaf},
+ {0x32c9, 0x0f},
+ {0x32ca, 0xf1},
+ {0x32cb, 0xe5},
+ {0x32cc, 0x00},
+ {0x32cd, 0x21},
+ {0x32ce, 0xdd},
+ {0x32cf, 0x0f},
+ {0x32d0, 0xe8},
+ {0x32d1, 0x6a},
+ {0x32d2, 0x0f},
+ {0x32d3, 0xf4},
+ {0x32d4, 0xfb},
+ {0x32d5, 0x00},
+ {0x32d6, 0x0c},
+ {0x32d7, 0x89},
+ {0x32d8, 0x00},
+ {0x32d9, 0x7c},
+ {0x32da, 0x79},
+ {0x32db, 0x0f},
+ {0x32dc, 0xde},
+ {0x32dd, 0x19},
+ {0x32de, 0x00},
+ {0x32df, 0x19},
+ {0x32e0, 0xe8},
+ {0x32e1, 0x0f},
+ {0x32e2, 0xf3},
+ {0x32e3, 0x41},
+ {0x32e4, 0x00},
+ {0x32e5, 0x03},
+ {0x32e6, 0x4c},
+ {0x32e7, 0x00},
+ {0x32e8, 0x05},
+ {0x32e9, 0x73},
+ {0x32ea, 0x0f},
+ {0x32eb, 0xd6},
+ {0x32ec, 0xa5},
+ {0x32ed, 0x00},
+ {0x32ee, 0x1f},
+ {0x32ef, 0x81},
+ {0x32f0, 0x0f},
+ {0x32f1, 0xdc},
+ {0x32f2, 0xe6},
+ {0x32f3, 0x00},
+ {0x32f4, 0x18},
+ {0x32f5, 0x65},
+ {0x32f6, 0x00},
+ {0x32f7, 0x00},
+ {0x32f8, 0x11},
+ {0x32f9, 0x0f},
+ {0x32fa, 0xed},
+ {0x32fb, 0x65},
+ {0x32fc, 0x00},
+ {0x32fd, 0x23},
+ {0x32fe, 0x12},
+ {0x32ff, 0x0f},
+ {0x3300, 0xcf},
+ {0x3301, 0x28},
+ {0x3302, 0x00},
+ {0x3303, 0x2b},
+ {0x3304, 0xda},
+ {0x3305, 0x0f},
+ {0x3306, 0xef},
+ {0x3307, 0xae},
+ {0x3308, 0x0f},
+ {0x3309, 0xeb},
+ {0x330a, 0x13},
+ {0x330b, 0x00},
+ {0x330c, 0x27},
+ {0x330d, 0xb8},
+ {0x330e, 0x0f},
+ {0x330f, 0xec},
+ {0x3310, 0x69},
+ {0x3311, 0x00},
+ {0x3312, 0x2f},
+ {0x3313, 0x5f},
+ {0x3314, 0x0f},
+ {0x3315, 0xdf},
+ {0x3316, 0x4f},
+ {0x3317, 0x00},
+ {0x3318, 0x05},
+ {0x3319, 0x70},
+ {0x331a, 0x00},
+ {0x331b, 0x0f},
+ {0x331c, 0xd2},
+ {0x331d, 0x0f},
+ {0x331e, 0xe1},
+ {0x331f, 0xd8},
+ {0x3320, 0x00},
+ {0x3321, 0x09},
+ {0x3322, 0xcf},
+ {0x3323, 0x0f},
+ {0x3324, 0xf2},
+ {0x3325, 0x6e},
+ {0x3326, 0x0f},
+ {0x3327, 0xf6},
+ {0x3328, 0xb4},
+ {0x3329, 0x00},
+ {0x332a, 0x0d},
+ {0x332b, 0x87},
+ {0x332c, 0x00},
+ {0x332d, 0x08},
+ {0x332e, 0x1e},
+ {0x332f, 0x0f},
+ {0x3330, 0xfa},
+ {0x3331, 0x6e},
+ {0x3332, 0x0f},
+ {0x3333, 0xff},
+ {0x3334, 0xaa},
+ {0x3335, 0x0f},
+ {0x3336, 0xf2},
+ {0x3337, 0xc0},
+ {0x3338, 0x00},
+ {0x3339, 0x1d},
+ {0x333a, 0x18},
+ {0x333b, 0x0f},
+ {0x333c, 0xef},
+ {0x333d, 0xed},
+ {0x333e, 0x0f},
+ {0x333f, 0xec},
+ {0x3340, 0xf6},
+ {0x3341, 0x00},
+ {0x3342, 0x16},
+ {0x3343, 0x8e},
+ {0x3344, 0x00},
+ {0x3345, 0x9c},
+ {0x3346, 0x52},
+ {0x3347, 0x0f},
+ {0x3348, 0xcf},
+ {0x3349, 0xb9},
+ {0x334a, 0x00},
+ {0x334b, 0x29},
+ {0x334c, 0xe9},
+ {0x334d, 0x0f},
+ {0x334e, 0xe2},
+ {0x334f, 0x83},
+ {0x3350, 0x00},
+ {0x3351, 0x11},
+ {0x3352, 0xcc},
+ {0x3353, 0x0f},
+ {0x3354, 0xff},
+ {0x3355, 0xf4},
+ {0x3356, 0x0f},
+ {0x3357, 0xc1},
+ {0x3358, 0xa4},
+ {0x3359, 0x00},
+ {0x335a, 0x2f},
+ {0x335b, 0xce},
+ {0x335c, 0x0f},
+ {0x335d, 0xc5},
+ {0x335e, 0xbb},
+ {0x335f, 0x00},
+ {0x3360, 0x35},
+ {0x3361, 0x2a},
+ {0x3362, 0x0f},
+ {0x3363, 0xe6},
+ {0x3364, 0x2a},
+ {0x3365, 0x0f},
+ {0x3366, 0xf7},
+ {0x3367, 0x44},
+ {0x3368, 0x00},
+ {0x3369, 0x31},
+ {0x336a, 0xfe},
+ {0x336b, 0x0f},
+ {0x336c, 0xb6},
+ {0x336d, 0x84},
+ {0x336e, 0x00},
+ {0x336f, 0x3c},
+ {0x3370, 0x71},
+ {0x3371, 0x0f},
+ {0x3372, 0xe5},
+ {0x3373, 0xfe},
+ {0x3374, 0x0f},
+ {0x3375, 0xf2},
+ {0x3376, 0x87},
+ {0x3377, 0x00},
+ {0x3378, 0x29},
+ {0x3379, 0x2b},
+ {0x337a, 0x0f},
+ {0x337b, 0xe5},
+ {0x337c, 0x3f},
+ {0x337d, 0x00},
+ {0x337e, 0x45},
+ {0x337f, 0xc6},
+ {0x3380, 0x0f},
+ {0x3381, 0xdf},
+ {0x3382, 0xe6},
+ {0x3383, 0x0f},
+ {0x3384, 0xfb},
+ {0x3385, 0x0f},
+ {0x3386, 0x00},
+ {0x3387, 0x0f},
+ {0x3388, 0xf4},
+ {0x3389, 0x0f},
+ {0x338a, 0xdf},
+ {0x338b, 0x72},
+ {0x338c, 0x00},
+ {0x338d, 0x0e},
+ {0x338e, 0xaf},
+ {0x338f, 0x0f},
+ {0x3390, 0xed},
+ {0x3391, 0x7a},
+ {0x3392, 0x0f},
+ {0x3393, 0xe5},
+ {0x3394, 0xab},
+ {0x3395, 0x00},
+ {0x3396, 0x18},
+ {0x3397, 0x43},
+ {0x3398, 0x00},
+ {0x3399, 0x1b},
+ {0x339a, 0x41},
+ {0x339b, 0x0f},
+ {0x339c, 0xea},
+ {0x339d, 0x84},
+ {0x339e, 0x0f},
+ {0x339f, 0xfd},
+ {0x33a0, 0xdb},
+ {0x33a1, 0x0f},
+ {0x33a2, 0xe9},
+ {0x33a3, 0xbd},
+ {0x33a4, 0x00},
+ {0x33a5, 0x30},
+ {0x33a6, 0x77},
+ {0x33a7, 0x0f},
+ {0x33a8, 0xe9},
+ {0x33a9, 0x93},
+ {0x33aa, 0x0f},
+ {0x33ab, 0xd7},
+ {0x33ac, 0xde},
+ {0x33ad, 0x00},
+ {0x33ae, 0x2a},
+ {0x33af, 0x14},
+ {0x309D, 0x62},
+ {0x309d, 0x22}, /* shading enable */
+ /*LC setting End */
+ }
+}; /* lc_setting} */
+
+static struct wake_lock s5k3e2fx_wake_lock;
+
+static inline void init_suspend(void)
+{
+ wake_lock_init(&s5k3e2fx_wake_lock, WAKE_LOCK_IDLE, "s5k3e2fx");
+}
+
+static inline void deinit_suspend(void)
+{
+ wake_lock_destroy(&s5k3e2fx_wake_lock);
+}
+
+static inline void prevent_suspend(void)
+{
+ wake_lock(&s5k3e2fx_wake_lock);
+}
+
+static inline void allow_suspend(void)
+{
+ wake_unlock(&s5k3e2fx_wake_lock);
+}
+
+struct reg_struct {
+/* PLL setting */
+ uint8_t pre_pll_clk_div; /* 0x0305 */
+ uint8_t pll_multiplier_msb; /* 0x0306 */
+ uint8_t pll_multiplier_lsb; /* 0x0307 */
+ uint8_t vt_pix_clk_div; /* 0x0301 */
+ uint8_t vt_sys_clk_div; /* 0x0303 */
+ uint8_t op_pix_clk_div; /* 0x0309 */
+ uint8_t op_sys_clk_div; /* 0x030B */
+/* Data Format */
+ uint8_t ccp_data_format_msb; /* 0x0112 */
+ uint8_t ccp_data_format_lsb; /* 0x0113 */
+/* Preview Output Size */
+ uint8_t x_output_size_msb; /* 0x034C */
+ uint8_t x_output_size_lsb; /* 0x034D */
+ uint8_t y_output_size_msb; /* 0x034E */
+ uint8_t y_output_size_lsb; /* 0x034F */
+/* add the X-Y addr setting position */
+ uint8_t x_addr_start_MSB; /* 0x0344 */
+ uint8_t x_addr_start_LSB; /* 0x0345 */
+ uint8_t y_addr_start_MSB; /* 0x0346 */
+ uint8_t y_addr_start_LSB; /* 0x0347 */
+ uint8_t x_addr_end_MSB; /* 0x0348 */
+ uint8_t x_addr_end_LSB; /* 0x0349 */
+ uint8_t y_addr_end_MSB; /* 0x034A */
+ uint8_t y_addr_end_LSB; /* 0x034B */
+/* change the setting position */
+/* Frame format */
+ uint8_t frame_length_lines_msb; /* 0x0340 */
+ uint8_t frame_length_lines_lsb; /* 0x0341 */
+ uint8_t line_length_pck_msb; /* 0x0342 */
+ uint8_t line_length_pck_lsb; /* 0x0343 */
+/* binning */
+ uint8_t x_even_inc; /* 0x0381 */
+ uint8_t x_odd_inc; /* 0x0383 */
+ uint8_t y_even_inc; /* 0x0385 */
+ uint8_t y_odd_inc; /* 0x0387 */
+ uint8_t binning_enable; /* 0x3014 */
+/* Samsung MSR Setting */
+ uint8_t sel_ccp; /* 0x30C4 */
+ uint8_t ld_start; /* 0x3000 */
+ uint8_t ld_end; /* 0x3001 */
+ uint8_t sl_start; /* 0x3002 */
+ uint8_t sl_end; /* 0x3003 */
+ uint8_t rx_start; /* 0x3004 */
+ uint8_t s1_start; /* 0x3005 */
+ uint8_t s1_end; /* 0x3006 */
+ uint8_t s1s_start; /* 0x3007 */
+ uint8_t s1s_end; /* 0x3008 */
+ uint8_t s3_start; /* 0x3009 */
+ uint8_t s3_end; /* 0x300A */
+ uint8_t cmp_en_start; /* 0x300B */
+ uint8_t clp_sl_start; /* 0x300C */
+ uint8_t clp_sl_end; /* 0x300D */
+ uint8_t off_start; /* 0x300E */
+ uint8_t rmp_en_start; /* 0x300F */
+ uint8_t tx_start; /* 0x3010 */
+ uint8_t tx_end; /* 0x3011 */
+ uint8_t stx_width; /* 0x3012 */
+/* Samsung other MSR setting */
+ uint8_t clamp_on; /* 0x3015 */
+ uint8_t reg_301d_reserved; /* 0x301D */
+ uint8_t vpix; /* 0x3024 */
+ uint8_t reg_3028_reserved; /* 0x3028 */
+ uint8_t reg_3070_reserved; /* 0x3070 */
+ uint8_t reg_3072_reserved; /* 0x3072 */
+ uint8_t reg_301b_reserved; /* 0x301B */
+ uint8_t offset; /* 0x307E */
+ uint8_t reg_30bd_reserved; /* 0x30BD */
+ uint8_t reg_30c2_reserved; /* 0x30C2 */
+ uint8_t shade_clk_enable; /* 0x30AC */
+ uint8_t reg_3051_reserved; /* 0x3051 */
+ uint8_t reg_3029_reserved; /* 0x3029 */
+ uint8_t reg_30bf_reserved; /* 0x30BF */
+ uint8_t reg_3022_reserved; /* 0x3022 */
+ uint8_t reg_3019_reserved; /* 0x3019 */
+/* end: Samsung other MSR setting */
+ uint8_t reg_3152_reserved; /* 0x3152 */
+/* Samsung signal output setting */
+ uint8_t reg_3150_reserved; /* 0x3150 */
+ uint8_t reg_3157_reserved; /* 0x3157 */
+ uint8_t reg_3159_reserved; /* 0x3159 */
+/* end: Samsung signal output setting */
+ uint8_t reg_315A_reserved; /* 0x315A */
+/* AEC Setting */
+ uint8_t analogue_gain_code_global_msb; /* 0x0204 */
+ uint8_t analogue_gain_code_global_lsb; /* 0x0205 */
+ uint8_t fine_integration_time; /* 0x0200 */
+ uint8_t coarse_integration_time; /* 0x0202 */
+/* LC Preview/Snapshot difference register */
+/* Preview LC Setting */
+ uint8_t sh4ch_blk_width_r; /* 0x309E */
+ uint8_t sh4ch_blk_height_r; /* 0x309F */
+ uint8_t sh4ch_step_x_r_MSB; /* 0x30A0 */
+ uint8_t sh4ch_step_x_r_LSB; /* 0x30A1 */
+ uint8_t sh4ch_step_y_r_MSB; /* 0x30A2 */
+ uint8_t sh4ch_step_y_r_LSB; /* 0x30A3 */
+ uint8_t sh4ch_start_blk_cnt_x_r; /* 0x30A4 */
+ uint8_t sh4ch_start_blk_int_x_r; /* 0x30A5 */
+ uint8_t sh4ch_start_frac_x_r_MSB; /* 0x30A6 */
+ uint8_t sh4ch_start_frac_x_r_LSB; /* 0x30A7 */
+ uint8_t sh4ch_start_blk_cnt_y_r; /* 0x30A8 */
+ uint8_t sh4ch_start_blk_int_y_r; /* 0x30A9 */
+ uint8_t sh4ch_start_frac_y_r_MSB; /* 0x30AA */
+ uint8_t sh4ch_start_frac_y_r_LSB; /* 0x30AB */
+/* end: LC Preview/Snapshot difference register */
+ uint32_t size_h;
+ uint32_t blk_l;
+ uint32_t size_w;
+ uint32_t blk_p;
+};
+
+struct reg_struct s5k3e2fx_reg_pat[2] = {
+ { /* Preview */
+/* PLL setting */
+ 0x06, /* pre_pll_clk_div REG=0x0305 */
+ 0x00, /* pll_multiplier_msb REG=0x0306 */
+ REG_PLL_MULTIPLIER_LSB_VALUE,
+ /* pll_multiplier_lsb REG=0x0307 */
+ 0x08, /* vt_pix_clk_div REG=0x0301 */
+ 0x01, /* vt_sys_clk_div REG=0x0303 */
+ 0x08, /* op_pix_clk_div REG=0x0309 */
+ 0x01, /* op_sys_clk_div REG=0x030B */
+/* Data Format */
+ 0x0a, /* ccp_data_format_msb REG=0x0112 */
+ 0x0a, /* ccp_data_format_lsb REG=0x0113 */
+/* Preview Output Size */
+ 0x05, /* x_output_size_msb REG=0x034C */
+ 0x10, /* x_output_size_lsb REG=0x034D */
+ 0x03, /* y_output_size_msb REG=0x034E */
+ 0xcc, /* y_output_size_lsb REG=0x034F */
+/* X-Y addr setting position. Start */
+ 0x00, /* x_addr_start_MSB REG=0x0344 */
+ 0x08, /* x_addr_start_LSB REG=0x0345 */
+ 0x00, /* y_addr_start_MSB REG=0x0346 */
+ 0x08, /* y_addr_start_LSB REG=0x0347 */
+ 0x0a, /* x_addr_end_MSB REG=0x0348 */
+ 0x27, /* x_addr_end_LSB REG=0x0349 */
+ 0x07, /* y_addr_end_MSB REG=0x034A */
+ 0x9f, /* y_addr_end_LSB REG=0x034B */
+/* change the setting position */
+/* Frame format */
+ 0x03, /* frame_length_lines_msb REG=0x0340 */
+ 0xe2, /* frame_length_lines_lsb REG=0x0341 */
+ 0x0a, /* line_length_pck_msb REG=0x0342 */
+ 0xac, /* line_length_pck_lsb REG=0x0343 */
+/* enable binning for preview */
+ 0x01, /* x_even_inc REG=0x0381 */
+ 0x01, /* x_odd_inc REG=0x0383 */
+ 0x01, /* y_even_inc REG=0x0385 */
+ 0x03, /* y_odd_inc REG=0x0387 */
+ 0x06, /* binning_enable REG=0x3014 */
+/* Samsung MSR Setting */
+ 0x01, /* sel_ccp REG=0x30C4 */
+ 0x03, /* ld_start REG=0x3000 */
+ 0x94, /* ld_end REG=0x3001 */
+ 0x02, /* sl_start REG=0x3002 */
+ 0x95, /* sl_end REG=0x3003 */
+ 0x0f, /* rx_start REG=0x3004 */
+ 0x05, /* s1_start REG=0x3005 */
+ 0x3c, /* s1_end REG=0x3006 */
+ 0x8c, /* s1s_start REG=0x3007 */
+ 0x93, /* s1s_end REG=0x3008 */
+ 0x05, /* s3_start REG=0x3009 */
+ 0x3a, /* s3_end REG=0x300A */
+ 0x10, /* cmp_en_start REG=0x300B */
+ 0x02, /* clp_sl_start REG=0x300C */
+ 0x3e, /* clp_sl_end REG=0x300D */
+ 0x02, /* off_start REG=0x300E */
+ 0x0e, /* rmp_en_start REG=0x300F */
+ 0x46, /* tx_start REG=0x3010 */
+ 0x64, /* tx_end REG=0x3011 */
+ 0x1e, /* stx_width REG=0x3012 */
+/* Samsung other MSR setting. */
+ 0x00, /* clamp_on REG=0x3015 */
+ 0x3f, /* reg_301d_reserved REG=0x301D */
+ 0x04, /* vpix REG=0x3024 */
+ 0x40, /* reg_3028_reserved REG=0x3028 */
+ 0xdf, /* reg_3070_reserved REG=0x3070 */
+ 0x20, /* reg_3072_reserved REG=0x3072 */
+ 0x73, /* reg_3073_reserved REG=0x301B */
+ 0x02, /* offset REG=0x307E */
+ 0x06, /* reg_30bd_reserved REG=0x30BD */
+ 0x0b, /* reg_30c2_reserved REG=0x30C2 */
+ 0x81, /* shade_clk_enable REG=0x30AC */
+ 0xe6, /* reg_3051_reserved REG=0x3051 */
+ 0x02, /* reg_3029_reserved REG=0x3029 */
+ 0x00, /* reg_30bf_reserved REG=0x30BF */
+ 0x87, /* reg_3022_reserved REG=0x3022 */
+ 0x60, /* reg_3019_reserved REG=0x3019 */
+/* end: Samsung other MSR setting. */
+ 0x08, /* reg_3152_reserved REG=0x3152 */
+ 0x50, /* reg_3150_reserved REG=0x3150 */
+/* Inverse PCLK */
+ 0x04, /* reg_3157_reserved REG=0x3157 */
+/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */
+ 0x0f, /* reg_3159_reserved REG=0x3159 */
+/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+ 0xf0, /* reg_315A_reserved REG=0x315A */
+/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+/* AEC Setting */
+ 0x00, /* analogue_gain_code_global_msb REG=0x0204 */
+ REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE,
+ /* analogue_gain_code_global_lsb REG=0x0205 */
+ 0x02, /* fine_integration_time REG=0x0200 */
+ 0x03, /* coarse_integration_time REG=0x0202 */
+/* LC Preview/Snapshot difference register. */
+/* Preview LC config Setting */
+ 0x52, /* sh4ch_blk_width_r REG=0x309E */
+ 0x3e, /* sh4ch_blk_height_r REG=0x309F */
+ 0x03, /* sh4ch_step_x_r_MSB REG=0x30A0 */
+ 0x1f, /* sh4ch_step_x_r_LSB REG=0x30A1 */
+ 0x04, /* sh4ch_step_y_r_MSB REG=0x30A2 */
+ 0x21, /* sh4ch_step_y_r_LSB REG=0x30A3 */
+ 0x04, /* sh4ch_start_blk_cnt_x_r REG=0x30A4 */
+ 0x00, /* sh4ch_start_blk_int_x_r REG=0x30A5 */
+ 0x0c, /* sh4ch_start_frac_x_r_MSB REG=0x30A6 */
+ 0x7c, /* sh4ch_start_frac_x_r_LSB REG=0x30A7 */
+ 0x04, /* sh4ch_start_blk_cnt_y_r REG=0x30A8 */
+ 0x00, /* sh4ch_start_blk_int_y_r REG=0x30A9 */
+ 0x10, /* sh4ch_start_frac_y_r_MSB REG=0x30AA */
+ 0x84, /* sh4ch_start_frac_y_r_LSB REG=0x30AB */
+/* end: LC Preview/Snapshot difference register. */
+ S5K3E2FX_QTR_SIZE_HEIGHT,
+ 18,
+ S5K3E2FX_QTR_SIZE_WIDTH,
+ 1436},
+ { /* Snapshot */
+/* PLL setting */
+ 0x06, /* pre_pll_clk_div REG=0x0305 */
+ 0x00, /* pll_multiplier_msb REG=0x0306 */
+ REG_PLL_MULTIPLIER_LSB_VALUE,
+ /* pll_multiplier_lsb REG=0x0307 */
+ 0x08, /* vt_pix_clk_div REG=0x0301 */
+ 0x01, /* vt_sys_clk_div REG=0x0303 */
+ 0x08, /* op_pix_clk_div REG=0x0309 */
+ 0x01, /* op_sys_clk_div REG=0x030B */
+/* Data Format */
+ 0x0a, /* ccp_data_format_msb REG=0x0112 */
+ 0x0a, /* ccp_data_format_lsb REG=0x0113 */
+/* Snapshot Output Size */
+ 0x0a, /* x_output_size_msb REG=0x034C */
+ 0x30, /* x_output_size_lsb REG=0x034D */
+ 0x07, /* y_output_size_msb REG=0x034E */
+ 0xa8, /* y_output_size_lsb REG=0x034F */
+/* add the X-Y addr setting position. */
+ 0x00, /* x_addr_start_MSB REG=0x0344 */
+ 0x00, /* x_addr_start_LSB REG=0x0345 */
+ 0x00, /* y_addr_start_MSB REG=0x0346 */
+ 0x00, /* y_addr_start_LSB REG=0x0347 */
+ 0x0a, /* x_addr_end_MSB REG=0x0348 */
+ 0x2F, /* x_addr_end_LSB REG=0x0349 */
+ 0x07, /* y_addr_end_MSB REG=0x034A */
+ 0xA7, /* y_addr_end_LSB REG=0x034B */
+/* Change the setting position. */
+/* Frame format */
+ 0x07, /* frame_length_lines_msb REG=0x0340 */
+ 0xb6, /* frame_length_lines_lsb REG=0x0341 */
+ 0x0a, /* line_length_pck_msb REG=0x0342 */
+ 0xac, /* line_length_pck_lsb REG=0x0343 */
+/* disable binning for snapshot */
+ 0x01, /* x_even_inc REG=0x0381 */
+ 0x01, /* x_odd_inc REG=0x0383 */
+ 0x01, /* y_even_inc REG=0x0385 */
+ 0x01, /* y_odd_inc REG=0x0387 */
+ 0x00, /* binning_enable REG=0x3014 */
+/* Samsung MSR Setting */
+ 0x01, /* sel_ccp REG=0x30C4 */
+ 0x03, /* ld_start REG=0x3000 */
+ 0x94, /* ld_end REG=0x3001 */
+ 0x02, /* sl_start REG=0x3002 */
+ 0x95, /* sl_end REG=0x3003 */
+ 0x0f, /* rx_start REG=0x3004 */
+ 0x05, /* s1_start REG=0x3005 */
+ 0x3c, /* s1_end REG=0x3006 */
+ 0x8c, /* s1s_start REG=0x3007 */
+ 0x93, /* s1s_end REG=0x3008 */
+ 0x05, /* s3_start REG=0x3009 */
+ 0x3a, /* s3_end REG=0x300A */
+ 0x10, /* cmp_en_start REG=0x300B */
+ 0x02, /* clp_sl_start REG=0x300C */
+ 0x3e, /* clp_sl_end REG=0x300D */
+ 0x02, /* off_start REG=0x300E */
+ 0x0e, /* rmp_en_start REG=0x300F */
+ 0x46, /* tx_start REG=0x3010 */
+ 0x64, /* tx_end REG=0x3011 */
+ 0x1e, /* stx_width REG=0x3012 */
+/* Add Samsung other MSR setting. */
+ 0x00, /* clamp_on REG=0x3015 */
+ 0x3f, /* reg_301d_reserved REG=0x301D */
+ 0x04, /* vpix REG=0x3024 */
+ 0x40, /* reg_3028_reserved REG=0x3028 */
+ 0xdf, /* reg_3070_reserved REG=0x3070 */
+ 0x20, /* reg_3072_reserved REG=0x3072 */
+ 0x73, /* reg_3073_reserved REG=0x301B */
+ 0x02, /* offset REG=0x307E */
+ 0x06, /* reg_30bd_reserved REG=0x30BD */
+ 0x0b, /* reg_30c2_reserved REG=0x30C2 */
+ 0x81, /* shade_clk_enable REG=0x30AC */
+ 0xe6, /* reg_3051_reserved REG=0x3051 */
+ 0x02, /* reg_3029_reserved REG=0x3029 */
+ 0x00, /* reg_30bf_reserved REG=0x30BF */
+ 0x87, /* reg_3022_reserved REG=0x3022 */
+ 0x60, /* reg_3019_reserved REG=0x3019 */
+/* end: Add Samsung other MSR setting. */
+ 0x08, /* reg_3152_reserved REG=0x3152 */
+/* Add Samsung signal output setting. */
+ 0x50, /* reg_3150_reserved REG=0x3150 */
+/* Inverse PCLK = 0x50 */
+ 0x04, /* reg_3157_reserved REG=0x3157 */
+/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */
+ 0x0f, /* reg_3159_reserved REG=0x3159 */
+/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+ 0xf0, /* reg_315A_reserved REG=0x315A */
+/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA, 10:6mA,
+ * 11:8mA
+ */
+/* AEC Setting */
+ 0x00, /* analogue_gain_code_global_msb REG=0x0204 */
+ REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE,
+ /* analogue_gain_code_global_lsb REG=0x0205 */
+ 0x02, /* fine_integration_time REG=0x0200 */
+ 0x03, /* coarse_integration_time REG=0x0202 */
+/* Add LC Preview/Snapshot diff register. */
+/* Snapshot LC config Setting */
+ 0x52, /* sh4ch_blk_width_r REG=0x309E */
+ 0x7b, /* sh4ch_blk_height_r REG=0x309F */
+ 0x03, /* sh4ch_step_x_r_MSB REG=0x30A0 */
+ 0x1f, /* sh4ch_step_x_r_LSB REG=0x30A1 */
+ 0x02, /* sh4ch_step_y_r_MSB REG=0x30A2 */
+ 0x15, /* sh4ch_step_y_r_LSB REG=0x30A3 */
+ 0x00, /* sh4ch_start_blk_cnt_x_r REG=0x30A4 */
+ 0x00, /* sh4ch_start_blk_int_x_r REG=0x30A5 */
+ 0x00, /* sh4ch_start_frac_x_r_MSB REG=0x30A6 */
+ 0x00, /* sh4ch_start_frac_x_r_LSB REG=0x30A7 */
+ 0x00, /* sh4ch_start_blk_cnt_y_r REG=0x30A8 */
+ 0x00, /* sh4ch_start_blk_int_y_r REG=0x30A9 */
+ 0x00, /* sh4ch_start_frac_y_r_MSB REG=0x30AA */
+ 0x00, /* sh4ch_start_frac_y_r_LSB REG=0x30AB */
+/* diff: Add LC Preview/Snapshot diff register. */
+ S5K3E2FX_FULL_SIZE_HEIGHT,
+ 14,
+ S5K3E2FX_FULL_SIZE_WIDTH,
+ 124}
+};
+
+struct s5k3e2fx_work {
+ struct work_struct work;
+};
+static struct s5k3e2fx_work *s5k3e2fx_sensorw;
+static struct i2c_client *s5k3e2fx_client;
+
+struct s5k3e2fx_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum msm_s_resolution prev_res;
+ enum msm_s_resolution pict_res;
+ enum msm_s_resolution curr_res;
+ enum msm_s_test_mode set_test;
+};
+
+static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue);
+
+#define MAX_I2C_RETRIES 20
+static int i2c_transfer_retry(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int len)
+{
+ int i2c_retry = 0;
+ int ns; /* number sent */
+
+ while (i2c_retry++ < MAX_I2C_RETRIES) {
+ ns = i2c_transfer(adap, msgs, len);
+ if (ns == len)
+ break;
+ pr_err("%s: try %d/%d: i2c_transfer sent: %d, len %d\n",
+ __func__,
+ i2c_retry, MAX_I2C_RETRIES, ns, len);
+ msleep(10);
+ }
+
+ return ns == len ? 0 : -EIO;
+}
+
+static inline int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ return i2c_transfer_retry(s5k3e2fx_client->adapter, msgs, 2);
+}
+
+static inline int s5k3e2fx_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ return i2c_transfer_retry(s5k3e2fx_client->adapter, msg, 1);
+}
+
+static int s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr,
+ unsigned char bdata)
+{
+ int rc = -EFAULT;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+
+ rc = s5k3e2fx_i2c_txdata(saddr, buf, 3);
+
+ if (rc < 0)
+ pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+
+ return rc;
+}
+
+static int s5k3e2fx_i2c_write_table(struct s5k3e2fx_i2c_reg_conf
+ *reg_cfg_tbl, int num)
+{
+ int i;
+ int rc = -EFAULT;
+ CDBG("s5k3e2fx_i2c_write_table starts\n");
+ for (i = 0; i < num; i++) {
+ CDBG("%d: waddr = 0x%x, bdata = 0x%x\n", i,
+ (int)reg_cfg_tbl->waddr, (int)reg_cfg_tbl->bdata);
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ reg_cfg_tbl->waddr,
+ reg_cfg_tbl->bdata);
+ if (rc < 0)
+ break;
+ reg_cfg_tbl++;
+ }
+
+ CDBG("s5k3e2fx_i2c_write_table ends\n");
+ return rc;
+}
+
+static int s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr,
+ unsigned short *rdata)
+{
+ int rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ pr_err("s5k3e2fx_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int s5k3e2fx_i2c_read_b(unsigned short saddr, unsigned short raddr,
+ unsigned short *rdata)
+{
+ int rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = s5k3e2fx_i2c_rxdata(saddr, buf, 1);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0];
+
+ if (rc < 0)
+ pr_err("s5k3e2fx_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+ uint16_t chipid = 0;
+ uint16_t modulever = 0;
+
+ CDBG("s5k3e2fx: gpio_request: %d\n", data->sensor_reset);
+ rc = gpio_request(data->sensor_reset, "s5k3e2fx");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else {
+ pr_err("s5k3e2fx: request GPIO(sensor_reset): %d failed\n",
+ data->sensor_reset);
+ goto init_probe_fail;
+ }
+ CDBG("s5k3e2fx: gpio_free: %d\n", data->sensor_reset);
+
+ gpio_free(data->sensor_reset);
+
+ msleep(20);
+
+ CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n");
+
+ rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, S5K3E2FX_REG_MODEL_ID,
+ &chipid);
+ if (rc < 0) {
+ pr_err("s5k3e2fx: read model_id failed: %d\n", rc);
+ goto init_probe_fail;
+ }
+ CDBG("s5k3e2fx_sensor_init(): model_id=0x%X\n", chipid);
+
+ if (chipid != S5K3E2FX_MODEL_ID) {
+ pr_err("S5K3E2FX wrong model_id = 0x%x\n", chipid);
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODULE_VER, &modulever);
+ if (rc < 0) {
+ pr_err("S5K3E2FX read module version failed, line=%d\n",
+ __LINE__);
+ goto init_probe_fail;
+ }
+ /* modulever = (0xF000 & modulever) >> 8; */
+ modulever = 0x00F0 & modulever;
+ CDBG("s5k3e2fx_sensor_init(): module version=0x%X\n", modulever);
+
+ if (modulever == 0x40)
+ g_usModuleVersion = 0;
+ else if (modulever == 0x50)
+ g_usModuleVersion = 1;
+ goto init_probe_done;
+
+init_probe_fail:
+ pr_err("s5k3e2fx: prob init sensor failed\n");
+init_probe_done:
+ return rc;
+}
+
+static int s5k3e2fx_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&s5k3e2fx_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id s5k3e2fx_i2c_id[] = {
+ {"s5k3e2fx", 0},
+ {}
+};
+
+static int s5k3e2fx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("s5k3e2fx_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL);
+ if (!s5k3e2fx_sensorw) {
+ pr_err("kzalloc failed\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, s5k3e2fx_sensorw);
+ s5k3e2fx_init_client(client);
+ s5k3e2fx_client = client;
+
+ msleep(50);
+
+ CDBG("s5k3e2fx_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ pr_err("s5k3e2fx_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static struct i2c_driver s5k3e2fx_i2c_driver = {
+ .id_table = s5k3e2fx_i2c_id,
+ .probe = s5k3e2fx_i2c_probe,
+ .driver = {
+ .name = "s5k3e2fx",
+ },
+};
+
+#if 0
+static int s5k3e2fx_test(enum msm_s_test_mode mo)
+{
+ int rc = 0;
+
+ if (mo == S_TEST_OFF)
+ rc = 0;
+ else
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t) mo);
+
+ return rc;
+}
+#endif
+static int s5k3e2fx_setting(enum msm_s_reg_update rupdate,
+ enum msm_s_setting rt)
+{
+ int rc = 0;
+ uint16_t num_lperf;
+
+ switch (rupdate) {
+ case S_UPDATE_PERIODIC:{
+ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+ struct s5k3e2fx_i2c_reg_conf tbl_1[] = {
+ {REG_X_OUTPUT_SIZE_MSB,
+ s5k3e2fx_reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ s5k3e2fx_reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ s5k3e2fx_reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB,
+ s5k3e2fx_reg_pat[rt].
+ y_output_size_lsb},
+ /* Start-End address */
+ {REG_X_ADDR_START_MSB,
+ s5k3e2fx_reg_pat[rt].x_addr_start_MSB},
+ {REG_X_ADDR_START_LSB,
+ s5k3e2fx_reg_pat[rt].x_addr_start_LSB},
+ {REG_Y_ADDR_START_MSB,
+ s5k3e2fx_reg_pat[rt].y_addr_start_MSB},
+ {REG_Y_ADDR_START_LSB,
+ s5k3e2fx_reg_pat[rt].y_addr_start_LSB},
+ {REG_X_ADDR_END_MSB,
+ s5k3e2fx_reg_pat[rt].x_addr_end_MSB},
+ {REG_X_ADDR_END_LSB,
+ s5k3e2fx_reg_pat[rt].x_addr_end_LSB},
+ {REG_Y_ADDR_END_MSB,
+ s5k3e2fx_reg_pat[rt].y_addr_end_MSB},
+ {REG_Y_ADDR_END_LSB,
+ s5k3e2fx_reg_pat[rt].y_addr_end_LSB},
+ /* Binning */
+ {REG_X_EVEN_INC,
+ s5k3e2fx_reg_pat[rt].x_even_inc},
+ {REG_X_ODD_INC,
+ s5k3e2fx_reg_pat[rt].x_odd_inc},
+ {REG_Y_EVEN_INC,
+ s5k3e2fx_reg_pat[rt].y_even_inc},
+ {REG_Y_ODD_INC,
+ s5k3e2fx_reg_pat[rt].y_odd_inc},
+ {REG_BINNING_ENABLE,
+ s5k3e2fx_reg_pat[rt].binning_enable},
+ };
+ struct s5k3e2fx_i2c_reg_conf tbl_2[] = {
+ {REG_FRAME_LENGTH_LINES_MSB, 0},
+ {REG_FRAME_LENGTH_LINES_LSB, 0},
+ {REG_LINE_LENGTH_PCK_MSB,
+ s5k3e2fx_reg_pat[rt].
+ line_length_pck_msb},
+ {REG_LINE_LENGTH_PCK_LSB,
+ s5k3e2fx_reg_pat[rt].
+ line_length_pck_lsb},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+ s5k3e2fx_reg_pat[rt].
+ analogue_gain_code_global_msb},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+ s5k3e2fx_reg_pat[rt].
+ analogue_gain_code_global_lsb},
+ {REG_FINE_INTEGRATION_TIME,
+ s5k3e2fx_reg_pat[rt].
+ fine_integration_time},
+ {REG_COARSE_INTEGRATION_TIME,
+ s5k3e2fx_reg_pat[rt].
+ coarse_integration_time},
+ /* LC Preview/Snapshot difference
+ * register
+ */
+ {REG_SH4CH_BLK_WIDTH_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_blk_width_r},
+ {REG_SH4CH_BLK_HEIGHT_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_blk_height_r},
+ {REG_SH4CH_STEP_X_R_MSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_step_x_r_MSB},
+ {REG_SH4CH_STEP_X_R_LSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_step_x_r_LSB},
+ {REG_SH4CH_STEP_Y_R_MSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_step_y_r_MSB},
+ {REG_SH4CH_STEP_Y_R_LSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_step_y_r_LSB},
+ {REG_SH4CH_START_BLK_CNT_X_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_blk_cnt_x_r},
+ {REG_SH4CH_START_BLK_INT_X_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_blk_int_x_r},
+ {REG_SH4CH_START_FRAC_X_R_MSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_frac_x_r_MSB},
+ {REG_SH4CH_START_FRAC_X_R_LSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_frac_x_r_LSB},
+ {REG_SH4CH_START_BLK_CNT_Y_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_blk_cnt_y_r},
+ {REG_SH4CH_START_BLK_INT_Y_R,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_blk_int_y_r},
+ {REG_SH4CH_START_FRAC_Y_R_MSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_frac_y_r_MSB},
+ {REG_SH4CH_START_FRAC_Y_R_LSB,
+ s5k3e2fx_reg_pat[rt].
+ sh4ch_start_frac_y_r_LSB},
+ };
+
+/* add EVT5 sensor Samsung difference MSR setting between Preview and Capture */
+
+ struct s5k3e2fx_i2c_reg_conf
+ tbl_only_for_EVT5[2][2] = {
+ { /* S_RES_PREVIEW */
+ {0x3062, 0x00},
+ {0x3063, 0xD6},
+ },
+ { /* S_RES_CAPTURE */
+ {0x3062, 0x01},
+ {0x3063, 0x16},
+ }
+ };
+
+ /* Most registers are directly applied at next frame after
+ writing except shutter and analog gain. Shutter and gain are
+ applied at 2nd or 1st frame later depending on register
+ writing time. When the camera is switched from preview to
+ snapshot, the first frame may have wrong shutter/gain and
+ should be discarded. The register REG_MASK_CORRUPTED_FRAMES
+ can discard the frame that has wrong shutter/gain. But in
+ preview mode, the frames should not be dropped. Otherwise
+ the preview will not be smooth. */
+ if (rt == S_RES_PREVIEW) {
+ /* Frames will be not discarded after exposure and gain are
+ written. */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_MASK_CORRUPTED_FRAMES, NO_MASK);
+ } else {
+ /* Solve greenish in lowlight. Prevent corrupted frame */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_MASK_CORRUPTED_FRAMES, MASK);
+ }
+
+/* solve greenish: hold for both */
+ rc = s5k3e2fx_i2c_write_b(
+ s5k3e2fx_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ CDBG("Binning_enable = 0x %2x"
+ "[s5k3e2fx.c s5k3e2fx_setting]\r\n",
+ s5k3e2fx_reg_pat[rt].binning_enable);
+
+ rc = s5k3e2fx_i2c_write_table(&tbl_1[0],
+ ARRAY_SIZE
+ (tbl_1));
+ if (rc < 0) {
+ pr_err("UPDATE_PERIODIC, tb1_1 failed");
+ return rc;
+ }
+
+ num_lperf =
+ (uint16_t) ((s5k3e2fx_reg_pat[rt].
+ frame_length_lines_msb
+ << 8) & 0xFF00) +
+ s5k3e2fx_reg_pat[rt].
+ frame_length_lines_lsb;
+
+ num_lperf =
+ num_lperf *
+ s5k3e2fx_ctrl->fps_divider / 0x0400;
+
+ tbl_2[0] =
+ (struct s5k3e2fx_i2c_reg_conf) {
+ REG_FRAME_LENGTH_LINES_MSB,
+ (num_lperf & 0xFF00) >> 8};
+ tbl_2[1] =
+ (struct s5k3e2fx_i2c_reg_conf) {
+ REG_FRAME_LENGTH_LINES_LSB,
+ (num_lperf & 0x00FF)};
+
+ rc = s5k3e2fx_i2c_write_table(&tbl_2[0],
+ ARRAY_SIZE
+ (tbl_2));
+ if (rc < 0) {
+ pr_err("UPDATE_PERIODIC, tb1_2 failed");
+ return rc;
+ }
+
+ /* only for evt5 */
+ if (g_usModuleVersion == 1) {
+ rc = s5k3e2fx_i2c_write_table
+ (&tbl_only_for_EVT5[rt][0],
+ 2);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* solve greenish: only release for preview */
+ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+ {
+ rc = s5k3e2fx_i2c_write_b(
+ s5k3e2fx_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = s5k3e2fx_i2c_write_b
+ (s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_STREAM);
+ if (rc < 0)
+ return rc;
+ }
+ break; /* UPDATE_PERIODIC */
+ }
+ case S_REG_INIT:{
+ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+ struct s5k3e2fx_i2c_reg_conf tbl_3[] = {
+/* {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET},*/
+ {S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_SW_STANDBY},
+ /*Output Size */
+ {REG_X_OUTPUT_SIZE_MSB,
+ s5k3e2fx_reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ s5k3e2fx_reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ s5k3e2fx_reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB,
+ s5k3e2fx_reg_pat[rt].
+ y_output_size_lsb},
+ /* Start-End address */
+ {REG_X_ADDR_START_MSB,
+ s5k3e2fx_reg_pat[rt].x_addr_start_MSB},
+ {REG_X_ADDR_START_LSB,
+ s5k3e2fx_reg_pat[rt].x_addr_start_LSB},
+ {REG_Y_ADDR_START_MSB,
+ s5k3e2fx_reg_pat[rt].y_addr_start_MSB},
+ {REG_Y_ADDR_START_LSB,
+ s5k3e2fx_reg_pat[rt].y_addr_start_LSB},
+ {REG_X_ADDR_END_MSB,
+ s5k3e2fx_reg_pat[rt].x_addr_end_MSB},
+ {REG_X_ADDR_END_LSB,
+ s5k3e2fx_reg_pat[rt].x_addr_end_LSB},
+ {REG_Y_ADDR_END_MSB,
+ s5k3e2fx_reg_pat[rt].y_addr_end_MSB},
+ {REG_Y_ADDR_END_LSB,
+ s5k3e2fx_reg_pat[rt].y_addr_end_LSB},
+ /* Binning */
+ {REG_X_EVEN_INC,
+ s5k3e2fx_reg_pat[rt].x_even_inc},
+ {REG_X_ODD_INC,
+ s5k3e2fx_reg_pat[rt].x_odd_inc},
+ {REG_Y_EVEN_INC,
+ s5k3e2fx_reg_pat[rt].y_even_inc},
+ {REG_Y_ODD_INC,
+ s5k3e2fx_reg_pat[rt].y_odd_inc},
+ {REG_BINNING_ENABLE,
+ s5k3e2fx_reg_pat[rt].binning_enable},
+ /* Frame format */
+ {REG_FRAME_LENGTH_LINES_MSB,
+ s5k3e2fx_reg_pat[rt].
+ frame_length_lines_msb},
+ {REG_FRAME_LENGTH_LINES_LSB,
+ s5k3e2fx_reg_pat[rt].
+ frame_length_lines_lsb},
+ {REG_LINE_LENGTH_PCK_MSB,
+ s5k3e2fx_reg_pat[rt].
+ line_length_pck_msb},
+ {REG_LINE_LENGTH_PCK_LSB,
+ s5k3e2fx_reg_pat[rt].
+ line_length_pck_lsb},
+ /* MSR setting */
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+ s5k3e2fx_reg_pat[rt].
+ analogue_gain_code_global_msb},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+ s5k3e2fx_reg_pat[rt].
+ analogue_gain_code_global_lsb},
+ {REG_FINE_INTEGRATION_TIME,
+ s5k3e2fx_reg_pat[rt].
+ fine_integration_time},
+ {REG_COARSE_INTEGRATION_TIME,
+ s5k3e2fx_reg_pat[rt].
+ coarse_integration_time},
+ {S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_STREAM},
+ };
+ unsigned short rData = 0;
+ mdelay(1);
+ s5k3e2fx_i2c_read_b(s5k3e2fx_client->
+ addr,
+ REG_3150_RESERVED,
+ &rData);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->
+ addr,
+ REG_3150_RESERVED,
+ (rData & 0xFFFE));
+ mdelay(1);
+ s5k3e2fx_i2c_read_b(s5k3e2fx_client->
+ addr,
+ REG_TYPE1_AF_ENABLE,
+ &rData);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->
+ addr,
+ REG_TYPE1_AF_ENABLE,
+ (rData | 0x0001));
+ mdelay(1);
+
+ /* reset fps_divider */
+ s5k3e2fx_ctrl->fps_divider = 1 * 0x0400;
+ /* write REG_INIT registers */
+ rc = s5k3e2fx_i2c_write_table(&tbl_3[0],
+ ARRAY_SIZE
+ (tbl_3));
+ if (rc < 0) {
+ pr_err("REG_INIT failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+ }
+ break; /* REG_INIT */
+
+ default:
+ rc = -EFAULT;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL);
+ if (!s5k3e2fx_ctrl) {
+ pr_err("s5k3e2fx_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400;
+ s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400;
+ s5k3e2fx_ctrl->set_test = S_TEST_OFF;
+ s5k3e2fx_ctrl->prev_res = S_QTR_SIZE;
+ s5k3e2fx_ctrl->pict_res = S_FULL_SIZE;
+
+ if (data)
+ s5k3e2fx_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK);
+
+ msleep(20);
+
+ msm_camio_camif_pad_reg_reset();
+ msleep(20);
+
+ rc = s5k3e2fx_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail1;
+
+ if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE)
+ rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW);
+ else
+ rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE);
+
+ if (rc < 0) {
+ pr_err("s5k3e2fx_setting failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3130, 0x03);
+ if (rc < 0)
+ goto init_fail1;
+
+ goto init_done;
+
+init_fail1:
+ kfree(s5k3e2fx_ctrl);
+init_done:
+ return rc;
+}
+
+static void s5k3e2fx_suspend_sensor(void)
+{
+ unsigned short rData = 0;
+ /*AF*/
+ s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr,
+ REG_TYPE1_AF_ENABLE, &rData);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_TYPE1_AF_ENABLE, (rData & 0xFFFE));
+ mdelay(1);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_SW_STANDBY);
+ msleep(210); /*for 5FPS */
+ /* hi z */
+ s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, REG_3150_RESERVED, &rData);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_3150_RESERVED, (rData | 0x0001));
+ mdelay(1);
+
+}
+
+static int s5k3e2fx_power_down(void)
+{
+ int rc = -EBADF;
+ s5k3e2fx_suspend_sensor();
+ return rc;
+}
+
+static int s5k3e2fx_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ s5k3e2fx_suspend_sensor();
+
+ kfree(s5k3e2fx_ctrl);
+ s5k3e2fx_ctrl = NULL;
+
+ allow_suspend();
+
+ CDBG("s5k3e2fx_release completed\n");
+
+ return rc;
+}
+
+static int s5k3e2fx_probe_init_lens_correction(
+ const struct msm_camera_sensor_info *data)
+{
+ int rc = 0;
+
+ /* LC setting */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_SOFTWARE_RESET,
+ S5K3E2FX_SOFTWARE_RESET);
+ mdelay(2);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_SW_STANDBY);
+ /*20090811 separates the EVT4/EVT5 sensor init and LC setting start */
+ s5k3e2fx_i2c_write_table(&Init_setting[g_usModuleVersion][0],
+ NUM_INIT_REG);
+
+ /* 090911 Add for Samsung VCM calibration current Start */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x0A);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x09);
+ mdelay(5);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3145, 0x04);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x80);
+ /* 090911 Add for Samsung VCM calibration current End */
+
+ s5k3e2fx_i2c_write_table(&lc_setting[g_usModuleVersion][0], NUM_LC_REG);
+
+ /*20090811 separates the EVT4/EVT5 sensor init and LC setting end */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_STREAM);
+ msleep(10);
+ s5k3e2fx_suspend_sensor();
+
+ return rc;
+}
+
+static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /* Q10 */
+
+ divider = (uint32_t)
+ ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 /
+ ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) *
+ (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p));
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t) (fps * divider / 0x00000400);
+}
+
+static uint16_t s5k3e2fx_get_prev_lines_pf(void)
+{
+ return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+}
+
+static uint16_t s5k3e2fx_get_prev_pixels_pl(void)
+{
+ return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p;
+}
+
+static uint16_t s5k3e2fx_get_pict_lines_pf(void)
+{
+ return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+}
+
+static uint16_t s5k3e2fx_get_pict_pixels_pl(void)
+{
+ return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+}
+
+static uint32_t s5k3e2fx_get_pict_max_exp_lc(void)
+{
+ uint32_t snapshot_lines_per_frame;
+
+ if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE)
+ snapshot_lines_per_frame =
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+ else
+ snapshot_lines_per_frame = 3961 * 3;
+
+ return snapshot_lines_per_frame;
+}
+
+static int s5k3e2fx_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q10 format */
+ int rc = 0;
+
+ s5k3e2fx_ctrl->fps_divider = fps->fps_div;
+
+ CDBG("s5k3e2fx_ctrl->fps_divider = %d\n",
+ s5k3e2fx_ctrl->fps_divider);
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_FRAME_LENGTH_LINES_MSB,
+ (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ s5k3e2fx_ctrl->fps_divider /
+ 0x400) & 0xFF00) >> 8);
+ if (rc < 0)
+ goto set_fps_done;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_FRAME_LENGTH_LINES_LSB,
+ (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ s5k3e2fx_ctrl->fps_divider /
+ 0x400) & 0xFF00));
+
+set_fps_done:
+ return rc;
+}
+
+static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ int rc = 0;
+
+ uint16_t max_legal_gain = 0x0200;
+ uint32_t ll_ratio; /* Q10 */
+ uint32_t ll_pck, fl_lines;
+ uint16_t offset = 4;
+ uint32_t gain_msb, gain_lsb;
+ uint32_t intg_t_msb, intg_t_lsb;
+ uint32_t ll_pck_msb, ll_pck_lsb;
+
+ struct s5k3e2fx_i2c_reg_conf tbl[2];
+
+ CDBG("Line:%d s5k3e2fx_write_exp_gain gain %d line %d\n",
+ __LINE__, gain, line);
+
+ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+
+ s5k3e2fx_ctrl->my_reg_gain = gain;
+ s5k3e2fx_ctrl->my_reg_line_count = (uint16_t) line;
+
+ fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+
+ ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p;
+
+ } else {
+
+ fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+
+ ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+ }
+
+ if (gain > max_legal_gain)
+ gain = max_legal_gain;
+
+ /* in Q10 */
+ line = (line * s5k3e2fx_ctrl->fps_divider);
+
+ if (fl_lines < (line / 0x400))
+ ll_ratio = (line / (fl_lines - offset));
+ else
+ ll_ratio = 0x400;
+
+/* solve greenish: only release for preview */
+ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0) {
+ pr_err("s5k3e2fx_i2c_write_b failed on line %d\n",
+ __LINE__);
+ return rc;
+ }
+ }
+
+ /* update gain registers */
+ gain_msb = (gain & 0xFF00) >> 8;
+ gain_lsb = gain & 0x00FF;
+ tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB;
+ tbl[0].bdata = gain_msb;
+ tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB;
+ tbl[1].bdata = gain_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+#if 1 /* Solve EVT5 greenish in lowlight*/
+ ll_pck = ll_pck * ll_ratio;
+ ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8;
+ ll_pck_lsb = (ll_pck / 0x400) & 0x00FF;
+ tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB;
+ tbl[0].bdata = ll_pck_msb;
+ tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB;
+ tbl[1].bdata = ll_pck_lsb;
+
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+#else
+ if (line / 0x400 + offset > fl_lines)
+ ll_pck = line / 0x400 + offset;
+ else
+ ll_pck = fl_lines;
+
+ ll_pck_msb = ((ll_pck) & 0xFF00) >> 8;
+ ll_pck_lsb = (ll_pck) & 0x00FF;
+ tbl[0].waddr = REG_FRAME_LENGTH_LINES_MSB;
+ tbl[0].bdata = ll_pck_msb;
+ tbl[1].waddr = REG_FRAME_LENGTH_LINES_LSB;
+ tbl[1].bdata = ll_pck_lsb;
+
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+#endif
+
+ line = line / 0x400;
+ intg_t_msb = (line & 0xFF00) >> 8;
+ intg_t_lsb = (line & 0x00FF);
+ tbl[0].waddr = REG_COARSE_INTEGRATION_TIME;
+ tbl[0].bdata = intg_t_msb;
+ tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB;
+ tbl[1].bdata = intg_t_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+
+/* solve greenish: release for both */
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0) {
+ pr_err("s5k3e2fx_i2c_write_b failed on line %d\n",
+ __LINE__);
+ return rc;
+ }
+
+write_gain_done:
+ return rc;
+}
+
+static int s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ pr_info("s5k3e2fx_set_pict_exp_gain gain %d line %d\n",
+ gain, line);
+
+ return s5k3e2fx_write_exp_gain(gain, line);
+}
+
+static int s5k3e2fx_video_config(int mode, int res)
+{
+ int rc;
+
+ switch (res) {
+ case S_QTR_SIZE:
+ pr_info("start sensor S_RES_PREVIEW config: %d\n", __LINE__);
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+ /* only apply my_reg for returning preview*/
+ rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain,
+ s5k3e2fx_ctrl->my_reg_line_count);
+ break;
+
+ case S_FULL_SIZE:
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return 0;
+ }
+
+ s5k3e2fx_ctrl->prev_res = res;
+ s5k3e2fx_ctrl->curr_res = res;
+ s5k3e2fx_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int s5k3e2fx_set_default_focus(void)
+{
+ int rc = 0;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, 0);
+ if (rc < 0)
+ return rc;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, 0);
+ if (rc < 0)
+ return rc;
+
+ s5k3e2fx_ctrl->curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int s5k3e2fx_move_focus(int direction, int num_steps)
+{
+ int rc = 0;
+ int i;
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_pos, pos_offset;
+ int16_t init_code = 0;
+ uint8_t next_pos_msb, next_pos_lsb;
+ int16_t s_move[5];
+ uint32_t gain; /* Q10 format */
+
+ if (direction == MOVE_NEAR)
+ step_direction = 20;
+ else if (direction == MOVE_FAR)
+ step_direction = -20;
+ else {
+ pr_err("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ actual_step = step_direction * (int16_t) num_steps;
+ pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos;
+ gain = actual_step * 0x400 / 5;
+
+ for (i = 0; i <= 4; i++) {
+ if (actual_step >= 0)
+ s_move[i] =
+ ((((i + 1) * gain + 0x200) -
+ (i * gain + 0x200)) / 0x400);
+ else
+ s_move[i] =
+ ((((i + 1) * gain - 0x200) -
+ (i * gain - 0x200)) / 0x400);
+ }
+
+ /* Ring Damping Code */
+ for (i = 0; i <= 4; i++) {
+ next_pos = (int16_t) (pos_offset + s_move[i]);
+
+ if (next_pos > (738 + init_code))
+ next_pos = 738 + init_code;
+ else if (next_pos < 0)
+ next_pos = 0;
+
+ CDBG("next_position in damping mode = %d\n", next_pos);
+ /* Writing the Values to the actuator */
+ if (next_pos == init_code)
+ next_pos = 0x00;
+
+ next_pos_msb = next_pos >> 8;
+ next_pos_lsb = next_pos & 0x00FF;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131,
+ next_pos_msb);
+ if (rc < 0)
+ break;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132,
+ next_pos_lsb);
+ if (rc < 0)
+ break;
+
+ pos_offset = next_pos;
+ s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code;
+ if (num_steps > 1)
+ mdelay(6);
+ else
+ mdelay(4);
+ }
+
+ return rc;
+}
+
+static int s5k3e2fx_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ long rc = 0;
+
+ if (copy_from_user(&cdata,
+ (void *)argp, sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+
+ if (copy_to_user((void *)argp, &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc = s5k3e2fx_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata, sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = s5k3e2fx_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = s5k3e2fx_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = s5k3e2fx_video_config(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = s5k3e2fx_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = s5k3e2fx_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = s5k3e2fx_set_default_focus();
+ break;
+
+/* case CFG_GET_AF_MAX_STEPS: */
+ case CFG_SET_EFFECT:
+ rc = s5k3e2fx_set_default_focus();
+ break;
+
+ case CFG_SET_LENS_SHADING:
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ prevent_suspend();
+ return rc;
+}
+
+static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ pr_info("%s\n", __func__);
+
+ rc = i2c_add_driver(&s5k3e2fx_i2c_driver);
+ if (rc < 0 || s5k3e2fx_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+
+ msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK);
+ msleep(20);
+
+ rc = s5k3e2fx_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+
+ /* lens correction */
+ s5k3e2fx_probe_init_lens_correction(info);
+ init_suspend();
+
+ s->s_init = s5k3e2fx_sensor_open_init;
+ s->s_release = s5k3e2fx_sensor_release;
+ s->s_config = s5k3e2fx_sensor_config;
+
+ return rc;
+
+probe_fail:
+ pr_err("SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int s5k3e2fx_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int rc;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+
+ if (!sinfo->need_suspend)
+ return 0;
+
+ CDBG("s5k3e2fx: camera suspend\n");
+ rc = gpio_request(sinfo->sensor_reset, "s5k3e2fx");
+ if (!rc)
+ gpio_direction_output(sinfo->sensor_reset, 0);
+ else {
+ pr_err("s5k3e2fx: request GPIO(sensor_reset) :%d faile\n",
+ sinfo->sensor_reset);
+ goto suspend_fail;
+ }
+ CDBG("s5k3e2fx: gpio_free:%d line:%d\n", sinfo->sensor_reset,
+ __LINE__);
+ gpio_free(sinfo->sensor_reset);
+
+suspend_fail:
+ return rc;
+}
+static void s5k3e2fx_sensor_resume_setting(void)
+{
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_SOFTWARE_RESET,
+ S5K3E2FX_SOFTWARE_RESET);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0100, 0x00);
+ /*--------------PLL setting for 80Mhz*/
+ /* PLL setting */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0305, 0x06);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0306, 0x00);
+ /*88 54.4Mhz */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0307, 0x83);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0301, 0x08);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0303, 0x01);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0309, 0x08);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x030b, 0x01);
+ /*--------------output size*/
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034c, 0x05);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034d, 0x10);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034e, 0x03);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034f, 0xcc);
+ /*--------------frame format (min blanking)*/
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0340, 0x03);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0341, 0xe2);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0342, 0x0a);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0343, 0xac);
+ /*--------------Binning */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0381, 0x01);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0383, 0x01);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0385, 0x01);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0387, 0x03);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3014, 0x06);
+ /*--------------MSR setting*/
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30c4, 0x01);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3000, 0x03);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3001, 0x94);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3002, 0x02);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3003, 0x95);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3004, 0x0f);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3005, 0x05);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3006, 0x3c);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3007, 0x8c);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3008, 0x93);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3009, 0x05);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300a, 0x3a);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300c, 0x02);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300d, 0x3e);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300f, 0x0e);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3010, 0x46);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3011, 0x64);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3012, 0x1e);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x301d, 0x3f);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3024, 0x04);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3028, 0x40);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3070, 0xdf);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x301b, 0x73);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x307e, 0x02);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30bd, 0x06);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30c2, 0x0b);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30ac, 0x81);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3151, 0xe6);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3029, 0x02);
+ /*--------------EVT4 setting*/
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30bf, 0x00);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3022, 0x87);
+ /*tune ADC to got batter yield rate in EDS */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3019, 0x60);
+ /*AF driving strength */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x3c);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3152, 0x08);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x315a, 0xaa);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3159, 0x0a);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0205, 0x80);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0202, 0x03);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0200, 0x02);
+}
+static int s5k3e2fx_resume(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+
+ if (!sinfo->need_suspend)
+ return 0;
+
+ CDBG("s5k3e2fx_resume\n");
+ /*init msm,clk ,GPIO,enable */
+ msm_camio_probe_on(pdev);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+
+ CDBG("msm_camio_probe_on\n");
+ /*read sensor ID and pull down reset */
+ msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK);
+ CDBG("msm_camio_clk_rate_set\n");
+ msleep(20);
+ s5k3e2fx_probe_init_sensor(sinfo);
+ CDBG("s5k3e2fx_probe_init_sensor\n");
+ /*init sensor,streaming on, SW init streaming off */
+ s5k3e2fx_sensor_resume_setting();
+ /*lens sharding */
+ s5k3e2fx_probe_init_lens_correction(sinfo);
+ /*stream on */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_STREAM);
+ /*software standby */
+ msleep(25);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3130, 0x00);
+ mdelay(1);
+ /*stream off */
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODE_SELECT,
+ S5K3E2FX_MODE_SELECT_SW_STANDBY);
+ mdelay(1);
+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3150, 0x51);
+ msleep(240);
+ /*set RST to low */
+ msm_camio_probe_off(pdev);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ CDBG("s5k3e2fx:resume done\n");
+ return rc;
+}
+
+static int __s5k3e2fx_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __s5k3e2fx_probe,
+ .driver = {
+ .name = "msm_camera_s5k3e2fx",
+ .owner = THIS_MODULE,
+ },
+ .suspend = s5k3e2fx_suspend,
+ .resume = s5k3e2fx_resume,
+};
+
+static int __init s5k3e2fx_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(s5k3e2fx_init);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e50..e780a75 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -482,6 +482,14 @@
host many different types of MODULbus daughterboards, including
CAN and GPIO controllers.
+config PM8058
+ bool "Qualcomm PM8058 Power Management IC"
+ depends on MSM_SSBI && ARCH_MSM7X30
+ default y if MSM_SSBI && ARCH_MSM7X30
+ help
+ Say yes here if your board is equipped with the Qualcomm
+ PM8058 PMIC.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e7..6722d31 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -71,3 +71,4 @@
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
+obj-$(CONFIG_PM8058) += pm8058-core.o
diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c
new file mode 100644
index 0000000..24410b5
--- /dev/null
+++ b/drivers/mfd/pm8058-core.c
@@ -0,0 +1,958 @@
+/*
+ * Copyright (c) 2010 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Author: Dima Zavin <dima@android.com>
+ * - Based on a driver from Code Aurora Forum.
+ *
+ * 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/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8058.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <asm-generic/gpio.h>
+
+#include <mach/msm_ssbi.h>
+
+enum {
+ DEBUG_IRQS = 1U << 0,
+};
+static int debug_mask = 0;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define REG_HWREV 0x0002 /* PMIC4 revision */
+
+#define REG_IRQ_PERM 0x01a6
+#define REG_IRQ_PERM_BLK_SEL 0x01ac
+#define REG_IRQ_ROOT 0x01bb
+#define REG_IRQ_M_STATUS1 0x01bc
+#define REG_IRQ_M_STATUS2 0x01bd
+#define REG_IRQ_M_STATUS3 0x01be
+#define REG_IRQ_M_STATUS4 0x01bf
+#define REG_IRQ_BLK_SEL 0x01c0
+#define REG_IRQ_IT_STATUS 0x01c1
+#define REG_IRQ_CONFIG 0x01c2
+#define REG_IRQ_RT_STATUS 0x01c3
+#define REG_GPIO_CTRL(x) (0x0150 + (x))
+
+#define IRQ_CFG_CLR (1 << 3)
+#define IRQ_CFG_MASK_RE (1 << 2)
+#define IRQ_CFG_MASK_FE (1 << 1)
+#define IRQ_CFG_LVL_SEL (1 << 0)
+
+#define NUM_BLOCKS 32
+#define IRQS_PER_BLOCK 8
+#define NUM_PMIRQS (NUM_BLOCKS * IRQS_PER_BLOCK)
+
+/* XXX: why are mpp's different than gpios? should we just put them into
+ * the gpio space? */
+#define MPP_IRQ_OFFSET (16 * 8)
+#define GPIO_IRQ_OFFSET (24 * 8)
+#define KEYPAD_IRQ_OFFSET (9 * 8 + 2)
+#define CHARGER_IRQ_OFFSET (1 * 8 + 7)
+
+/* this defines banks of irq space. We want to provide a compact irq space
+ * to the kernel, but there several ranges of irqs in an otherwise sparse
+ * map of available/accessible irqs on the pm8058. So,
+ *
+ * bank 0 - GPIO IRQs start=(24 * 8) cnt=40 (gpios 0-39)
+ * bank 1 - MPP IRQs start=(16 * 8) cnt=12 (mpps 0-11)
+ * bank 2 - keypad irqs start=(9*8 + 1) cnt=2
+ * bank 3 - charger irqs start=(1*8 + 7) cnt=7 (ends at 2*8 + 5)
+ *
+ */
+struct pm8058_irq_bank {
+ unsigned int start; /* will be added to the chip irq_base */
+ unsigned int cnt;
+ unsigned int offset; /* offset into device's real irq map */
+};
+
+static struct pm8058_irq_bank pm8058_irq_banks[] = {
+ {
+ .start = PM8058_FIRST_GPIO_IRQ,
+ .cnt = PM8058_NUM_GPIO_IRQS,
+ .offset = GPIO_IRQ_OFFSET,
+ },
+ {
+ .start = PM8058_FIRST_MPP_IRQ,
+ .cnt = PM8058_NUM_MPP_IRQS,
+ .offset = MPP_IRQ_OFFSET,
+ },
+ {
+ .start = PM8058_FIRST_KEYPAD_IRQ,
+ .cnt = PM8058_NUM_KEYPAD_IRQS,
+ .offset = KEYPAD_IRQ_OFFSET,
+ },
+ {
+ .start = PM8058_FIRST_CHARGER_IRQ,
+ .cnt = PM8058_NUM_CHARGER_IRQS,
+ .offset = CHARGER_IRQ_OFFSET,
+ },
+};
+#define NUM_IRQ_BANKS ARRAY_SIZE(pm8058_irq_banks)
+
+struct pm8058_irq_group {
+ u16 stat_reg;
+ u8 valid_mask;
+ u8 root_mask;
+ u8 block_offset;
+};
+
+static const struct pm8058_irq_group pm8058_irq_groups[] = {
+ {
+ .stat_reg = REG_IRQ_M_STATUS1,
+ .valid_mask = 0x6,
+ .root_mask = 0x2,
+ .block_offset = 0,
+ },
+ {
+ .stat_reg = REG_IRQ_M_STATUS2,
+ .valid_mask = 0x2,
+ .root_mask = 0x4,
+ .block_offset = 8,
+ },
+ {
+ .stat_reg = REG_IRQ_M_STATUS4,
+ .valid_mask = 0x1f,
+ .root_mask = 0x10,
+ .block_offset = 24,
+ },
+};
+#define NUM_ROOT_GROUPS ARRAY_SIZE(pm8058_irq_groups)
+
+struct pm8058_irq_info {
+ u8 cfg;
+ u8 cfg_val;
+ u8 mask;
+ u8 blk;
+ u8 blk_bit;
+ u8 wake;
+};
+
+struct pm8058 {
+ struct device *dev;
+ unsigned int devirq;
+
+ spinlock_t lock;
+
+ unsigned int irq_base;
+ struct pm8058_irq_info irqs[PM8058_NUM_IRQS];
+ unsigned int pmirqs[NUM_PMIRQS];
+ int wake_cnt;
+
+ struct gpio_chip gpio_chip;
+ u8 gpio_flags[PM8058_NUM_GPIOS];
+
+ struct pm8058_platform_data *pdata;
+
+ struct platform_device *kp_pdev;
+ struct platform_device *charger_pdev;
+};
+
+static struct pm8058 *the_pm8058;
+
+static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val);
+static int get_curr_irq_stat(struct pm8058 *pmic, unsigned int irq);
+
+int pm8058_readb(struct device *dev, u16 addr, u8 *val)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+EXPORT_SYMBOL(pm8058_readb);
+
+int pm8058_writeb(struct device *dev, u16 addr, u8 val)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+EXPORT_SYMBOL(pm8058_writeb);
+
+int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, int cnt)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+EXPORT_SYMBOL(pm8058_write_buf);
+
+int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+EXPORT_SYMBOL(pm8058_read_buf);
+
+static int _dir_map[] = {
+ [0] = 0x3,
+ [PM8058_GPIO_INPUT] = 0x0,
+ [PM8058_GPIO_OUTPUT] = 0x2,
+ [PM8058_GPIO_OUTPUT_HIGH] = 0x2,
+};
+
+int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio,
+ struct pm8058_pin_config *cfg)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+ unsigned long flags;
+ int ret;
+ u8 bank[6];
+
+ gpio -= pmic->gpio_chip.base;
+
+ /* bit 7 - write
+ * bit 6:4 - bank select */
+ bank[0] = ((1 << 7) | (0 << 4) | ((cfg->vin_src & 0x7) << 1) | 0x1);
+ bank[1] = ((1 << 7) | (1 << 4) | (_dir_map[cfg->dir] << 2) |
+ ((cfg->flags & PM8058_GPIO_OPEN_DRAIN ? 0x1 : 0) << 1) |
+ ((cfg->dir & PM8058_GPIO_OUTPUT_HIGH ? 0x1 : 0) << 0));
+ bank[2] = ((1 << 7) | (2 << 4) | ((cfg->pull_up & 0x7) << 1));
+ bank[3] = ((1 << 7) | (3 << 4) |
+ ((cfg->strength & 0x3) << 2) |
+ ((cfg->flags & PM8058_GPIO_HIGH_Z ? 0x1 : 0x0) << 0));
+ bank[4] = ((1 << 7) | (4 << 4) | ((cfg->func & 0x7) << 1));
+ bank[5] = ((1 << 7) | (5 << 4) |
+ ((cfg->flags & PM8058_GPIO_INV_IRQ_POL ? 0 : 1) << 3));
+
+ spin_lock_irqsave(&pmic->lock, flags);
+
+ pmic->gpio_flags[gpio] = cfg->flags | PM8058_GPIO_CONFIGURED;
+ ret = pm8058_write_buf(pmic->dev, REG_GPIO_CTRL(gpio),
+ bank, sizeof(bank));
+
+ spin_unlock_irqrestore(&pmic->lock, flags);
+
+ if (ret)
+ pr_err("%s: failed writing config for gpio %d (%d)\n", __func__,
+ gpio, ret);
+ return ret;
+}
+EXPORT_SYMBOL(pm8058_gpio_mux_cfg);
+
+int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg)
+{
+ if (!the_pm8058)
+ return -ENODEV;
+ return pm8058_gpio_mux_cfg(the_pm8058->dev, gpio, cfg);
+}
+EXPORT_SYMBOL(pm8058_gpio_mux);
+
+/* gpio funcs */
+static int read_gpio_bank(struct pm8058 *pmic, unsigned gpio, u8 bank, u8 *val)
+{
+ int ret;
+
+ ret = pm8058_writeb(pmic->dev, REG_GPIO_CTRL(gpio), (bank & 0x7) << 4);
+ if (ret)
+ goto out;
+ ret = pm8058_readb(pmic->dev, REG_GPIO_CTRL(gpio), val);
+ if (ret)
+ goto out;
+out:
+ return ret;
+}
+
+static int pm8058_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+ unsigned long flags;
+ int ret;
+ u8 bank1;
+ u8 bank3;
+ u8 bank5;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ if (pmic->gpio_flags[gpio] & PM8058_GPIO_CONFIGURED) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = read_gpio_bank(pmic, gpio, 1, &bank1);
+ if (ret) {
+ pr_err("%s: can't read bank 1\n", __func__);
+ goto out;
+ }
+
+ ret = read_gpio_bank(pmic, gpio, 3, &bank3);
+ if (ret) {
+ pr_err("%s: can't read bank 3\n", __func__);
+ goto out;
+ }
+
+ ret = read_gpio_bank(pmic, gpio, 5, &bank5);
+ if (ret) {
+ pr_err("%s: can't read bank 5\n", __func__);
+ goto out;
+ }
+
+ pmic->gpio_flags[gpio] = bank1 & 0x2 ? PM8058_GPIO_OPEN_DRAIN : 0;
+ pmic->gpio_flags[gpio] |= bank3 & 0x1 ? PM8058_GPIO_HIGH_Z : 0;
+ pmic->gpio_flags[gpio] |= bank5 & 0x8 ? 0 : PM8058_GPIO_INV_IRQ_POL;
+ pmic->gpio_flags[gpio] |= PM8058_GPIO_CONFIGURED;
+
+out:
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return 0;
+}
+
+static void pm8058_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+ unsigned long flags;
+
+ /* XXX: set high Z maybe?? */
+ spin_lock_irqsave(&pmic->lock, flags);
+ pmic->gpio_flags[gpio] = 0;
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+
+static int gpio_set_dir(struct pm8058 *pmic, unsigned gpio, int dir)
+{
+ unsigned long flags;
+ int ret;
+ u8 val;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ /* only need to write bank1 */
+ val = (pmic->gpio_flags[gpio] & PM8058_GPIO_OPEN_DRAIN ? 0x1 : 0) << 1;
+ val |= ((1 << 7) | (1 << 4) | (_dir_map[dir] << 2) |
+ (dir & PM8058_GPIO_OUTPUT_HIGH ? 0x1 : 0x0));
+ ret = pm8058_writeb(pmic->dev, REG_GPIO_CTRL(gpio), val);
+ if (ret)
+ pr_err("%s: erorr setting dir %x (%d)\n", __func__, dir, ret);
+
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return ret;
+}
+
+static int pm8058_gpio_direction_in(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+
+ return gpio_set_dir(pmic, gpio, PM8058_GPIO_INPUT);
+}
+
+static int pm8058_gpio_direction_out(struct gpio_chip *chip, unsigned gpio,
+ int val)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+
+ val = val ? PM8058_GPIO_OUTPUT_HIGH : PM8058_GPIO_OUTPUT;
+ return gpio_set_dir(pmic, gpio, val);
+}
+
+static void pm8058_gpio_set(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+
+ /* XXX: for now, let's always force the gpio to be an output when
+ * the user calls this func. I'm not even sure that it's wrong to
+ * assume that. */
+ val = val ? PM8058_GPIO_OUTPUT_HIGH : PM8058_GPIO_OUTPUT;
+ gpio_set_dir(pmic, gpio, val);
+}
+
+static int pm8058_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+
+ /* XXX: assumes gpio maps 1:1 to irq @ 0 */
+ return get_curr_irq_stat(pmic, gpio);
+}
+
+static int pm8058_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip);
+ return pmic->irq_base + gpio;
+}
+
+static struct gpio_chip pm8058_base_gpio_chip = {
+ .label = "pm8058",
+ .owner = THIS_MODULE,
+ .request = pm8058_gpio_request,
+ .free = pm8058_gpio_free,
+ .direction_input = pm8058_gpio_direction_in,
+ .get = pm8058_gpio_get,
+ .direction_output = pm8058_gpio_direction_out,
+ .set = pm8058_gpio_set,
+ .to_irq = pm8058_gpio_to_irq,
+};
+
+/* irq funcs */
+static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ ret = pm8058_writeb(pmic->dev, REG_IRQ_BLK_SEL, blk);
+ if (ret) {
+ pr_err("%s: error setting block select (%d)\n", __func__, ret);
+ goto done;
+ }
+
+ ret = pm8058_readb(pmic->dev, reg, val);
+ if (ret)
+ pr_err("%s: error setting bit select (%d)\n", __func__, ret);
+
+done:
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return ret;
+}
+
+static int get_curr_irq_stat(struct pm8058 *pmic, unsigned int irq)
+{
+ int ret;
+ u8 val;
+
+ ret = read_irq_block_reg(pmic, pmic->irqs[irq].blk, REG_IRQ_RT_STATUS,
+ &val);
+ if (ret) {
+ pr_err("%s: can't read irq %d status\n", __func__, irq);
+ goto done;
+ }
+
+ ret = !!(val & (1 << pmic->irqs[irq].blk_bit));
+
+done:
+ return ret;
+}
+
+int pm8058_irq_get_status(struct device *dev, unsigned int irq)
+{
+ struct pm8058 *pmic = dev_get_drvdata(dev);
+
+ if (irq >= PM8058_NUM_IRQS)
+ return -EINVAL;
+ return get_curr_irq_stat(pmic, irq);
+}
+EXPORT_SYMBOL(pm8058_irq_get_status);
+
+static int cfg_irq_blk_bit_perm(struct pm8058 *pmic, u8 blk, u8 mask)
+{
+ int ret;
+ unsigned long flags;
+ u8 tmp;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ ret = pm8058_writeb(pmic->dev, REG_IRQ_PERM_BLK_SEL, blk);
+ if (ret) {
+ pr_err("%s: error setting block select (%d)\n", __func__, ret);
+ goto done;
+ }
+
+ ret = pm8058_readb(pmic->dev, REG_IRQ_PERM, &tmp);
+ if (ret) {
+ pr_err("%s: error getting (%d)\n", __func__, ret);
+ goto done;
+ }
+
+ ret = pm8058_writeb(pmic->dev, REG_IRQ_PERM, tmp | mask);
+ if (ret)
+ pr_err("%s: error writing %d 0x%x 0x%x (0x%x)\n", __func__,
+ ret, blk, REG_IRQ_PERM, mask);
+
+done:
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return ret;
+}
+
+static int _write_irq_blk_bit_cfg(struct pm8058 *pmic, u8 blk, u8 bit, u8 cfg)
+{
+ int ret;
+
+ ret = pm8058_writeb(pmic->dev, REG_IRQ_BLK_SEL, blk);
+ if (ret) {
+ pr_err("%s: error setting block select (%d)\n", __func__, ret);
+ goto done;
+ }
+
+ cfg = (1 << 7) | (cfg & 0xf) | (bit << 4);
+ ret = pm8058_writeb(pmic->dev, REG_IRQ_CONFIG, cfg);
+ if (ret)
+ pr_err("%s: error writing irq cfg (%d)\n", __func__, ret);
+
+done:
+ return ret;
+}
+
+static int write_irq_config_locked(struct pm8058 *pmic, unsigned int irq,
+ u8 cfg)
+{
+ return _write_irq_blk_bit_cfg(pmic, pmic->irqs[irq].blk,
+ pmic->irqs[irq].blk_bit, cfg);
+}
+
+static int do_irq_master(struct pm8058 *pmic, int group)
+{
+ int i;
+ int j;
+ int ret;
+ u8 val;
+ unsigned long stat;
+
+ ret = pm8058_readb(pmic->dev, pm8058_irq_groups[group].stat_reg, &val);
+ if (ret) {
+ pr_err("%s: Can't read master status\n", __func__);
+ goto done;
+ }
+
+ if (debug_mask & DEBUG_IRQS)
+ pr_info("%s: master %d %02x\n", __func__, group, val);
+ stat = val & pm8058_irq_groups[group].valid_mask;
+ for_each_set_bit(i, &stat, BITS_PER_BYTE) {
+ u8 blk = pm8058_irq_groups[group].block_offset + i;
+ unsigned long blk_stat;
+
+ ret = read_irq_block_reg(pmic, blk, REG_IRQ_IT_STATUS, &val);
+ if (ret) {
+ pr_err("%s: can't read block status\n", __func__);
+ goto done;
+ }
+
+ blk_stat = val;
+ for_each_set_bit(j, &blk_stat, BITS_PER_BYTE) {
+ u8 irq = blk * 8 + j;
+
+ /* XXX: we should mask these out and count em' */
+ if (pmic->pmirqs[irq] == 0xffffffffU) {
+ pr_warning("Unexpected pmirq %d\n", irq);
+ continue;
+ }
+ generic_handle_irq(pmic->pmirqs[irq] + pmic->irq_base);
+ }
+ }
+
+done:
+ return ret;
+}
+
+static void pm8058_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pm8058 *pmic = get_irq_data(irq);
+ int ret;
+ int i;
+ u8 root;
+
+ desc->chip->ack(irq);
+ ret = pm8058_readb(pmic->dev, REG_IRQ_ROOT, &root);
+ if (ret) {
+ pr_err("%s: Can't read root status\n", __func__);
+ return;
+ }
+
+ if (debug_mask & DEBUG_IRQS)
+ pr_info("%s: root %02x\n", __func__, root);
+ for (i = 0; i < NUM_ROOT_GROUPS; ++i) {
+ if (root & pm8058_irq_groups[i].root_mask)
+ do_irq_master(pmic, i);
+ }
+}
+
+static void pm8058_irq_ack(unsigned int _irq)
+{
+ struct pm8058 *pmic = get_irq_chip_data(_irq);
+ unsigned int irq = _irq - pmic->irq_base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ write_irq_config_locked(pmic, irq,
+ pmic->irqs[irq].cfg_val | IRQ_CFG_CLR);
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+
+static void pm8058_irq_mask(unsigned int _irq)
+{
+ struct pm8058 *pmic = get_irq_chip_data(_irq);
+ unsigned int irq = _irq - pmic->irq_base;
+ struct pm8058_irq_info *irq_info = &pmic->irqs[irq];
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ irq_info->mask = IRQ_CFG_MASK_FE | IRQ_CFG_MASK_RE;
+ irq_info->cfg_val = irq_info->cfg | irq_info->mask;
+ write_irq_config_locked(pmic, irq, irq_info->cfg_val);
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+
+static void pm8058_irq_unmask(unsigned int _irq)
+{
+ struct pm8058 *pmic = get_irq_chip_data(_irq);
+ unsigned int irq = _irq - pmic->irq_base;
+ struct pm8058_irq_info *irq_info = &pmic->irqs[irq];
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ irq_info->mask = 0;
+ irq_info->cfg_val = irq_info->cfg;
+ write_irq_config_locked(pmic, irq, irq_info->cfg_val);
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+
+static void pm8058_irq_disable(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ pm8058_irq_mask(irq);
+ desc->status |= IRQ_MASKED;
+}
+
+static void pm8058_irq_enable(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ pm8058_irq_unmask(irq);
+ desc->status &= ~IRQ_MASKED;
+}
+
+static int pm8058_irq_set_type(unsigned int _irq, unsigned int flow_type)
+{
+ struct pm8058 *pmic = get_irq_chip_data(_irq);
+ unsigned int irq = _irq - pmic->irq_base;
+ struct pm8058_irq_info *irq_info = &pmic->irqs[irq];
+ unsigned long flags;
+ int ret;
+ u8 cfg;
+
+ cfg = IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ cfg &= ~IRQ_CFG_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ cfg &= ~IRQ_CFG_MASK_FE;
+ __set_irq_handler_unlocked(_irq, handle_edge_irq);
+ } else {
+ cfg |= IRQ_CFG_LVL_SEL;
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ cfg &= ~IRQ_CFG_MASK_RE;
+ else
+ cfg &= ~IRQ_CFG_MASK_FE;
+ __set_irq_handler_unlocked(_irq, handle_level_irq);
+ }
+
+ /* in case the irq was masked when the type was set, we don't want
+ * to unmask it */
+ spin_lock_irqsave(&pmic->lock, flags);
+ irq_info->cfg = cfg;
+ irq_info->cfg_val = irq_info->cfg | irq_info->mask;
+ ret = write_irq_config_locked(pmic, irq,
+ irq_info->cfg_val | IRQ_CFG_CLR);
+ spin_unlock_irqrestore(&pmic->lock, flags);
+
+ return ret;
+}
+
+static int pm8058_irq_set_wake(unsigned int _irq, unsigned int on)
+{
+ struct pm8058 *pmic = get_irq_chip_data(_irq);
+ unsigned int irq = _irq - pmic->irq_base;
+ struct pm8058_irq_info *irq_info = &pmic->irqs[irq];
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic->lock, flags);
+ if (on) {
+ if (!irq_info->wake) {
+ irq_info->wake = 1;
+ pmic->wake_cnt++;
+ }
+ } else {
+ if (irq_info->wake) {
+ irq_info->wake = 0;
+ pmic->wake_cnt--;
+ }
+ }
+ spin_unlock_irqrestore(&pmic->lock, flags);
+
+ return 0;
+}
+
+static struct irq_chip pm8058_irq_chip = {
+ .name = "pm8058",
+ .ack = pm8058_irq_ack,
+ .mask = pm8058_irq_mask,
+ .unmask = pm8058_irq_unmask,
+ .disable = pm8058_irq_disable,
+ .enable = pm8058_irq_enable,
+ .set_type = pm8058_irq_set_type,
+ .set_wake = pm8058_irq_set_wake,
+};
+
+static int pm8058_irq_init(struct pm8058 *pmic, unsigned int irq_base)
+{
+ int i;
+ int j;
+
+ /* mask/clear all the irqs */
+ for (i = 0; i < NUM_BLOCKS; ++i)
+ for (j = 0; j < IRQS_PER_BLOCK; ++j)
+ _write_irq_blk_bit_cfg(pmic, i, j, (IRQ_CFG_MASK_RE |
+ IRQ_CFG_MASK_FE |
+ IRQ_CFG_CLR));
+
+ memset(pmic->pmirqs, 0xff, NUM_PMIRQS * sizeof(pmic->pmirqs[0]));
+ for (i = 0; i < NUM_IRQ_BANKS; ++i) {
+ struct pm8058_irq_bank *bank = &pm8058_irq_banks[i];
+
+ for (j = 0; j < bank->cnt; ++j) {
+ unsigned int irq = bank->start + j;
+ unsigned int pmirq = bank->offset + j;
+
+ BUG_ON(irq >= PM8058_NUM_IRQS);
+
+ /* by default mask the irq */
+ pmic->irqs[irq].cfg = 0;
+ pmic->irqs[irq].mask =
+ IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE;
+ pmic->irqs[irq].cfg_val = pmic->irqs[irq].mask;
+ pmic->irqs[irq].blk = pmirq / 8;
+ pmic->irqs[irq].blk_bit = pmirq % 8;
+ pmic->pmirqs[pmirq] = irq;
+
+ BUG_ON(pmic->irqs[irq].blk >= NUM_BLOCKS);
+
+ /* XXX: slightly inefficient since we can end up
+ * doing it 8 times per block per bank, but it's
+ * the easiet. Optimize if gets too slow. */
+
+ /* ensure we set the permissions for the irqs in
+ * this bank */
+ cfg_irq_blk_bit_perm(pmic, pmic->irqs[irq].blk,
+ 1 << pmic->irqs[irq].blk_bit);
+
+ set_irq_chip(irq_base + irq, &pm8058_irq_chip);
+ set_irq_chip_data(irq_base + irq, pmic);
+ set_irq_handler(irq_base + irq, handle_edge_irq);
+ set_irq_flags(irq_base + irq, IRQF_VALID);
+ }
+
+ }
+
+ return 0;
+}
+
+static struct platform_device *add_child_device(
+ struct pm8058 *pmic, const char *name, void *pdata,
+ struct resource *res, int num_res)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_alloc(name, -1);
+ if (!pdev) {
+ pr_err("%s: cannot allocate pdev for '%s'\n", __func__, name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ pdev->dev.parent = pmic->dev;
+ pdev->dev.platform_data = pdata;
+
+ ret = platform_device_add_resources(pdev, res, num_res);
+ if (ret) {
+ pr_err("%s: can't add resources for '%s'\n", __func__, name);
+ goto err;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ pr_err("%s: cannot add child platform device '%s'\n", __func__,
+ name);
+ goto err;
+ }
+ return pdev;
+
+err:
+ if (pdev)
+ platform_device_put(pdev);
+ return ERR_PTR(ret);
+}
+
+static int add_keypad_device(struct pm8058 *pmic, void *pdata)
+{
+ struct platform_device *pdev;
+ struct resource irq_res[] = {
+ {
+ .start = pmic->irq_base + PM8058_KEYPAD_IRQ,
+ .end = pmic->irq_base + PM8058_KEYPAD_IRQ,
+ .flags = IORESOURCE_IRQ,
+ .name = "kp_sense",
+ },
+ {
+ .start = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ,
+ .end = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ,
+ .flags = IORESOURCE_IRQ,
+ .name = "kp_stuck",
+ }
+ };
+
+ pdev = add_child_device(pmic, "pm8058-keypad", pdata, irq_res,
+ ARRAY_SIZE(irq_res));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ pmic->kp_pdev = pdev;
+ return 0;
+}
+
+static int add_charger_device(struct pm8058 *pmic, void *pdata)
+{
+ struct platform_device *pdev;
+ struct resource irq_res[] = {
+ {
+ .start = pmic->irq_base + PM8058_CHGVAL_IRQ,
+ .end = pmic->irq_base + PM8058_CHGVAL_IRQ,
+ .flags = IORESOURCE_IRQ,
+ .name = "chgval_irq",
+ },
+ {
+ .start = pmic->irq_base + PM8058_FASTCHG_IRQ,
+ .end = pmic->irq_base + PM8058_FASTCHG_IRQ,
+ .flags = IORESOURCE_IRQ,
+ .name = "fastchg_irq",
+ }
+ };
+
+ pdev = add_child_device(pmic, "pm8058-charger", pdata, irq_res,
+ ARRAY_SIZE(irq_res));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ pmic->charger_pdev = pdev;
+ return 0;
+}
+
+static int pm8058_probe(struct platform_device *pdev)
+{
+ struct pm8058_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8058 *pmic;
+ int devirq;
+ int ret;
+ u8 val;
+
+ if (!pdata) {
+ pr_err("%s: no platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ devirq = platform_get_irq(pdev, 0);
+ if (devirq < 0) {
+ pr_err("%s: missing devirq\n", __func__);
+ return devirq;
+ }
+
+ pmic = kzalloc(sizeof(struct pm8058), GFP_KERNEL);
+ if (!pmic) {
+ pr_err("%s: Cannot alloc pm8058 struct\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* Read PMIC chip revision */
+ ret = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ if (ret)
+ goto err_read_rev;
+ pr_info("%s: PMIC revision: %x\n", __func__, val);
+
+ pmic->dev = &pdev->dev;
+ pmic->irq_base = pdata->irq_base;
+ pmic->devirq = devirq;
+ spin_lock_init(&pmic->lock);
+ pmic->pdata = pdata;
+ platform_set_drvdata(pdev, pmic);
+
+ ret = pm8058_irq_init(pmic, pmic->irq_base);
+ if (ret)
+ goto err_irq_init;
+
+ memcpy(&pmic->gpio_chip, &pm8058_base_gpio_chip,
+ sizeof(struct gpio_chip));
+ pmic->gpio_chip.dev = pmic->dev;
+ pmic->gpio_chip.base = pdata->gpio_base;
+ pmic->gpio_chip.ngpio = PM8058_NUM_GPIOS;
+
+ ret = gpiochip_add(&pmic->gpio_chip);
+ if (ret) {
+ pr_err("%s: can't register gpio chip\n", __func__);
+ goto err_gpiochip_add;
+ }
+
+ set_irq_type(devirq, IRQ_TYPE_LEVEL_LOW);
+ set_irq_data(devirq, pmic);
+ set_irq_chained_handler(devirq, pm8058_irq_handler);
+ set_irq_wake(devirq, 1);
+
+ the_pm8058 = pmic;
+
+ if (pdata->init) {
+ ret = pdata->init(pmic->dev);
+ if (ret) {
+ pr_err("%s: error in board init\n", __func__);
+ goto err_pdata_init;
+ }
+ }
+
+ if (pdata->keypad_pdata) {
+ ret = add_keypad_device(pmic, pdata->keypad_pdata);
+ if (ret) {
+ pr_err("%s: can't add child keypad device\n", __func__);
+ goto err_add_kp_dev;
+ }
+ }
+
+ if (pdata->charger_pdata) {
+ ret = add_charger_device(pmic, pdata->charger_pdata);
+ if (ret) {
+ pr_err("%s: can't add child charger dev\n", __func__);
+ goto err_add_charger_dev;
+ }
+ }
+
+ return 0;
+
+err_add_charger_dev:
+ if (pmic->kp_pdev)
+ platform_device_put(pmic->kp_pdev);
+err_add_kp_dev:
+err_pdata_init:
+ the_pm8058 = NULL;
+ set_irq_wake(devirq, 0);
+ set_irq_chained_handler(devirq, NULL);
+ WARN_ON(gpiochip_remove(&pmic->gpio_chip));
+err_gpiochip_add:
+err_irq_init:
+ platform_set_drvdata(pdev, NULL);
+err_read_rev:
+ kfree(pmic);
+ return ret;
+}
+
+static struct platform_driver pm8058_driver = {
+ .probe = pm8058_probe,
+ .driver = {
+ .name = "pm8058-core",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8058_init(void)
+{
+ return platform_driver_register(&pm8058_driver);
+}
+postcore_initcall(pm8058_init);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 86f2258..ab4a951 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -323,6 +323,24 @@
If you say yes here you get support for Asahi Kasei's
orientation sensor AK8975.
+config SENSORS_AKM8973
+ tristate "AKM8973 Compass Driver"
+ depends on I2C
+ help
+ AKM8973 Compass Driver implemented by HTC.
+
+config SENSORS_AKM8976
+ tristate "AKM8976 Compass Driver"
+ depends on I2C
+ help
+ AKM8976 Compass Driver implemented by HTC.
+
+config VP_A1026
+ tristate "A1026 Voice Processor Driver"
+ depends on I2C
+ help
+ A1026 Voice Processor Driver implemented by HTC.
+
config EP93XX_PWM
tristate "EP93xx PWM support"
depends on ARCH_EP93XX
@@ -407,5 +425,6 @@
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
source "drivers/misc/iwmc3200top/Kconfig"
+source "drivers/misc/video_core/720p/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fe5ad59..48ce8fe 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -38,3 +38,7 @@
obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o
obj-$(CONFIG_APANIC) += apanic.o
obj-$(CONFIG_SENSORS_AK8975) += akm8975.o
+obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o
+obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o
+obj-$(CONFIG_VP_A1026) += a1026.o
+obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/
diff --git a/drivers/misc/a1026.c b/drivers/misc/a1026.c
new file mode 100644
index 0000000..7eaa3e1
--- /dev/null
+++ b/drivers/misc/a1026.c
@@ -0,0 +1,1146 @@
+/* drivers/i2c/chips/a1026.c - a1026 voice processor driver
+ *
+ * Copyright (C) 2009 HTC 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/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/a1026.h>
+
+#define DEBUG (0)
+#define ENABLE_DIAG_IOCTLS (0)
+
+static struct i2c_client *this_client;
+static struct a1026_platform_data *pdata;
+
+static int execute_cmdmsg(unsigned int);
+
+static struct mutex a1026_lock;
+static int a1026_opened;
+static int a1026_suspended;
+static int control_a1026_clk;
+static unsigned int a1026_NS_state = A1026_NS_STATE_AUTO;
+static int a1026_current_config = A1026_PATH_SUSPEND;
+static int a1026_param_ID;
+
+struct vp_ctxt {
+ unsigned char *data;
+ unsigned int img_size;
+};
+
+struct vp_ctxt the_vp;
+
+static int a1026_i2c_read(char *rxData, int length)
+{
+ int rc;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msgs, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("%s: rx[%d] = %2x\n", __func__, i, rxData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int a1026_i2c_write(char *txData, int length)
+{
+ int rc;
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ rc = i2c_transfer(this_client->adapter, msg, 1);
+ if (rc < 0) {
+ pr_err("%s: transfer error %d\n", __func__, rc);
+ return rc;
+ }
+
+#if DEBUG
+ {
+ int i = 0;
+ for (i = 0; i < length; i++)
+ pr_info("%s: tx[%d] = %2x\n", __func__, i, txData[i]);
+ }
+#endif
+
+ return 0;
+}
+
+static int a1026_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct vp_ctxt *vp = &the_vp;
+
+ mutex_lock(&a1026_lock);
+
+ if (a1026_opened) {
+ pr_err("%s: busy\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+
+ file->private_data = vp;
+ vp->img_size = 0;
+ a1026_opened = 1;
+done:
+ mutex_unlock(&a1026_lock);
+ return rc;
+}
+
+static int a1026_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&a1026_lock);
+ a1026_opened = 0;
+ mutex_unlock(&a1026_lock);
+
+ return 0;
+}
+
+static void a1026_i2c_sw_reset(unsigned int reset_cmd)
+{
+ int rc = 0;
+ unsigned char msgbuf[4];
+
+ msgbuf[0] = (reset_cmd >> 24) & 0xFF;
+ msgbuf[1] = (reset_cmd >> 16) & 0xFF;
+ msgbuf[2] = (reset_cmd >> 8) & 0xFF;
+ msgbuf[3] = reset_cmd & 0xFF;
+
+ pr_info("%s: %08x\n", __func__, reset_cmd);
+
+ rc = a1026_i2c_write(msgbuf, 4);
+ if (!rc)
+ msleep(20);
+}
+
+static ssize_t a1026_bootup_init(struct file *file, struct a1026img *img)
+{
+ struct vp_ctxt *vp = file->private_data;
+ int rc, pass = 0;
+ int remaining;
+ int retry = RETRY_CNT;
+ unsigned char *index;
+ char buf[2];
+
+ if (img->img_size > A1026_MAX_FW_SIZE) {
+ pr_err("%s: invalid a1026 image size %d\n", __func__,
+ img->img_size);
+ return -EINVAL;
+ }
+
+ vp->data = kmalloc(img->img_size, GFP_KERNEL);
+ if (!vp->data) {
+ pr_err("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ vp->img_size = img->img_size;
+ if (copy_from_user(vp->data, img->buf, img->img_size)) {
+ pr_err("%s: copy from user failed\n", __func__);
+ kfree(vp->data);
+ return -EFAULT;
+ }
+
+ while (retry--) {
+ /* Reset A1026 chip */
+ gpio_set_value(pdata->gpio_a1026_reset, 0);
+
+ /* Enable A1026 clock */
+ if (control_a1026_clk)
+ gpio_set_value(pdata->gpio_a1026_clk, 1);
+ mdelay(1);
+
+ /* Take out of reset */
+ gpio_set_value(pdata->gpio_a1026_reset, 1);
+
+ msleep(50); /* Delay before send I2C command */
+
+ /* Boot Cmd to A1026 */
+ buf[0] = A1026_msg_BOOT >> 8;
+ buf[1] = A1026_msg_BOOT & 0xff;
+
+ rc = a1026_i2c_write(buf, 2);
+ if (rc < 0) {
+ pr_err("%s: set boot mode error (%d retries left)\n",
+ __func__, retry);
+ continue;
+ }
+
+ mdelay(1); /* use polling */
+ rc = a1026_i2c_read(buf, 1);
+ if (rc < 0) {
+ pr_err("%s: boot mode ack error (%d retries left)\n",
+ __func__, retry);
+ continue;
+ }
+
+ if (buf[0] != A1026_msg_BOOT_ACK) {
+ pr_err("%s: not a boot-mode ack (%d retries left)\n",
+ __func__, retry);
+ continue;
+ }
+
+ remaining = vp->img_size / 32;
+ index = vp->data;
+
+ pr_info("%s: starting to load image (%d passes)...\n",
+ __func__,
+ remaining + !!(vp->img_size % 32));
+
+ for (; remaining; remaining--, index += 32) {
+ rc = a1026_i2c_write(index, 32);
+ if (rc < 0)
+ break;
+ }
+
+ if (rc >= 0 && vp->img_size % 32)
+ rc = a1026_i2c_write(index, vp->img_size % 32);
+
+ if (rc < 0) {
+ pr_err("%s: fw load error %d (%d retries left)\n",
+ __func__, rc, retry);
+ continue;
+ }
+
+ msleep(20); /* Delay time before issue a Sync Cmd */
+
+ pr_info("%s: firmware loaded successfully\n", __func__);
+
+ rc = execute_cmdmsg(A100_msg_Sync);
+ if (rc < 0) {
+ pr_err("%s: sync command error %d (%d retries left)\n",
+ __func__, rc, retry);
+ continue;
+ }
+
+ pass = 1;
+ break;
+ }
+
+ /* Put A1026 into sleep mode */
+ rc = execute_cmdmsg(A100_msg_Sleep);
+ if (rc < 0) {
+ pr_err("%s: suspend error\n", __func__);
+ goto set_suspend_err;
+ }
+
+ a1026_suspended = 1;
+ a1026_current_config = A1026_PATH_SUSPEND;
+
+ msleep(120);
+ /* Disable A1026 clock */
+ if (control_a1026_clk)
+ gpio_set_value(pdata->gpio_a1026_clk, 0);
+
+set_suspend_err:
+ if (pass && !rc)
+ pr_info("%s: initialized!\n", __func__);
+ else
+ pr_err("%s: initialization failed\n", __func__);
+
+ kfree(vp->data);
+ return rc;
+}
+
+unsigned char phonecall_receiver[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x05, /* SetAlgorithmParm, 0x0005:25dB Max Suppression */
+ 0x80,0x17,0x00,0x20, /* SetAlgorithmParmID, 0x0020:Tx PostEq Mode */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:On always */
+ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x15,0x00,0xFA, /* SetDigitalOutputGain, 0x00:Tx, 0xFA:(-6 dB) */
+};
+
+unsigned char phonecall_headset[] = {
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0xF8, /* SetDigitalOutputGain, 0x00:Tx, 0xF8:(-8 dB) */
+};
+
+unsigned char phonecall_speaker[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002 */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0xFD, /* SetDigitalOutputGain, 0x00:Tx, 0xFD:(-3 dB) */
+};
+
+unsigned char phonecall_bt[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1-mic External (MD) */
+ 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char phonecall_tty[] = {
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */
+ 0x80,0x15,0x00,0xFB, /* SetDigitalOutputGain, 0x00:Tx, 0xFB:(-5 dB) */
+};
+
+unsigned char INT_MIC_recording_receiver[] = {
+ 0x80,0x26,0x00,0x07, /* SelectRouting, 0x0007:Pri,Snk,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char EXT_MIC_recording[] = {
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char INT_MIC_recording_speaker[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char BACK_MIC_recording[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x01, /* SetAlgorithmParm, 0x0001:Yes */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No Suppression */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x06, /* SetDigitalOutputGain, 0x00:Tx, 0x06:(6 dB) */
+};
+
+unsigned char vr_no_ns_receiver[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x09:(12 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_no_ns_headset[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1M-DG (1-mic digital input) */
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_no_ns_speaker[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_no_ns_bt[] = {
+ 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */
+ 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */
+ 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_ns_receiver[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */
+ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_ns_headset[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1-mic External (MD) */
+ 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:20dB Max Suppression */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_ns_speaker[] = {
+ 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char vr_ns_bt[] = {
+ 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */
+ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */
+ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */
+ 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:20dB Max Suppression */
+ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */
+ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */
+ 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */
+ 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */
+};
+
+unsigned char suspend_mode[] = {
+ 0x80,0x10,0x00,0x01
+};
+
+static ssize_t chk_wakeup_a1026(void)
+{
+ int rc = 0, retry = 3;
+
+ if (a1026_suspended == 1) {
+ /* Enable A1026 clock */
+ if (control_a1026_clk) {
+ gpio_set_value(pdata->gpio_a1026_clk, 1);
+ mdelay(1);
+ }
+
+ gpio_set_value(pdata->gpio_a1026_wakeup, 0);
+ msleep(120);
+
+ do {
+ rc = execute_cmdmsg(A100_msg_Sync);
+ } while ((rc < 0) && --retry);
+
+ gpio_set_value(pdata->gpio_a1026_wakeup, 1);
+ if (rc < 0) {
+ pr_err("%s: failed (%d)\n", __func__, rc);
+ goto wakeup_sync_err;
+ }
+
+ a1026_suspended = 0;
+ }
+wakeup_sync_err:
+ return rc;
+}
+
+/* Filter commands according to noise suppression state forced by
+ * A1026_SET_NS_STATE ioctl.
+ *
+ * For this function to operate properly, all configurations must include
+ * both A100_msg_Bypass and Mic_Config commands even if default values
+ * are selected or if Mic_Config is useless because VP is off
+ */
+int a1026_filter_vp_cmd(int cmd, int mode)
+{
+ int msg = (cmd >> 16) & 0xFFFF;
+ int filtered_cmd = cmd;
+
+ if (a1026_NS_state == A1026_NS_STATE_AUTO)
+ return cmd;
+
+ switch (msg) {
+ case A100_msg_Bypass:
+ if (a1026_NS_state == A1026_NS_STATE_OFF)
+ filtered_cmd = A1026_msg_VP_OFF;
+ else
+ filtered_cmd = A1026_msg_VP_ON;
+ break;
+ case A100_msg_SetAlgorithmParmID:
+ a1026_param_ID = cmd & 0xFFFF;
+ break;
+ case A100_msg_SetAlgorithmParm:
+ if (a1026_param_ID == Mic_Config) {
+ if (a1026_NS_state == A1026_NS_STATE_CT)
+ filtered_cmd = (msg << 16);
+ else if (a1026_NS_state == A1026_NS_STATE_FT)
+ filtered_cmd = (msg << 16) | 0x0002;
+ }
+ break;
+ default:
+ if (mode == A1026_CONFIG_VP)
+ filtered_cmd = -1;
+ break;
+ }
+
+ pr_info("%s: %x filtered = %x, a1026_NS_state %d, mode %d\n", __func__,
+ cmd, filtered_cmd, a1026_NS_state, mode);
+
+ return filtered_cmd;
+}
+
+int a1026_set_config(char newid, int mode)
+{
+ int i = 0, rc = 0, size = 0;
+ int number_of_cmd_sets, rd_retry_cnt;
+ unsigned int sw_reset = 0;
+ unsigned char *i2c_cmds;
+ unsigned char *index = 0;
+ unsigned char ack_buf[A1026_CMD_FIFO_DEPTH * 4];
+ unsigned char rdbuf[4];
+
+ if ((a1026_suspended) && (newid == A1026_PATH_SUSPEND))
+ return rc;
+
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+
+ sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE);
+
+ switch (newid) {
+ case A1026_PATH_INCALL_RECEIVER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = phonecall_receiver;
+ size = sizeof(phonecall_receiver);
+ break;
+ case A1026_PATH_INCALL_HEADSET:
+ gpio_set_value(pdata->gpio_a1026_micsel, 1);
+ i2c_cmds = phonecall_headset;
+ size = sizeof(phonecall_headset);
+ break;
+ case A1026_PATH_INCALL_SPEAKER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = phonecall_speaker;
+ size = sizeof(phonecall_speaker);
+ break;
+ case A1026_PATH_INCALL_BT:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = phonecall_bt;
+ size = sizeof(phonecall_bt);
+ break;
+ case A1026_PATH_INCALL_TTY:
+ gpio_set_value(pdata->gpio_a1026_micsel, 1);
+ i2c_cmds = phonecall_tty;
+ size = sizeof(phonecall_tty);
+ break;
+ case A1026_PATH_VR_NO_NS_RECEIVER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_no_ns_receiver;
+ size = sizeof(vr_no_ns_receiver);
+ break;
+ case A1026_PATH_VR_NO_NS_HEADSET:
+ gpio_set_value(pdata->gpio_a1026_micsel, 1);
+ i2c_cmds = vr_no_ns_headset;
+ size = sizeof(vr_no_ns_headset);
+ break;
+ case A1026_PATH_VR_NO_NS_SPEAKER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_no_ns_speaker;
+ size = sizeof(vr_no_ns_speaker);
+ break;
+ case A1026_PATH_VR_NO_NS_BT:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_no_ns_bt;
+ size = sizeof(vr_no_ns_bt);
+ break;
+ case A1026_PATH_VR_NS_RECEIVER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_ns_receiver;
+ size = sizeof(vr_ns_receiver);
+ break;
+ case A1026_PATH_VR_NS_HEADSET:
+ gpio_set_value(pdata->gpio_a1026_micsel, 1);
+ i2c_cmds = vr_ns_headset;
+ size = sizeof(vr_ns_headset);
+ break;
+ case A1026_PATH_VR_NS_SPEAKER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_ns_speaker;
+ size = sizeof(vr_ns_speaker);
+ break;
+ case A1026_PATH_VR_NS_BT:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = vr_ns_bt;
+ size = sizeof(vr_ns_bt);
+ break;
+ case A1026_PATH_RECORD_RECEIVER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = INT_MIC_recording_receiver;
+ size = sizeof(INT_MIC_recording_receiver);
+ break;
+ case A1026_PATH_RECORD_HEADSET:
+ gpio_set_value(pdata->gpio_a1026_micsel, 1);
+ i2c_cmds = EXT_MIC_recording;
+ size = sizeof(EXT_MIC_recording);
+ break;
+ case A1026_PATH_RECORD_SPEAKER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = INT_MIC_recording_speaker;
+ size = sizeof(INT_MIC_recording_speaker);
+ break;
+ case A1026_PATH_RECORD_BT:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = phonecall_bt;
+ size = sizeof(phonecall_bt);
+ break;
+ case A1026_PATH_SUSPEND:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = (unsigned char *)suspend_mode;
+ size = sizeof(suspend_mode);
+ break;
+ case A1026_PATH_CAMCORDER:
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ i2c_cmds = BACK_MIC_recording;
+ size = sizeof(BACK_MIC_recording);
+ break;
+ default:
+ pr_err("%s: invalid cmd %d\n", __func__, newid);
+ rc = -1;
+ goto input_err;
+ break;
+ }
+
+ a1026_current_config = newid;
+ pr_info("%s: change to mode %d\n", __func__, newid);
+
+ pr_info("%s: block write start (size = %d)\n", __func__, size);
+#if DEBUG
+ for (i = 1; i <= size; i++) {
+ pr_info("%x ", *(i2c_cmds + i - 1));
+ if ( !(i % 4))
+ pr_info("\n");
+ }
+#endif
+
+ rc = a1026_i2c_write(i2c_cmds, size);
+ if (rc < 0) {
+ pr_err("A1026 CMD block write error!\n");
+ a1026_i2c_sw_reset(sw_reset);
+ return rc;
+ }
+ pr_info("%s: block write end\n", __func__);
+
+ /* Don't need to get Ack after sending out a suspend command */
+ if (*i2c_cmds == 0x80 && *(i2c_cmds + 1) == 0x10
+ && *(i2c_cmds + 2) == 0x00 && *(i2c_cmds + 3) == 0x01) {
+ a1026_suspended = 1;
+ /* Disable A1026 clock */
+ msleep(120);
+ if (control_a1026_clk)
+ gpio_set_value(pdata->gpio_a1026_clk, 0);
+ return rc;
+ }
+
+ memset(ack_buf, 0, sizeof(ack_buf));
+ msleep(20);
+ pr_info("%s: CMD ACK block read start\n", __func__);
+ rc = a1026_i2c_read(ack_buf, size);
+ if (rc < 0) {
+ pr_err("%s: CMD ACK block read error\n", __func__);
+ a1026_i2c_sw_reset(sw_reset);
+ return rc;
+ } else {
+ pr_info("%s: CMD ACK block read end\n", __func__);
+#if DEBUG
+ for (i = 1; i <= size; i++) {
+ pr_info("%x ", ack_buf[i-1]);
+ if ( !(i % 4))
+ pr_info("\n");
+ }
+#endif
+ index = ack_buf;
+ number_of_cmd_sets = size / 4;
+ do {
+ if (*index == 0x00) {
+ rd_retry_cnt = POLLING_RETRY_CNT;
+rd_retry:
+ if (rd_retry_cnt--) {
+ memset(rdbuf, 0, sizeof(rdbuf));
+ rc = a1026_i2c_read(rdbuf, 4);
+ if (rc < 0)
+ return rc;
+#if DEBUG
+ for (i = 0; i < sizeof(rdbuf); i++) {
+ pr_info("0x%x\n", rdbuf[i]);
+ }
+ pr_info("-----------------\n");
+#endif
+ if (rdbuf[0] == 0x00) {
+ msleep(20);
+ goto rd_retry;
+ }
+ } else {
+ pr_err("%s: CMD ACK Not Ready\n",
+ __func__);
+ return -EBUSY;
+ }
+ } else if (*index == 0xff) { /* illegal cmd */
+ return -ENOEXEC;
+ } else if (*index == 0x80) {
+ index += 4;
+ }
+ } while (--number_of_cmd_sets);
+ }
+input_err:
+ return rc;
+}
+
+int execute_cmdmsg(unsigned int msg)
+{
+ int rc = 0;
+ int retries, pass = 0;
+ unsigned char msgbuf[4];
+ unsigned char chkbuf[4];
+ unsigned int sw_reset = 0;
+
+ sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE);
+
+ msgbuf[0] = (msg >> 24) & 0xFF;
+ msgbuf[1] = (msg >> 16) & 0xFF;
+ msgbuf[2] = (msg >> 8) & 0xFF;
+ msgbuf[3] = msg & 0xFF;
+
+ memcpy(chkbuf, msgbuf, 4);
+
+ rc = a1026_i2c_write(msgbuf, 4);
+ if (rc < 0) {
+ pr_err("%s: error %d\n", __func__, rc);
+ a1026_i2c_sw_reset(sw_reset);
+ return rc;
+ }
+
+ /* We don't need to get Ack after sending out a suspend command */
+ if (msg == A100_msg_Sleep)
+ return rc;
+
+ retries = POLLING_RETRY_CNT;
+ while (retries--) {
+ rc = 0;
+
+ msleep(20); /* use polling */
+ memset(msgbuf, 0, sizeof(msgbuf));
+ rc = a1026_i2c_read(msgbuf, 4);
+ if (rc < 0) {
+ pr_err("%s: ack-read error %d (%d retries)\n", __func__,
+ rc, retries);
+ continue;
+ }
+
+ if (msgbuf[0] == 0x80 && msgbuf[1] == chkbuf[1]) {
+ pass = 1;
+ break;
+ } else if (msgbuf[0] == 0xff && msgbuf[1] == 0xff) {
+ pr_err("%s: illegal cmd %08x\n", __func__, msg);
+ rc = -EINVAL;
+ break;
+ } else if ( msgbuf[0] == 0x00 && msgbuf[1] == 0x00 ) {
+ pr_info("%s: not ready (%d retries)\n", __func__,
+ retries);
+ rc = -EBUSY;
+ } else {
+ pr_info("%s: cmd/ack mismatch: (%d retries left)\n",
+ __func__,
+ retries);
+#if DEBUG
+ pr_info("%s: msgbuf[0] = %x\n", __func__, msgbuf[0]);
+ pr_info("%s: msgbuf[1] = %x\n", __func__, msgbuf[1]);
+ pr_info("%s: msgbuf[2] = %x\n", __func__, msgbuf[2]);
+ pr_info("%s: msgbuf[3] = %x\n", __func__, msgbuf[3]);
+#endif
+ rc = -EBUSY;
+ }
+ }
+
+ if (!pass) {
+ pr_err("%s: failed execute cmd %08x (%d)\n", __func__,
+ msg, rc);
+ a1026_i2c_sw_reset(sw_reset);
+ }
+ return rc;
+}
+
+#if ENABLE_DIAG_IOCTLS
+static int a1026_set_mic_state(char miccase)
+{
+ int rc = 0;
+ unsigned int cmd_msg = 0;
+
+ switch (miccase) {
+ case 1: /* Mic-1 ON / Mic-2 OFF */
+ cmd_msg = 0x80260007;
+ break;
+ case 2: /* Mic-1 OFF / Mic-2 ON */
+ cmd_msg = 0x80260015;
+ break;
+ case 3: /* both ON */
+ cmd_msg = 0x80260001;
+ break;
+ case 4: /* both OFF */
+ cmd_msg = 0x80260006;
+ break;
+ default:
+ pr_info("%s: invalid input %d\n", __func__, miccase);
+ rc = -EINVAL;
+ break;
+ }
+ rc = execute_cmdmsg(cmd_msg);
+ return rc;
+}
+
+static int exe_cmd_in_file(unsigned char *incmd)
+{
+ int rc = 0;
+ int i = 0;
+ unsigned int cmd_msg = 0;
+ unsigned char tmp = 0;
+
+ for (i = 0; i < 4; i++) {
+ tmp = *(incmd + i);
+ cmd_msg |= (unsigned int)tmp;
+ if (i != 3)
+ cmd_msg = cmd_msg << 8;
+ }
+ rc = execute_cmdmsg(cmd_msg);
+ if (rc < 0)
+ pr_err("%s: cmd %08x error %d\n", __func__, cmd_msg, rc);
+ return rc;
+}
+#endif /* ENABLE_DIAG_IOCTLS */
+
+static int
+a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct a1026img img;
+ int rc = 0;
+#if ENABLE_DIAG_IOCTLS
+ char msg[4];
+ int mic_cases = 0;
+ int mic_sel = 0;
+#endif
+ int pathid = 0;
+ unsigned int ns_state;
+
+ switch (cmd) {
+ case A1026_BOOTUP_INIT:
+ img.buf = 0;
+ img.img_size = 0;
+ if (copy_from_user(&img, argp, sizeof(img)))
+ return -EFAULT;
+ rc = a1026_bootup_init(file, &img);
+ break;
+ case A1026_SET_CONFIG:
+ if (copy_from_user(&pathid, argp, sizeof(pathid)))
+ return -EFAULT;
+ rc = a1026_set_config(pathid, A1026_CONFIG_FULL);
+ if (rc < 0)
+ pr_err("%s: A1026_SET_CONFIG (%d) error %d!\n",
+ __func__, pathid, rc);
+ break;
+ case A1026_SET_NS_STATE:
+ if (copy_from_user(&ns_state, argp, sizeof(ns_state)))
+ return -EFAULT;
+ pr_info("%s: set noise suppression %d\n", __func__, ns_state);
+ if (ns_state < 0 || ns_state >= A1026_NS_NUM_STATES)
+ return -EINVAL;
+ a1026_NS_state = ns_state;
+ if (!a1026_suspended)
+ a1026_set_config(a1026_current_config,
+ A1026_CONFIG_VP);
+ break;
+#if ENABLE_DIAG_IOCTLS
+ case A1026_SET_MIC_ONOFF:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(&mic_cases, argp, sizeof(mic_cases)))
+ return -EFAULT;
+ rc = a1026_set_mic_state(mic_cases);
+ if (rc < 0)
+ pr_err("%s: A1026_SET_MIC_ONOFF %d error %d!\n",
+ __func__, mic_cases, rc);
+ break;
+ case A1026_SET_MICSEL_ONOFF:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(&mic_sel, argp, sizeof(mic_sel)))
+ return -EFAULT;
+ gpio_set_value(pdata->gpio_a1026_micsel, !!mic_sel);
+ rc = 0;
+ break;
+ case A1026_READ_DATA:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ rc = a1026_i2c_read(msg, 4);
+ if (copy_to_user(argp, &msg, 4))
+ return -EFAULT;
+ break;
+ case A1026_WRITE_MSG:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(msg, argp, sizeof(msg)))
+ return -EFAULT;
+ rc = a1026_i2c_write(msg, 4);
+ break;
+ case A1026_SYNC_CMD:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ msg[0] = 0x80;
+ msg[1] = 0x00;
+ msg[2] = 0x00;
+ msg[3] = 0x00;
+ rc = a1026_i2c_write(msg, 4);
+ break;
+ case A1026_SET_CMD_FILE:
+ rc = chk_wakeup_a1026();
+ if (rc < 0)
+ return rc;
+ if (copy_from_user(msg, argp, sizeof(msg)))
+ return -EFAULT;
+ rc = exe_cmd_in_file(msg);
+ break;
+#endif /* ENABLE_DIAG_IOCTLS */
+ default:
+ pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct file_operations a1026_fops = {
+ .owner = THIS_MODULE,
+ .open = a1026_open,
+ .release = a1026_release,
+ .ioctl = a1026_ioctl,
+};
+
+static struct miscdevice a1026_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "audience_a1026",
+ .fops = &a1026_fops,
+};
+
+static int a1026_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int rc = 0;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ rc = -ENOMEM;
+ pr_err("%s: platform data is NULL\n", __func__);
+ goto err_alloc_data_failed;
+ }
+ }
+
+ this_client = client;
+
+ rc = gpio_request(pdata->gpio_a1026_clk, "a1026");
+ if (rc < 0) {
+ control_a1026_clk = 0;
+ goto chk_gpio_micsel;
+ }
+ control_a1026_clk = 1;
+
+ rc = gpio_direction_output(pdata->gpio_a1026_clk, 1);
+ if (rc < 0) {
+ pr_err("%s: request clk gpio direction failed\n", __func__);
+ goto err_free_gpio_clk;
+ }
+
+chk_gpio_micsel:
+ rc = gpio_request(pdata->gpio_a1026_micsel, "a1026");
+ if (rc < 0) {
+ pr_err("%s: gpio request mic_sel pin failed\n", __func__);
+ goto err_free_gpio_micsel;
+ }
+
+ rc = gpio_direction_output(pdata->gpio_a1026_micsel, 1);
+ if (rc < 0) {
+ pr_err("%s: request mic_sel gpio direction failed\n", __func__);
+ goto err_free_gpio_micsel;
+ }
+
+ rc = gpio_request(pdata->gpio_a1026_wakeup, "a1026");
+ if (rc < 0) {
+ pr_err("%s: gpio request wakeup pin failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_direction_output(pdata->gpio_a1026_wakeup, 1);
+ if (rc < 0) {
+ pr_err("%s: request wakeup gpio direction failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_request(pdata->gpio_a1026_reset, "a1026");
+ if (rc < 0) {
+ pr_err("%s: gpio request reset pin failed\n", __func__);
+ goto err_free_gpio;
+ }
+
+ rc = gpio_direction_output(pdata->gpio_a1026_reset, 1);
+ if (rc < 0) {
+ pr_err("%s: request reset gpio direction failed\n", __func__);
+ goto err_free_gpio_all;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c check functionality error\n", __func__);
+ rc = -ENODEV;
+ goto err_free_gpio_all;
+ }
+
+ if (control_a1026_clk)
+ gpio_set_value(pdata->gpio_a1026_clk, 1);
+ gpio_set_value(pdata->gpio_a1026_micsel, 0);
+ gpio_set_value(pdata->gpio_a1026_wakeup, 1);
+ gpio_set_value(pdata->gpio_a1026_reset, 1);
+
+ rc = misc_register(&a1026_device);
+ if (rc) {
+ pr_err("%s: a1026_device register failed\n", __func__);
+ goto err_free_gpio_all;
+ }
+
+ return 0;
+
+err_free_gpio_all:
+ gpio_free(pdata->gpio_a1026_reset);
+err_free_gpio:
+ gpio_free(pdata->gpio_a1026_wakeup);
+err_free_gpio_micsel:
+ gpio_free(pdata->gpio_a1026_micsel);
+err_free_gpio_clk:
+ if (control_a1026_clk)
+ gpio_free(pdata->gpio_a1026_clk);
+err_alloc_data_failed:
+ return rc;
+}
+
+static int a1026_remove(struct i2c_client *client)
+{
+ struct a1026_platform_data *p1026data = i2c_get_clientdata(client);
+ kfree(p1026data);
+
+ return 0;
+}
+
+static int a1026_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int a1026_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id a1026_id[] = {
+ { "audience_a1026", 0 },
+ { }
+};
+
+static struct i2c_driver a1026_driver = {
+ .probe = a1026_probe,
+ .remove = a1026_remove,
+ .suspend = a1026_suspend,
+ .resume = a1026_resume,
+ .id_table = a1026_id,
+ .driver = {
+ .name = "audience_a1026",
+ },
+};
+
+static int __init a1026_init(void)
+{
+ pr_info("%s\n", __func__);
+ mutex_init(&a1026_lock);
+
+ return i2c_add_driver(&a1026_driver);
+}
+
+static void __exit a1026_exit(void)
+{
+ i2c_del_driver(&a1026_driver);
+}
+
+module_init(a1026_init);
+module_exit(a1026_exit);
+
+MODULE_DESCRIPTION("A1026 voice processor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/akm8973.c b/drivers/misc/akm8973.c
new file mode 100644
index 0000000..723eac7
--- /dev/null
+++ b/drivers/misc/akm8973.c
@@ -0,0 +1,753 @@
+/*
+ * drivers/i2c/chips/akm8973.c - akm8973 compass driver
+ *
+ * Copyright (C) 2008-2009 HTC Corporation.
+ * Author: viral wang <viralwang@gmail.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.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <asm/gpio.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/akm8973.h>
+
+#define DEBUG 0
+#define MAX_FAILURE_COUNT 3
+
+static struct i2c_client *this_client;
+
+struct akm8973_data {
+ struct input_dev *input_dev;
+ struct work_struct work;
+};
+
+/* Addresses to scan -- protected by sense_data_mutex */
+static char sense_data[RBUFF_SIZE + 1];
+static struct mutex sense_data_mutex;
+#define AKM8973_RETRY_COUNT 10
+static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq);
+static DECLARE_WAIT_QUEUE_HEAD(open_wq);
+
+static atomic_t data_ready;
+static atomic_t open_count;
+static atomic_t open_flag;
+static atomic_t reserve_open_flag;
+
+static atomic_t m_flag;
+static atomic_t a_flag;
+static atomic_t t_flag;
+static atomic_t mv_flag;
+
+static int failure_count = 0;
+
+static short akmd_delay = 0;
+
+static struct akm8973_platform_data *pdata;
+
+static int AKI2C_RxData(char *rxData, int length)
+{
+ uint8_t loop_i;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxData,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) {
+ if (i2c_transfer(this_client->adapter, msgs, 2) > 0) {
+ break;
+ }
+ mdelay(10);
+ }
+
+ if (loop_i >= AKM8973_RETRY_COUNT) {
+ printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int AKI2C_TxData(char *txData, int length)
+{
+ uint8_t loop_i;
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) {
+ if (i2c_transfer(this_client->adapter, msg, 1) > 0) {
+ break;
+ }
+ mdelay(10);
+ }
+
+ if (loop_i >= AKM8973_RETRY_COUNT) {
+ printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void AKECS_Reset(void)
+{
+ gpio_set_value(pdata->reset, 0);
+ udelay(120);
+ gpio_set_value(pdata->reset, 1);
+}
+
+static int AKECS_StartMeasure(void)
+{
+ char buffer[2];
+
+ atomic_set(&data_ready, 0);
+
+ /* Set measure mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_MEASURE;
+
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static int AKECS_PowerDown(void)
+{
+ char buffer[2];
+ int ret;
+
+ /* Set powerdown mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_POWERDOWN;
+ /* Set data */
+ ret = AKI2C_TxData(buffer, 2);
+ if (ret < 0)
+ return ret;
+
+ /* Dummy read for clearing INT pin */
+ buffer[0] = AKECS_REG_TMPS;
+ /* Read data */
+ ret = AKI2C_RxData(buffer, 1);
+ if (ret < 0)
+ return ret;
+ return ret;
+}
+
+static int AKECS_StartE2PRead(void)
+{
+ char buffer[2];
+
+ /* Set measure mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_E2P_READ;
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static int AKECS_GetData(void)
+{
+ char buffer[RBUFF_SIZE + 1];
+ int ret;
+
+ memset(buffer, 0, RBUFF_SIZE + 1);
+ buffer[0] = AKECS_REG_ST;
+ ret = AKI2C_RxData(buffer, RBUFF_SIZE+1);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&sense_data_mutex);
+ memcpy(sense_data, buffer, sizeof(buffer));
+ atomic_set(&data_ready, 1);
+ wake_up(&data_ready_wq);
+ mutex_unlock(&sense_data_mutex);
+
+ return 0;
+}
+
+static int AKECS_SetMode(char mode)
+{
+ int ret;
+
+ switch (mode) {
+ case AKECS_MODE_MEASURE:
+ ret = AKECS_StartMeasure();
+ break;
+ case AKECS_MODE_E2P_READ:
+ ret = AKECS_StartE2PRead();
+ break;
+ case AKECS_MODE_POWERDOWN:
+ ret = AKECS_PowerDown();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* wait at least 300us after changing mode */
+ mdelay(1);
+ return ret;
+}
+
+static int AKECS_TransRBuff(char *rbuf, int size)
+{
+ wait_event_interruptible_timeout(data_ready_wq,
+ atomic_read(&data_ready), 1000);
+ if (!atomic_read(&data_ready)) {
+ /* Ignore data errors if there are no open handles */
+ if (atomic_read(&open_count) > 0) {
+ printk(KERN_ERR
+ "AKM8973 AKECS_TransRBUFF: Data not ready\n");
+ failure_count++;
+ if (failure_count >= MAX_FAILURE_COUNT) {
+ printk(KERN_ERR
+ "AKM8973 AKECS_TransRBUFF: successive %d failure.\n",
+ failure_count);
+ atomic_set(&open_flag, -1);
+ wake_up(&open_wq);
+ failure_count = 0;
+ }
+ }
+ return -1;
+ }
+
+ mutex_lock(&sense_data_mutex);
+ memcpy(&rbuf[1], &sense_data[1], size);
+ atomic_set(&data_ready, 0);
+ mutex_unlock(&sense_data_mutex);
+
+ failure_count = 0;
+ return 0;
+}
+
+
+static void AKECS_Report_Value(short *rbuf)
+{
+ struct akm8973_data *data = i2c_get_clientdata(this_client);
+#if DEBUG
+ printk(KERN_INFO"AKECS_Report_Value: yaw = %d, pitch = %d, roll = %d\n", rbuf[0],
+ rbuf[1], rbuf[2]);
+ printk(KERN_INFO" tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3],
+ rbuf[4], rbuf[5]);
+ printk(KERN_INFO" G_Sensor: x = %d LSB, y = %d LSB, z = %d LSB\n",
+ rbuf[6], rbuf[7], rbuf[8]);
+#endif
+ /* Report magnetic sensor information */
+ if (atomic_read(&m_flag)) {
+ input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
+ input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
+ input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
+ input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);
+ }
+
+ /* Report acceleration sensor information */
+ if (atomic_read(&a_flag)) {
+ input_report_abs(data->input_dev, ABS_X, rbuf[6]);
+ input_report_abs(data->input_dev, ABS_Y, rbuf[7]);
+ input_report_abs(data->input_dev, ABS_Z, rbuf[8]);
+ input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]);
+ }
+
+ /* Report temperature information */
+ if (atomic_read(&t_flag))
+ input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]);
+
+ if (atomic_read(&mv_flag)) {
+ input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]);
+ input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]);
+ input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]);
+ }
+
+ input_sync(data->input_dev);
+}
+
+static int AKECS_GetOpenStatus(void)
+{
+ wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0));
+ return atomic_read(&open_flag);
+}
+
+static int AKECS_GetCloseStatus(void)
+{
+ wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0));
+ return atomic_read(&open_flag);
+}
+
+static void AKECS_CloseDone(void)
+{
+ atomic_set(&m_flag, 1);
+ atomic_set(&a_flag, 1);
+ atomic_set(&t_flag, 1);
+ atomic_set(&mv_flag, 1);
+}
+
+static int akm_aot_open(struct inode *inode, struct file *file)
+{
+ int ret = -1;
+ if (atomic_cmpxchg(&open_count, 0, 1) == 0) {
+ if (atomic_cmpxchg(&open_flag, 0, 1) == 0) {
+ atomic_set(&reserve_open_flag, 1);
+ enable_irq(this_client->irq);
+ wake_up(&open_wq);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int akm_aot_release(struct inode *inode, struct file *file)
+{
+ atomic_set(&reserve_open_flag, 0);
+ atomic_set(&open_flag, 0);
+ atomic_set(&open_count, 0);
+ wake_up(&open_wq);
+ disable_irq(this_client->irq);
+ return 0;
+}
+
+static int
+akm_aot_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ short flag;
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ case ECS_IOCTL_APP_SET_AFLAG:
+ case ECS_IOCTL_APP_SET_TFLAG:
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ atomic_set(&m_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_MFLAG:
+ flag = atomic_read(&m_flag);
+ break;
+ case ECS_IOCTL_APP_SET_AFLAG:
+ atomic_set(&a_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_AFLAG:
+ flag = atomic_read(&a_flag);
+ break;
+ case ECS_IOCTL_APP_SET_TFLAG:
+ atomic_set(&t_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_TFLAG:
+ flag = atomic_read(&t_flag);
+ break;
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ atomic_set(&mv_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ flag = atomic_read(&mv_flag);
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ akmd_delay = flag;
+ break;
+ case ECS_IOCTL_APP_GET_DELAY:
+ flag = akmd_delay;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_GET_MFLAG:
+ case ECS_IOCTL_APP_GET_AFLAG:
+ case ECS_IOCTL_APP_GET_TFLAG:
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ case ECS_IOCTL_APP_GET_DELAY:
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int akmd_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int akmd_release(struct inode *inode, struct file *file)
+{
+ AKECS_CloseDone();
+ return 0;
+}
+
+static int
+akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ void __user *argp = (void __user *)arg;
+
+ char msg[RBUFF_SIZE + 1], rwbuf[5];
+ int ret = -1, status;
+ short mode, value[12], delay;
+ char project_name[64];
+ short layouts[4][3][3];
+ int i, j, k;
+
+
+ switch (cmd) {
+ case ECS_IOCTL_WRITE:
+ case ECS_IOCTL_READ:
+ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_SET_MODE:
+ if (copy_from_user(&mode, argp, sizeof(mode)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_SET_YPR:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_WRITE:
+ if (rwbuf[0] < 2)
+ return -EINVAL;
+ ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_READ:
+ if (rwbuf[0] < 1)
+ return -EINVAL;
+ ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_RESET:
+ AKECS_Reset();
+ break;
+ case ECS_IOCTL_SET_MODE:
+ ret = AKECS_SetMode((char)mode);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_GETDATA:
+ ret = AKECS_TransRBuff(msg, RBUFF_SIZE);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_SET_YPR:
+ AKECS_Report_Value(value);
+ break;
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ status = AKECS_GetOpenStatus();
+ break;
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ status = AKECS_GetCloseStatus();
+ break;
+ case ECS_IOCTL_GET_DELAY:
+ delay = akmd_delay;
+ break;
+ case ECS_IOCTL_GET_PROJECT_NAME:
+ strncpy(project_name, pdata->project_name, 64);
+ break;
+ case ECS_IOCTL_GET_MATRIX:
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 3; j++)
+ for (k = 0; k < 3; k++) {
+ layouts[i][j][k] = pdata->layouts[i][j][k];
+ }
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GETDATA:
+ if (copy_to_user(argp, &msg, sizeof(msg)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_DELAY:
+ if (copy_to_user(argp, &delay, sizeof(delay)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_PROJECT_NAME:
+ if (copy_to_user(argp, project_name, sizeof(project_name)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_MATRIX:
+ if (copy_to_user(argp, layouts, sizeof(layouts)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void akm_work_func(struct work_struct *work)
+{
+ if (AKECS_GetData() < 0)
+ printk(KERN_ERR "AKM8973 akm_work_func: Get data failed\n");
+ enable_irq(this_client->irq);
+}
+
+static irqreturn_t akm8973_interrupt(int irq, void *dev_id)
+{
+ struct akm8973_data *data = dev_id;
+ disable_irq_nosync(this_client->irq);
+ schedule_work(&data->work);
+ return IRQ_HANDLED;
+}
+
+static struct file_operations akmd_fops = {
+ .owner = THIS_MODULE,
+ .open = akmd_open,
+ .release = akmd_release,
+ .ioctl = akmd_ioctl,
+};
+
+static struct file_operations akm_aot_fops = {
+ .owner = THIS_MODULE,
+ .open = akm_aot_open,
+ .release = akm_aot_release,
+ .ioctl = akm_aot_ioctl,
+};
+
+
+static struct miscdevice akm_aot_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8973_aot",
+ .fops = &akm_aot_fops,
+};
+
+
+static struct miscdevice akmd_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8973_daemon",
+ .fops = &akmd_fops,
+};
+
+int akm8973_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct akm8973_data *akm;
+ int err = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ akm = kzalloc(sizeof(struct akm8973_data), GFP_KERNEL);
+ if (!akm) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ INIT_WORK(&akm->work, akm_work_func);
+ i2c_set_clientdata(client, akm);
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ printk(KERN_ERR"AKM8973 akm8973_probe: platform data is NULL\n");
+ goto exit_platform_data_null;
+ }
+ this_client = client;
+
+ err = AKECS_PowerDown();
+ if (err < 0) {
+ printk(KERN_ERR"AKM8973 akm8973_probe: set power down mode error\n");
+ goto exit_set_mode_failed;
+ }
+
+ err = request_irq(client->irq, akm8973_interrupt, IRQF_TRIGGER_HIGH,
+ "akm8973", akm);
+ disable_irq(this_client->irq);
+
+ if (err < 0) {
+ printk(KERN_ERR"AKM8973 akm8973_probe: request irq failed\n");
+ goto exit_irq_request_failed;
+ }
+
+ akm->input_dev = input_allocate_device();
+
+ if (!akm->input_dev) {
+ err = -ENOMEM;
+ printk(KERN_ERR
+ "AKM8973 akm8973_probe: Failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ set_bit(EV_ABS, akm->input_dev->evbit);
+ /* yaw */
+ input_set_abs_params(akm->input_dev, ABS_RX, 0, 360, 0, 0);
+ /* pitch */
+ input_set_abs_params(akm->input_dev, ABS_RY, -180, 180, 0, 0);
+ /* roll */
+ input_set_abs_params(akm->input_dev, ABS_RZ, -90, 90, 0, 0);
+ /* x-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_X, -1872, 1872, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Y, -1872, 1872, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Z, -1872, 1872, 0, 0);
+ /* temparature */
+ input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);
+ /* status of magnetic sensor */
+ input_set_abs_params(akm->input_dev, ABS_RUDDER, -32768, 3, 0, 0);
+ /* status of acceleration sensor */
+ input_set_abs_params(akm->input_dev, ABS_WHEEL, -32768, 3, 0, 0);
+ /* step count */
+ input_set_abs_params(akm->input_dev, ABS_GAS, 0, 65535, 0, 0);
+ /* x-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0);
+ /* y-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0);
+ /* z-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0);
+
+ akm->input_dev->name = "compass";
+
+ err = input_register_device(akm->input_dev);
+
+ if (err) {
+ printk(KERN_ERR
+ "AKM8973 akm8973_probe: Unable to register input device: %s\n",
+ akm->input_dev->name);
+ goto exit_input_register_device_failed;
+ }
+
+ err = misc_register(&akmd_device);
+ if (err) {
+ printk(KERN_ERR "AKM8973 akm8973_probe: akmd_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = misc_register(&akm_aot_device);
+ if (err) {
+ printk(KERN_ERR
+ "AKM8973 akm8973_probe: akm_aot_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ mutex_init(&sense_data_mutex);
+
+ init_waitqueue_head(&data_ready_wq);
+ init_waitqueue_head(&open_wq);
+
+ /* As default, report all information */
+ atomic_set(&m_flag, 1);
+ atomic_set(&a_flag, 1);
+ atomic_set(&t_flag, 1);
+ atomic_set(&mv_flag, 1);
+
+ return 0;
+
+exit_misc_device_register_failed:
+exit_input_register_device_failed:
+ input_free_device(akm->input_dev);
+exit_input_dev_alloc_failed:
+ free_irq(client->irq, akm);
+exit_irq_request_failed:
+exit_set_mode_failed:
+exit_platform_data_null:
+ kfree(akm);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ return err;
+
+}
+
+static int akm8973_remove(struct i2c_client *client)
+{
+ struct akm8973_data *akm = i2c_get_clientdata(client);
+ free_irq(client->irq, akm);
+ input_unregister_device(akm->input_dev);
+ kfree(akm);
+ return 0;
+}
+static const struct i2c_device_id akm8973_id[] = {
+ { AKM8973_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver akm8973_driver = {
+ .probe = akm8973_probe,
+ .remove = akm8973_remove,
+ .id_table = akm8973_id,
+ .driver = {
+ .name = AKM8973_I2C_NAME,
+ },
+};
+
+static int __init akm8973_init(void)
+{
+ printk(KERN_INFO "AKM8973 compass driver: init\n");
+ return i2c_add_driver(&akm8973_driver);
+}
+
+static void __exit akm8973_exit(void)
+{
+ i2c_del_driver(&akm8973_driver);
+}
+
+module_init(akm8973_init);
+module_exit(akm8973_exit);
+
+MODULE_AUTHOR("viral wang <viral_wang@htc.com>");
+MODULE_DESCRIPTION("AKM8973 compass driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/akm8976.c b/drivers/misc/akm8976.c
new file mode 100644
index 0000000..ee99ae8
--- /dev/null
+++ b/drivers/misc/akm8976.c
@@ -0,0 +1,1132 @@
+/* drivers/i2c/chips/akm8976.c - akm8976 compass driver
+ *
+ * Copyright (C) 2007-2008 HTC Corporation.
+ * Author: Hou-Kun Chen <houkun.chen@gmail.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.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/akm8976.h>
+
+#define DEBUG 0
+#define MAX_FAILURE_COUNT 10
+
+static struct i2c_client *this_client;
+
+struct akm8976_data {
+ struct input_dev *input_dev;
+ struct work_struct work;
+};
+
+/* Addresses to scan -- protected by sense_data_mutex */
+static char sense_data[RBUFF_SIZE + 1];
+static struct mutex sense_data_mutex;
+
+static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq);
+static DECLARE_WAIT_QUEUE_HEAD(open_wq);
+
+static char cspec_num;
+static atomic_t cspec_frq;
+
+static atomic_t data_ready;
+static atomic_t open_count;
+static atomic_t open_flag;
+static atomic_t reserve_open_flag;
+
+static atomic_t m_flag;
+static atomic_t a_flag;
+static atomic_t t_flag;
+static atomic_t mv_flag;
+
+static int pffd_mode = 0;
+static int failure_count = 0;
+
+static short akmd_delay = 0;
+
+static atomic_t suspend_flag = ATOMIC_INIT(0);
+
+static struct akm8976_platform_data *pdata;
+static int revision = -1;
+/* AKM HW info */
+static ssize_t gsensor_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ sprintf(buf, "AK8976A_%#x\n", revision);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static DEVICE_ATTR(vendor, 0444, gsensor_vendor_show, NULL);
+
+static struct kobject *android_gsensor_kobj;
+
+static int gsensor_sysfs_init(void)
+{
+ int ret ;
+
+ android_gsensor_kobj = kobject_create_and_add("android_gsensor", NULL);
+ if (android_gsensor_kobj == NULL) {
+ printk(KERN_ERR
+ "AKM8976 gsensor_sysfs_init:"\
+ "subsystem_register failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sysfs_create_file(android_gsensor_kobj, &dev_attr_vendor.attr);
+ if (ret) {
+ printk(KERN_ERR
+ "AKM8976 gsensor_sysfs_init:"\
+ "sysfs_create_group failed\n");
+ goto err4;
+ }
+
+ return 0 ;
+err4:
+ kobject_del(android_gsensor_kobj);
+err:
+ return ret ;
+}
+
+/* following are the sysfs callback functions */
+
+#define config_ctrl_reg(name,address) \
+static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,address)); \
+} \
+static ssize_t name##_store(struct device *dev, struct device_attribute *attr, \
+ const char *buf,size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ unsigned long val = simple_strtoul(buf, NULL, 10); \
+ if (val > 0xff) \
+ return -EINVAL; \
+ i2c_smbus_write_byte_data(client,address, val); \
+ return count; \
+} \
+static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store)
+
+config_ctrl_reg(ms1, AKECS_REG_MS1);
+config_ctrl_reg(ms2, AKECS_REG_MS2);
+config_ctrl_reg(ms3, AKECS_REG_MS3);
+
+static int AKI2C_RxData(char *rxData, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxData,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msgs, 2) < 0) {
+ printk(KERN_ERR "AKM8976 AKI2C_RxData: transfer error\n");
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int AKI2C_TxData(char *txData, int length)
+{
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msg, 1) < 0) {
+ printk(KERN_ERR "AKM8976 AKI2C_TxData: transfer error\n");
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int AKECS_Init(void)
+{
+ char buffer[4];
+
+ cspec_num = CSPEC_SEQ_NUM;
+ atomic_set(&cspec_frq, CSPEC_SFRQ_32);
+
+ /* Prepare data */
+ buffer[0] = AKECS_REG_MS2;
+ buffer[1] = ((CSPEC_AINT << 7) |
+ (cspec_num << 5) |
+ (atomic_read(&cspec_frq) << 4) |
+ (CSPEC_MCS << 1) | (CSPEC_MKS));
+ buffer[2] = (CSPEC_INTEN << 2);
+
+ return AKI2C_TxData(buffer, 3);
+}
+
+static void AKECS_Reset(void)
+{
+ gpio_set_value(pdata->reset, 0);
+ udelay(120);
+ gpio_set_value(pdata->reset, 1);
+}
+
+static int AKECS_StartMeasure(void)
+{
+ char buffer[2];
+ int ret;
+
+ buffer[0] = AKECS_REG_MS2;
+ buffer[1] = ((CSPEC_AINT << 7) |
+ (cspec_num << 5) |
+ (atomic_read(&cspec_frq) << 4) |
+ (CSPEC_MCS << 1) | (CSPEC_MKS));
+
+ /* Set data */
+ ret = AKI2C_TxData(buffer, 2);
+ if (ret < 0)
+ return ret;
+
+ /* Set measure mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_MEASURE;
+
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static int AKECS_StartPFFD(void)
+{
+ char buffer[2];
+ int ret;
+
+ /* Set PFFD mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_PFFD;
+ /* Set data */
+ ret = AKI2C_TxData(buffer, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(pdata->clk_on, 1);
+ if (ret < 0)
+ return ret;
+
+ pffd_mode = 1;
+ return ret;
+}
+
+static int AKECS_PowerDown(void)
+{
+ char buffer[2];
+ int ret;
+
+ /* Set powerdown mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_POWERDOWN;
+ /* Set data */
+ ret = AKI2C_TxData(buffer, 2);
+ if (ret < 0)
+ return ret;
+
+ /* Dummy read for clearing INT pin */
+ buffer[0] = AKECS_REG_TMPS;
+ /* Read data */
+ ret = AKI2C_RxData(buffer, 1);
+ if (ret < 0)
+ return ret;
+
+ if (pffd_mode == 1) {
+ pffd_mode = 0;
+ ret = gpio_direction_output(pdata->clk_on, 0);
+ }
+ return ret;
+}
+
+static int AKECS_StartE2PRead(void)
+{
+ char buffer[2];
+
+ /* Set measure mode */
+ buffer[0] = AKECS_REG_MS1;
+ buffer[1] = AKECS_MODE_E2P_READ;
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static int AKECS_GetData(void)
+{
+ char buffer[RBUFF_SIZE + 1];
+ int ret;
+
+ memset(buffer, 0, RBUFF_SIZE + 1);
+ buffer[0] = AKECS_REG_ST;
+ ret = AKI2C_RxData(buffer, 32);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&sense_data_mutex);
+ memcpy(sense_data, buffer, sizeof(buffer));
+ atomic_set(&data_ready, 1);
+ wake_up(&data_ready_wq);
+ mutex_unlock(&sense_data_mutex);
+
+ return 0;
+}
+
+static int AKECS_SetMode(char mode)
+{
+ int ret, status;
+ char buffer[1];
+
+ if (mode == AKECS_MODE_MEASURE_SNG) {
+ /* Check INT pin before mode setting */
+ status = gpio_get_value(pdata->intr);
+ if (status) {
+ printk(KERN_INFO
+ "AKM8976 AKECS_SetMode:"\
+ "dummy read to reset INT pin \n");
+ buffer[0] = AKECS_REG_TMPS;
+ ret = AKI2C_RxData(buffer, 1);
+ if (ret < 0)
+ return ret;
+ status = gpio_get_value(pdata->intr);
+ printk(KERN_INFO
+ "AKM8976 AKECS_SetMode:"\
+ "after dummy read, status = %d \n",
+ status);
+ }
+ }
+
+ switch (mode) {
+ case AKECS_MODE_MEASURE_SNG:
+ cspec_num = CSPEC_SNG_NUM;
+ ret = AKECS_StartMeasure();
+ break;
+ case AKECS_MODE_MEASURE_SEQ:
+ cspec_num = CSPEC_SEQ_NUM;
+ ret = AKECS_StartMeasure();
+ break;
+ case AKECS_MODE_PFFD:
+ ret = AKECS_StartPFFD();
+ break;
+ case AKECS_MODE_E2P_READ:
+ ret = AKECS_StartE2PRead();
+ break;
+ case AKECS_MODE_POWERDOWN:
+ ret = AKECS_PowerDown();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* wait at least 300us after changing mode */
+ msleep(1);
+ return ret;
+}
+
+static int AKECS_TransRBuff(char *rbuf, int size)
+{
+ wait_event_interruptible_timeout(data_ready_wq,
+ atomic_read(&data_ready), 1000);
+
+ if (!atomic_read(&data_ready)) {
+ if (!atomic_read(&suspend_flag)) {
+ printk(KERN_ERR
+ "AKM8976 AKECS_TransRBUFF: Data not ready\n");
+ failure_count++;
+ if (failure_count >= MAX_FAILURE_COUNT) {
+ printk(KERN_ERR
+ "AKM8976 AKECS_TransRBUFF:"\
+ "successive %d failure.\n",
+ failure_count);
+ atomic_set(&open_flag, -1);
+ wake_up(&open_wq);
+ failure_count = 0;
+ }
+ }
+ return -1;
+ }
+
+ if ((sense_data[0] & 0x02) == 0x02) {
+ printk(KERN_ERR "AKM8976 AKECS_TransRBUFF: Data error\n");
+ return -1;
+ }
+
+ mutex_lock(&sense_data_mutex);
+ memcpy(&rbuf[1], &sense_data[1], size);
+ atomic_set(&data_ready, 0);
+ mutex_unlock(&sense_data_mutex);
+
+
+ failure_count = 0;
+ return 0;
+}
+
+static int AKECS_Set_PERST(void)
+{
+ char buffer[2];
+
+ buffer[0] = AKECS_REG_MS3;
+ buffer[1] = ((CSPEC_INTEN << 2) | 0x01);
+
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static int AKECS_Set_G0RST(void)
+{
+ char buffer[2];
+
+ buffer[0] = AKECS_REG_MS3;
+ buffer[1] = ((CSPEC_INTEN << 2) | 0x02);
+
+ /* Set data */
+ return AKI2C_TxData(buffer, 2);
+}
+
+static void AKECS_Report_Value(short *rbuf)
+{
+ struct akm8976_data *data = i2c_get_clientdata(this_client);
+#if DEBUG
+ printk(KERN_INFO
+ "AKECS_Report_Value: yaw = %d, pitch = %d, roll = %d\n",
+ rbuf[0], rbuf[1], rbuf[2]);
+ printk(KERN_INFO
+ " tmp = %d, m_stat= %d, g_stat=%d\n",
+ rbuf[3], rbuf[4], rbuf[5]);
+ printk(KERN_INFO
+ " G_Sensor: x = %d LSB, y = %d LSB, z = %d LSB\n",
+ rbuf[6], rbuf[7], rbuf[8]);
+#endif
+ /* Report magnetic sensor information */
+ if (atomic_read(&m_flag)) {
+ input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
+ input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
+ input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
+ input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);
+ }
+
+ /* Report acceleration sensor information */
+ if (atomic_read(&a_flag)) {
+ input_report_abs(data->input_dev, ABS_X, rbuf[6]);
+ input_report_abs(data->input_dev, ABS_Y, rbuf[7]);
+ input_report_abs(data->input_dev, ABS_Z, rbuf[8]);
+ input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]);
+ }
+
+ /* Report temperature information */
+ if (atomic_read(&t_flag)) {
+ input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]);
+ }
+
+ if (atomic_read(&mv_flag)) {
+ input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]);
+ input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]);
+ input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]);
+ }
+
+ input_sync(data->input_dev);
+}
+
+static void AKECS_Report_StepCount(short count)
+{
+ struct akm8976_data *data = i2c_get_clientdata(this_client);
+#if DEBUG
+ printk(KERN_INFO"AKECS_Report_StepCount: %d \n", count);
+#endif
+
+ /* Report pedometer information */
+ input_report_abs(data->input_dev, ABS_GAS, count);
+ input_sync(data->input_dev);
+}
+
+static int AKECS_GetOpenStatus(void)
+{
+ wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0));
+ return atomic_read(&open_flag);
+}
+
+static int AKECS_GetCloseStatus(void)
+{
+ wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0));
+ return atomic_read(&open_flag);
+}
+
+static void AKECS_CloseDone(void)
+{
+ atomic_set(&m_flag, 1);
+ atomic_set(&a_flag, 1);
+ atomic_set(&t_flag, 1);
+ atomic_set(&mv_flag, 1);
+}
+
+static int akm_aot_open(struct inode *inode, struct file *file)
+{
+ int ret = -1;
+ if (atomic_cmpxchg(&open_count, 0, 1) == 0) {
+ if (atomic_cmpxchg(&open_flag, 0, 1) == 0) {
+ atomic_set(&reserve_open_flag, 1);
+ wake_up(&open_wq);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int akm_aot_release(struct inode *inode, struct file *file)
+{
+ atomic_set(&reserve_open_flag, 0);
+ atomic_set(&open_flag, 0);
+ atomic_set(&open_count, 0);
+ wake_up(&open_wq);
+ return 0;
+}
+
+static int
+akm_aot_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ short flag;
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ case ECS_IOCTL_APP_SET_AFLAG:
+ case ECS_IOCTL_APP_SET_TFLAG:
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ atomic_set(&m_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_MFLAG:
+ flag = atomic_read(&m_flag);
+ break;
+ case ECS_IOCTL_APP_SET_AFLAG:
+ atomic_set(&a_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_AFLAG:
+ flag = atomic_read(&a_flag);
+ break;
+ case ECS_IOCTL_APP_SET_TFLAG:
+ atomic_set(&t_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_TFLAG:
+ flag = atomic_read(&t_flag);
+ break;
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ atomic_set(&mv_flag, flag);
+ break;
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ flag = atomic_read(&mv_flag);
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ akmd_delay = flag;
+ break;
+ case ECS_IOCTL_APP_GET_DELAY:
+ flag = akmd_delay;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_GET_MFLAG:
+ case ECS_IOCTL_APP_GET_AFLAG:
+ case ECS_IOCTL_APP_GET_TFLAG:
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ case ECS_IOCTL_APP_GET_DELAY:
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int akm_pffd_open(struct inode *inode, struct file *file)
+{
+ int ret = -1;
+ if (atomic_cmpxchg(&open_count, 0, 1) == 0) {
+ if (atomic_cmpxchg(&open_flag, 0, 2) == 0) {
+ atomic_set(&reserve_open_flag, 2);
+ wake_up(&open_wq);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int akm_pffd_release(struct inode *inode, struct file *file)
+{
+ atomic_set(&reserve_open_flag, 0);
+ atomic_set(&open_flag, 0);
+ atomic_set(&open_count, 0);
+ wake_up(&open_wq);
+ return 0;
+}
+
+static int
+akm_pffd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ short flag;
+ int ret;
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_RESET_PEDOMETER:
+ ret = AKECS_Set_PERST();
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ akmd_delay = flag;
+ break;
+ case ECS_IOCTL_APP_GET_DELAY:
+ flag = akmd_delay;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_GET_DELAY:
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int akmd_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int akmd_release(struct inode *inode, struct file *file)
+{
+ AKECS_CloseDone();
+ return 0;
+}
+
+static int
+akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ void __user *argp = (void __user *)arg;
+
+ char msg[RBUFF_SIZE + 1], rwbuf[5], numfrq[2];
+ int ret = -1, status;
+ short mode, value[12], step_count, delay;
+ char *pbuffer = 0;
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ case ECS_IOCTL_WRITE:
+ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_SET_MODE:
+ if (copy_from_user(&mode, argp, sizeof(mode)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_SET_YPR:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_SET_STEP_CNT:
+ if (copy_from_user(&step_count, argp, sizeof(step_count)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_INIT:
+ ret = AKECS_Init();
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_RESET:
+ AKECS_Reset();
+ break;
+ case ECS_IOCTL_READ:
+ if (rwbuf[0] < 1)
+ return -EINVAL;
+ ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_WRITE:
+ if (rwbuf[0] < 2)
+ return -EINVAL;
+ ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_SET_MODE:
+ ret = AKECS_SetMode((char)mode);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_GETDATA:
+ ret = AKECS_TransRBuff(msg, RBUFF_SIZE);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_GET_NUMFRQ:
+ numfrq[0] = cspec_num;
+ numfrq[1] = atomic_read(&cspec_frq);
+ break;
+ case ECS_IOCTL_SET_PERST:
+ ret = AKECS_Set_PERST();
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_SET_G0RST:
+ ret = AKECS_Set_G0RST();
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_SET_YPR:
+ AKECS_Report_Value(value);
+ break;
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ status = AKECS_GetOpenStatus();
+ break;
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ status = AKECS_GetCloseStatus();
+ break;
+ case ECS_IOCTL_SET_STEP_CNT:
+ AKECS_Report_StepCount(step_count);
+ break;
+ case ECS_IOCTL_GET_CALI_DATA:
+ pbuffer = get_akm_cal_ram();
+ break;
+ case ECS_IOCTL_GET_DELAY:
+ delay = akmd_delay;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GETDATA:
+ if (copy_to_user(argp, &msg, sizeof(msg)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_NUMFRQ:
+ if (copy_to_user(argp, &numfrq, sizeof(numfrq)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_CALI_DATA:
+ if (copy_to_user(argp, pbuffer, MAX_CALI_SIZE))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_DELAY:
+ if (copy_to_user(argp, &delay, sizeof(delay)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void akm_work_func(struct work_struct *work)
+{
+ if (AKECS_GetData() < 0)
+ printk(KERN_ERR "AKM8976 akm_work_func: Get data failed\n");
+ enable_irq(this_client->irq);
+}
+
+static irqreturn_t akm8976_interrupt(int irq, void *dev_id)
+{
+ struct akm8976_data *data = dev_id;
+ disable_irq_nosync(this_client->irq);
+ schedule_work(&data->work);
+ return IRQ_HANDLED;
+}
+
+static int akm8976_init_client(struct i2c_client *client)
+{
+ struct akm8976_data *data;
+ int ret;
+
+ data = i2c_get_clientdata(client);
+
+ mutex_init(&sense_data_mutex);
+
+ ret = request_irq(client->irq, akm8976_interrupt, IRQF_TRIGGER_HIGH,
+ "akm8976", data);
+
+ if (ret < 0) {
+ printk(KERN_ERR "akm8976_init_client: request irq failed\n");
+ goto err;
+ }
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ } else {
+ pdata->reset = ECS_RST;
+ pdata->clk_on = ECS_CLK_ON;
+ pdata->intr = ECS_INTR;
+ }
+ }
+
+ ret = gpio_request(pdata->reset, "akm8976");
+ if (ret < 0) {
+ printk(KERN_ERR
+ "akm8976_init_client: request reset gpio failed\n");
+ goto err_free_irq;
+ }
+ ret = gpio_direction_output(pdata->reset, 1);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "akm8976_init_client: request reset gpio failed\n");
+ goto err_free_gpio;
+ }
+
+ ret = gpio_request(pdata->clk_on, "akm8976");
+ if (ret < 0) {
+ printk(KERN_ERR
+ "akm8976_init_client: request clock gpio failed\n");
+ goto err_free_gpio;
+ }
+
+ ret = gpio_direction_output(pdata->clk_on, 0);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "akm8976_init_client: request clock gpio failed\n");
+ goto err_free_gpio_2;
+ }
+
+ init_waitqueue_head(&data_ready_wq);
+ init_waitqueue_head(&open_wq);
+
+ /* As default, report all information */
+ atomic_set(&m_flag, 1);
+ atomic_set(&a_flag, 1);
+ atomic_set(&t_flag, 1);
+ atomic_set(&mv_flag, 1);
+
+ return 0;
+
+err_free_gpio_2:
+ gpio_free(pdata->clk_on);
+err_free_gpio:
+ gpio_free(pdata->reset);
+err_free_irq:
+ free_irq(client->irq, 0);
+err_alloc_data_failed:
+err:
+ return ret;
+}
+
+static struct file_operations akmd_fops = {
+ .owner = THIS_MODULE,
+ .open = akmd_open,
+ .release = akmd_release,
+ .ioctl = akmd_ioctl,
+};
+
+static struct file_operations akm_aot_fops = {
+ .owner = THIS_MODULE,
+ .open = akm_aot_open,
+ .release = akm_aot_release,
+ .ioctl = akm_aot_ioctl,
+};
+
+static struct file_operations akm_pffd_fops = {
+ .owner = THIS_MODULE,
+ .open = akm_pffd_open,
+ .release = akm_pffd_release,
+ .ioctl = akm_pffd_ioctl,
+};
+
+static struct miscdevice akm_aot_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8976_aot",
+ .fops = &akm_aot_fops,
+};
+
+static struct miscdevice akm_pffd_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8976_pffd",
+ .fops = &akm_pffd_fops,
+};
+
+static struct miscdevice akmd_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8976_daemon",
+ .fops = &akmd_fops,
+};
+
+static int akm8976_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct akm8976_data *akm;
+ int err;
+ char rxData[2];
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ akm = kzalloc(sizeof(struct akm8976_data), GFP_KERNEL);
+ if (!akm) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ INIT_WORK(&akm->work, akm_work_func);
+ i2c_set_clientdata(client, akm);
+ akm8976_init_client(client);
+ this_client = client;
+
+ /* Set EEPROM access mode */
+ err = AKECS_StartE2PRead();
+ if (err < 0)
+ goto exit_input_dev_alloc_failed;
+ /* Read ETS from EEPROM */
+ rxData[0] = 0x42;
+ err = AKI2C_RxData(rxData, 1);
+ if (err < 0)
+ goto exit_input_dev_alloc_failed;
+ revision = (0x03 & (rxData[0] >> 6));
+
+ /* Set Power down mode */
+ err = AKECS_PowerDown();
+ if (err < 0)
+ goto exit_input_dev_alloc_failed;
+
+ akm->input_dev = input_allocate_device();
+
+ if (!akm->input_dev) {
+ err = -ENOMEM;
+ printk(KERN_ERR
+ "akm8976_probe: Failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ set_bit(EV_ABS, akm->input_dev->evbit);
+ /* yaw */
+ input_set_abs_params(akm->input_dev, ABS_RX, 0, 360, 0, 0);
+ /* pitch */
+ input_set_abs_params(akm->input_dev, ABS_RY, -180, 180, 0, 0);
+ /* roll */
+ input_set_abs_params(akm->input_dev, ABS_RZ, -90, 90, 0, 0);
+ /* x-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_X, -1872, 1872, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Y, -1872, 1872, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Z, -1872, 1872, 0, 0);
+ /* temparature */
+ input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);
+ /* status of magnetic sensor */
+ input_set_abs_params(akm->input_dev, ABS_RUDDER, -32768, 3, 0, 0);
+ /* status of acceleration sensor */
+ input_set_abs_params(akm->input_dev, ABS_WHEEL, -32768, 3, 0, 0);
+ /* step count */
+ input_set_abs_params(akm->input_dev, ABS_GAS, 0, 65535, 0, 0);
+ /* x-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0);
+ /* y-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0);
+ /* z-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0);
+
+ akm->input_dev->name = "compass";
+
+ err = input_register_device(akm->input_dev);
+
+ if (err) {
+ printk(KERN_ERR
+ "akm8976_probe: Unable to register input device: %s\n",
+ akm->input_dev->name);
+ goto exit_input_register_device_failed;
+ }
+
+ err = misc_register(&akmd_device);
+ if (err) {
+ printk(KERN_ERR
+ "akm8976_probe: akmd_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = misc_register(&akm_aot_device);
+ if (err) {
+ printk(KERN_ERR
+ "akm8976_probe: akm_aot_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = misc_register(&akm_pffd_device);
+ if (err) {
+ printk(KERN_ERR
+ "akm8976_probe: akm_pffd_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_ms1);
+ err = device_create_file(&client->dev, &dev_attr_ms2);
+ err = device_create_file(&client->dev, &dev_attr_ms3);
+
+ gsensor_sysfs_init();
+
+ return 0;
+
+exit_misc_device_register_failed:
+exit_input_register_device_failed:
+ input_free_device(akm->input_dev);
+exit_input_dev_alloc_failed:
+ kfree(akm);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ return err;
+}
+
+static int akm8976_remove(struct i2c_client *client)
+{
+ struct akm8976_data *akm = i2c_get_clientdata(client);
+ free_irq(client->irq, akm);
+ input_unregister_device(akm->input_dev);
+ kfree(akm);
+ return 0;
+}
+
+static int akm8976_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ atomic_set(&suspend_flag, 1);
+ if (atomic_read(&open_flag) == 2)
+ AKECS_SetMode(AKECS_MODE_POWERDOWN);
+
+ atomic_set(&reserve_open_flag, atomic_read(&open_flag));
+ atomic_set(&open_flag, 0);
+ wake_up(&open_wq);
+ disable_irq(this_client->irq);
+ return 0;
+}
+
+static int akm8976_resume(struct i2c_client *client)
+{
+ enable_irq(this_client->irq);
+ if (atomic_read(&open_flag) == 2)
+ AKECS_SetMode(AKECS_MODE_PFFD);
+ atomic_set(&suspend_flag, 0);
+ atomic_set(&open_flag, atomic_read(&reserve_open_flag));
+ wake_up(&open_wq);
+ return 0;
+}
+
+static const struct i2c_device_id akm8976_id[] = {
+ { "akm8976", 0 },
+ { }
+};
+
+static struct i2c_driver akm8976_driver = {
+ .probe = akm8976_probe,
+ .remove = akm8976_remove,
+ .suspend = akm8976_suspend,
+ .resume = akm8976_resume,
+ .id_table = akm8976_id,
+ .driver = {
+ .name = "akm8976",
+ },
+};
+
+static int __init akm8976_init(void)
+{
+ printk(KERN_INFO "AKM8976A compass driver: init\n");
+ return i2c_add_driver(&akm8976_driver);
+}
+
+static void __exit akm8976_exit(void)
+{
+ i2c_del_driver(&akm8976_driver);
+}
+
+module_init(akm8976_init);
+module_exit(akm8976_exit);
+
+MODULE_AUTHOR("Hou-Kun Chen <hk_chen@htc.com>");
+MODULE_DESCRIPTION("AKM8976A compass driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/video_core/720p/Kconfig b/drivers/misc/video_core/720p/Kconfig
new file mode 100644
index 0000000..53808f9
--- /dev/null
+++ b/drivers/misc/video_core/720p/Kconfig
@@ -0,0 +1,35 @@
+#
+# VIDEO CORE
+#
+menuconfig MSM_720P_CORE
+ bool "720P Core Video Driver"
+ depends on ARCH_MSM7X30
+ default n
+ ---help---
+ Say Y here to see options for video device drivers.
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if MSM_720P_CORE
+
+config MSM_VIDEO_CORE_REG
+ tristate "MSM Video core registration"
+ depends on MSM_720P_CORE
+ default n
+ help
+ This option enables support for Video core.
+
+config MSM_VIDEO_CORE_VENC
+ tristate "Video encoder"
+ depends on MSM_VIDEO_CORE_REG
+ default n
+ help
+ This option enables support for Video encoder.
+
+config MSM_VIDEO_CORE_VDEC
+ tristate "Video decoder"
+ depends on MSM_VIDEO_CORE_REG
+ default n
+ help
+ This option enables support for Video decoder.
+
+endif # MSM_720P_CORE
diff --git a/drivers/misc/video_core/720p/Makefile b/drivers/misc/video_core/720p/Makefile
new file mode 100644
index 0000000..77aa694
--- /dev/null
+++ b/drivers/misc/video_core/720p/Makefile
@@ -0,0 +1,40 @@
+
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/ddl
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/dec
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/enc
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/resource_tracker
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/scheduler
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/vcd
+EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/init
+
+obj-$(CONFIG_MSM_VIDEO_CORE_REG) += video_corereg.o
+video_corereg-objs := ddl/vcd_ddl_firmware.o \
+ ddl/vcd_ddl_metadata.o \
+ ddl/video_core_720p.o \
+ ddl/vcd_ddl_utils.o \
+ ddl/vcd_ddl.o \
+ ddl/vcd_ddl_helper.o \
+ ddl/vcd_ddl_interrupt_handler.o \
+ ddl/vcd_ddl_hal.o \
+ ddl/vcd_ddl_properties.o \
+ init/video_core_init.o \
+ resource_tracker/vcd_res_tracker.o \
+ scheduler/vid_frame_scheduler_utils.o \
+ scheduler/vid_frame_scheduler.o \
+ scheduler/vid_frame_scheduler_api.o \
+ vcd/vcd_api.o \
+ vcd/vcd_power_sm.o \
+ vcd/vcd_client_sm.o \
+ vcd/vcd_device_sm.o \
+ vcd/vcd_sub.o \
+ ddl/vcd_ddl_errors.o
+
+obj-$(CONFIG_MSM_VIDEO_CORE_VDEC) += video_decoder.o
+
+video_decoder-objs := dec/vdec.o
+
+obj-$(CONFIG_MSM_VIDEO_CORE_VENC) += video_encoder.o
+
+video_encoder-objs := enc/venc.o \
+ enc/venc_internal.o
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.c b/drivers/misc/video_core/720p/ddl/vcd_ddl.c
new file mode 100644
index 0000000..22b7362
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.c
@@ -0,0 +1,575 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config, void *client_data)
+{
+ struct ddl_context *ddl_ctxt;
+ u32 status = VCD_S_SUCCESS;
+
+ if (!ddl_init_config || !ddl_init_config->ddl_callback ||
+ !ddl_init_config->core_virtual_base_addr) {
+ pr_err("ddl_dev_init:Bad_argument\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ ddl_ctxt = ddl_get_context();
+
+ if (DDL_IS_INITIALIZED(ddl_ctxt)) {
+ pr_err("ddl_dev_init:Multiple_init\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_ctxt)) {
+ pr_err("ddl_dev_init:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+
+ memset(ddl_ctxt, 0, sizeof(struct ddl_context));
+
+ DDL_BUSY(ddl_ctxt);
+
+ ddl_ctxt->ddl_callback = ddl_init_config->ddl_callback;
+ ddl_ctxt->pf_interrupt_clr = ddl_init_config->pf_interrupt_clr;
+ ddl_ctxt->core_virtual_base_addr =
+ ddl_init_config->core_virtual_base_addr;
+ ddl_ctxt->client_data = client_data;
+
+ ddl_ctxt->intr_status = DDL_INVALID_INTR_STATUS;
+
+ vidc_720p_set_device_virtual_base(ddl_ctxt->core_virtual_base_addr);
+
+ ddl_ctxt->current_ddl = NULL;
+ ddl_move_command_state(ddl_ctxt, DDL_CMD_INVALID);
+
+ ddl_client_transact(DDL_INIT_CLIENTS, NULL);
+
+ if (!ddl_dma_alloc(&ddl_ctxt->context_buf_addr, DDL_CONTEXT_MEMORY, npelly_context)) {
+ pr_err("ddl_dev_init:Context_alloc_fail\n");
+ status = VCD_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (!ddl_dma_alloc(&ddl_ctxt->db_line_buffer, DDL_DB_LINE_BUF_SIZE, npelly_dbl)) {
+ pr_err("ddl_dev_init:Line_buf_alloc_fail\n");
+ status = VCD_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (!ddl_dma_alloc(&ddl_ctxt->data_partition_tempbuf,
+ DDL_MPEG4_DATA_PARTITION_BUF_SIZE, npelly_mpeg4)) {
+ pr_err("ddl_dev_init:"
+ "Data_partition_buf_alloc_fail\n");
+ status = VCD_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (!ddl_dma_alloc(&ddl_ctxt->metadata_shared_input,
+ DDL_METADATA_TOTAL_INPUTBUFSIZE, npelly_meta)) {
+ pr_err("ddl_dev_init:"
+ "metadata_shared_input_alloc_fail\n");
+ status = VCD_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (!ddl_dma_alloc(&ddl_ctxt->dbg_core_dump, DDL_DBG_CORE_DUMP_SIZE, npelly_debug)) {
+ pr_err("ddl_dev_init:"
+ "dbg_core_dump_alloc_failed\n");
+ status = VCD_ERR_ALLOC_FAIL;
+ ddl_ctxt->enable_dbg_core_dump = 0;
+ goto out;
+ }
+
+out:
+ if (status) {
+ ddl_release_context_buffers(ddl_ctxt);
+ DDL_IDLE(ddl_ctxt);
+ return status;
+ }
+
+ ddl_move_command_state(ddl_ctxt, DDL_CMD_DMA_INIT);
+
+ ddl_core_init(ddl_ctxt);
+
+ return status;
+}
+
+u32 ddl_device_release(void *client_data)
+{
+ struct ddl_context *ddl_ctxt;
+
+ ddl_ctxt = ddl_get_context();
+
+ if (DDL_IS_BUSY(ddl_ctxt)) {
+ pr_err("ddl_dev_rel:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+
+ if (!DDL_IS_INITIALIZED(ddl_ctxt)) {
+ pr_err("ddl_dev_rel:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!ddl_client_transact(DDL_ACTIVE_CLIENT, NULL)) {
+ pr_err("ddl_dev_rel:Client_present_err\n");
+ return VCD_ERR_CLIENT_PRESENT;
+ }
+ DDL_BUSY(ddl_ctxt);
+
+ ddl_ctxt->device_state = DDL_DEVICE_NOTINIT;
+ ddl_ctxt->client_data = client_data;
+ ddl_move_command_state(ddl_ctxt, DDL_CMD_INVALID);
+ vidc_720p_stop_fw();
+
+ pr_debug("FW_ENDDONE\n");
+ ddl_release_context_buffers(ddl_ctxt);
+
+ DDL_IDLE(ddl_ctxt);
+
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_open(u32 **ddl_handle, u32 decoding)
+{
+ struct ddl_context *ddl_context;
+ struct ddl_client_context *ddl;
+ u32 status;
+
+ if (!ddl_handle) {
+ pr_err("ddl_open:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ ddl_context = ddl_get_context();
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_open:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ status = ddl_client_transact(DDL_GET_CLIENT, &ddl);
+
+ if (status) {
+ pr_err("ddl_open:Client_trasac_failed\n");
+ return status;
+ }
+
+ ddl_move_client_state(ddl, DDL_CLIENT_OPEN);
+
+ ddl->codec_data.hdr.decoding = decoding;
+ ddl->decoding = decoding;
+
+ ddl_set_default_meta_data_hdr(ddl);
+
+ ddl_set_initial_default_values(ddl);
+
+ *ddl_handle = (u32 *) ddl;
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_close(u32 **ddl_handle)
+{
+ struct ddl_context *ddl_context;
+ struct ddl_client_context **pp_ddl = (struct ddl_client_context **)
+ ddl_handle;
+
+ if (!pp_ddl || !*pp_ddl) {
+ pr_err("ddl_close:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ ddl_context = ddl_get_context();
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_close:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!DDLCLIENT_STATE_IS(*pp_ddl, DDL_CLIENT_OPEN)) {
+ pr_err("ddl_close:Not_in_open_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ ddl_move_client_state(*pp_ddl, DDL_CLIENT_INVALID);
+
+ ddl_client_transact(DDL_FREE_CLIENT, pp_ddl);
+
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data)
+{
+ struct ddl_client_context *ddl =
+ (struct ddl_client_context *)ddl_handle;
+ struct ddl_context *ddl_context;
+ struct ddl_encoder_data *enc;
+ u32 dpb_size;
+
+ ddl_context = ddl_get_context();
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_enc_start:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_enc_start:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || ddl->decoding) {
+ pr_err("ddl_enc_start:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+ pr_err("ddl_enc_start:Not_opened\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!ddl_encoder_ready_to_start(ddl)) {
+ pr_err("ddl_enc_start:Err_param_settings\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ enc = &ddl->codec_data.encoder;
+
+ dpb_size = ddl_get_yuv_buffer_size(&enc->frame_size,
+ &enc->re_con_buf_format, false);
+
+ dpb_size *= DDL_ENC_NUM_DPB_BUFFERS;
+ if (!ddl_dma_alloc(&enc->enc_dpb_addr, dpb_size, npelly_enc_dpb)) {
+ pr_err("ddl_enc_start:Dpb_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ if ((enc->codec_type.codec == VCD_CODEC_MPEG4 &&
+ !enc->short_header.short_header) ||
+ enc->codec_type.codec == VCD_CODEC_H264) {
+ if (!ddl_dma_alloc(&enc->seq_header, DDL_ENC_SEQHEADER_SIZE, npelly_enc_seq)) {
+ ddl_dma_free(&enc->enc_dpb_addr);
+ pr_err("ddl_enc_start:Seq_hdr_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ } else {
+ enc->seq_header.size = 0;
+ enc->seq_header.virt_addr = NULL;
+ }
+
+ DDL_BUSY(ddl_context);
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+ ddl_channel_set(ddl);
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_start(u32 *ddl_handle, struct vcd_phys_sequence_hdr *hdr,
+ void *client_data)
+{
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+ struct ddl_context *ddl_context;
+ struct ddl_decoder_data *decoder;
+
+ ddl_context = ddl_get_context();
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_dec_start:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_dec_start:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || !ddl->decoding) {
+ pr_err("ddl_dec_start:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+ pr_err("ddl_dec_start:Not_in_opened_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (hdr && (!hdr->sz || !hdr->addr)) {
+ pr_err("ddl_dec_start:Bad_param_seq_header\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ if (!ddl_decoder_ready_to_start(ddl, hdr)) {
+ pr_err("ddl_dec_start:Err_param_settings\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ DDL_BUSY(ddl_context);
+
+ decoder = &ddl->codec_data.decoder;
+ if (hdr) {
+ decoder->header_in_start = true;
+ decoder->decode_config = *hdr;
+ } else {
+ decoder->header_in_start = false;
+ decoder->decode_config.sz = 0;
+ }
+
+ if (decoder->codec_type.codec == VCD_CODEC_H264) {
+ if (!ddl_dma_alloc(&decoder->h264Vsp_temp_buffer,
+ DDL_DECODE_H264_VSPTEMP_BUFSIZE,
+ npelly_dec_h264)) {
+ DDL_IDLE(ddl_context);
+ pr_err("ddl_dec_start:H264Sps_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ }
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+
+ ddl_channel_set(ddl);
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *in_bits,
+ void *client_data)
+{
+ u32 vcd_status = VCD_S_SUCCESS;
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+ struct ddl_context *ddl_context = ddl_get_context();
+
+#ifdef CORE_TIMING_INFO
+ ddl_get_core_start_time(0);
+#endif
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_dec_frame:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_dec_frame:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || !ddl->decoding) {
+ pr_err("ddl_dec_frame:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (!in_bits || ((!in_bits->vcd_frm.phys_addr ||
+ !in_bits->vcd_frm.data_len) &&
+ !(VCD_FRAME_FLAG_EOS & in_bits->vcd_frm.flags))) {
+ pr_err("ddl_dec_frame:Bad_input_param\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ DDL_BUSY(ddl_context);
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+
+ ddl->input_frame = *in_bits;
+
+ if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+ ddl_decode_frame_run(ddl);
+ } else {
+ if (!ddl->codec_data.decoder.dp_buf.no_of_dec_pic_buf) {
+ pr_err("ddl_dec_frame:Dpbs_requied\n");
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ } else if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+ vcd_status = ddl_decode_set_buffers(ddl);
+ } else if (DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_INITCODEC)) {
+ ddl->codec_data.decoder.decode_config.addr =
+ ddl->input_frame.vcd_frm.phys_addr;
+ ddl->codec_data.decoder.decode_config.sz =
+ ddl->input_frame.vcd_frm.data_len;
+ ddl_decode_init_codec(ddl);
+ } else {
+ pr_err("Dec_frame:Wrong_state\n");
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ if (vcd_status)
+ DDL_IDLE(ddl_context);
+ }
+ return vcd_status;
+}
+
+u32 ddl_encode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *input_frame,
+ struct ddl_frame_data_tag *out_bits, void *client_data)
+{
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+ struct ddl_context *ddl_context = ddl_get_context();
+
+#ifdef CORE_TIMING_INFO
+ ddl_get_core_start_time(1);
+#endif
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_encode_frame:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_encode_frame:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || ddl->decoding) {
+ pr_err("ddl_encode_frame:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (!input_frame || !input_frame->vcd_frm.phys_addr ||
+ ddl->codec_data.encoder.input_buf_req.size !=
+ input_frame->vcd_frm.data_len) {
+ pr_err("ddl_encode_frame:Bad_input_params\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ if ((input_frame->vcd_frm.phys_addr + input_frame->vcd_frm.offset) &
+ DDL_STREAMBUF_ALIGN_GUARD_BYTES) {
+ pr_err("ddl_encode_frame:unaligned_yuv_start_addr\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ if (!out_bits || !out_bits->vcd_frm.phys_addr ||
+ !out_bits->vcd_frm.alloc_len) {
+ pr_err("ddl_encode_frame:Bad_output_params\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ if ((ddl->codec_data.encoder.output_buf_req.size +
+ out_bits->vcd_frm.offset) >
+ out_bits->vcd_frm.alloc_len) {
+ pr_err("ddl_encode_frame:offset > min_buf_size\n");
+ }
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+ pr_err("ddl_encode_frame:Wrong_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ DDL_BUSY(ddl_context);
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+
+ ddl->input_frame = *input_frame;
+ ddl->output_frame = *out_bits;
+
+ ddl_encode_frame_run(ddl);
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data)
+{
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+ struct ddl_context *ddl_context;
+
+ ddl_context = ddl_get_context();
+
+#ifdef CORE_TIMING_INFO
+ ddl_reset_time_variables(0);
+#endif
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_dec_end:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_dec_end:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || !ddl->decoding) {
+ pr_err("ddl_dec_end:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+ !DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+ !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) &&
+ !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)) {
+ pr_err("ddl_dec_end:Wrong_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ DDL_BUSY(ddl_context);
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+
+ ddl_channel_end(ddl);
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data)
+{
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+ struct ddl_context *ddl_context;
+
+ ddl_context = ddl_get_context();
+
+#ifdef CORE_TIMING_INFO
+ ddl_reset_time_variables(1);
+#endif
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_enc_end:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ if (DDL_IS_BUSY(ddl_context)) {
+ pr_err("ddl_enc_end:Ddl_busy\n");
+ return VCD_ERR_BUSY;
+ }
+ if (!ddl || ddl->decoding) {
+ pr_err("ddl_enc_end:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) &&
+ !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) &&
+ !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)) {
+ pr_err("ddl_enc_end:Wrong_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ DDL_BUSY(ddl_context);
+
+ ddl_context->current_ddl = ddl;
+ ddl_context->client_data = client_data;
+
+ ddl_channel_end(ddl);
+ return VCD_S_SUCCESS;
+}
+
+u32 ddl_reset_hw(u32 mode)
+{
+ struct ddl_context *ddl_context;
+ struct ddl_client_context *ddl;
+ int client_num;
+
+ pr_debug("ddl_reset_hw:called\n");
+ ddl_context = ddl_get_context();
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ DDL_BUSY(ddl_context);
+
+ if (ddl_context->core_virtual_base_addr)
+ vidc_720p_do_sw_reset();
+
+ ddl_context->device_state = DDL_DEVICE_NOTINIT;
+ for (client_num = 0; client_num < VCD_MAX_NO_CLIENT; ++client_num) {
+ ddl = ddl_context->ddl_clients[client_num];
+ ddl_context->ddl_clients[client_num] = NULL;
+ if (ddl) {
+ ddl_release_client_internal_buffers(ddl);
+ ddl_client_transact(DDL_FREE_CLIENT, &ddl);
+ }
+ }
+
+ ddl_release_context_buffers(ddl_context);
+ memset(ddl_context, 0, sizeof(struct ddl_context));
+
+ return true;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.h b/drivers/misc/video_core/720p/ddl/vcd_ddl.h
new file mode 100644
index 0000000..8f4e924
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.h
@@ -0,0 +1,286 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_H_
+#define _VCD_DDL_H_
+#include "vcd_ddl_api.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_firmware.h"
+#include "video_core_720p.h"
+
+#define DDL_BUSY_STATE 1
+#define DDL_IDLE_STATE 0
+#define DDL_ERROR_STATE 2
+#define DDL_IS_BUSY(ddl_context) \
+ (((ddl_context)->ddl_busy != DDL_IDLE_STATE))
+#define DDL_BUSY(ddl_context) \
+ ((ddl_context)->ddl_busy = DDL_BUSY_STATE)
+#define DDL_IDLE(ddl_context) \
+ ((ddl_context)->ddl_busy = DDL_IDLE_STATE)
+#define DDL_ERROR(ddl_context) \
+ ((ddl_context)->ddl_busy = DDL_ERROR_STATE)
+
+#define DDL_DEVICE_NOTINIT 0
+#define DDL_DEVICE_INITED 1
+#define DDL_DEVICE_HWFATAL 2
+#define DDL_IS_INITIALIZED(ddl_context) \
+ (ddl_context->device_state == DDL_DEVICE_INITED)
+
+#define DDLCOMMAND_STATE_IS(ddl_context, command_state) \
+ (command_state == (ddl_context)->cmd_state)
+
+#define DDLCLIENT_STATE_IS(ddl, cs) \
+ (cs == (ddl)->client_state)
+
+#define DDL_DPB_OP_INIT 1
+#define DDL_DPB_OP_MARK_FREE 2
+#define DDL_DPB_OP_MARK_BUSY 3
+#define DDL_DPB_OP_SET_MASK 4
+#define DDL_DPB_OP_RETRIEVE 5
+
+#define DDL_INIT_CLIENTS 0
+#define DDL_GET_CLIENT 1
+#define DDL_FREE_CLIENT 2
+#define DDL_ACTIVE_CLIENT 3
+
+#define DDL_INVALID_CHANNEL_ID ((u32)~0)
+#define DDL_INVALID_CODEC_TYPE ((u32)~0)
+#define DDL_INVALID_INTR_STATUS ((u32)~0)
+
+#define DDL_ENC_REQ_IFRAME 0x1
+#define DDL_ENC_CHANGE_IPERIOD 0x2
+#define DDL_ENC_CHANGE_BITRATE 0x4
+#define DDL_ENC_CHANGE_FRAMERATE 0x8
+
+#define DDL_DEC_REQ_OUTPUT_FLUSH 0x1
+
+struct ddl_dma_buffer {
+ void *virt_addr;
+ phys_addr_t phys_addr;
+ size_t size;
+};
+
+enum ddl_cmd_state {
+ DDL_CMD_INVALID = 0x0,
+ DDL_CMD_DMA_INIT = 0x1,
+ DDL_CMD_CPU_RESET = 0x2,
+ DDL_CMD_CHANNEL_SET = 0x3,
+ DDL_CMD_INIT_CODEC = 0x4,
+ DDL_CMD_HEADER_PARSE = 0x5,
+ DDL_CMD_DECODE_SET_DPB = 0x6,
+ DDL_CMD_DECODE_FRAME = 0x7,
+ DDL_CMD_ENCODE_FRAME = 0x8,
+ DDL_CMD_EOS = 0x9,
+ DDL_CMD_CHANNEL_END = 0xA,
+ DDL_CMD_32BIT = 0x7FFFFFFF
+};
+
+enum ddl_client_state {
+ DDL_CLIENT_INVALID = 0x0,
+ DDL_CLIENT_OPEN = 0x1,
+ DDL_CLIENT_WAIT_FOR_CHDONE = 0x2,
+ DDL_CLIENT_WAIT_FOR_INITCODEC = 0x3,
+ DDL_CLIENT_WAIT_FOR_INITCODECDONE = 0x4,
+ DDL_CLIENT_WAIT_FOR_DPB = 0x5,
+ DDL_CLIENT_WAIT_FOR_DPBDONE = 0x6,
+ DDL_CLIENT_WAIT_FOR_FRAME = 0x7,
+ DDL_CLIENT_WAIT_FOR_FRAME_DONE = 0x8,
+ DDL_CLIENT_WAIT_FOR_EOS_DONE = 0x9,
+ DDL_CLIENT_WAIT_FOR_CHEND = 0xA,
+ DDL_CLIENT_FATAL_ERROR = 0xB,
+ DDL_CLIENT_32BIT = 0x7FFFFFFF
+};
+
+struct ddl_mask {
+ u32 client_mask;
+ u32 hw_mask;
+};
+
+struct ddl_context;
+
+struct ddl_client_context;
+
+struct ddl_codec_data_hdr {
+ u32 decoding;
+};
+
+struct ddl_encoder_data {
+ struct ddl_codec_data_hdr hdr;
+ struct vcd_property_codec codec_type;
+ struct vcd_property_frame_size frame_size;
+ struct vcd_property_frame_rate frame_rate;
+ struct vcd_property_target_bitrate target_bit_rate;
+ struct vcd_property_profile profile;
+ struct vcd_property_level level;
+ struct vcd_property_rate_control rc_type;
+ struct vcd_property_multi_slice multi_slice;
+ u32 meta_data_enable_flag;
+ u32 suffix;
+ struct ddl_dma_buffer meta_data_input;
+ phys_addr_t meta_data_offset;
+ struct vcd_property_short_header short_header;
+ struct vcd_property_vop_timing vop_timing;
+ u32 hdr_ext_control;
+ struct vcd_property_db_config db_control;
+ struct vcd_property_entropy_control entropy_control;
+ struct vcd_property_i_period period;
+ struct vcd_property_session_qp session_qp;
+ struct vcd_property_qp_range qp_range;
+ struct vcd_property_rc_level rc_level;
+ u32 r_cframe_skip;
+ u32 vb_vbuffer_size;
+ struct vcd_property_frame_level_rc_params frame_level_rc;
+ struct vcd_property_adaptive_rc_params adaptive_rc;
+ struct vcd_property_intra_refresh_mb_number intra_refresh;
+ struct vcd_property_buffer_format buf_format;
+ struct vcd_property_buffer_format re_con_buf_format;
+ u32 dynamic_prop_change;
+ u32 dynmic_prop_change_req;
+ u32 ext_enc_control_val;
+ struct vidc_720p_enc_frame_info enc_frame_info;
+ struct ddl_dma_buffer enc_dpb_addr;
+ struct ddl_dma_buffer seq_header;
+ struct vcd_buffer_requirement input_buf_req;
+ struct vcd_buffer_requirement output_buf_req;
+ struct vcd_buffer_requirement client_input_buf_req;
+ struct vcd_buffer_requirement client_output_buf_req;
+};
+
+struct ddl_decoder_data {
+ struct ddl_codec_data_hdr hdr;
+ struct vcd_property_codec codec_type;
+ struct vcd_property_buffer_format buf_format;
+ struct vcd_property_frame_size frame_size;
+ struct vcd_property_frame_size client_frame_size;
+ struct vcd_property_profile profile;
+ struct vcd_property_level level;
+ u32 progressive_only;
+ u32 meta_data_enable_flag;
+ u32 suffix;
+ struct ddl_dma_buffer meta_data_input;
+ struct ddl_dma_buffer ref_buffer;
+ size_t meta_data_offset;
+ struct vcd_property_post_filter post_filter;
+ struct vcd_phys_sequence_hdr decode_config;
+ u32 header_in_start;
+ u32 min_dpb_num;
+ size_t y_cb_cr_size;
+ struct ddl_property_dec_pic_buffers dp_buf;
+ struct ddl_mask dpb_mask;
+ u32 dynamic_prop_change;
+ u32 dynmic_prop_change_req;
+ struct vidc_720p_dec_disp_info dec_disp_info;
+ struct ddl_dma_buffer dpb_comv_buffer;
+ struct ddl_dma_buffer h264Vsp_temp_buffer;
+ struct vcd_buffer_requirement actual_input_buf_req;
+ struct vcd_buffer_requirement min_input_buf_req;
+ struct vcd_buffer_requirement client_input_buf_req;
+ struct vcd_buffer_requirement actual_output_buf_req;
+ struct vcd_buffer_requirement min_output_buf_req;
+ struct vcd_buffer_requirement client_output_buf_req;
+};
+
+union ddl_codec_data {
+ struct ddl_codec_data_hdr hdr;
+ struct ddl_decoder_data decoder;
+ struct ddl_encoder_data encoder;
+};
+
+struct ddl_context {
+ u8 *core_virtual_base_addr;
+ void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size,
+ u32 *ddl_handle, void *const client_data);
+ void *client_data;
+ void (*pf_interrupt_clr) (void);
+ enum ddl_cmd_state cmd_state;
+ struct ddl_client_context *current_ddl;
+ struct ddl_dma_buffer context_buf_addr;
+ struct ddl_dma_buffer db_line_buffer;
+ struct ddl_dma_buffer data_partition_tempbuf;
+ struct ddl_dma_buffer metadata_shared_input;
+ struct ddl_dma_buffer dbg_core_dump;
+ u32 enable_dbg_core_dump;
+ struct ddl_client_context *ddl_clients[VCD_MAX_NO_CLIENT];
+ u32 device_state;
+ u32 ddl_busy;
+ u32 intr_status;
+ u32 cmd_err_status;
+ u32 disp_pic_err_status;
+ u32 op_failed;
+};
+
+struct ddl_client_context {
+ struct ddl_context *ddl_context;
+ enum ddl_client_state client_state;
+ u32 decoding;
+ u32 channel_id;
+ struct ddl_frame_data_tag input_frame;
+ struct ddl_frame_data_tag output_frame;
+ union ddl_codec_data codec_data;
+};
+
+struct ddl_context *ddl_get_context(void);
+void ddl_move_command_state(struct ddl_context *ddl_context,
+ enum ddl_cmd_state command_state);
+void ddl_move_client_state(struct ddl_client_context *ddl,
+ enum ddl_client_state client_state);
+void ddl_core_init(struct ddl_context *);
+void ddl_core_start_cpu(struct ddl_context *);
+void ddl_channel_set(struct ddl_client_context *);
+void ddl_channel_end(struct ddl_client_context *);
+void ddl_encode_init_codec(struct ddl_client_context *);
+void ddl_decode_init_codec(struct ddl_client_context *);
+void ddl_encode_frame_run(struct ddl_client_context *);
+void ddl_decode_frame_run(struct ddl_client_context *);
+void ddl_decode_eos_run(struct ddl_client_context *);
+void ddl_release_context_buffers(struct ddl_context *);
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl);
+u32 ddl_decode_set_buffers(struct ddl_client_context *);
+u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *dec,
+ struct ddl_frame_data_tag *in_out_frame, u32 operation);
+u32 ddl_client_transact(u32, struct ddl_client_context **);
+void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *dec,
+ u32 estimate);
+void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *enc);
+void ddl_set_default_dec_property(struct ddl_client_context *);
+u32 ddl_encoder_ready_to_start(struct ddl_client_context *);
+u32 ddl_decoder_ready_to_start(struct ddl_client_context *,
+ struct vcd_phys_sequence_hdr *);
+size_t ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size,
+ struct vcd_property_buffer_format *buf_format, u32 interlace);
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+ u32 interlace);
+void ddl_encode_dynamic_property(struct ddl_client_context *ddl, u32 enable);
+void ddl_decode_dynamic_property(struct ddl_client_context *ddl, u32 enable);
+void ddl_set_initial_default_values(struct ddl_client_context *ddl);
+u32 ddl_handle_core_errors(struct ddl_context *ddl_context);
+void ddl_client_fatal_cb(struct ddl_context *ddl_context);
+void ddl_hw_fatal_cb(struct ddl_context *ddl_context);
+u32 ddl_hal_engine_reset(struct ddl_context *ddl_context);
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h
new file mode 100644
index 0000000..b3ce6b7
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_API_H_
+#define _VCD_DDL_API_H_
+#include "vcd_ddl_internal_property.h"
+
+struct ddl_init_config {
+ u8 *core_virtual_base_addr;
+ void (*pf_interrupt_clr) (void);
+ void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size,
+ u32 *ddl_handle, void *const client_data);
+};
+
+struct ddl_frame_data_tag {
+ struct vcd_frame_data vcd_frm;
+ u32 intrlcd_ip_frm_tag;
+ u32 frm_trans_end;
+ u32 frm_delta;
+};
+
+u32 ddl_device_init(struct ddl_init_config *ddl_init_config, void *client_data);
+u32 ddl_device_release(void *client_data);
+u32 ddl_open(u32 **ddl_handle, u32 decoding);
+u32 ddl_close(u32 **ddl_handle);
+u32 ddl_encode_start(u32 *ddl_handle, void *client_data);
+u32 ddl_encode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *input_frame,
+ struct ddl_frame_data_tag *output_bit, void *client_data);
+u32 ddl_encode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_decode_start(u32 *ddl_handle, struct vcd_phys_sequence_hdr *header,
+ void *client_data);
+u32 ddl_decode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *in_bits,
+ void *client_data);
+u32 ddl_decode_end(u32 *ddl_handle, void *client_data);
+u32 ddl_set_property(u32 *ddl_handle, struct vcd_property_hdr *property_hdr,
+ void *property_value);
+u32 ddl_get_property(u32 *ddl_handle, struct vcd_property_hdr *property_hdr,
+ void *property_value);
+void ddl_read_and_clear_interrupt(void);
+u32 ddl_process_core_response(void);
+u32 ddl_reset_hw(u32 mode);
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h
new file mode 100644
index 0000000..8e0cfcd
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_CORE_H_
+#define _VCD_DDL_CORE_H_
+
+#define DDL_LINEAR_BUF_ALIGN_MASK 0xFFFFFFF8U
+#define DDL_LINEAR_BUF_ALIGN_GUARD_BYTES 0x7
+#define DDL_LINEAR_BUFFER_ALIGN_BYTES 8
+
+#define DDL_TILE_BUF_ALIGN_MASK 0xFFFFE000U
+#define DDL_TILE_BUF_ALIGN_GUARD_BYTES 0x1FFF
+#define DDL_TILE_BUFFER_ALIGN_BYTES 8192
+
+#define DDL_MAX_FRAME_WIDTH 1280
+#define DDL_MAX_FRAME_HEIGHT 720
+
+#define DDL_MAX_DP_FRAME_WIDTH 352
+#define DDL_MAX_DP_FRAME_HEIGHT 288
+
+#define DDL_SW_RESET_SLEEP 10
+
+#define VCD_MAX_NO_CLIENT 4
+#define VCD_FRAME_COMMAND_DEPTH 1
+#define VCD_GENERAL_COMMAND_DEPTH 1
+#define VCD_COMMAND_EXCLUSIVE true
+
+#define DDL_HW_TIMEOUT_IN_MS 1000
+
+#define DDL_STREAMBUF_ALIGN_GUARD_BYTES 0x7
+
+#define DDL_CONTEXT_MEMORY (1024 * 15 * (VCD_MAX_NO_CLIENT + 1))
+#define DDL_DB_LINE_BUF_SIZE \
+(((((DDL_MAX_FRAME_WIDTH * 4) - 1) / 256) + 1) * 8 * 1024)
+#define DDL_MPEG4_DATA_PARTITION_BUF_SIZE (64 * 1024)
+#define DDL_DECODE_H264_VSPTEMP_BUFSIZE 0x51c00
+#define DDL_ENC_NUM_DPB_BUFFERS 2
+
+#define DDL_DBG_CORE_DUMP_SIZE (10 * 1024)
+
+#define DDL_BUFEND_PAD 256
+#define DDL_ENC_SEQHEADER_SIZE (256+DDL_BUFEND_PAD)
+#define DDL_MAX_BUFFER_COUNT 32
+
+#define DDL_MPEG_REFBUF_COUNT 2
+
+#define DDL_MPEG_COMV_BUF_NO 2
+#define DDL_H263_COMV_BUF_NO 0
+#define DDL_COMV_BUFLINE_NO 128
+#define DDL_VC1_COMV_BUFLINE_NO 32
+#define DDL_MINIMUM_BYTE_PER_SLICE 1920
+
+#define DDL_MAX_H264_QP 51
+#define DDL_MAX_MPEG4_QP 31
+
+//TODO clean this dirty thing
+#define DDL_PADDING_HACK(addr) \
+ (addr) = (u32)((((u32)(addr) + DDL_STREAMBUF_ALIGN_GUARD_BYTES) & \
+ ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES)) + DDL_BUFEND_PAD)
+
+#define DDL_FRAMESIZE_DIV_FACTOR 0xF
+#define DDL_ALLOW_DEC_FRAMESIZE(w, h) (\
+ (w) <= DDL_MAX_FRAME_WIDTH && \
+ (h) <= DDL_MAX_FRAME_HEIGHT && \
+ (((w) >= 32 && (h) >= 16) || ((w) >= 16 && (h) >= 32)))
+
+#define DDL_ALLOW_ENC_FRAMESIZE(w, h) (\
+ (w) <= DDL_MAX_FRAME_WIDTH && \
+ (h) <= DDL_MAX_FRAME_HEIGHT && \
+ (w) >= 32 && (h) >= 32 && \
+ !((w) & DDL_FRAMESIZE_DIV_FACTOR) && \
+ !((h) & DDL_FRAMESIZE_DIV_FACTOR))
+
+#define DDL_TILE_ALIGN_WIDTH 128
+#define DDL_TILE_ALIGN_HEIGHT 32
+#define DDL_TILE_MULTIPLY_FACTOR 8192
+#define DDL_TILE_ALIGN(val, grid) \
+ (((val) + (grid) - 1) / (grid) * (grid))
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c
new file mode 100644
index 0000000..1aad338
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c
@@ -0,0 +1,509 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define ERR(x...) printk(KERN_ERR x)
+
+#define INVALID_CHANNEL_NUMBER 1
+#define INVALID_COMMAND_ID 2
+#define CHANNEL_ALREADY_IN_USE 3
+#define CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE 4
+#define CHANNEL_SET_ERROR_INIT_CODEC 5
+#define INIT_CODEC_ALREADY_CALLED 6
+#define CHANNEL_SET_ERROR_INIT_BUFFERS 7
+#define INIT_CODEC_ERROR_INIT_BUFFERS 8
+#define INIT_BUFFER_ALREADY_CALLED 9
+#define CHANNEL_SET_ERROR_FRAME_RUN 10
+#define INIT_CODEC_ERROR_FRAME_RUN 11
+#define INIT_BUFFERS_ERROR_FRAME_RUN 12
+#define CODEC_LIMIT_EXCEEDED 13
+#define FIRMWARE_SIZE_ZERO 14
+#define FIRMWARE_ADDRESS_EXT_ZERO 15
+#define CONTEXT_DMA_IN_ERROR 16
+#define CONTEXT_DMA_OUT_ERROR 17
+#define PROGRAM_DMA_ERROR 18
+#define CONTEXT_STORE_EXT_ADD_ZERO 19
+#define MEM_ALLOCATION_FAILED 20
+
+
+#define UNSUPPORTED_FEATURE_IN_PROFILE 27
+#define RESOLUTION_NOT_SUPPORTED 28
+#define HEADER_NOT_FOUND 52
+#define MB_NUM_INVALID 61
+#define FRAME_RATE_NOT_SUPPORTED 62
+#define INVALID_QP_VALUE 63
+#define INVALID_RC_REACTION_COEFFICIENT 64
+#define INVALID_CPB_SIZE_AT_GIVEN_LEVEL 65
+
+#define ALLOC_DPB_SIZE_NOT_SUFFICIENT 71
+#define ALLOC_DB_SIZE_NOT_SUFFICIENT 72
+#define ALLOC_COMV_SIZE_NOT_SUFFICIENT 73
+#define NUM_BUF_OUT_OF_RANGE 74
+#define NULL_CONTEXT_POINTER 75
+#define NULL_COMAMND_CONTROL_COMM_POINTER 76
+#define NULL_METADATA_INPUT_POINTER 77
+#define NULL_DPB_POINTER 78
+#define NULL_DB_POINTER 79
+#define NULL_COMV_POINTER 80
+
+#define DIVIDE_BY_ZERO 81
+#define BIT_STREAM_BUF_EXHAUST 82
+#define DMA_NOT_STOPPED 83
+#define DMA_TX_NOT_COMPLETE 84
+
+#define MB_HEADER_NOT_DONE 85
+#define MB_COEFF_NOT_DONE 86
+#define CODEC_SLICE_NOT_DONE 87
+#define VME_NOT_READY 88
+#define VC1_BITPLANE_DECODE_ERR 89
+
+
+#define VSP_NOT_READY 90
+#define BUFFER_FULL_STATE 91
+
+#define RESOLUTION_MISMATCH 112
+#define NV_QUANT_ERR 113
+#define SYNC_MARKER_ERR 114
+#define FEATURE_NOT_SUPPORTED 115
+#define MEM_CORRUPTION 116
+#define INVALID_REFERENCE_FRAME 117
+#define PICTURE_CODING_TYPE_ERR 118
+#define MV_RANGE_ERR 119
+#define PICTURE_STRUCTURE_ERR 120
+#define SLICE_ADDR_INVALID 121
+#define NON_PAIRED_FIELD_NOT_SUPPORTED 122
+#define NON_FRAME_DATA_RECEIVED 123
+#define INCOMPLETE_FRAME 124
+#define NO_BUFFER_RELEASED_FROM_HOST 125
+#define PICTURE_MANAGEMENT_ERROR 128
+#define INVALID_MMCO 129
+#define INVALID_PIC_REORDERING 130
+#define INVALID_POC_TYPE 131
+#define ACTIVE_SPS_NOT_PRESENT 132
+#define ACTIVE_PPS_NOT_PRESENT 133
+#define INVALID_SPS_ID 134
+#define INVALID_PPS_ID 135
+
+
+#define METADATA_NO_SPACE_QP 151
+#define METADATA_NO_SAPCE_CONCEAL_MB 152
+#define METADATA_NO_SPACE_VC1_PARAM 153
+#define METADATA_NO_SPACE_SEI 154
+#define METADATA_NO_SPACE_VUI 155
+#define METADATA_NO_SPACE_EXTRA 156
+#define METADATA_NO_SPACE_DATA_NONE 157
+#define FRAME_RATE_UNKNOWN 158
+#define ASPECT_RATIO_UNKOWN 159
+#define COLOR_PRIMARIES_UNKNOWN 160
+#define TRANSFER_CHAR_UNKWON 161
+#define MATRIX_COEFF_UNKNOWN 162
+#define NON_SEQ_SLICE_ADDR 163
+#define BROKEN_LINK 164
+#define FRAME_CONCEALED 165
+#define PROFILE_UNKOWN 166
+#define LEVEL_UNKOWN 167
+#define BIT_RATE_NOT_SUPPORTED 168
+#define COLOR_DIFF_FORMAT_NOT_SUPPORTED 169
+#define NULL_EXTRA_METADATA_POINTER 170
+#define SYNC_POINT_NOT_RECEIVED_STARTED_DECODING 171
+#define NULL_FW_DEBUG_INFO_POINTER 172
+#define ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT 173
+#define MAX_STAGE_COUNTER_EXCEEDED 174
+
+#define METADATA_NO_SPACE_MB_INFO 180
+#define METADATA_NO_SPACE_SLICE_SIZE 181
+#define RESOLUTION_WARNING 182
+
+void ddl_hw_fatal_cb(struct ddl_context *ddl_context)
+{
+ /* Invalidate the command state */
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ ddl_context->device_state = DDL_DEVICE_HWFATAL;
+
+ /* callback to the client to indicate hw fatal error */
+ ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL,
+ VCD_ERR_HW_FATAL, NULL, 0,
+ (void *)ddl_context->current_ddl,
+ ddl_context->client_data);
+
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_handle_hw_fatal_errors(struct ddl_context
+ *ddl_context)
+{
+ u32 status = false;
+
+ switch (ddl_context->cmd_err_status) {
+
+ case INVALID_CHANNEL_NUMBER:
+ case INVALID_COMMAND_ID:
+ case CHANNEL_ALREADY_IN_USE:
+ case CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE:
+ case CHANNEL_SET_ERROR_INIT_CODEC:
+ case INIT_CODEC_ALREADY_CALLED:
+ case CHANNEL_SET_ERROR_INIT_BUFFERS:
+ case INIT_CODEC_ERROR_INIT_BUFFERS:
+ case INIT_BUFFER_ALREADY_CALLED:
+ case CHANNEL_SET_ERROR_FRAME_RUN:
+ case INIT_CODEC_ERROR_FRAME_RUN:
+ case INIT_BUFFERS_ERROR_FRAME_RUN:
+ case CODEC_LIMIT_EXCEEDED:
+ case FIRMWARE_SIZE_ZERO:
+ case FIRMWARE_ADDRESS_EXT_ZERO:
+
+ case CONTEXT_DMA_IN_ERROR:
+ case CONTEXT_DMA_OUT_ERROR:
+ case PROGRAM_DMA_ERROR:
+ case CONTEXT_STORE_EXT_ADD_ZERO:
+ case MEM_ALLOCATION_FAILED:
+
+ case DIVIDE_BY_ZERO:
+ case DMA_NOT_STOPPED:
+ case DMA_TX_NOT_COMPLETE:
+
+ case VSP_NOT_READY:
+ case BUFFER_FULL_STATE:
+ ERR("HW FATAL ERROR");
+ ddl_hw_fatal_cb(ddl_context);
+ status = true;
+ break;
+ }
+ return status;
+}
+
+void ddl_client_fatal_cb(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl =
+ ddl_context->current_ddl;
+
+ if (ddl_context->cmd_state == DDL_CMD_DECODE_FRAME)
+ ddl_decode_dynamic_property(ddl, false);
+ else if (ddl_context->cmd_state == DDL_CMD_ENCODE_FRAME)
+ ddl_encode_dynamic_property(ddl, false);
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ ddl_move_client_state(ddl, DDL_CLIENT_FATAL_ERROR);
+
+ ddl_context->ddl_callback
+ (
+ VCD_EVT_IND_HWERRFATAL,
+ VCD_ERR_CLIENT_FATAL,
+ NULL,
+ 0,
+ (void *)ddl,
+ ddl_context->client_data
+ );
+
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_handle_client_fatal_errors(struct ddl_context
+ *ddl_context)
+{
+ u32 status = false;
+
+ switch (ddl_context->cmd_err_status) {
+ case UNSUPPORTED_FEATURE_IN_PROFILE:
+ case RESOLUTION_NOT_SUPPORTED:
+ case HEADER_NOT_FOUND:
+ case INVALID_SPS_ID:
+ case INVALID_PPS_ID:
+
+ case MB_NUM_INVALID:
+ case FRAME_RATE_NOT_SUPPORTED:
+ case INVALID_QP_VALUE:
+ case INVALID_RC_REACTION_COEFFICIENT:
+ case INVALID_CPB_SIZE_AT_GIVEN_LEVEL:
+
+ case ALLOC_DPB_SIZE_NOT_SUFFICIENT:
+ case ALLOC_DB_SIZE_NOT_SUFFICIENT:
+ case ALLOC_COMV_SIZE_NOT_SUFFICIENT:
+ case NUM_BUF_OUT_OF_RANGE:
+ case NULL_CONTEXT_POINTER:
+ case NULL_COMAMND_CONTROL_COMM_POINTER:
+ case NULL_METADATA_INPUT_POINTER:
+ case NULL_DPB_POINTER:
+ case NULL_DB_POINTER:
+ case NULL_COMV_POINTER:
+ {
+ status = true;
+ break;
+ }
+ }
+
+ if (!status)
+ ERR("UNKNOWN-OP-FAILED");
+
+ ddl_client_fatal_cb(ddl_context);
+
+ return true;
+}
+
+static void ddl_input_failed_cb(struct ddl_context *ddl_context,
+ u32 vcd_event, u32 vcd_status)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ if (ddl->decoding)
+ ddl_decode_dynamic_property(ddl, false);
+ else
+ ddl_encode_dynamic_property(ddl, false);
+
+ ddl_context->ddl_callback(vcd_event,
+ vcd_status, &ddl->input_frame,
+ sizeof(struct ddl_frame_data_tag),
+ (void *)ddl, ddl_context->client_data);
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+}
+
+static u32 ddl_handle_core_recoverable_errors(struct ddl_context \
+ *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ u32 vcd_status = VCD_S_SUCCESS;
+ u32 vcd_event = VCD_EVT_RESP_INPUT_DONE;
+ u32 eos = false, pending_display = 0, release_mask = 0;
+
+ if (ddl_context->cmd_state != DDL_CMD_DECODE_FRAME &&
+ ddl_context->cmd_state != DDL_CMD_ENCODE_FRAME) {
+ return false;
+ }
+ switch (ddl_context->cmd_err_status) {
+ case NON_PAIRED_FIELD_NOT_SUPPORTED:
+ {
+ vcd_status = VCD_ERR_INTRLCD_FIELD_DROP;
+ break;
+ }
+ case NO_BUFFER_RELEASED_FROM_HOST:
+ {
+ /* lets check sanity of this error */
+ release_mask =
+ ddl->codec_data.decoder.dpb_mask.hw_mask;
+ while (release_mask > 0) {
+ if ((release_mask & 0x1))
+ pending_display += 1;
+ release_mask >>= 1;
+ }
+
+ if (pending_display >=
+ ddl->codec_data.decoder.min_dpb_num) {
+ DBG("FWISSUE-REQBUF!!");
+ /* callback to client for client fatal error */
+ ddl_client_fatal_cb(ddl_context);
+ return true ;
+ }
+ vcd_event = VCD_EVT_RESP_OUTPUT_REQ;
+ break;
+ }
+ case BIT_STREAM_BUF_EXHAUST:
+ case MB_HEADER_NOT_DONE:
+ case MB_COEFF_NOT_DONE:
+ case CODEC_SLICE_NOT_DONE:
+ case VME_NOT_READY:
+ case VC1_BITPLANE_DECODE_ERR:
+ {
+ u32 reset_core;
+ /* need to reset the internal core hw engine */
+ reset_core = ddl_hal_engine_reset(ddl_context);
+ if (!reset_core)
+ return true;
+ /* fall through to process bitstream error handling */
+ }
+ case RESOLUTION_MISMATCH:
+ case NV_QUANT_ERR:
+ case SYNC_MARKER_ERR:
+ case FEATURE_NOT_SUPPORTED:
+ case MEM_CORRUPTION:
+ case INVALID_REFERENCE_FRAME:
+ case PICTURE_CODING_TYPE_ERR:
+ case MV_RANGE_ERR:
+ case PICTURE_STRUCTURE_ERR:
+ case SLICE_ADDR_INVALID:
+ case NON_FRAME_DATA_RECEIVED:
+ case INCOMPLETE_FRAME:
+ case PICTURE_MANAGEMENT_ERROR:
+ case INVALID_MMCO:
+ case INVALID_PIC_REORDERING:
+ case INVALID_POC_TYPE:
+ case ACTIVE_SPS_NOT_PRESENT:
+ case ACTIVE_PPS_NOT_PRESENT:
+ {
+ vcd_status = VCD_ERR_BITSTREAM_ERR;
+ break;
+ }
+ }
+
+ if (!vcd_status && vcd_event == VCD_EVT_RESP_INPUT_DONE)
+ return false;
+
+ ddl->input_frame.frm_trans_end = true;
+
+ eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) &&
+ ((VCD_FRAME_FLAG_EOS & ddl->input_frame.
+ vcd_frm.flags)));
+
+ if ((ddl->decoding && eos) ||
+ (!ddl->decoding))
+ ddl->input_frame.frm_trans_end = false;
+
+ if (vcd_event == VCD_EVT_RESP_INPUT_DONE &&
+ ddl->decoding &&
+ !ddl->codec_data.decoder.header_in_start &&
+ !ddl->codec_data.decoder.dec_disp_info.img_size_x &&
+ !ddl->codec_data.decoder.dec_disp_info.img_size_y
+ ) {
+ /* this is first frame seq. header only case */
+ vcd_status = VCD_S_SUCCESS;
+ ddl->input_frame.vcd_frm.flags |=
+ VCD_FRAME_FLAG_CODECCONFIG;
+ ddl->input_frame.frm_trans_end = !eos;
+ /* put just some non - zero value */
+ ddl->codec_data.decoder.dec_disp_info.img_size_x = 0xff;
+ }
+ /* inform client about input failed */
+ ddl_input_failed_cb(ddl_context, vcd_event, vcd_status);
+
+ /* for Encoder case, we need to send output done also */
+ if (!ddl->decoding) {
+ /* transaction is complete after this callback */
+ ddl->output_frame.frm_trans_end = !eos;
+ /* error case: NO data present */
+ ddl->output_frame.vcd_frm.data_len = 0;
+ /* call back to client for output frame done */
+ ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+ VCD_ERR_FAIL, &(ddl->output_frame),
+ sizeof(struct ddl_frame_data_tag),
+ (void *)ddl, ddl_context->client_data);
+
+ if (eos) {
+ DBG("ENC-EOS_DONE");
+ /* send client EOS DONE callback */
+ ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+ VCD_S_SUCCESS, NULL, 0, (void *)ddl,
+ ddl_context->client_data);
+ }
+ }
+
+ /* if it is decoder EOS case */
+ if (ddl->decoding && eos)
+ ddl_decode_eos_run(ddl);
+ else
+ DDL_IDLE(ddl_context);
+
+ return true;
+}
+
+static u32 ddl_handle_core_warnings(u32 err_status)
+{
+ u32 status = false;
+
+ switch (err_status) {
+ case FRAME_RATE_UNKNOWN:
+ case ASPECT_RATIO_UNKOWN:
+ case COLOR_PRIMARIES_UNKNOWN:
+ case TRANSFER_CHAR_UNKWON:
+ case MATRIX_COEFF_UNKNOWN:
+ case NON_SEQ_SLICE_ADDR:
+ case BROKEN_LINK:
+ case FRAME_CONCEALED:
+ case PROFILE_UNKOWN:
+ case LEVEL_UNKOWN:
+ case BIT_RATE_NOT_SUPPORTED:
+ case COLOR_DIFF_FORMAT_NOT_SUPPORTED:
+ case NULL_EXTRA_METADATA_POINTER:
+ case SYNC_POINT_NOT_RECEIVED_STARTED_DECODING:
+
+ case NULL_FW_DEBUG_INFO_POINTER:
+ case ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT:
+ case MAX_STAGE_COUNTER_EXCEEDED:
+
+ case METADATA_NO_SPACE_MB_INFO:
+ case METADATA_NO_SPACE_SLICE_SIZE:
+ case RESOLUTION_WARNING:
+
+ /* decoder warnings */
+ case METADATA_NO_SPACE_QP:
+ case METADATA_NO_SAPCE_CONCEAL_MB:
+ case METADATA_NO_SPACE_VC1_PARAM:
+ case METADATA_NO_SPACE_SEI:
+ case METADATA_NO_SPACE_VUI:
+ case METADATA_NO_SPACE_EXTRA:
+ case METADATA_NO_SPACE_DATA_NONE:
+ {
+ status = true;
+ DBG("CMD-WARNING-IGNORED!!");
+ break;
+ }
+ }
+ return status;
+}
+
+u32 ddl_handle_core_errors(struct ddl_context *ddl_context)
+{
+ u32 status = false;
+
+ if (!ddl_context->cmd_err_status &&
+ !ddl_context->disp_pic_err_status)
+ return false;
+
+ if (ddl_context->cmd_state == DDL_CMD_INVALID) {
+ DBG("SPURIOUS_INTERRUPT_ERROR");
+ return true;
+ }
+
+ if (!ddl_context->op_failed) {
+ u32 disp_status;
+ status = ddl_handle_core_warnings(ddl_context->
+ cmd_err_status);
+ disp_status = ddl_handle_core_warnings(
+ ddl_context->disp_pic_err_status);
+ if (!status && !disp_status)
+ DBG("ddl_warning:Unknown");
+
+ return false;
+ }
+
+ ERR("\n %s(): OPFAILED!!", __func__);
+ ERR("\n CMD_ERROR_STATUS = %u, DISP_ERR_STATUS = %u",
+ ddl_context->cmd_err_status,
+ ddl_context->disp_pic_err_status);
+
+ status = ddl_handle_hw_fatal_errors(ddl_context);
+
+ if (!status)
+ status = ddl_handle_core_recoverable_errors(ddl_context);
+
+ if (!status)
+ status = ddl_handle_client_fatal_errors(ddl_context);
+
+ return status;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c
new file mode 100644
index 0000000..d501893
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c
@@ -0,0 +1,212 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/firmware.h>
+
+#include "video_core_type.h"
+#include "vcd_ddl_firmware.h"
+
+struct vcd_firmware_table {
+ bool prepared;
+ struct device *dev;
+ struct vcd_firmware fw[6];
+};
+
+//TODO max_sz is kinda sucky, a better way?
+static struct vcd_firmware_table vcd_fw_table = {
+ .prepared = false,
+ .fw[0] = {
+ .filename = "vidc_720p_command_control.fw",
+ .change_endian = false,
+ .max_sz = 12288,
+ },
+ .fw[1] = {
+ .filename = "vidc_720p_mp4_dec_mc.fw",
+ .change_endian = true,
+ .max_sz = 32768,
+ },
+ .fw[2] = {
+ .filename = "vidc_720p_h263_dec_mc.fw",
+ .change_endian = true,
+ .max_sz = 24576,
+ },
+ .fw[3] = {
+ .filename = "vidc_720p_h264_dec_mc.fw",
+ .change_endian = true,
+ .max_sz = 45056,
+ },
+ .fw[4] = {
+ .filename = "vidc_720p_mp4_enc_mc.fw",
+ .change_endian = true,
+ .max_sz = 32768,
+ },
+ .fw[5] = {
+ .filename = "vidc_720p_h264_enc_mc.fw",
+ .change_endian = true,
+ .max_sz = 36864,
+ },
+
+};
+
+static void vcd_fw_change_endian(struct vcd_firmware *vcd_fw)
+{
+ size_t i;
+ u8 tmp;
+ u8 *fw = vcd_fw->virt_addr;
+ for (i = 0; i < vcd_fw->sz; i += 4) {
+ tmp = fw[i];
+ fw[i] = fw[i + 3];
+ fw[i + 3] = tmp;
+
+ tmp = fw[i + 1];
+ fw[i + 1] = fw[i + 2];
+ fw[i + 2] = tmp;
+ }
+}
+
+static int vcd_fw_prepare(struct vcd_firmware *vcd_fw)
+{
+ int rc;
+ const struct firmware *fw;
+
+ rc = request_firmware(&fw, vcd_fw->filename, vcd_fw_table.dev);
+ if (rc) {
+ pr_err("request_firmware(%s) failed %d\n", vcd_fw->filename,
+ rc);
+ return rc;
+ }
+
+ if (fw->size > vcd_fw->max_sz) {
+ pr_err("firmware %s is larger than allocated size (%u > %u)\n",
+ vcd_fw->filename, fw->size, vcd_fw->max_sz);
+ rc = -ENOMEM;
+ goto out;
+ }
+ vcd_fw->sz = fw->size;
+ memcpy(vcd_fw->virt_addr, fw->data, fw->size);
+
+ if (vcd_fw->change_endian)
+ vcd_fw_change_endian(vcd_fw);
+
+ pr_info("prepared firmware %s\n", vcd_fw->filename);
+
+out:
+ release_firmware(fw);
+ return rc;
+}
+
+int vcd_fw_prepare_all()
+{
+ int i;
+ int rc = 0;
+
+ if (vcd_fw_table.prepared)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) {
+ rc = vcd_fw_prepare(&vcd_fw_table.fw[i]);
+ if (rc)
+ goto out;
+ }
+ vcd_fw_table.prepared = true;
+
+out:
+ return rc;
+}
+
+int vcd_fw_init(struct device *dev) {
+ int i;
+ vcd_fw_table.dev = dev;
+ for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) {
+ struct vcd_firmware *fw = &vcd_fw_table.fw[i];
+ fw->virt_addr = dma_alloc_coherent(NULL, fw->max_sz,
+ &fw->phys_addr, GFP_KERNEL);
+ if (!fw->virt_addr) {
+ pr_err("failed to allocate %d for %s\n", fw->max_sz,
+ fw->filename);
+ vcd_fw_exit();
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+void vcd_fw_exit(void) {
+ int i;
+ vcd_fw_table.prepared = false;
+ for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) {
+ struct vcd_firmware *fw = &vcd_fw_table.fw[i];
+ if (!fw->virt_addr)
+ continue;
+ dma_free_coherent(NULL, fw->max_sz, fw->virt_addr,
+ fw->phys_addr);
+ }
+}
+
+struct vcd_firmware *vcd_fw_get_boot_fw(void)
+{
+ if (!vcd_fw_table.prepared)
+ return NULL;
+ return &vcd_fw_table.fw[0];
+}
+
+struct vcd_firmware *vcd_fw_get_fw(bool is_decode, enum vcd_codec codec)
+{
+ if (!vcd_fw_table.prepared)
+ return NULL;
+
+ if (is_decode) {
+ switch (codec) {
+ case VCD_CODEC_DIVX_4:
+ case VCD_CODEC_DIVX_5:
+ case VCD_CODEC_DIVX_6:
+ case VCD_CODEC_XVID:
+ case VCD_CODEC_MPEG4:
+ return &vcd_fw_table.fw[1];
+ case VCD_CODEC_H264:
+ return &vcd_fw_table.fw[3];
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ /* vidc_720p_vc1_dec_mc.fw - untested */
+ break;
+ case VCD_CODEC_MPEG2:
+ /* vidc_720p_mp2_dec_mc.fw - untested */
+ break;
+ case VCD_CODEC_H263:
+ return &vcd_fw_table.fw[2];
+ default:
+ break;
+ }
+ } else {
+ switch (codec) {
+ case VCD_CODEC_H263:
+ case VCD_CODEC_MPEG4:
+ return &vcd_fw_table.fw[4];
+ case VCD_CODEC_H264:
+ return &vcd_fw_table.fw[5];
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+bool vcd_fw_is_codec_supported(bool is_decode, enum vcd_codec codec)
+{
+ return vcd_fw_get_fw(is_decode, codec) != NULL;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h
new file mode 100644
index 0000000..467765a
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_FIRMWARE_H_
+#define _VCD_DDL_FIRMWARE_H_
+
+#include <linux/device.h>
+
+#include "vcd_property.h"
+
+struct vcd_firmware {
+ const char *filename;
+ bool change_endian;
+ phys_addr_t phys_addr;
+ void *virt_addr;
+ size_t sz; /* real size of firmware (unknown until load time) */
+ size_t max_sz; /* size for allocation at init time */
+};
+
+int vcd_fw_init(struct device *dev);
+void vcd_fw_exit(void);
+int vcd_fw_prepare_all(void);
+struct vcd_firmware *vcd_fw_get_boot_fw(void);
+struct vcd_firmware *vcd_fw_get_fw(bool is_decode, enum vcd_codec codec);
+bool vcd_fw_is_codec_supported(bool is_decode, enum vcd_codec codec);
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c
new file mode 100644
index 0000000..1b1b488
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c
@@ -0,0 +1,735 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+void ddl_core_init(struct ddl_context *ddl_context)
+{
+ char *psz_version;
+ enum vidc_720p_endian_type dma_endian;
+ u32 interrupt_off;
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel;
+ u32 intr_mask = 0x0;
+ struct vcd_firmware *vcd_fw;
+
+ vcd_fw = vcd_fw_get_boot_fw();
+ dma_endian = VIDC_720P_BIG_ENDIAN; /* use default endian */
+
+ interrupt_off = false;
+ interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL;
+
+ intr_mask |= VIDC_720P_INTR_BUFFER_FULL;
+ intr_mask |= VIDC_720P_INTR_FW_DONE;
+ intr_mask |= VIDC_720P_INTR_DMA_DONE;
+ intr_mask |= VIDC_720P_INTR_FRAME_DONE;
+
+ vidc_720p_do_sw_reset();
+
+ vidc_720p_init(&psz_version, vcd_fw->sz, vcd_fw->phys_addr, dma_endian,
+ interrupt_off, interrupt_sel, intr_mask);
+ return;
+}
+
+void ddl_core_start_cpu(struct ddl_context *ddl_context)
+{
+ enum vidc_720p_endian_type dma_endian;
+ u32 dbg_core_dump_buf_size = 0;
+
+ dma_endian = VIDC_720P_LITTLE_ENDIAN; /* use reverse endian */
+
+ ddl_move_command_state(ddl_context, DDL_CMD_CPU_RESET);
+
+ DBG("VSP_BUF_ADDR_SIZE %d", ddl_context->context_buf_addr.size);
+ if (ddl_context->enable_dbg_core_dump) {
+ dbg_core_dump_buf_size = ddl_context->dbg_core_dump.size;
+ }
+
+ vidc_720p_start_cpu(dma_endian, ddl_context->context_buf_addr.phys_addr,
+ ddl_context->dbg_core_dump.phys_addr, dbg_core_dump_buf_size);
+}
+
+void ddl_channel_set(struct ddl_client_context *ddl)
+{
+ enum vidc_720p_enc_dec_selection_type enc_dec_sel;
+ enum vidc_720p_codec_type codec;
+ enum vcd_codec vcd_codec;
+ struct vcd_firmware *vcd_fw;
+
+ if (ddl->decoding) {
+ enc_dec_sel = VIDC_720P_DECODER;
+ vcd_codec = ddl->codec_data.decoder.codec_type.codec;
+ } else {
+ enc_dec_sel = VIDC_720P_ENCODER;
+ vcd_codec = ddl->codec_data.encoder.codec_type.codec;
+ }
+ switch (vcd_codec) {
+ default:
+ case VCD_CODEC_MPEG4:
+ codec = VIDC_720P_MPEG4;
+ if (ddl->decoding) {
+ vidc_720p_decode_set_mpeg4_data_partitionbuffer(
+ ddl->ddl_context->data_partition_tempbuf.phys_addr);
+ }
+ break;
+ case VCD_CODEC_H264:
+ codec = VIDC_720P_H264;
+ break;
+ case VCD_CODEC_DIVX_4:
+ case VCD_CODEC_DIVX_5:
+ case VCD_CODEC_DIVX_6:
+ codec = VIDC_720P_DIVX;
+ break;
+ case VCD_CODEC_XVID:
+ codec = VIDC_720P_XVID;
+ break;
+ case VCD_CODEC_H263:
+ codec = VIDC_720P_H263;
+ break;
+ case VCD_CODEC_MPEG2:
+ codec = VIDC_720P_MPEG2;
+ break;
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ codec = VIDC_720P_VC1;
+ break;
+ }
+
+ vcd_fw = vcd_fw_get_fw(ddl->decoding, vcd_codec);
+
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_SET);
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHDONE);
+
+ vidc_720p_set_channel(ddl->channel_id, enc_dec_sel, codec,
+ vcd_fw->phys_addr, vcd_fw->sz);
+}
+
+void ddl_decode_init_codec(struct ddl_client_context *ddl)
+{
+ u32 seq_h = 0, seq_e = 0, start_byte_num = 0;
+ struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+ struct vcd_phys_sequence_hdr *seq_hdr = &decoder->decode_config;
+ enum vidc_720p_memory_access_method_type mem_access_method;
+
+ ddl_metadata_enable(ddl);
+
+ vidc_720p_decode_set_error_control(true);
+
+ vidc_720p_decode_set_mpeg4Post_filter(decoder->post_filter.post_filter);
+ if (decoder->codec_type.codec == VCD_CODEC_VC1_RCV) {
+ vidc_720p_set_frame_size(decoder->client_frame_size.width,
+ decoder->client_frame_size.height);
+ } else {
+ vidc_720p_set_frame_size(0x0, 0x0);
+ }
+
+ switch (decoder->buf_format.buffer_format) {
+ default:
+ case VCD_BUFFER_FORMAT_NV12:
+ mem_access_method = VIDC_720P_TILE_LINEAR;
+ break;
+ case VCD_BUFFER_FORMAT_TILE_4x2:
+ mem_access_method = VIDC_720P_TILE_64x32;
+ break;
+ }
+ pr_debug("HEADER-PARSE-START\n");
+
+ seq_h = seq_hdr->addr;
+ start_byte_num = 8 - (seq_h & DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+ seq_e = seq_h + seq_hdr->sz;
+ seq_h &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+ DDL_PADDING_HACK(seq_e);
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE);
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_HEADER_PARSE);
+
+ vidc_720p_decode_bitstream_header(ddl->channel_id, seq_hdr->sz,
+ start_byte_num, seq_h, seq_e, mem_access_method);
+}
+
+void ddl_decode_dynamic_property(struct ddl_client_context *ddl, u32 enable)
+{
+ struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+ struct vcd_frame_data *bit_stream = &ddl->input_frame.vcd_frm;
+
+ if (!enable) {
+ if (decoder->dynmic_prop_change_req) {
+ decoder->dynmic_prop_change_req = false;
+ vidc_720p_decode_dynamic_req_reset();
+ }
+ return;
+ }
+ if ((decoder->dynamic_prop_change & DDL_DEC_REQ_OUTPUT_FLUSH)) {
+ decoder->dynmic_prop_change_req = true;
+ decoder->dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH);
+ decoder->dpb_mask.hw_mask = 0;
+ vidc_720p_decode_dynamic_req_set(VIDC_720P_FLUSH_REQ);
+ }
+ if ((decoder->meta_data_enable_flag & VCD_METADATA_PASSTHROUGH) &&
+ (VCD_FRAME_FLAG_EXTRADATA & bit_stream->flags)) {
+ phys_addr_t extra_datastart = bit_stream->phys_addr +
+ bit_stream->offset + bit_stream->data_len;
+ extra_datastart = (extra_datastart + 3) & ~0x03;
+
+ decoder->dynmic_prop_change_req = true;
+
+ vidc_720p_decode_setpassthrough_start(extra_datastart);
+
+ vidc_720p_decode_dynamic_req_set(VIDC_720P_EXTRADATA);
+ }
+}
+
+void ddl_encode_dynamic_property(struct ddl_client_context *ddl, u32 enable)
+{
+ struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+ u32 enc_param_change = 0;
+
+ if (!enable) {
+ if (encoder->dynmic_prop_change_req) {
+ encoder->dynmic_prop_change_req = false;
+ encoder->ext_enc_control_val &=
+ ~(VIDC_720P_ENC_IFRAME_REQ);
+ vidc_720p_encode_set_control_param
+ (encoder->ext_enc_control_val);
+ vidc_720p_encoder_set_param_change(enc_param_change);
+ }
+ return;
+ }
+ if ((encoder->dynamic_prop_change & DDL_ENC_REQ_IFRAME)) {
+ encoder->dynamic_prop_change &= ~(DDL_ENC_REQ_IFRAME);
+ encoder->ext_enc_control_val |= VIDC_720P_ENC_IFRAME_REQ;
+ vidc_720p_encode_set_control_param
+ (encoder->ext_enc_control_val);
+ }
+ if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_BITRATE)) {
+ vidc_720p_encode_set_bit_rate(
+ encoder->target_bit_rate.target_bitrate);
+ enc_param_change |= VIDC_720P_ENC_BITRATE_CHANGE;
+ encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_BITRATE);
+ }
+ if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_IPERIOD)) {
+ vidc_720p_encode_set_i_period(encoder->period.frames);
+ enc_param_change |= VIDC_720P_ENC_IPERIOD_CHANGE;
+ encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_IPERIOD);
+ }
+ if ((encoder->dynamic_prop_change &
+ DDL_ENC_CHANGE_FRAMERATE)) {
+ vidc_720p_encode_set_fps
+ ((encoder->frame_rate.fps_numerator * 1000) /
+ encoder->frame_rate.fps_denominator);
+ enc_param_change |= VIDC_720P_ENC_FRAMERATE_CHANGE;
+ encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_FRAMERATE);
+ }
+ if (enc_param_change)
+ vidc_720p_encoder_set_param_change(enc_param_change);
+}
+
+static void ddl_encode_set_profile_level(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+ u32 profile;
+ u32 level;
+
+ switch (encoder->profile.profile) {
+ default:
+ case VCD_PROFILE_MPEG4_SP:
+ profile = VIDC_720P_PROFILE_MPEG4_SP;
+ break;
+ case VCD_PROFILE_MPEG4_ASP:
+ profile = VIDC_720P_PROFILE_MPEG4_ASP;
+ break;
+ case VCD_PROFILE_H264_BASELINE:
+ profile = VIDC_720P_PROFILE_H264_BASELINE;
+ break;
+ case VCD_PROFILE_H264_MAIN:
+ profile = VIDC_720P_PROFILE_H264_MAIN;
+ break;
+ case VCD_PROFILE_H264_HIGH:
+ profile = VIDC_720P_PROFILE_H264_HIGH;
+ break;
+ case VCD_PROFILE_H263_BASELINE:
+ profile = VIDC_720P_PROFILE_H263_BASELINE;
+ break;
+ }
+ switch (encoder->level.level) {
+ default:
+ case VCD_LEVEL_MPEG4_0:
+ level = VIDC_720P_MPEG4_LEVEL0;
+ break;
+ case VCD_LEVEL_MPEG4_0b:
+ level = VIDC_720P_MPEG4_LEVEL0b;
+ break;
+ case VCD_LEVEL_MPEG4_1:
+ level = VIDC_720P_MPEG4_LEVEL1;
+ break;
+ case VCD_LEVEL_MPEG4_2:
+ level = VIDC_720P_MPEG4_LEVEL2;
+ break;
+ case VCD_LEVEL_MPEG4_3:
+ level = VIDC_720P_MPEG4_LEVEL3;
+ break;
+ case VCD_LEVEL_MPEG4_3b:
+ level = VIDC_720P_MPEG4_LEVEL3b;
+ break;
+ case VCD_LEVEL_MPEG4_4:
+ case VCD_LEVEL_MPEG4_4a:
+ level = VIDC_720P_MPEG4_LEVEL4a;
+ break;
+ case VCD_LEVEL_MPEG4_5:
+ level = VIDC_720P_MPEG4_LEVEL5;
+ break;
+ case VCD_LEVEL_MPEG4_6:
+ level = VIDC_720P_MPEG4_LEVEL6;
+ break;
+ case VCD_LEVEL_H264_1:
+ level = VIDC_720P_H264_LEVEL1;
+ break;
+ case VCD_LEVEL_H264_1b:
+ level = VIDC_720P_H264_LEVEL1b;
+ break;
+ case VCD_LEVEL_H264_1p1:
+ level = VIDC_720P_H264_LEVEL1p1;
+ break;
+ case VCD_LEVEL_H264_1p2:
+ level = VIDC_720P_H264_LEVEL1p2;
+ break;
+ case VCD_LEVEL_H264_1p3:
+ level = VIDC_720P_H264_LEVEL1p3;
+ break;
+ case VCD_LEVEL_H264_2:
+ level = VIDC_720P_H264_LEVEL2;
+ break;
+ case VCD_LEVEL_H264_2p1:
+ level = VIDC_720P_H264_LEVEL2p1;
+ break;
+ case VCD_LEVEL_H264_2p2:
+ level = VIDC_720P_H264_LEVEL2p2;
+ break;
+ case VCD_LEVEL_H264_3:
+ level = VIDC_720P_H264_LEVEL3;
+ break;
+ case VCD_LEVEL_H264_3p1:
+ level = VIDC_720P_H264_LEVEL3p1;
+ break;
+ case VCD_LEVEL_H263_10:
+ level = VIDC_720P_H263_LEVEL10;
+ break;
+ case VCD_LEVEL_H263_20:
+ level = VIDC_720P_H263_LEVEL20;
+ break;
+ case VCD_LEVEL_H263_30:
+ level = VIDC_720P_H263_LEVEL30;
+ break;
+ case VCD_LEVEL_H263_40:
+ level = VIDC_720P_H263_LEVEL40;
+ break;
+ case VCD_LEVEL_H263_45:
+ level = VIDC_720P_H263_LEVEL45;
+ break;
+ case VCD_LEVEL_H263_50:
+ level = VIDC_720P_H263_LEVEL50;
+ break;
+ case VCD_LEVEL_H263_60:
+ level = VIDC_720P_H263_LEVEL60;
+ break;
+ case VCD_LEVEL_H263_70:
+ level = VIDC_720P_H263_LEVEL70;
+ break;
+ }
+ vidc_720p_encode_set_profile(profile, level);
+}
+
+void ddl_encode_init_codec(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+ enum vidc_720p_memory_access_method_type mem_access_method;
+ enum vidc_720p_DBConfig_type db_config;
+ enum vidc_720p_MSlice_selection_type m_slice_sel;
+
+ ddl_encode_set_profile_level(ddl);
+
+ vidc_720p_set_frame_size
+ (encoder->frame_size.width, encoder->frame_size.height);
+ vidc_720p_encode_set_qp_params
+ (encoder->qp_range.max_qp, encoder->qp_range.min_qp);
+ vidc_720p_encode_set_rc_config
+ (encoder->rc_level.frame_level_rc,
+ encoder->rc_level.mb_level_rc,
+ encoder->session_qp.iframe_qp,
+ encoder->session_qp.frame_qp);
+
+ if (encoder->r_cframe_skip) {
+ if (encoder->vb_vbuffer_size) {
+ encoder->ext_enc_control_val = (0x2 << 0x2) |
+ (encoder->vb_vbuffer_size << 0x10);
+ } else
+ encoder->ext_enc_control_val = (0x1 << 2);
+ } else
+ encoder->ext_enc_control_val = 0;
+
+ vidc_720p_encode_set_fps
+ ((encoder->frame_rate.fps_numerator * 1000) /
+ encoder->frame_rate.fps_denominator);
+
+ vidc_720p_encode_set_vop_time(
+ encoder->vop_timing.vop_time_resolution, 0);
+
+ if (encoder->rc_level.frame_level_rc) {
+ vidc_720p_encode_set_bit_rate
+ (encoder->target_bit_rate.target_bitrate);
+
+ vidc_720p_encode_set_frame_level_rc_params
+ (encoder->frame_level_rc.reaction_coeff);
+ }
+ if (encoder->rc_level.mb_level_rc) {
+ vidc_720p_encode_set_mb_level_rc_params
+ (encoder->adaptive_rc.dark_region_as_flag,
+ encoder->adaptive_rc.smooth_region_as_flag,
+ encoder->adaptive_rc.static_region_as_flag,
+ encoder->adaptive_rc.activity_region_flag);
+ }
+ if (encoder->codec_type.codec == VCD_CODEC_MPEG4) {
+ vidc_720p_encode_set_short_header
+ (encoder->short_header.short_header);
+
+ if (encoder->hdr_ext_control) {
+ vidc_720p_encode_set_hec_period
+ (encoder->hdr_ext_control);
+ encoder->ext_enc_control_val |= (0x1 << 0x1);
+ }
+ }
+ /* set extended encoder control settings */
+ vidc_720p_encode_set_control_param
+ (encoder->ext_enc_control_val);
+
+ if (encoder->codec_type.codec == VCD_CODEC_H264) {
+ enum vidc_720p_entropy_sel_type entropy_sel;
+ enum vidc_720p_cabac_model_type cabac_model_number;
+ switch (encoder->entropy_control.entropy_sel) {
+ default:
+ case VCD_ENTROPY_SEL_CAVLC:
+ entropy_sel = VIDC_720P_ENTROPY_SEL_CAVLC;
+ break;
+ case VCD_ENTROPY_SEL_CABAC:
+ entropy_sel = VIDC_720P_ENTROPY_SEL_CABAC;
+ break;
+ }
+ switch (encoder->entropy_control.cabac_model) {
+ default:
+ case VCD_CABAC_MODEL_NUMBER_0:
+ cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_0;
+ break;
+ case VCD_CABAC_MODEL_NUMBER_1:
+ cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_1;
+ break;
+ case VCD_CABAC_MODEL_NUMBER_2:
+ cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_2;
+ break;
+ }
+ vidc_720p_encode_set_entropy_control
+ (entropy_sel, cabac_model_number);
+ switch (encoder->db_control.db_config) {
+ default:
+ case VCD_DB_ALL_BLOCKING_BOUNDARY:
+ db_config = VIDC_720P_DB_ALL_BLOCKING_BOUNDARY;
+ break;
+ case VCD_DB_DISABLE:
+ db_config = VIDC_720P_DB_DISABLE;
+ break;
+ case VCD_DB_SKIP_SLICE_BOUNDARY:
+ db_config = VIDC_720P_DB_SKIP_SLICE_BOUNDARY;
+ break;
+ }
+ vidc_720p_encode_set_db_filter_control
+ (db_config,
+ encoder->db_control.slice_alpha_offset,
+ encoder->db_control.slice_beta_offset);
+ }
+
+ vidc_720p_encode_set_intra_refresh_mb_number
+ (encoder->intra_refresh.cir_mb_number);
+
+ switch (encoder->multi_slice.m_slice_sel) {
+ default:
+ case VCD_MSLICE_OFF:
+ m_slice_sel = VIDC_720P_MSLICE_OFF;
+ break;
+ case VCD_MSLICE_BY_MB_COUNT:
+ m_slice_sel = VIDC_720P_MSLICE_BY_MB_COUNT;
+ break;
+ case VCD_MSLICE_BY_BYTE_COUNT:
+ m_slice_sel = VIDC_720P_MSLICE_BY_BYTE_COUNT;
+ break;
+ case VCD_MSLICE_BY_GOB:
+ m_slice_sel = VIDC_720P_MSLICE_BY_GOB;
+ break;
+ }
+ vidc_720p_encode_set_multi_slice_info(m_slice_sel,
+ encoder->multi_slice.m_slice_size);
+
+ vidc_720p_encode_set_dpb_buffer(encoder->enc_dpb_addr.phys_addr,
+ encoder->enc_dpb_addr.size);
+
+ pr_debug("ENC_DPB_ADDR_SIZE %u\n", encoder->enc_dpb_addr.size);
+
+ vidc_720p_encode_set_i_period(encoder->period.frames);
+
+ ddl_metadata_enable(ddl);
+
+ if (encoder->seq_header.virt_addr) {
+ phys_addr_t ext_buffer_start;
+ phys_addr_t ext_buffer_end;
+ u32 start_byte_num;
+ ext_buffer_start = encoder->seq_header.phys_addr;
+ ext_buffer_end = ext_buffer_start + encoder->seq_header.size;
+ start_byte_num = ext_buffer_start &
+ DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ ext_buffer_end &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ pr_debug("ENC_SEQHDR_ALLOC_SIZE %u\n",
+ encoder->seq_header.size);
+ vidc_720p_encode_set_seq_header_buffer(ext_buffer_start,
+ ext_buffer_end, start_byte_num);
+ }
+
+ if (encoder->re_con_buf_format.buffer_format ==
+ VCD_BUFFER_FORMAT_NV12)
+ mem_access_method = VIDC_720P_TILE_LINEAR;
+ else
+ mem_access_method = VIDC_720P_TILE_16x16;
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE);
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_INIT_CODEC);
+
+ vidc_720p_encode_init_codec(ddl->channel_id, mem_access_method);
+}
+
+void ddl_channel_end(struct ddl_client_context *ddl)
+{
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHEND);
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_END);
+
+ vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_CHEND);
+}
+
+void ddl_encode_frame_run(struct ddl_client_context *ddl)
+{
+ phys_addr_t ext_buffer_start;
+ phys_addr_t ext_buffer_end;
+ phys_addr_t y_addr;
+ phys_addr_t c_addr;
+ u32 start_byte_number;
+ struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+ struct vcd_frame_data *stream = &ddl->output_frame.vcd_frm;
+
+ ext_buffer_start = stream->phys_addr + stream->offset;
+ ext_buffer_end = ddl_encode_set_metadata_output_buf(ddl);
+ start_byte_number = ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ if (start_byte_number) {
+ u32 *data;
+ ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ data = (u32 *)((u32)stream->virt_addr + stream->offset -
+ start_byte_number);
+ vidc_720p_encode_unalign_bitstream(data[0], data[1]);
+ }
+
+ y_addr = ddl->input_frame.vcd_frm.phys_addr +
+ ddl->input_frame.vcd_frm.offset;
+ c_addr = y_addr + encoder->frame_size.height *
+ encoder->frame_size.width;
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE);
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_ENCODE_FRAME);
+
+ if (encoder->dynamic_prop_change) {
+ encoder->dynmic_prop_change_req = true;
+ ddl_encode_dynamic_property(ddl, true);
+ }
+ vidc_720p_encode_set_vop_time(encoder->vop_timing.vop_time_resolution,
+ ddl->input_frame.frm_delta);
+
+ vidc_720p_encode_frame(ddl->channel_id, ext_buffer_start,
+ ext_buffer_end, start_byte_number, y_addr, c_addr);
+}
+
+u32 ddl_decode_set_buffers(struct ddl_client_context *ddl)
+{
+ struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder);
+ u32 comv_buf_size = DDL_COMV_BUFLINE_NO, comv_buf_no = 0;
+ u32 ref_buf_no = 0;
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) {
+ pr_debug("STATE-CRITICAL\n");
+ return VCD_ERR_FAIL;
+ }
+
+ switch (decoder->codec_type.codec) {
+ default:
+ case VCD_CODEC_DIVX_4:
+ case VCD_CODEC_DIVX_5:
+ case VCD_CODEC_DIVX_6:
+ case VCD_CODEC_XVID:
+ case VCD_CODEC_MPEG2:
+ case VCD_CODEC_MPEG4:
+ comv_buf_no = DDL_MPEG_COMV_BUF_NO;
+ ref_buf_no = DDL_MPEG_REFBUF_COUNT;
+ break;
+ case VCD_CODEC_H263:
+ comv_buf_no = DDL_H263_COMV_BUF_NO;
+ break;
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ comv_buf_no = decoder->client_output_buf_req.actual_count + 1;
+ comv_buf_size = DDL_VC1_COMV_BUFLINE_NO;
+ break;
+ case VCD_CODEC_H264:
+ comv_buf_no = decoder->client_output_buf_req.actual_count;
+ break;
+ }
+
+ if (comv_buf_no) {
+ comv_buf_size *= (comv_buf_no *
+ (((decoder->client_frame_size.width + 15) >> 4)) *
+ (((decoder->client_frame_size.height + 15) >> 4) + 1));
+ if (!ddl_dma_alloc(&decoder->dpb_comv_buffer, comv_buf_size, npelly_dec_dpb)) {
+ pr_err("Dec_set_buf:Comv_buf_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ vidc_720p_decode_set_comv_buffer(decoder->dpb_comv_buffer.
+ phys_addr, decoder->dpb_comv_buffer.size);
+ }
+ decoder->ref_buffer.phys_addr = 0;
+ if (ref_buf_no) {
+ u32 size, yuv_size;
+ yuv_size = ddl_get_yuv_buffer_size(&decoder->
+ client_frame_size, &decoder->buf_format,
+ (!decoder->progressive_only));
+ size = yuv_size * ref_buf_no;
+
+ if (!ddl_dma_alloc(&decoder->ref_buffer, size, npelly_dec_ref)) {
+ ddl_dma_free(&decoder->dpb_comv_buffer);
+ pr_err("Dec_set_buf:mpeg_ref_buf_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ }
+ ddl_decode_set_metadata_output(decoder);
+
+ ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_INIT);
+
+ if (decoder->codec_type.codec == VCD_CODEC_H264) {
+ vidc_720p_decode_setH264VSPBuffer(
+ decoder->h264Vsp_temp_buffer.phys_addr);
+ pr_debug("VSP_BUF_ADDR_SIZE %u\n",
+ decoder->h264Vsp_temp_buffer.size);
+ }
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE);
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_SET_DPB);
+
+ vidc_720p_submit_command(ddl->channel_id,
+ VIDC_720P_CMD_INITBUFFERS);
+ return VCD_S_SUCCESS;
+}
+
+void ddl_decode_frame_run(struct ddl_client_context *ddl)
+{
+ phys_addr_t ext_buffer_start;
+ phys_addr_t ext_buffer_end = 0;
+ u32 start_byte_num;
+ struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+ struct vcd_frame_data *bit_stream = &ddl->input_frame.vcd_frm;
+
+ if (!bit_stream->data_len || !bit_stream->phys_addr) {
+ ddl_decode_eos_run(ddl);
+ return;
+ }
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE);
+
+ ddl_decode_dynamic_property(ddl, true);
+
+ ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+
+ ext_buffer_start = bit_stream->phys_addr + bit_stream->offset;
+ start_byte_num = 8 - (ext_buffer_start &
+ DDL_STREAMBUF_ALIGN_GUARD_BYTES);
+ ext_buffer_end = ext_buffer_start + bit_stream->data_len;
+ ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ DDL_PADDING_HACK(ext_buffer_end);
+
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_FRAME);
+
+ vidc_720p_decode_frame(ddl->channel_id, ext_buffer_start,
+ ext_buffer_end, bit_stream->data_len, start_byte_num,
+ bit_stream->ip_frm_tag);
+}
+
+void ddl_decode_eos_run(struct ddl_client_context *ddl)
+{
+ struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE);
+
+ ddl_decode_dynamic_property(ddl, true);
+
+ ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK);
+
+ decoder->dynmic_prop_change_req = true;
+
+ ddl_move_command_state(ddl->ddl_context, DDL_CMD_EOS);
+
+ vidc_720p_issue_eos(ddl->channel_id);
+}
+
+u32 ddl_hal_engine_reset(struct ddl_context *ddl_context)
+{
+ u32 eng_reset;
+ u32 channel_id = 0;
+ enum vidc_720p_endian_type dma_endian;
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel;
+ u32 intr_mask = 0x0;
+
+ if (ddl_context->current_ddl)
+ channel_id = ddl_context->current_ddl->channel_id;
+
+ interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL;
+ /* Enable all the supported interrupt */
+ intr_mask |= VIDC_720P_INTR_BUFFER_FULL;
+ intr_mask |= VIDC_720P_INTR_FW_DONE;
+ intr_mask |= VIDC_720P_INTR_DMA_DONE;
+ intr_mask |= VIDC_720P_INTR_FRAME_DONE;
+
+ /* use reverse endian after boot code download */
+ dma_endian = VIDC_720P_LITTLE_ENDIAN;
+
+ /* Need to reset MFC silently */
+ eng_reset = vidc_720p_engine_reset(channel_id, dma_endian,
+ interrupt_sel, intr_mask);
+ if (!eng_reset) {
+ /* call the hw fatal callback if engine reset fails */
+ ddl_hw_fatal_cb(ddl_context);
+ }
+ return eng_reset ;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c
new file mode 100644
index 0000000..17bf125
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c
@@ -0,0 +1,218 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_ddl_utils.h"
+
+struct ddl_context *ddl_get_context(void)
+{
+ static struct ddl_context ddl_context;
+ return &ddl_context;
+}
+
+void ddl_move_client_state(struct ddl_client_context *ddl,
+ enum ddl_client_state client_state)
+{
+ ddl->client_state = client_state;
+}
+
+void ddl_move_command_state(struct ddl_context *ddl_context,
+ enum ddl_cmd_state command_state)
+{
+ ddl_context->cmd_state = command_state;
+}
+
+u32 ddl_client_transact(u32 operation, struct ddl_client_context **pddl_client)
+{
+ u32 ret_status = VCD_ERR_FAIL;
+ u32 i;
+ struct ddl_context *ddl_context;
+
+ ddl_context = ddl_get_context();
+ switch (operation) {
+ case DDL_FREE_CLIENT:
+ if (pddl_client && *pddl_client) {
+ u32 channel_id;
+ channel_id = (*pddl_client)->channel_id;
+ if (channel_id < VCD_MAX_NO_CLIENT)
+ ddl_context->ddl_clients[channel_id] = NULL;
+ else
+ pr_warn("CHID_CORRUPTION\n");
+ kfree(*pddl_client);
+ *pddl_client = NULL;
+ ret_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_GET_CLIENT:
+ ret_status = VCD_ERR_MAX_CLIENT;
+ for (i = 0; i < VCD_MAX_NO_CLIENT && ret_status ==
+ VCD_ERR_MAX_CLIENT; ++i) {
+ if (!ddl_context->ddl_clients[i]) {
+ *pddl_client = (struct ddl_client_context *)
+ kzalloc((sizeof(
+ struct ddl_client_context)),
+ GFP_KERNEL);
+ if (!*pddl_client) {
+ ret_status = VCD_ERR_ALLOC_FAIL;
+ break;
+ }
+ ddl_context->ddl_clients[i] = *pddl_client;
+ (*pddl_client)->channel_id = i;
+ (*pddl_client)->ddl_context = ddl_context;
+ ret_status = VCD_S_SUCCESS;
+ }
+ }
+ break;
+ case DDL_INIT_CLIENTS:
+ for (i = 0; i < VCD_MAX_NO_CLIENT; ++i)
+ ddl_context->ddl_clients[i] = NULL;
+ ret_status = VCD_S_SUCCESS;
+ break;
+ case DDL_ACTIVE_CLIENT:
+ for (i = 0; i < VCD_MAX_NO_CLIENT; ++i) {
+ if (ddl_context->ddl_clients[i]) {
+ ret_status = VCD_S_SUCCESS;
+ break;
+ }
+ }
+ break;
+ default:
+ ret_status = VCD_ERR_ILLEGAL_PARM;
+ break;
+ }
+ return ret_status;
+}
+
+u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *dec,
+ struct ddl_frame_data_tag *in_out_frame, u32 operation)
+{
+ u32 vcd_status = VCD_S_SUCCESS;
+ u32 i;
+ struct ddl_frame_data_tag *found_frame = NULL;
+ struct ddl_mask *dpb_mask = &dec->dpb_mask;
+
+ switch (operation) {
+ case DDL_DPB_OP_MARK_BUSY:
+ case DDL_DPB_OP_MARK_FREE:
+ for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) {
+ if (in_out_frame->vcd_frm.phys_addr == dec->dp_buf.
+ dec_pic_buffers[i].vcd_frm.phys_addr) {
+ found_frame = &dec->dp_buf.dec_pic_buffers[i];
+ break;
+ }
+ }
+
+ if (!found_frame) {
+ in_out_frame->vcd_frm.phys_addr = 0;
+ vcd_status = VCD_ERR_BAD_POINTER;
+ pr_debug("BUF_NOT_FOUND\n");
+ break;
+ }
+ if (operation == DDL_DPB_OP_MARK_BUSY) {
+ dpb_mask->hw_mask &= ~(0x1 << i);
+ *in_out_frame = *found_frame;
+ } else if (operation == DDL_DPB_OP_MARK_FREE) {
+ dpb_mask->client_mask |= 0x1 << i;
+ *found_frame = *in_out_frame;
+ }
+
+ break;
+ case DDL_DPB_OP_SET_MASK:
+ dpb_mask->hw_mask |= dpb_mask->client_mask;
+ dpb_mask->client_mask = 0;
+ vidc_720p_decode_set_dpb_release_buffer_mask(dpb_mask->hw_mask);
+ break;
+ case DDL_DPB_OP_INIT:
+ {
+ size_t dpb_size = !dec->meta_data_offset ?
+ dec->dp_buf.dec_pic_buffers[0].vcd_frm.alloc_len :
+ dec->meta_data_offset;
+ vidc_720p_decode_set_dpb_details(dec->dp_buf.no_of_dec_pic_buf,
+ dpb_size, dec->ref_buffer.phys_addr);
+ for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) {
+ vidc_720p_decode_set_dpb_buffers(i, dec->dp_buf.
+ dec_pic_buffers[i].vcd_frm.phys_addr);
+ pr_debug("DEC_DPB_BUFn_SIZE %u\n", dec->dp_buf.
+ dec_pic_buffers[i].vcd_frm.alloc_len);
+ }
+ break;
+ }
+ case DDL_DPB_OP_RETRIEVE:
+ {
+ u32 position;
+ u32 *mask;
+ if (dpb_mask->client_mask) {
+ mask = &dpb_mask->client_mask;
+ } else if (dpb_mask->hw_mask) {
+ mask = &dpb_mask->hw_mask;
+ } else {
+ in_out_frame->vcd_frm.phys_addr = 0;
+ break;
+ }
+ position = 0x1;
+ for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) {
+ if (*mask & position) {
+ found_frame = &dec->dp_buf.dec_pic_buffers[i];
+ *mask &= ~position;
+ *in_out_frame = *found_frame;
+ break;
+ }
+ position <<= 1;
+ }
+ if (!found_frame)
+ in_out_frame->vcd_frm.phys_addr = 0;
+ break;
+ }
+ }
+ return vcd_status;
+}
+
+void ddl_release_context_buffers(struct ddl_context *ddl_context)
+{
+ ddl_dma_free(&ddl_context->context_buf_addr);
+ ddl_dma_free(&ddl_context->db_line_buffer);
+ ddl_dma_free(&ddl_context->data_partition_tempbuf);
+ ddl_dma_free(&ddl_context->metadata_shared_input);
+ ddl_dma_free(&ddl_context->dbg_core_dump);
+}
+
+void ddl_release_client_internal_buffers(struct ddl_client_context *ddl)
+{
+ if (ddl->decoding) {
+ struct ddl_decoder_data *dec = &(ddl->codec_data.decoder);
+ ddl_dma_free(&dec->h264Vsp_temp_buffer);
+ ddl_dma_free(&dec->dpb_comv_buffer);
+ ddl_dma_free(&dec->ref_buffer);
+ kfree(dec->dp_buf.dec_pic_buffers);
+ dec->dp_buf.dec_pic_buffers = NULL;
+ ddl_decode_dynamic_property(ddl, false);
+ dec->decode_config.sz = 0;
+ dec->decode_config.addr = 0;
+ dec->dpb_mask.client_mask = 0;
+ dec->dpb_mask.hw_mask = 0;
+ dec->dp_buf.no_of_dec_pic_buf = 0;
+ dec->dynamic_prop_change = 0;
+
+ } else {
+ struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+ ddl_dma_free(&encoder->enc_dpb_addr);
+ ddl_dma_free(&encoder->seq_header);
+ ddl_encode_dynamic_property(ddl, false);
+ encoder->dynamic_prop_change = 0;
+ }
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h
new file mode 100644
index 0000000..3f3c6e2
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h
@@ -0,0 +1,91 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_INTERNAL_PROPERTY_H_
+#define _VCD_DDL_INTERNAL_PROPERTY_H_
+#include "vcd_api.h"
+
+#define VCD_EVT_RESP_DDL_BASE 0x3000
+#define VCD_EVT_RESP_DEVICE_INIT (VCD_EVT_RESP_DDL_BASE + 0x1)
+#define VCD_EVT_RESP_OUTPUT_REQ (VCD_EVT_RESP_DDL_BASE + 0x2)
+#define VCD_EVT_RESP_EOS_DONE (VCD_EVT_RESP_DDL_BASE + 0x3)
+#define VCD_EVT_RESP_TRANSACTION_PENDING (VCD_EVT_RESP_DDL_BASE + 0x4)
+
+#define VCD_S_DDL_ERR_BASE 0x90000000
+#define VCD_ERR_MAX_NO_CODEC (VCD_S_DDL_ERR_BASE + 0x1)
+#define VCD_ERR_CLIENT_PRESENT (VCD_S_DDL_ERR_BASE + 0x2)
+#define VCD_ERR_CLIENT_FATAL (VCD_S_DDL_ERR_BASE + 0x3)
+
+#define VCD_I_CUSTOM_BASE (VCD_I_RESERVED_BASE)
+#define VCD_I_RC_LEVEL_CONFIG (VCD_I_CUSTOM_BASE + 0x1)
+#define VCD_I_FRAME_LEVEL_RC (VCD_I_CUSTOM_BASE + 0x2)
+#define VCD_I_ADAPTIVE_RC (VCD_I_CUSTOM_BASE + 0x3)
+#define VCD_I_CUSTOM_DDL_BASE (VCD_I_RESERVED_BASE + 0x100)
+#define DDL_I_INPUT_BUF_REQ (VCD_I_CUSTOM_DDL_BASE + 0x1)
+#define DDL_I_OUTPUT_BUF_REQ (VCD_I_CUSTOM_DDL_BASE + 0x2)
+#define DDL_I_DPB (VCD_I_CUSTOM_DDL_BASE + 0x3)
+#define DDL_I_DPB_RELEASE (VCD_I_CUSTOM_DDL_BASE + 0x4)
+#define DDL_I_DPB_RETRIEVE (VCD_I_CUSTOM_DDL_BASE + 0x5)
+#define DDL_I_REQ_OUTPUT_FLUSH (VCD_I_CUSTOM_DDL_BASE + 0x6)
+#define DDL_I_SEQHDR_ALIGN_BYTES (VCD_I_CUSTOM_DDL_BASE + 0x7)
+#define DDL_I_SEQHDR_PRESENT (VCD_I_CUSTOM_DDL_BASE + 0xb)
+#define DDL_I_CAPABILITY (VCD_I_CUSTOM_DDL_BASE + 0x8)
+#define DDL_I_FRAME_PROC_UNITS (VCD_I_CUSTOM_DDL_BASE + 0x9)
+
+struct vcd_property_rc_level {
+ u32 frame_level_rc;
+ u32 mb_level_rc;
+};
+
+struct vcd_property_frame_level_rc_params {
+ u32 reaction_coeff;
+};
+
+struct vcd_property_adaptive_rc_params {
+ u32 dark_region_as_flag;
+ u32 smooth_region_as_flag;
+ u32 static_region_as_flag;
+ u32 activity_region_flag;
+};
+
+struct ddl_frame_data_tag;
+
+struct ddl_property_dec_pic_buffers {
+ struct ddl_frame_data_tag *dec_pic_buffers;
+ u32 no_of_dec_pic_buf;
+};
+
+struct ddl_property_capability {
+ u32 max_num_client;
+ u32 general_command_depth;
+ u32 frame_command_depth;
+ u32 exclusive;
+ u32 ddl_time_out_in_ms;
+};
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c
new file mode 100644
index 0000000..6269f05
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c
@@ -0,0 +1,878 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+static void ddl_decoder_input_done_callback(struct ddl_client_context *ddl,
+ u32 frame_transact_end);
+static void ddl_decoder_ouput_done_callback(struct ddl_client_context *ddl,
+ u32 frame_transact_end);
+
+static u32 ddl_get_frame_type(struct vcd_frame_data *frame, u32 frame_type);
+
+static void ddl_getdec_profilelevel(struct ddl_decoder_data *dec,
+ u32 profile, u32 level);
+
+static void ddl_dma_done_callback(struct ddl_context *ddl_context)
+{
+ if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_DMA_INIT)) {
+ pr_debug("UNKNOWN_DMADONE\n");
+ return;
+ }
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ pr_debug("DMA_DONE");
+ ddl_core_start_cpu(ddl_context);
+}
+
+static void ddl_cpu_started_callback(struct ddl_context *ddl_context)
+{
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ pr_debug("CPU-STARTED");
+
+ if (!vidc_720p_cpu_start()) {
+ ddl_hw_fatal_cb(ddl_context);
+ return;
+ }
+
+ vidc_720p_set_deblock_line_buffer(ddl_context->db_line_buffer.phys_addr,
+ ddl_context->db_line_buffer.size);
+ ddl_context->device_state = DDL_DEVICE_INITED;
+ ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT, VCD_S_SUCCESS,
+ NULL, 0, NULL, ddl_context->client_data);
+ DDL_IDLE(ddl_context);
+}
+
+
+static void ddl_eos_done_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ u32 displaystatus;
+
+ if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_EOS)) {
+ pr_debug("UNKWN_EOSDONE");
+ ddl_client_fatal_cb(ddl_context);
+ return;
+ }
+
+ if (!ddl || !ddl->decoding || !DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+ pr_debug("STATE-CRITICAL-EOSDONE");
+ ddl_client_fatal_cb(ddl_context);
+ return;
+ }
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ vidc_720p_eos_info(&displaystatus);
+ if ((enum vidc_720p_display_status_type) displaystatus
+ != VIDC_720P_EMPTY_BUFFER) {
+ pr_debug("EOSDONE-EMPTYBUF-ISSUE");
+ }
+
+ ddl_decode_dynamic_property(ddl, false);
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+ pr_debug("EOS_DONE");
+ ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, VCD_S_SUCCESS, NULL,
+ 0, (u32 *) ddl, ddl_context->client_data);
+
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_channel_set_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ u32 return_status = false;
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHDONE)) {
+ pr_debug("STATE-CRITICAL-CHSET");
+ DDL_IDLE(ddl_context);
+ return return_status;
+ }
+ pr_debug("Channel-set");
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC);
+
+ if (ddl->decoding) {
+ if (ddl->codec_data.decoder.header_in_start) {
+ ddl_decode_init_codec(ddl);
+ } else {
+ ddl_context->ddl_callback(VCD_EVT_RESP_START,
+ VCD_S_SUCCESS, NULL, 0, (u32 *) ddl,
+ ddl_context->client_data);
+ DDL_IDLE(ddl_context);
+ return_status = true;
+ }
+ } else {
+ ddl_encode_init_codec(ddl);
+ }
+ return return_status;
+}
+
+static void ddl_init_codec_done_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ struct ddl_encoder_data *encoder;
+
+ if (!ddl || ddl->decoding || !DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+ pr_debug("STATE-CRITICAL-INITCODEC");
+ ddl_client_fatal_cb(ddl_context);
+ return;
+ }
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+ pr_debug("INIT_CODEC_DONE");
+
+ encoder = &ddl->codec_data.encoder;
+ if (encoder->seq_header.virt_addr)
+ vidc_720p_encode_get_header(&encoder->seq_header.size);
+
+ ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0,
+ (u32 *) ddl, ddl_context->client_data);
+
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_header_done_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ struct ddl_decoder_data *dec;
+ struct vidc_720p_seq_hdr_info_type seq_hdr_info;
+ u32 vcd_event = VCD_EVT_RESP_START;
+ u32 vcd_status = VCD_S_SUCCESS;
+ u32 req_cb = true, bret = true;
+
+ if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_HEADER_PARSE)) {
+ pr_debug("UNKWN_HEADERDONE");
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+
+ if (!ddl || !ddl->decoding || !DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_INITCODECDONE)) {
+ pr_debug("STATE-CRITICAL-HDDONE");
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPB);
+ pr_debug("HEADER_DONE");
+
+ vidc_720p_decode_get_seq_hdr_info(&seq_hdr_info);
+
+ dec = &(ddl->codec_data.decoder);
+ dec->frame_size.width = seq_hdr_info.img_size_x;
+ dec->frame_size.height = seq_hdr_info.img_size_y;
+ dec->min_dpb_num = seq_hdr_info.min_num_dpb;
+ dec->y_cb_cr_size = seq_hdr_info.min_dpb_size;
+ dec->progressive_only = 1 - seq_hdr_info.progressive;
+ ddl_getdec_profilelevel(dec, seq_hdr_info.profile, seq_hdr_info.level);
+ ddl_calculate_stride(&dec->frame_size, !dec->progressive_only);
+ if (seq_hdr_info.crop_exists) {
+ dec->frame_size.width -= (seq_hdr_info.crop_right_offset +
+ seq_hdr_info.crop_left_offset);
+ dec->frame_size.height -= (seq_hdr_info.crop_top_offset +
+ seq_hdr_info.crop_bottom_offset);
+ }
+ ddl_set_default_decoder_buffer_req(dec, false);
+ if (seq_hdr_info.data_partitioned == 0x1 &&
+ dec->codec_type.codec == VCD_CODEC_MPEG4 &&
+ seq_hdr_info.img_size_x > DDL_MAX_DP_FRAME_WIDTH &&
+ seq_hdr_info.img_size_y > DDL_MAX_DP_FRAME_HEIGHT) {
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+
+
+ if (dec->header_in_start) {
+ dec->client_frame_size = dec->frame_size;
+ dec->client_output_buf_req = dec->actual_output_buf_req;
+ if ((dec->frame_size.width * dec->frame_size.height) >=
+ (800*480)) {
+ if ((dec->actual_output_buf_req.actual_count + 2) < 10)
+ dec->client_output_buf_req.actual_count = 10;
+ else
+ dec->client_output_buf_req.actual_count += 2;
+ } else
+ dec->client_output_buf_req.actual_count =
+ dec->actual_output_buf_req.actual_count + 5;
+
+ dec->client_input_buf_req = dec->actual_input_buf_req;
+ } else {
+ DBG("%s(): width = %d client_frame_size.width = %d\n",
+ __func__, dec->frame_size.width,
+ dec->client_frame_size.width);
+ DBG("%s(): height = %d client_frame_size.height = %d\n",
+ __func__, dec->frame_size.height,
+ dec->client_frame_size.height);
+ DBG("%s(): size = %d client_frame_size size = %d\n",
+ __func__, dec->actual_output_buf_req.size,
+ dec->client_output_buf_req.size);
+ DBG("%s(): min_dpb_num = %d actual_count = %d\n", __func__,
+ dec->min_dpb_num,
+ dec->client_output_buf_req.actual_count);
+
+ bret = false;
+
+ if (dec->frame_size.width == dec->client_frame_size.width &&
+ dec->frame_size.height ==
+ dec->client_frame_size.height &&
+ dec->actual_output_buf_req.size <=
+ dec->client_output_buf_req.size &&
+ dec->min_dpb_num <=
+ dec->client_output_buf_req.actual_count) {
+ vcd_status = ddl_decode_set_buffers(ddl);
+ if (!vcd_status) {
+ req_cb = false;
+ } else {
+ ddl_client_fatal_cb(ddl_context);
+ req_cb = true;
+ }
+ } else {
+ dec->client_frame_size = dec->frame_size;
+ dec->client_output_buf_req = dec->actual_output_buf_req;
+ dec->client_input_buf_req = dec->actual_input_buf_req;
+ pr_err("%s:Decode_reconfig_not_supported\n", __func__);
+ vcd_event = VCD_EVT_IND_RECONFIG;
+ }
+ }
+
+ if (req_cb) {
+ ddl_context->ddl_callback(vcd_event, vcd_status, NULL, 0,
+ (u32 *) ddl, ddl_context->client_data);
+ DDL_IDLE(ddl_context);
+ }
+ return bret;
+}
+
+static u32 ddl_dpb_buffers_set_done_callback(struct ddl_context
+ *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE)) {
+ pr_debug("STATE-CRITICAL-DPBDONE\n");
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+ pr_debug("INTR_DPBDONE\n");
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+ ddl_decode_frame_run(ddl);
+ return false;
+}
+
+static void ddl_encoder_frame_run_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder);
+ u32 eos_present = false;
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) {
+ pr_debug("STATE-CRITICAL-ENCFRMRUN\n");
+ ddl_client_fatal_cb(ddl_context);
+ return;
+ }
+
+ pr_debug("ENC_FRM_RUN_DONE\n");
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ vidc_720p_enc_frame_info(&encoder->enc_frame_info);
+
+ ddl->output_frame.vcd_frm.ip_frm_tag =
+ ddl->input_frame.vcd_frm.ip_frm_tag;
+ ddl->output_frame.vcd_frm.data_len =
+ encoder->enc_frame_info.enc_size;
+ ddl->output_frame.vcd_frm.flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+ ddl_get_frame_type(&(ddl->output_frame.vcd_frm),
+ encoder->enc_frame_info.frame_type);
+ ddl_process_encoder_metadata(ddl);
+
+ ddl_encode_dynamic_property(ddl, false);
+
+ ddl->input_frame.frm_trans_end = false;
+ ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS,
+ &(ddl->input_frame), sizeof(struct ddl_frame_data_tag),
+ (u32 *) ddl, ddl_context->client_data);
+
+#ifdef CORE_TIMING_INFO
+ ddl_calc_core_time(1);
+#endif
+ /* check the presence of EOS */
+ eos_present = VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags;
+
+ ddl->output_frame.frm_trans_end = !eos_present;
+ ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+ &(ddl->output_frame), sizeof(struct ddl_frame_data_tag),
+ (u32 *) ddl, ddl_context->client_data);
+
+ if (eos_present) {
+ pr_debug("ENC-EOS_DONE\n");
+ ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE,
+ VCD_S_SUCCESS, NULL, 0, (u32 *)ddl,
+ ddl_context->client_data);
+ }
+
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_decoder_frame_run_callback(struct ddl_context
+ *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ struct ddl_decoder_data *dec = &(ddl->codec_data.decoder);
+ struct vidc_720p_dec_disp_info *dec_disp_info = &(dec->dec_disp_info);
+ u32 callback_end = false;
+ u32 status = true, eos_present = false;;
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) {
+ pr_debug("STATE-CRITICAL-DECFRMRUN\n");
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+
+ pr_debug("DEC_FRM_RUN_DONE\n");
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ vidc_720p_decode_display_info(dec_disp_info);
+
+ ddl_decode_dynamic_property(ddl, false);
+
+ if (dec_disp_info->resl_change) {
+ pr_err("ddl_dec_frm_done:"
+ "Dec_reconfig_no_tsupported\n");
+ }
+
+ if ((VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags)) {
+ callback_end = false;
+ eos_present = true;
+ }
+
+ if (dec_disp_info->disp_status == VIDC_720P_DECODE_ONLY ||
+ dec_disp_info->disp_status ==
+ VIDC_720P_DECODE_AND_DISPLAY) {
+ if (!eos_present)
+ callback_end = (dec_disp_info->disp_status ==
+ VIDC_720P_DECODE_ONLY);
+
+ ddl_decoder_input_done_callback(ddl, callback_end);
+ }
+
+ if (dec_disp_info->disp_status == VIDC_720P_DECODE_AND_DISPLAY ||
+ dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) {
+ if (!eos_present)
+ callback_end = (dec_disp_info->disp_status ==
+ VIDC_720P_DECODE_AND_DISPLAY);
+
+ ddl_decoder_ouput_done_callback(ddl, callback_end);
+ }
+
+ if (dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) {
+ /* send the same input once again for decoding */
+ ddl_decode_frame_run(ddl);
+ /* client need to ignore the interrupt */
+ status = false;
+ } else if (eos_present) {
+ /* send EOS command to HW */
+ ddl_decode_eos_run(ddl);
+ /* client need to ignore the interrupt */
+ status = false;
+ } else {
+ ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME);
+ /* move to Idle */
+ DDL_IDLE(ddl_context);
+ }
+ return status;
+}
+
+static u32 ddl_eos_frame_done_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl = ddl_context->current_ddl;
+ struct ddl_decoder_data *dec = &(ddl->codec_data.decoder);
+ struct vidc_720p_dec_disp_info *dec_disp_info =
+ &(dec->dec_disp_info);
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) {
+ pr_err("STATE-CRITICAL-EOSFRMRUN\n");
+ ddl_client_fatal_cb(ddl_context);
+ return true;
+ }
+ pr_debug("EOS_FRM_RUN_DONE\n");
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+
+ vidc_720p_decode_display_info(dec_disp_info);
+
+ ddl_decode_dynamic_property(ddl, false);
+
+ if (ddl_context->op_failed == 0x1)
+ pr_err("ddl_eos_frm_done:OPFAILED!!\n");
+ else if (dec_disp_info->resl_change)
+ pr_err("ddl_eos_frm_done:Dec_reconfig!!\n");
+
+ if (dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY)
+ ddl_decoder_ouput_done_callback(ddl, false);
+ else
+ pr_debug("STATE-CRITICAL-WRONG-DISP-STATUS\n");
+
+ ddl_decoder_dpb_transact(dec, NULL, DDL_DPB_OP_SET_MASK);
+ ddl_move_command_state(ddl_context, DDL_CMD_EOS);
+ vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_FRAMERUN);
+ return false;
+}
+
+static void ddl_channel_end_callback(struct ddl_context *ddl_context)
+{
+ struct ddl_client_context *ddl;
+
+ ddl_move_command_state(ddl_context, DDL_CMD_INVALID);
+ pr_debug("CH_END_DONE\n");
+
+ ddl = ddl_context->current_ddl;
+ if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHEND)) {
+ pr_debug("STATE-CRITICAL-CHEND\n");
+ DDL_IDLE(ddl_context);
+ return;
+ }
+
+ ddl_release_client_internal_buffers(ddl);
+ ddl_context->ddl_callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0,
+ (u32 *) ddl, ddl_context->client_data);
+ ddl_move_client_state(ddl, DDL_CLIENT_OPEN);
+ DDL_IDLE(ddl_context);
+}
+
+static u32 ddl_operation_done_callback(struct ddl_context *ddl_context)
+{
+ u32 return_status = true;
+
+ switch (ddl_context->cmd_state) {
+ case DDL_CMD_DECODE_FRAME:
+ return_status = ddl_decoder_frame_run_callback(ddl_context);
+ break;
+ case DDL_CMD_ENCODE_FRAME:
+ ddl_encoder_frame_run_callback(ddl_context);
+ break;
+ case DDL_CMD_CHANNEL_SET:
+ return_status = ddl_channel_set_callback(ddl_context);
+ break;
+ case DDL_CMD_INIT_CODEC:
+ ddl_init_codec_done_callback(ddl_context);
+ break;
+ case DDL_CMD_HEADER_PARSE:
+ return_status = ddl_header_done_callback(ddl_context);
+ break;
+ case DDL_CMD_DECODE_SET_DPB:
+ return_status = ddl_dpb_buffers_set_done_callback(ddl_context);
+ break;
+ case DDL_CMD_CHANNEL_END:
+ ddl_channel_end_callback(ddl_context);
+ break;
+ case DDL_CMD_EOS:
+ return_status = ddl_eos_frame_done_callback(ddl_context);
+ break;
+ case DDL_CMD_CPU_RESET:
+ ddl_cpu_started_callback(ddl_context);
+ break;
+ default:
+ pr_debug("UNKWN_OPDONE\n");
+ return_status = false;
+ break;
+ }
+ return return_status;
+}
+
+static u32 ddl_process_intr_status(struct ddl_context *ddl_context,
+ u32 int_status)
+{
+ u32 status = true;
+ switch (int_status) {
+ case VIDC_720P_INTR_FRAME_DONE:
+ status = ddl_operation_done_callback(ddl_context);
+ break;
+ case VIDC_720P_INTR_DMA_DONE:
+ ddl_dma_done_callback(ddl_context);
+ status = false;
+ break;
+ case VIDC_720P_INTR_FW_DONE:
+ ddl_eos_done_callback(ddl_context);
+ break;
+ case VIDC_720P_INTR_BUFFER_FULL:
+ pr_err("BUF_FULL_INTR\n");
+ break;
+ default:
+ pr_err("UNKWN_INTR\n");
+ break;
+ }
+ return status;
+}
+
+void ddl_read_and_clear_interrupt(void)
+{
+ struct ddl_context *ddl_context;
+
+ ddl_context = ddl_get_context();
+ if (!ddl_context->core_virtual_base_addr) {
+ pr_err("SPURIOUS_INTERRUPT\n");
+ return;
+ }
+ vidc_720p_get_interrupt_status(&ddl_context->intr_status,
+ &ddl_context->cmd_err_status,
+ &ddl_context->disp_pic_err_status,
+ &ddl_context->op_failed);
+
+ vidc_720p_interrupt_done_clear();
+}
+
+u32 ddl_process_core_response(void)
+{
+ struct ddl_context *ddl_context;
+ u32 return_status = true;
+
+ ddl_context = ddl_get_context();
+ if (!ddl_context->core_virtual_base_addr) {
+ pr_err("UNKWN_INTR\n");
+ return false;
+ }
+ if (ddl_context->intr_status == DDL_INVALID_INTR_STATUS) {
+ pr_err("INTERRUPT_NOT_READ\n");
+ return false;
+ }
+
+ if (!ddl_handle_core_errors(ddl_context)) {
+ return_status = ddl_process_intr_status(ddl_context,
+ ddl_context->intr_status);
+ }
+
+ if (ddl_context->pf_interrupt_clr)
+ (*ddl_context->pf_interrupt_clr)();
+
+ ddl_context->intr_status = DDL_INVALID_INTR_STATUS;
+ return return_status;
+}
+
+static void ddl_decoder_input_done_callback(
+ struct ddl_client_context *ddl, u32 frame_transact_end)
+{
+ struct vidc_720p_dec_disp_info *dec_disp_info =
+ &(ddl->codec_data.decoder.dec_disp_info);
+ struct vcd_frame_data *input_vcd_frm = &(ddl->input_frame.vcd_frm);
+ ddl_get_frame_type(input_vcd_frm, dec_disp_info->input_frame_type);
+
+ input_vcd_frm->interlaced = (dec_disp_info->input_is_interlace);
+
+ input_vcd_frm->offset += dec_disp_info->input_bytes_consumed;
+ input_vcd_frm->data_len -= dec_disp_info->input_bytes_consumed;
+
+ ddl->input_frame.frm_trans_end = frame_transact_end;
+ ddl->ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE,
+ VCD_S_SUCCESS,
+ &ddl->input_frame,
+ sizeof(struct ddl_frame_data_tag),
+ (void *)ddl,
+ ddl->ddl_context->client_data);
+}
+
+static void ddl_decoder_ouput_done_callback(struct ddl_client_context *ddl,
+ u32 frame_transact_end)
+{
+ struct ddl_decoder_data *dec = &ddl->codec_data.decoder;
+ struct vidc_720p_dec_disp_info *dec_disp_info = &dec->dec_disp_info;
+ struct ddl_frame_data_tag *output_frame = &ddl->output_frame;
+ struct vcd_frame_data *output_vcd_frm = &output_frame->vcd_frm;
+ u32 vcd_status;
+ phys_addr_t free_luma_dpb = 0;
+
+ output_vcd_frm->phys_addr = dec_disp_info->y_addr;
+
+ if (dec->codec_type.codec == VCD_CODEC_MPEG4 ||
+ dec->codec_type.codec == VCD_CODEC_VC1 ||
+ dec->codec_type.codec == VCD_CODEC_VC1_RCV) {
+ vidc_720p_decode_skip_frm_details(&free_luma_dpb);
+ if (free_luma_dpb)
+ output_vcd_frm->phys_addr = free_luma_dpb;
+ }
+
+ vcd_status = ddl_decoder_dpb_transact(dec, output_frame,
+ DDL_DPB_OP_MARK_BUSY);
+
+ output_vcd_frm->ip_frm_tag = dec_disp_info->tag_top;
+ if (dec_disp_info->crop_exists == 0x1) {
+ output_vcd_frm->dec_op_prop.disp_frm.left =
+ dec_disp_info->crop_left_offset;
+ output_vcd_frm->dec_op_prop.disp_frm.top =
+ dec_disp_info->crop_top_offset;
+ output_vcd_frm->dec_op_prop.disp_frm.right =
+ dec_disp_info->img_size_x -
+ dec_disp_info->crop_right_offset;
+ output_vcd_frm->dec_op_prop.disp_frm.bottom =
+ dec_disp_info->img_size_y -
+ dec_disp_info->crop_bottom_offset;
+ } else {
+ output_vcd_frm->dec_op_prop.disp_frm.left = 0;
+ output_vcd_frm->dec_op_prop.disp_frm.top = 0;
+ output_vcd_frm->dec_op_prop.disp_frm.right =
+ dec_disp_info->img_size_x;
+ output_vcd_frm->dec_op_prop.disp_frm.bottom =
+ dec_disp_info->img_size_y;
+ }
+ if (!dec_disp_info->disp_is_interlace) {
+ output_vcd_frm->interlaced = false;
+ output_frame->intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID;
+ } else {
+ output_vcd_frm->interlaced = true;
+ output_frame->intrlcd_ip_frm_tag = dec_disp_info->tag_bottom;
+ }
+
+ output_vcd_frm->offset = 0;
+ output_vcd_frm->data_len = dec->y_cb_cr_size;
+
+ if (free_luma_dpb)
+ output_vcd_frm->data_len = 0;
+
+ output_vcd_frm->flags |= VCD_FRAME_FLAG_ENDOFFRAME;
+
+ if (!vcd_status)
+ ddl_process_decoder_metadata(ddl);
+ output_frame->frm_trans_end = frame_transact_end;
+
+#ifdef CORE_TIMING_INFO
+ ddl_calc_core_time(0);
+#endif
+
+ ddl->ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE,
+ vcd_status, output_frame, sizeof(struct ddl_frame_data_tag),
+ (void *)ddl, ddl->ddl_context->client_data);
+}
+
+static u32 ddl_get_frame_type(struct vcd_frame_data *frame, u32 frame_type)
+{
+ enum vidc_720p_frame_type e_frame_type =
+ (enum vidc_720p_frame_type)frame_type;
+ u32 status = true;
+
+ switch (e_frame_type) {
+ case VIDC_720P_IFRAME:
+ frame->flags |= VCD_FRAME_FLAG_SYNCFRAME;
+ frame->frame_type = VCD_FRAME_I;
+ break;
+ case VIDC_720P_PFRAME:
+ frame->frame_type = VCD_FRAME_P;
+ break;
+ case VIDC_720P_BFRAME:
+ frame->frame_type = VCD_FRAME_B;
+ break;
+ case VIDC_720P_NOTCODED:
+ frame->data_len = 0;
+ break;
+ default:
+ pr_debug("CRITICAL-FRAMETYPE\n");
+ status = false;
+ break;
+ }
+ return status;
+}
+
+static void ddl_getmpeg4_declevel(enum vcd_codec_level_type *vcd_level,
+ u32 level)
+{
+ switch (level) {
+ case VIDC_720P_MPEG4_LEVEL0:
+ *vcd_level = VCD_LEVEL_MPEG4_0;
+ break;
+ case VIDC_720P_MPEG4_LEVEL0b:
+ *vcd_level = VCD_LEVEL_MPEG4_0b;
+ break;
+ case VIDC_720P_MPEG4_LEVEL1:
+ *vcd_level = VCD_LEVEL_MPEG4_1;
+ break;
+ case VIDC_720P_MPEG4_LEVEL2:
+ *vcd_level = VCD_LEVEL_MPEG4_2;
+ break;
+ case VIDC_720P_MPEG4_LEVEL3:
+ *vcd_level = VCD_LEVEL_MPEG4_3;
+ break;
+ case VIDC_720P_MPEG4_LEVEL3b:
+ *vcd_level = VCD_LEVEL_MPEG4_3b;
+ break;
+ case VIDC_720P_MPEG4_LEVEL4a:
+ *vcd_level = VCD_LEVEL_MPEG4_4a;
+ break;
+ case VIDC_720P_MPEG4_LEVEL5:
+ *vcd_level = VCD_LEVEL_MPEG4_5;
+ break;
+ case VIDC_720P_MPEG4_LEVEL6:
+ *vcd_level = VCD_LEVEL_MPEG4_6;
+ break;
+ }
+}
+
+static void ddl_geth264_declevel(enum vcd_codec_level_type *vcd_level,
+ u32 level)
+{
+ switch (level) {
+ case VIDC_720P_H264_LEVEL1:
+ *vcd_level = VCD_LEVEL_H264_1;
+ break;
+ case VIDC_720P_H264_LEVEL1b:
+ *vcd_level = VCD_LEVEL_H264_1b;
+ break;
+ case VIDC_720P_H264_LEVEL1p1:
+ *vcd_level = VCD_LEVEL_H264_1p1;
+ break;
+ case VIDC_720P_H264_LEVEL1p2:
+ *vcd_level = VCD_LEVEL_H264_1p2;
+ break;
+ case VIDC_720P_H264_LEVEL1p3:
+ *vcd_level = VCD_LEVEL_H264_1p3;
+ break;
+ case VIDC_720P_H264_LEVEL2:
+ *vcd_level = VCD_LEVEL_H264_2;
+ break;
+ case VIDC_720P_H264_LEVEL2p1:
+ *vcd_level = VCD_LEVEL_H264_2p1;
+ break;
+ case VIDC_720P_H264_LEVEL2p2:
+ *vcd_level = VCD_LEVEL_H264_2p2;
+ break;
+ case VIDC_720P_H264_LEVEL3:
+ *vcd_level = VCD_LEVEL_H264_3;
+ break;
+ case VIDC_720P_H264_LEVEL3p1:
+ *vcd_level = VCD_LEVEL_H264_3p1;
+ break;
+ case VIDC_720P_H264_LEVEL3p2:
+ *vcd_level = VCD_LEVEL_H264_3p2;
+ break;
+ }
+}
+
+static void ddl_get_vc1_dec_level(enum vcd_codec_level_type *vcd_level,
+ u32 level, enum vcd_codec_profile_type vc1_profile)
+{
+ if (vc1_profile == VCD_PROFILE_VC1_ADVANCE) {
+ switch (level) {
+ case VIDC_720P_VC1_LEVEL0:
+ *vcd_level = VCD_LEVEL_VC1_0;
+ break;
+ case VIDC_720P_VC1_LEVEL1:
+ *vcd_level = VCD_LEVEL_VC1_1;
+ break;
+ case VIDC_720P_VC1_LEVEL2:
+ *vcd_level = VCD_LEVEL_VC1_2;
+ break;
+ case VIDC_720P_VC1_LEVEL3:
+ *vcd_level = VCD_LEVEL_VC1_3;
+ break;
+ case VIDC_720P_VC1_LEVEL4:
+ *vcd_level = VCD_LEVEL_VC1_4;
+ break;
+ }
+ return;
+ }
+
+ /* now determine the Main and Simple profile level */
+ switch (level) {
+ case VIDC_720P_VC1_LEVEL_LOW:
+ *vcd_level = VCD_LEVEL_VC1_LOW;
+ break;
+ case VIDC_720P_VC1_LEVEL_MED:
+ *vcd_level = VCD_LEVEL_VC1_MEDIUM;
+ break;
+ case VIDC_720P_VC1_LEVEL_HIGH:
+ *vcd_level = VCD_LEVEL_VC1_HIGH;
+ break;
+ }
+}
+
+static void ddl_get_mpeg2_dec_level(enum vcd_codec_level_type *vcd_level,
+ u32 level)
+{
+ switch (level) {
+ case VIDCL_720P_MPEG2_LEVEL_LOW:
+ *vcd_level = VCD_LEVEL_MPEG2_LOW;
+ break;
+ case VIDCL_720P_MPEG2_LEVEL_MAIN:
+ *vcd_level = VCD_LEVEL_MPEG2_MAIN;
+ break;
+ case VIDCL_720P_MPEG2_LEVEL_HIGH14:
+ *vcd_level = VCD_LEVEL_MPEG2_HIGH_14;
+ break;
+ }
+}
+
+static void ddl_getdec_profilelevel(struct ddl_decoder_data *dec,
+ u32 profile, u32 level)
+{
+ enum vcd_codec_profile_type vcd_profile = VCD_PROFILE_UNKNOWN;
+ enum vcd_codec_level_type vcd_level = VCD_LEVEL_UNKNOWN;
+
+ switch (dec->codec_type.codec) {
+ case VCD_CODEC_MPEG4:
+ if (profile == VIDC_720P_PROFILE_MPEG4_SP)
+ vcd_profile = VCD_PROFILE_MPEG4_SP;
+ else if (profile == VIDC_720P_PROFILE_MPEG4_ASP)
+ vcd_profile = VCD_PROFILE_MPEG4_ASP;
+
+ ddl_getmpeg4_declevel(&vcd_level, level);
+ break;
+ case VCD_CODEC_H264:
+ if (profile == VIDC_720P_PROFILE_H264_BASELINE)
+ vcd_profile = VCD_PROFILE_H264_BASELINE;
+ else if (profile == VIDC_720P_PROFILE_H264_MAIN)
+ vcd_profile = VCD_PROFILE_H264_MAIN;
+ else if (profile == VIDC_720P_PROFILE_H264_HIGH)
+ vcd_profile = VCD_PROFILE_H264_HIGH;
+ ddl_geth264_declevel(&vcd_level, level);
+ break;
+ default:
+ case VCD_CODEC_H263:
+ break;
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ if (profile == VIDC_720P_PROFILE_VC1_SP)
+ vcd_profile = VCD_PROFILE_VC1_SIMPLE;
+ else if (profile == VIDC_720P_PROFILE_VC1_MAIN)
+ vcd_profile = VCD_PROFILE_VC1_MAIN;
+ else if (profile == VIDC_720P_PROFILE_VC1_ADV)
+ vcd_profile = VCD_PROFILE_VC1_ADVANCE;
+ ddl_get_vc1_dec_level(&vcd_level, level, vcd_profile);
+ break;
+ case VCD_CODEC_MPEG2:
+ if (profile == VIDC_720P_PROFILE_MPEG2_MAIN)
+ vcd_profile = VCD_PROFILE_MPEG2_MAIN;
+ else if (profile == VIDC_720P_PROFILE_MPEG2_SP)
+ vcd_profile = VCD_PROFILE_MPEG2_SIMPLE;
+ ddl_get_mpeg2_dec_level(&vcd_level, level);
+ break;
+ }
+
+ dec->profile.profile = vcd_profile;
+ dec->level.level = vcd_level;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c
new file mode 100644
index 0000000..230e7ec
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c
@@ -0,0 +1,545 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+static u32 *ddl_metadata_hdr_entry(struct ddl_client_context *ddl,
+ u32 meta_data_type)
+{
+ u32 skip_words;
+ u32 *buffer;
+
+ if (ddl->decoding) {
+ buffer = ddl->codec_data.decoder.meta_data_input.virt_addr;
+ skip_words = 32 + 1;
+ buffer += skip_words;
+
+ switch (meta_data_type) {
+ default:
+ case VCD_METADATA_DATANONE:
+ skip_words = 0;
+ break;
+ case VCD_METADATA_QPARRAY:
+ skip_words = 3;
+ break;
+ case VCD_METADATA_CONCEALMB:
+ skip_words = 6;
+ break;
+ case VCD_METADATA_VC1:
+ skip_words = 9;
+ break;
+ case VCD_METADATA_SEI:
+ skip_words = 12;
+ break;
+ case VCD_METADATA_VUI:
+ skip_words = 15;
+ break;
+ case VCD_METADATA_PASSTHROUGH:
+ skip_words = 18;
+ break;
+ case VCD_METADATA_QCOMFILLER:
+ skip_words = 21;
+ break;
+ }
+ } else {
+ buffer = ddl->codec_data.encoder.meta_data_input.virt_addr;
+ skip_words = 2;
+ buffer += skip_words;
+
+ switch (meta_data_type) {
+ default:
+ case VCD_METADATA_DATANONE:
+ skip_words = 0;
+ break;
+ case VCD_METADATA_ENC_SLICE:
+ skip_words = 3;
+ break;
+ case VCD_METADATA_QCOMFILLER:
+ skip_words = 6;
+ break;
+ }
+ }
+
+ buffer += skip_words;
+ return buffer;
+}
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl)
+{
+ struct ddl_dma_buffer *main_buffer =
+ &ddl->ddl_context->metadata_shared_input;
+ struct ddl_dma_buffer *b;
+ u32 *hdr;
+
+ if (ddl->decoding)
+ b = &ddl->codec_data.decoder.meta_data_input;
+ else
+ b = &ddl->codec_data.encoder.meta_data_input;
+
+ b->phys_addr = main_buffer->phys_addr +
+ DDL_METADATA_CLIENT_INPUTBUFSIZE * ddl->channel_id;
+ b->virt_addr = (void *)((u8 *)main_buffer->virt_addr +
+ DDL_METADATA_CLIENT_INPUTBUFSIZE * ddl->channel_id);
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 1;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 1;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_DATANONE);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 2;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 2;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE;
+
+ if (ddl->decoding) {
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QPARRAY);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 3;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 3;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_CONCEALMB);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 4;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 4;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_CONCEALMB;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_SEI);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 5;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 5;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VUI);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 6;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 6;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VC1);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 7;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 7;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1;
+
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_PASSTHROUGH);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 8;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 8;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] =
+ VCD_METADATA_PASSTHROUGH;
+
+ } else {
+ hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_ENC_SLICE);
+ hdr[DDL_METADATA_HDR_VERSION_INDEX] = 9;
+ hdr[DDL_METADATA_HDR_PORT_INDEX] = 9;
+ hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_ENC_SLICE;
+ }
+}
+
+static u32 ddl_supported_metadata_flag(struct ddl_client_context *ddl)
+{
+ u32 flag = 0;
+
+ if (ddl->decoding) {
+ enum vcd_codec codec =
+ ddl->codec_data.decoder.codec_type.codec;
+
+ flag |= VCD_METADATA_CONCEALMB | VCD_METADATA_PASSTHROUGH |
+ VCD_METADATA_QPARRAY;
+ if (codec == VCD_CODEC_H264)
+ flag |= VCD_METADATA_SEI | VCD_METADATA_VUI;
+ else if (codec == VCD_CODEC_VC1 || codec == VCD_CODEC_VC1_RCV)
+ flag |= VCD_METADATA_VC1;
+ } else {
+ flag |= VCD_METADATA_ENC_SLICE;
+ }
+
+ return flag;
+}
+
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl)
+{
+ if (ddl->decoding)
+ ddl->codec_data.decoder.meta_data_enable_flag = 0;
+ else
+ ddl->codec_data.encoder.meta_data_enable_flag = 0;
+}
+
+void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data *dec,
+ struct vcd_property_frame_size *frame_size,
+ struct vcd_buffer_requirement *output_buf_req)
+{
+ u32 flag = dec->meta_data_enable_flag;
+ u32 suffix = 0;
+ u32 size = 0;
+
+ if (!flag) {
+ dec->suffix = 0;
+ return;
+ }
+
+ if (flag & VCD_METADATA_QPARRAY) {
+ u32 num_of_mb = ((frame_size->width * frame_size->height) >> 8);
+ size = DDL_METADATA_HDR_SIZE;
+ size += num_of_mb;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += size;
+ }
+ if (flag & VCD_METADATA_CONCEALMB) {
+ u32 num_of_mb = ((frame_size->width * frame_size->height) >> 8);
+ size = DDL_METADATA_HDR_SIZE;
+ size *= (4 * num_of_mb / 2);
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += size;
+ }
+ if (flag & VCD_METADATA_VC1) {
+ size = DDL_METADATA_HDR_SIZE;
+ size += DDL_METADATA_VC1_PAYLOAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += size;
+ }
+ if (flag & VCD_METADATA_SEI) {
+ size = DDL_METADATA_HDR_SIZE;
+ size += DDL_METADATA_SEI_PAYLOAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += (size * DDL_METADATA_SEI_MAX);
+ }
+ if (flag & VCD_METADATA_VUI) {
+ size = DDL_METADATA_HDR_SIZE;
+ size += DDL_METADATA_VUI_PAYLOAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += (size);
+ }
+ if (flag & VCD_METADATA_PASSTHROUGH) {
+ size = DDL_METADATA_HDR_SIZE;
+ size += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += (size);
+ }
+ size = DDL_METADATA_EXTRADATANONE_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += (size);
+
+ suffix += DDL_METADATA_EXTRAPAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(suffix);
+
+ dec->suffix = suffix;
+ output_buf_req->size += suffix;
+ return;
+}
+
+void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data *enc)
+{
+ u32 flag = enc->meta_data_enable_flag;
+ u32 suffix = 0;
+ u32 size = 0;
+
+ if (!flag) {
+ enc->suffix = 0;
+ return;
+ }
+
+ if (flag & VCD_METADATA_ENC_SLICE) {
+ u32 num_of_mb = enc->frame_size.width * enc->frame_size.height /
+ 16 / 16;
+ size = DDL_METADATA_HDR_SIZE + 4 + 8 * num_of_mb;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += size;
+ }
+
+ size = DDL_METADATA_EXTRADATANONE_SIZE;
+ DDL_METADATA_ALIGNSIZE(size);
+ suffix += (size);
+
+ suffix += DDL_METADATA_EXTRAPAD_SIZE;
+ DDL_METADATA_ALIGNSIZE(suffix);
+
+ enc->suffix = suffix;
+ enc->output_buf_req.size += suffix;
+}
+
+static u32 ddl_set_metadata_enable_client_open(struct ddl_client_context *ddl,
+ struct vcd_property_meta_data_enable *meta_data_enable,
+ u32 *meta_data_enable_flag)
+{
+ if (!meta_data_enable->meta_data_enable_flag) {
+ *meta_data_enable_flag = 0;
+ if (ddl->decoding) {
+ ddl_set_default_decoder_buffer_req(
+ &ddl->codec_data.decoder, true);
+ } else {
+ ddl_set_default_encoder_buffer_req(
+ &ddl->codec_data.encoder);
+ }
+
+ } else {
+ u32 flag = ddl_supported_metadata_flag(ddl);
+ flag &= meta_data_enable->meta_data_enable_flag;
+ if (flag) {
+ flag |= DDL_METADATA_MANDATORY;
+ if (flag != *meta_data_enable_flag) {
+ *meta_data_enable_flag = flag;
+
+ if (ddl->decoding) {
+ ddl_set_default_decoder_buffer_req(
+ &ddl->codec_data.decoder, true);
+ } else {
+ ddl_set_default_encoder_buffer_req(
+ &ddl->codec_data.encoder);
+ }
+
+ }
+ }
+ }
+ return VCD_S_SUCCESS;
+}
+
+static u32 ddl_set_metadata_header(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *property_hdr,
+ struct vcd_property_metadata_hdr *hdr)
+{
+ u32 flag;
+ if (sizeof(struct vcd_property_metadata_hdr) != property_hdr->sz)
+ return VCD_ERR_ILLEGAL_PARM;
+
+ flag = ddl_supported_metadata_flag(ddl);
+ flag |= DDL_METADATA_MANDATORY;
+ flag &= hdr->meta_data_id_type;
+ if (!(flag & (flag - 1))) {
+ u32 *hdr_entry = ddl_metadata_hdr_entry(ddl, flag);
+ hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = hdr->version;
+ hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = hdr->port_index;
+ hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = hdr->type;
+ return VCD_S_SUCCESS;
+ }
+ return VCD_ERR_ILLEGAL_PARM;
+}
+
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *prop, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ if (prop->id == VCD_I_METADATA_ENABLE) {
+ struct vcd_property_meta_data_enable *meta_data_enable =
+ (struct vcd_property_meta_data_enable *)value;
+ u32 *meta_data_enable_flag;
+ enum vcd_codec codec;
+ if (ddl->decoding) {
+ meta_data_enable_flag = &ddl->codec_data.decoder.
+ meta_data_enable_flag;
+ codec = ddl->codec_data.decoder.codec_type.codec;
+ } else {
+ meta_data_enable_flag = &ddl->codec_data.encoder.
+ meta_data_enable_flag;
+ codec = ddl->codec_data.encoder.codec_type.codec;
+ }
+ if (sizeof(struct vcd_property_meta_data_enable) == prop->sz &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+ codec) {
+ vcd_status = ddl_set_metadata_enable_client_open(ddl,
+ meta_data_enable, meta_data_enable_flag);
+ }
+ } else if (prop->id == VCD_I_METADATA_HEADER) {
+ vcd_status = ddl_set_metadata_header(ddl, prop, value);
+ }
+ return vcd_status;
+}
+
+u32 ddl_get_metadata_params(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *prop, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct vcd_property_meta_data_enable *enable;
+ struct vcd_property_metadata_hdr *hdr;
+
+ if (prop->id == VCD_I_METADATA_ENABLE && prop->sz == sizeof(*enable)) {
+ enable = value;
+ enable->meta_data_enable_flag = ddl->decoding ?
+ ddl->codec_data.decoder.meta_data_enable_flag :
+ ddl->codec_data.encoder.meta_data_enable_flag;
+ vcd_status = VCD_S_SUCCESS;
+ } else if (prop->id == VCD_I_METADATA_HEADER &&
+ sizeof(*hdr) == prop->sz) {
+ u32 flag = ddl_supported_metadata_flag(ddl);
+ hdr = value;
+ flag |= DDL_METADATA_MANDATORY;
+ flag &= hdr->meta_data_id_type;
+ if (!(flag & (flag - 1))) {
+ u32 *hdr_entry = ddl_metadata_hdr_entry(ddl, flag);
+ hdr->version =
+ hdr_entry[DDL_METADATA_HDR_VERSION_INDEX];
+ hdr->port_index =
+ hdr_entry[DDL_METADATA_HDR_PORT_INDEX];
+ hdr->type = hdr_entry[DDL_METADATA_HDR_TYPE_INDEX];
+ vcd_status = VCD_S_SUCCESS;
+ }
+ }
+ return vcd_status;
+}
+
+void ddl_metadata_enable(struct ddl_client_context *ddl)
+{
+ u32 flag, hal_flag = 0;
+ phys_addr_t input;
+ if (ddl->decoding) {
+ flag = ddl->codec_data.decoder.meta_data_enable_flag;
+ input = ddl->codec_data.decoder.meta_data_input.phys_addr;
+ } else {
+ flag = ddl->codec_data.encoder.meta_data_enable_flag;
+ input = ddl->codec_data.encoder.meta_data_input.phys_addr;
+ }
+ if (flag) {
+ if (flag & VCD_METADATA_QPARRAY)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_QP;
+ if (flag & VCD_METADATA_CONCEALMB)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_CONCEALMB;
+ if (flag & VCD_METADATA_VC1)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_VC1;
+ if (flag & VCD_METADATA_SEI)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_SEI;
+ if (flag & VCD_METADATA_VUI)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_VUI;
+ if (flag & VCD_METADATA_ENC_SLICE)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_ENCSLICE;
+ if (flag & VCD_METADATA_PASSTHROUGH)
+ hal_flag |= VIDC_720P_METADATA_ENABLE_PASSTHROUGH;
+ } else {
+ input = 0;
+ }
+ vidc_720p_metadata_enable(hal_flag, input);
+}
+
+phys_addr_t ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *encoder = &ddl->codec_data.encoder;
+ u32 *buffer;
+ struct vcd_frame_data *stream = &(ddl->output_frame.vcd_frm);
+ phys_addr_t ext_buffer_end;
+ phys_addr_t hw_metadata_start;
+
+ ext_buffer_end = stream->phys_addr + stream->alloc_len;
+ if (!encoder->meta_data_enable_flag) {
+ ext_buffer_end &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+ return ext_buffer_end;
+ }
+ hw_metadata_start = (ext_buffer_end - encoder->suffix) &
+ ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+
+ ext_buffer_end = (hw_metadata_start - 1) &
+ ~DDL_STREAMBUF_ALIGN_GUARD_BYTES;
+
+ buffer = encoder->meta_data_input.virt_addr;
+
+ *buffer++ = encoder->suffix;
+
+ *buffer = hw_metadata_start;
+
+ encoder->meta_data_offset = hw_metadata_start - stream->phys_addr;
+
+ return ext_buffer_end;
+}
+
+void ddl_decode_set_metadata_output(struct ddl_decoder_data *dec)
+{
+ int i;
+ u32 *buffer;
+
+ if (!dec->meta_data_enable_flag) {
+ dec->meta_data_offset = 0;
+ return;
+ }
+
+ dec->meta_data_offset = ddl_get_yuv_buffer_size(&dec->client_frame_size,
+ &dec->buf_format, !dec->progressive_only);
+
+ buffer = dec->meta_data_input.virt_addr;
+
+ *buffer++ = dec->suffix;
+
+ for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i)
+ *buffer++ = dec->dp_buf.dec_pic_buffers[i].vcd_frm.phys_addr +
+ dec->meta_data_offset;
+}
+
+//TOOD consider combining ddl_process_xxx_metadata
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *enc = &ddl->codec_data.encoder;
+ struct vcd_frame_data *frm = &ddl->output_frame.vcd_frm;
+ u32 *qfill_hdr;
+ u32 *qfill;
+ unsigned long tmp;
+ size_t qfill_sz;
+
+ if (!enc->meta_data_enable_flag) {
+ frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA;
+ return;
+ }
+
+ if (!enc->enc_frame_info.metadata_exists) {
+ frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA;
+ return;
+ }
+ frm->flags |= VCD_FRAME_FLAG_EXTRADATA;
+
+ tmp = (unsigned long)frm->virt_addr + frm->offset + frm->data_len;
+ qfill = (u32 *)ALIGN(tmp, 4);
+
+ qfill_sz = enc->meta_data_offset + (u8 *)frm->virt_addr - (u8 *)qfill;
+
+ qfill_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+
+ *qfill++ = qfill_sz;
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_PORT_INDEX];
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+ *qfill = qfill_sz - DDL_METADATA_HDR_SIZE;
+}
+
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl)
+{
+ struct ddl_decoder_data *dec = &ddl->codec_data.decoder;
+ struct vcd_frame_data *frm = &ddl->output_frame.vcd_frm;
+ u32 *qfill_hdr;
+ u32 *qfill;
+ size_t qfill_sz;
+ unsigned long tmp;
+
+ if (!dec->meta_data_enable_flag) {
+ frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA;
+ return;
+ }
+ if (!dec->dec_disp_info.metadata_exists) {
+ frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA;
+ return;
+ }
+ frm->flags |= VCD_FRAME_FLAG_EXTRADATA;
+
+ if (frm->data_len == dec->meta_data_offset)
+ return;
+
+ tmp = (unsigned long)frm->virt_addr + frm->offset + frm->data_len;
+ qfill = (u32 *)ALIGN(tmp, 4);
+
+ qfill_sz = dec->meta_data_offset + (u8 *)frm->virt_addr - (u8 *)qfill;
+
+ qfill_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER);
+
+ *qfill++ = qfill_sz;
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_VERSION_INDEX];
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_PORT_INDEX];
+ *qfill++ = qfill_hdr[DDL_METADATA_HDR_TYPE_INDEX];
+ *qfill = qfill_sz - DDL_METADATA_HDR_SIZE;
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h
new file mode 100644
index 0000000..b0fa84c
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_METADATA_H_
+#define _VCD_DDL_METADATA_H_
+
+#define DDL_MAX_DEC_METADATATYPE (8)
+#define DDL_MAX_ENC_METADATATYPE (3)
+
+#define DDL_METADATA_EXTRAPAD_SIZE (256)
+#define DDL_METADATA_HDR_SIZE (20)
+
+#define DDL_METADATA_EXTRADATANONE_SIZE (24)
+
+#define DDL_METADATA_ALIGNSIZE(x) ((x) = (((x) + 0x7) & ~0x7))
+
+#define DDL_METADATA_MANDATORY (VCD_METADATA_DATANONE | VCD_METADATA_QCOMFILLER)
+
+#define DDL_METADATA_VC1_PAYLOAD_SIZE (38*4)
+
+#define DDL_METADATA_SEI_PAYLOAD_SIZE (100)
+#define DDL_METADATA_SEI_MAX (5)
+
+#define DDL_METADATA_VUI_PAYLOAD_SIZE (256)
+
+#define DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE (68)
+
+#define DDL_METADATA_CLIENT_INPUTBUFSIZE (256)
+#define DDL_METADATA_TOTAL_INPUTBUFSIZE \
+ (DDL_METADATA_CLIENT_INPUTBUFSIZE * VCD_MAX_NO_CLIENT)
+
+#define DDL_METADATA_HDR_VERSION_INDEX 0
+#define DDL_METADATA_HDR_PORT_INDEX 1
+#define DDL_METADATA_HDR_TYPE_INDEX 2
+
+
+void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl);
+u32 ddl_get_metadata_params(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *property_hdr, void *property_value);
+u32 ddl_set_metadata_params(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *property_hdr, void *property_value);
+void ddl_set_default_metadata_flag(struct ddl_client_context *ddl);
+void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data *dec,
+ struct vcd_property_frame_size *frame_size,
+ struct vcd_buffer_requirement *output_buf_req);
+void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data *enc);
+void ddl_metadata_enable(struct ddl_client_context *ddl);
+phys_addr_t ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl);
+void ddl_decode_set_metadata_output(struct ddl_decoder_data *decoder);
+void ddl_process_encoder_metadata(struct ddl_client_context *ddl);
+void ddl_process_decoder_metadata(struct ddl_client_context *ddl);
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c
new file mode 100644
index 0000000..6e4037b
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c
@@ -0,0 +1,1395 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+static u32 ddl_set_dec_property(struct ddl_client_context *pddl,
+ struct vcd_property_hdr *hdr, void *value);
+static u32 ddl_set_enc_property(struct ddl_client_context *pddl,
+ struct vcd_property_hdr *hdr, void *value);
+static u32 ddl_get_dec_property(struct ddl_client_context *pddl,
+ struct vcd_property_hdr *hdr, void *value);
+static u32 ddl_get_enc_property(struct ddl_client_context *pddl,
+ struct vcd_property_hdr *hdr, void *value);
+static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data *enc,
+ struct vcd_property_hdr *hdr, void *value);
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl);
+static void ddl_set_default_enc_profile(struct ddl_encoder_data *enc);
+static void ddl_set_default_enc_level(struct ddl_encoder_data *enc);
+static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data *enc);
+static void ddl_set_default_enc_intra_period(struct ddl_encoder_data *enc);
+static void ddl_set_default_enc_rc_params(struct ddl_encoder_data *enc);
+static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement
+ *orig, struct vcd_buffer_requirement *req);
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *dec);
+static u32 ddl_set_dec_buffers(struct ddl_decoder_data *dec,
+ struct ddl_property_dec_pic_buffers *dpb);
+
+u32 ddl_set_property(u32 *ddl_handle, struct vcd_property_hdr *hdr,
+ void *value)
+{
+ u32 vcd_status;
+ struct ddl_context *ddl_context;
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+
+ if (!hdr || !value) {
+ pr_err("ddl_set_prop:Bad_argument\n");
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ ddl_context = ddl_get_context();
+
+ if (!DDL_IS_INITIALIZED(ddl_context)) {
+ pr_err("ddl_set_prop:Not_inited\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!ddl) {
+ pr_err("ddl_set_prop:Bad_handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+ if (ddl->decoding)
+ vcd_status = ddl_set_dec_property(ddl, hdr, value);
+ else
+ vcd_status = ddl_set_enc_property(ddl, hdr, value);
+ if (vcd_status)
+ pr_err("ddl_set_prop:FAILED\n");
+
+ return vcd_status;
+}
+
+u32 ddl_get_property(u32 *ddl_handle, struct vcd_property_hdr *hdr, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct ddl_context *ddl_context;
+ struct ddl_client_context *ddl = (struct ddl_client_context *)
+ ddl_handle;
+
+ if (!hdr || !value)
+ return VCD_ERR_ILLEGAL_PARM;
+
+ if (hdr->id == DDL_I_CAPABILITY) {
+ struct ddl_property_capability *cap;
+ if (sizeof(*cap) == hdr->sz) {
+ cap = value;
+ cap->max_num_client = VCD_MAX_NO_CLIENT;
+ cap->exclusive = VCD_COMMAND_EXCLUSIVE;
+ cap->frame_command_depth = VCD_FRAME_COMMAND_DEPTH;
+ cap->general_command_depth = VCD_GENERAL_COMMAND_DEPTH;
+ cap->ddl_time_out_in_ms = DDL_HW_TIMEOUT_IN_MS;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ return vcd_status;
+ }
+ ddl_context = ddl_get_context();
+ if (!DDL_IS_INITIALIZED(ddl_context))
+ return VCD_ERR_ILLEGAL_OP;
+
+ if (!ddl)
+ return VCD_ERR_BAD_HANDLE;
+
+ if (ddl->decoding)
+ vcd_status = ddl_get_dec_property(ddl, hdr, value);
+ else
+ vcd_status = ddl_get_enc_property(ddl, hdr, value);
+ if (vcd_status)
+ pr_err("ddl_get_prop:FAILED\n");
+
+ return vcd_status;
+}
+
+u32 ddl_decoder_ready_to_start(struct ddl_client_context *ddl,
+ struct vcd_phys_sequence_hdr *seq_hdr)
+{
+ struct ddl_decoder_data *dec = &ddl->codec_data.decoder;
+ if (!dec->codec_type.codec) {
+ pr_err("ddl_dec_start_check:Codec_not_set\n");
+ return false;
+ }
+ if (!seq_hdr && (!dec->client_frame_size.height ||
+ !dec->client_frame_size.width)) {
+ pr_err("ddl_dec_start_check:"
+ "Client_height_width_default\n");
+ return false;
+ }
+ return true;
+}
+
+u32 ddl_encoder_ready_to_start(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *enc = &ddl->codec_data.encoder;
+
+ if (!enc->codec_type.codec || !enc->frame_size.height ||
+ !enc->frame_size.width ||
+ !enc->frame_rate.fps_denominator ||
+ !enc->frame_rate.fps_numerator ||
+ !enc->target_bit_rate.target_bitrate) {
+ return false;
+ }
+ return true;
+}
+
+static u32 ddl_set_dec_property(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *hdr, void *value) {
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct ddl_decoder_data *dec = &ddl->codec_data.decoder;
+ switch (hdr->id) {
+ case DDL_I_DPB_RELEASE:
+ if (sizeof(struct ddl_frame_data_tag) == hdr->sz &&
+ dec->dp_buf.no_of_dec_pic_buf) {
+ vcd_status = ddl_decoder_dpb_transact(dec, value,
+ DDL_DPB_OP_MARK_FREE);
+ }
+ break;
+ case DDL_I_DPB:
+ {
+ struct ddl_property_dec_pic_buffers *dpb = value;
+ if (sizeof(*dpb) == hdr->sz &&
+ (DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_INITCODEC) ||
+ DDLCLIENT_STATE_IS(ddl,
+ DDL_CLIENT_WAIT_FOR_DPB)) &&
+ dpb->no_of_dec_pic_buf >=
+ dec->client_output_buf_req.actual_count) {
+ vcd_status = ddl_set_dec_buffers(dec, dpb);
+ }
+ break;
+ }
+ case DDL_I_REQ_OUTPUT_FLUSH:
+ if (sizeof(u32) == hdr->sz) {
+ dec->dynamic_prop_change |= DDL_DEC_REQ_OUTPUT_FLUSH;
+ dec->dpb_mask.client_mask = 0;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_INPUT_BUF_REQ:
+ {
+ struct vcd_buffer_requirement *buf_req = value;
+ if (sizeof(*buf_req) == hdr->sz &&
+ ddl_valid_buffer_requirement(
+ &dec->min_input_buf_req, buf_req)) {
+ dec->client_input_buf_req = *buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case DDL_I_OUTPUT_BUF_REQ:
+ {
+ struct vcd_buffer_requirement *buf_req = value;
+ if (sizeof(*buf_req) == hdr->sz &&
+ ddl_valid_buffer_requirement(
+ &dec->min_output_buf_req, buf_req)) {
+ dec->client_output_buf_req = *buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_CODEC:
+ {
+ struct vcd_property_codec *codec = value;
+ if (sizeof(*codec) == hdr->sz &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+ if (!vcd_fw_is_codec_supported(true, codec->codec)) {
+ vcd_status = VCD_ERR_NOT_SUPPORTED;
+ break;
+ }
+ dec->codec_type = *codec;
+ ddl_set_default_dec_property(ddl);
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_POST_FILTER:
+ if (sizeof(struct vcd_property_post_filter) == hdr->sz &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+ (dec->codec_type.codec == VCD_CODEC_MPEG4 ||
+ dec->codec_type.codec == VCD_CODEC_MPEG2)) {
+ dec->post_filter = *(struct vcd_property_post_filter *)
+ value;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_FRAME_SIZE:
+ {
+ struct vcd_property_frame_size *frame_size = value;
+ if ((sizeof(*frame_size) == hdr->sz) &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+ if (dec->client_frame_size.height != frame_size->height
+ || dec->client_frame_size.width !=
+ frame_size->width) {
+ dec->client_frame_size = *frame_size;
+ ddl_calculate_stride(&dec->client_frame_size,
+ !dec->progressive_only);
+ ddl_set_default_decoder_buffer_req(dec, true);
+ }
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_BUFFER_FORMAT:
+ {
+ struct vcd_property_buffer_format *tile = value;
+ if (sizeof(*tile) == hdr->sz &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+ (tile->buffer_format == VCD_BUFFER_FORMAT_NV12
+ || tile->buffer_format ==
+ VCD_BUFFER_FORMAT_TILE_4x2)) {
+ if (tile->buffer_format !=
+ dec->buf_format.buffer_format) {
+ dec->buf_format = *tile;
+ ddl_set_default_decoder_buffer_req(dec, true);
+ }
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_METADATA_ENABLE:
+ case VCD_I_METADATA_HEADER:
+ vcd_status = ddl_set_metadata_params(ddl, hdr, value);
+ break;
+ default:
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ break;
+ }
+ return vcd_status;
+}
+
+static u32 ddl_set_enc_property(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *hdr, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct ddl_encoder_data *enc = &ddl->codec_data.encoder;
+
+ if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) {
+ vcd_status = ddl_set_enc_dynamic_property(enc, hdr, value);
+ return vcd_status;
+ }
+
+ if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) {
+ pr_err("ddl_set_enc_property:"
+ "Fails_as_not_in_open_state\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ switch (hdr->id) {
+ case VCD_I_TARGET_BITRATE:
+ {
+ struct vcd_property_target_bitrate *bitrate = value;
+ if (sizeof(*bitrate) == hdr->sz &&
+ bitrate->target_bitrate) {
+ enc->target_bit_rate = *bitrate;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_FRAME_RATE:
+ {
+ struct vcd_property_frame_rate *framerate = value;
+ if (sizeof(*framerate) == hdr->sz &&
+ framerate->fps_denominator &&
+ framerate->fps_numerator) {
+ enc->frame_rate = *framerate;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_FRAME_SIZE:
+ {
+ struct vcd_property_frame_size *framesize = value;
+ if (sizeof(*framesize) == hdr->sz &&
+ DDL_ALLOW_ENC_FRAMESIZE(framesize->width,
+ framesize->height)) {
+ enc->frame_size = *framesize;
+ ddl_calculate_stride(&enc->frame_size, false);
+ ddl_set_default_encoder_buffer_req(enc);
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_CODEC:
+ {
+ struct vcd_property_codec *codec = value;
+ if (sizeof(*codec) == hdr->sz) {
+ if (!vcd_fw_is_codec_supported(false, codec->codec)) {
+ vcd_status = VCD_ERR_NOT_SUPPORTED;
+ break;
+ }
+ enc->codec_type = *codec;
+ ddl_set_default_enc_property(ddl);
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_REQ_IFRAME:
+ vcd_status = VCD_S_SUCCESS;
+ break;
+ case VCD_I_INTRA_PERIOD:
+ {
+ struct vcd_property_i_period *iperiod = value;
+ if (sizeof(*iperiod) == hdr->sz && !iperiod->bframes) {
+ enc->period = *iperiod;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_PROFILE:
+ {
+ struct vcd_property_profile *profile = value;
+ if (sizeof(*profile) == hdr->sz &&
+ ((enc->codec_type.codec == VCD_CODEC_MPEG4 &&
+ (profile->profile == VCD_PROFILE_MPEG4_SP ||
+ profile->profile == VCD_PROFILE_MPEG4_ASP)) ||
+ ((enc->codec_type.codec == VCD_CODEC_H264 &&
+ profile->profile >= VCD_PROFILE_H264_BASELINE &&
+ profile->profile <= VCD_PROFILE_H264_HIGH)) ||
+ (enc->codec_type.codec == VCD_CODEC_H263 &&
+ profile->profile == VCD_PROFILE_H263_BASELINE))
+ ) {
+ enc->profile = *profile;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_LEVEL:
+ {
+ struct vcd_property_level *level = value;
+ if (sizeof(*level) == hdr->sz &&
+ ((enc->codec_type.codec == VCD_CODEC_MPEG4 &&
+ level->level >= VCD_LEVEL_MPEG4_0 &&
+ level->level <= VCD_LEVEL_MPEG4_6) ||
+ (enc->codec_type.codec == VCD_CODEC_H264 &&
+ level->level >= VCD_LEVEL_H264_1 &&
+ level->level <= VCD_LEVEL_H264_3p1) ||
+ (enc->codec_type.codec == VCD_CODEC_H263 &&
+ level->level >= VCD_LEVEL_H263_10 &&
+ level->level <= VCD_LEVEL_H263_70))) {
+ enc->level = *level;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_MULTI_SLICE:
+ {
+ struct vcd_property_multi_slice *multislice = value;
+ switch (multislice->m_slice_sel) {
+ case VCD_MSLICE_OFF:
+ vcd_status = VCD_S_SUCCESS;
+ break;
+ case VCD_MSLICE_BY_GOB:
+ if (enc->codec_type.codec == VCD_CODEC_H263)
+ vcd_status = VCD_S_SUCCESS;
+ break;
+ case VCD_MSLICE_BY_MB_COUNT:
+ if (multislice->m_slice_size >= 1 &&
+ (multislice->m_slice_size <=
+ (enc->frame_size.height
+ * enc->frame_size.width / 16 / 16))) {
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_MSLICE_BY_BYTE_COUNT:
+ if (multislice->m_slice_size <
+ DDL_MINIMUM_BYTE_PER_SLICE) {
+ vcd_status = VCD_S_SUCCESS;
+ break;
+ }
+ default:
+ break;
+ }
+ if (sizeof(struct vcd_property_multi_slice) == hdr->sz &&
+ !vcd_status) {
+ enc->multi_slice = *multislice;
+ }
+ break;
+ }
+ case VCD_I_RATE_CONTROL:
+ {
+ struct vcd_property_rate_control *ratecontrol_type = value;
+ if (sizeof(*ratecontrol_type) == hdr->sz &&
+ ratecontrol_type->rate_control >=
+ VCD_RATE_CONTROL_OFF &&
+ ratecontrol_type->rate_control <=
+ VCD_RATE_CONTROL_CBR_CFR) {
+ enc->rc_type = *ratecontrol_type;
+ ddl_set_default_enc_rc_params(enc);
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_SHORT_HEADER:
+ if (sizeof(struct vcd_property_short_header) == hdr->sz &&
+ enc->codec_type.codec == VCD_CODEC_MPEG4) {
+ enc->short_header =
+ *(struct vcd_property_short_header *)value;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_VOP_TIMING:
+ {
+ struct vcd_property_vop_timing *voptime = value;
+ if (sizeof(*voptime) == hdr->sz && enc->frame_rate.fps_numerator
+ <= voptime->vop_time_resolution) {
+ enc->vop_timing = *voptime;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_HEADER_EXTENSION:
+ if (sizeof(u32) == hdr->sz && enc->codec_type.codec ==
+ VCD_CODEC_MPEG4) {
+ enc->hdr_ext_control = *(u32 *)value;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_ENTROPY_CTRL:
+ {
+ struct vcd_property_entropy_control *entropy = value;
+ if (sizeof(*entropy) == hdr->sz &&
+ enc->codec_type.codec == VCD_CODEC_H264 &&
+ entropy->entropy_sel >= VCD_ENTROPY_SEL_CAVLC &&
+ entropy->entropy_sel <= VCD_ENTROPY_SEL_CABAC) {
+ enc->entropy_control = *entropy;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_DEBLOCKING:
+ {
+ struct vcd_property_db_config *db = value;
+ if (sizeof(*db) == hdr->sz &&
+ enc->codec_type.codec == VCD_CODEC_H264 &&
+ db->db_config >= VCD_DB_ALL_BLOCKING_BOUNDARY &&
+ db->db_config <= VCD_DB_SKIP_SLICE_BOUNDARY) {
+ enc->db_control = *db;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_QP_RANGE:
+ {
+ struct vcd_property_qp_range *qp = value;
+ if (sizeof(*qp) == hdr->sz && qp->min_qp <= qp->max_qp &&
+ ((enc->codec_type.codec == VCD_CODEC_H264 &&
+ qp->max_qp <= DDL_MAX_H264_QP) ||
+ qp->max_qp <= DDL_MAX_MPEG4_QP)) {
+ enc->qp_range = *qp;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_SESSION_QP:
+ {
+ struct vcd_property_session_qp *qp = value;
+ if ((sizeof(*qp) == hdr->sz) &&
+ qp->iframe_qp >= enc->qp_range.min_qp &&
+ qp->iframe_qp <= enc->qp_range.max_qp &&
+ qp->frame_qp >= enc->qp_range.min_qp &&
+ qp->frame_qp <= enc->qp_range.max_qp) {
+ enc->session_qp = *qp;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_RC_LEVEL_CONFIG:
+ {
+ struct vcd_property_rc_level *rc_level = value;
+ if (sizeof(*rc_level) == hdr->sz &&
+ (enc->rc_type.rate_control >=
+ VCD_RATE_CONTROL_VBR_VFR ||
+ enc->rc_type.rate_control <=
+ VCD_RATE_CONTROL_CBR_VFR) &&
+ (!rc_level->mb_level_rc ||
+ enc->codec_type.codec == VCD_CODEC_H264)) {
+ enc->rc_level = *rc_level;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_FRAME_LEVEL_RC:
+ {
+ struct vcd_property_frame_level_rc_params *rc = value;
+ if (sizeof(*rc) == hdr->sz && rc->reaction_coeff &&
+ enc->rc_level.frame_level_rc) {
+ enc->frame_level_rc = *rc;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_ADAPTIVE_RC:
+ {
+ struct vcd_property_adaptive_rc_params *rc = value;
+ if (sizeof(*rc) == hdr->sz && enc->codec_type.codec ==
+ VCD_CODEC_H264 && enc->rc_level.mb_level_rc) {
+ enc->adaptive_rc = *rc;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_INTRA_REFRESH:
+ {
+ struct vcd_property_intra_refresh_mb_number *mbnum = value;
+ u32 frame_mbnum = (enc->frame_size.width / 16) *
+ (enc->frame_size.height / 16);
+ if (sizeof(*mbnum) == hdr->sz && mbnum->cir_mb_number <=
+ frame_mbnum) {
+ enc->intra_refresh = *mbnum;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_BUFFER_FORMAT:
+ {
+ struct vcd_property_buffer_format *tile = value;
+ if (sizeof(*tile) == hdr->sz && tile->buffer_format ==
+ VCD_BUFFER_FORMAT_NV12) {
+ enc->buf_format = *tile;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case DDL_I_INPUT_BUF_REQ:
+ {
+ struct vcd_buffer_requirement *buf_req = value;
+ if (sizeof(*buf_req) == hdr->sz && ddl_valid_buffer_requirement(
+ &enc->input_buf_req, buf_req)) {
+ enc->client_input_buf_req = *buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case DDL_I_OUTPUT_BUF_REQ:
+ {
+ struct vcd_buffer_requirement *buf_req = value;
+ if (sizeof(*buf_req) == hdr->sz && ddl_valid_buffer_requirement(
+ &enc->output_buf_req, buf_req)) {
+ enc->client_output_buf_req = *buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_METADATA_ENABLE:
+ case VCD_I_METADATA_HEADER:
+ vcd_status = ddl_set_metadata_params(ddl, hdr, value);
+ break;
+ default:
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ break;
+ }
+ return vcd_status;
+}
+
+static u32 ddl_get_dec_property(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *hdr, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct ddl_decoder_data *dec = &ddl->codec_data.decoder;
+
+ switch (hdr->id) {
+ case VCD_I_FRAME_SIZE:
+ if (sizeof(struct vcd_property_frame_size) == hdr->sz) {
+ if (dec->client_frame_size.width) {
+ struct vcd_property_frame_size *size = value;
+ *size = dec->client_frame_size;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_PROFILE:
+ if (sizeof(struct vcd_property_profile) == hdr->sz) {
+ *(struct vcd_property_profile *)value = dec->profile;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_LEVEL:
+ if (sizeof(struct vcd_property_level) == hdr->sz) {
+ *(struct vcd_property_level *)value = dec->level;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_PROGRESSIVE_ONLY:
+ if (sizeof(u32) == hdr->sz) {
+ *(u32 *)value = dec->progressive_only;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_INPUT_BUF_REQ:
+ if (sizeof(struct vcd_buffer_requirement) == hdr->sz) {
+ if (dec->client_input_buf_req.size) {
+ *(struct vcd_buffer_requirement *)value =
+ dec->client_input_buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case DDL_I_OUTPUT_BUF_REQ:
+ if (sizeof(struct vcd_buffer_requirement) == hdr->sz) {
+ if (dec->client_output_buf_req.size) {
+ *(struct vcd_buffer_requirement *)value =
+ dec->client_output_buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_CODEC:
+ if (sizeof(struct vcd_property_codec) == hdr->sz) {
+ if (dec->codec_type.codec) {
+ *(struct vcd_property_codec *)value =
+ dec->codec_type;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_BUFFER_FORMAT:
+ if (sizeof(struct vcd_property_buffer_format) == hdr->sz) {
+ *(struct vcd_property_buffer_format *)value =
+ dec->buf_format;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_POST_FILTER:
+ if (sizeof(struct vcd_property_post_filter) == hdr->sz) {
+ *(struct vcd_property_post_filter *)value =
+ dec->post_filter;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_SEQHDR_ALIGN_BYTES:
+ if (sizeof(u32) == hdr->sz) {
+ *(u32 *)value = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_FRAME_PROC_UNITS:
+ if (sizeof(u32) == hdr->sz && dec->client_frame_size.width &&
+ dec->client_frame_size.height) {
+ *(u32 *)value = ((dec->client_frame_size.width >> 4) *
+ (dec->client_frame_size.height >> 4));
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_DPB_RETRIEVE:
+ if (sizeof(struct ddl_frame_data_tag) == hdr->sz) {
+ vcd_status = ddl_decoder_dpb_transact(dec,
+ (struct ddl_frame_data_tag *)value,
+ DDL_DPB_OP_RETRIEVE);
+ }
+ break;
+ case VCD_I_METADATA_ENABLE:
+ case VCD_I_METADATA_HEADER:
+ vcd_status = ddl_get_metadata_params(ddl, hdr, value);
+ break;
+ default:
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ break;
+ }
+ return vcd_status;
+}
+
+static u32 ddl_get_enc_property(struct ddl_client_context *ddl,
+ struct vcd_property_hdr *hdr, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ struct ddl_encoder_data *enc = &ddl->codec_data.encoder;
+
+ struct vcd_property_entropy_control *entropy_control;
+ struct vcd_property_intra_refresh_mb_number *intra_refresh;
+
+ switch (hdr->id) {
+ case VCD_I_CODEC:
+ if (sizeof(struct vcd_property_codec) == hdr->sz) {
+ *(struct vcd_property_codec *)value = enc->codec_type;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_FRAME_SIZE:
+ if (sizeof(struct vcd_property_frame_size) == hdr->sz) {
+ *(struct vcd_property_frame_size *)value =
+ enc->frame_size;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_FRAME_RATE:
+ if (sizeof(struct vcd_property_frame_rate) == hdr->sz) {
+ *(struct vcd_property_frame_rate *)value =
+ enc->frame_rate;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_TARGET_BITRATE:
+ if (sizeof(struct vcd_property_target_bitrate) == hdr->sz) {
+ *(struct vcd_property_target_bitrate *)value =
+ enc->target_bit_rate;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_RATE_CONTROL:
+ if (sizeof(struct vcd_property_rate_control) == hdr->sz) {
+ *(struct vcd_property_rate_control *)value =
+ enc->rc_type;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_PROFILE:
+ if (sizeof(struct vcd_property_profile) == hdr->sz) {
+ *(struct vcd_property_profile *)value = enc->profile;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_LEVEL:
+ if (sizeof(struct vcd_property_level) == hdr->sz) {
+ *(struct vcd_property_level *)value = enc->level;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_MULTI_SLICE:
+ if (sizeof(struct vcd_property_multi_slice) == hdr->sz) {
+ *(struct vcd_property_multi_slice *)value =
+ enc->multi_slice;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_SEQ_HEADER:
+ {
+ struct vcd_sequence_hdr *seq_hdr = value;
+ if (enc->seq_header.size && sizeof(struct vcd_sequence_hdr) ==
+ hdr->sz && enc->seq_header.size <=
+ seq_hdr->sz) {
+ memcpy(seq_hdr->addr, enc->seq_header.virt_addr,
+ enc->seq_header.size);
+ seq_hdr->sz = enc->seq_header.size;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case DDL_I_SEQHDR_PRESENT:
+ if (sizeof(u32) == hdr->sz) {
+ if ((enc->codec_type.codec == VCD_CODEC_MPEG4 &&
+ !enc->short_header.short_header) ||
+ enc->codec_type.codec ==
+ VCD_CODEC_H264)
+ *(u32 *)value = 0x1;
+ else
+ *(u32 *)value = 0x0;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_VOP_TIMING:
+ if (sizeof(struct vcd_property_vop_timing) == hdr->sz) {
+ *(struct vcd_property_vop_timing *)value =
+ enc->vop_timing;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_SHORT_HEADER:
+ if (sizeof(struct vcd_property_short_header) == hdr->sz) {
+ if (enc->codec_type.codec == VCD_CODEC_MPEG4) {
+ *(struct vcd_property_short_header *)value =
+ enc->short_header;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_ENTROPY_CTRL:
+ entropy_control = value;
+ if (sizeof(struct vcd_property_entropy_control) == hdr->sz) {
+ if (enc->codec_type.codec == VCD_CODEC_H264) {
+ *entropy_control = enc->entropy_control;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_DEBLOCKING:
+ if (sizeof(struct vcd_property_db_config) == hdr->sz) {
+ if (enc->codec_type.codec == VCD_CODEC_H264) {
+ *(struct vcd_property_db_config *)value =
+ enc->db_control;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_INTRA_PERIOD:
+ if (sizeof(struct vcd_property_i_period) == hdr->sz) {
+ *(struct vcd_property_i_period *)value = enc->period;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_QP_RANGE:
+ if (sizeof(struct vcd_property_qp_range) == hdr->sz) {
+ *(struct vcd_property_qp_range *)value = enc->qp_range;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_SESSION_QP:
+ if (sizeof(struct vcd_property_session_qp) == hdr->sz) {
+ *(struct vcd_property_session_qp *)value =
+ enc->session_qp;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_RC_LEVEL_CONFIG:
+ if (sizeof(struct vcd_property_rc_level) == hdr->sz) {
+ *(struct vcd_property_rc_level *)value = enc->rc_level;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_FRAME_LEVEL_RC:
+ if (sizeof(struct vcd_property_frame_level_rc_params) ==
+ hdr->sz) {
+ *(struct vcd_property_frame_level_rc_params *)value =
+ enc->frame_level_rc;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_ADAPTIVE_RC:
+ if (sizeof(struct vcd_property_adaptive_rc_params) ==
+ hdr->sz) {
+ *(struct vcd_property_adaptive_rc_params *)value =
+ enc->adaptive_rc;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_INTRA_REFRESH:
+ intra_refresh = value;
+ if (sizeof(struct vcd_property_intra_refresh_mb_number) ==
+ hdr->sz) {
+ *intra_refresh = enc->intra_refresh;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_INPUT_BUF_REQ:
+ if (sizeof(struct vcd_buffer_requirement) == hdr->sz) {
+ if (enc->output_buf_req.size) {
+ *(struct vcd_buffer_requirement *)value =
+ enc->client_input_buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case DDL_I_OUTPUT_BUF_REQ:
+ if (sizeof(struct vcd_buffer_requirement) == hdr->sz) {
+ if (enc->output_buf_req.size) {
+ *(struct vcd_buffer_requirement *)value =
+ enc->client_output_buf_req;
+ vcd_status = VCD_S_SUCCESS;
+ } else {
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ }
+ }
+ break;
+ case VCD_I_BUFFER_FORMAT:
+ if (sizeof(struct vcd_property_buffer_format) == hdr->sz) {
+ *(struct vcd_property_buffer_format *)value =
+ enc->buf_format;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case DDL_I_FRAME_PROC_UNITS:
+ if (sizeof(u32) == hdr->sz && enc->frame_size.width &&
+ enc->frame_size.height) {
+ *(u32 *)value = ((enc->frame_size.width >> 4) *
+ (enc->frame_size.height >> 4));
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_HEADER_EXTENSION:
+ if (sizeof(u32) == hdr->sz && enc->codec_type.codec ==
+ VCD_CODEC_MPEG4) {
+ *(u32 *)value = enc->hdr_ext_control;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_METADATA_ENABLE:
+ case VCD_I_METADATA_HEADER:
+ vcd_status = ddl_get_metadata_params(ddl, hdr, value);
+ break;
+ default:
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ break;
+ }
+ return vcd_status;
+}
+
+static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data *enc,
+ struct vcd_property_hdr *hdr, void *value)
+{
+ u32 vcd_status = VCD_ERR_ILLEGAL_PARM;
+ switch (hdr->id) {
+ case VCD_I_REQ_IFRAME:
+ if (sizeof(struct vcd_property_req_i_frame) == hdr->sz) {
+ enc->dynamic_prop_change |= DDL_ENC_REQ_IFRAME;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_TARGET_BITRATE:
+ if (sizeof(struct vcd_property_target_bitrate) == hdr->sz) {
+ enc->target_bit_rate =
+ *(struct vcd_property_target_bitrate *)value;
+ enc->dynamic_prop_change |= DDL_ENC_CHANGE_BITRATE;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_INTRA_PERIOD:
+ {
+ struct vcd_property_i_period *iperiod = value;
+ if (sizeof(struct vcd_property_i_period) == hdr->sz &&
+ !iperiod->bframes) {
+ enc->period = *iperiod;
+ enc->dynamic_prop_change |= DDL_ENC_CHANGE_IPERIOD;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ case VCD_I_FRAME_RATE:
+ {
+ struct vcd_property_frame_rate *frame_rate = value;
+ if (sizeof(struct vcd_property_frame_rate) == hdr->sz &&
+ frame_rate->fps_denominator &&
+ frame_rate->fps_numerator &&
+ frame_rate->fps_denominator <=
+ frame_rate->fps_numerator) {
+ enc->frame_rate = *frame_rate;
+ enc->dynamic_prop_change |= DDL_ENC_CHANGE_FRAMERATE;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ }
+ default:
+ vcd_status = VCD_ERR_ILLEGAL_OP;
+ break;
+ }
+ return vcd_status;
+}
+
+void ddl_set_default_dec_property(struct ddl_client_context *ddl)
+{
+ struct ddl_decoder_data *dec = &(ddl->codec_data.decoder);
+
+ if (dec->codec_type.codec == VCD_CODEC_MPEG4 ||
+ dec->codec_type.codec == VCD_CODEC_MPEG2) {
+ dec->post_filter.post_filter = true;
+ } else {
+ dec->post_filter.post_filter = false;
+ }
+ dec->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+ dec->client_frame_size.height = 144;
+ dec->client_frame_size.width = 176;
+ dec->client_frame_size.stride = 176;
+ dec->client_frame_size.scan_lines = 144;
+ dec->progressive_only = 1;
+ ddl_set_default_metadata_flag(ddl);
+
+ ddl_set_default_decoder_buffer_req(dec, true);
+}
+
+static void ddl_set_default_enc_property(struct ddl_client_context *ddl)
+{
+ struct ddl_encoder_data *enc = &(ddl->codec_data.encoder);
+
+ ddl_set_default_enc_profile(enc);
+ ddl_set_default_enc_level(enc);
+
+ enc->rc_type.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+ ddl_set_default_enc_rc_params(enc);
+
+ ddl_set_default_enc_intra_period(enc);
+
+ enc->intra_refresh.cir_mb_number = 0;
+ ddl_set_default_enc_vop_timing(enc);
+
+ enc->multi_slice.m_slice_size = VCD_MSLICE_OFF;
+ enc->short_header.short_header = false;
+
+ enc->entropy_control.entropy_sel = VCD_ENTROPY_SEL_CAVLC;
+ enc->entropy_control.cabac_model = VCD_CABAC_MODEL_NUMBER_0;
+ enc->db_control.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY;
+ enc->db_control.slice_alpha_offset = 0;
+ enc->db_control.slice_beta_offset = 0;
+
+ enc->re_con_buf_format.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2;
+
+ enc->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+
+ enc->hdr_ext_control = 0;
+
+ ddl_set_default_metadata_flag(ddl);
+
+ ddl_set_default_encoder_buffer_req(enc);
+}
+
+static void ddl_set_default_enc_profile(struct ddl_encoder_data *enc)
+{
+ enum vcd_codec codec = enc->codec_type.codec;
+ if (codec == VCD_CODEC_MPEG4)
+ enc->profile.profile = VCD_PROFILE_MPEG4_SP;
+ else if (codec == VCD_CODEC_H264)
+ enc->profile.profile = VCD_PROFILE_H264_BASELINE;
+ else
+ enc->profile.profile = VCD_PROFILE_H263_BASELINE;
+}
+
+static void ddl_set_default_enc_level(struct ddl_encoder_data *enc)
+{
+ enum vcd_codec codec = enc->codec_type.codec;
+ if (codec == VCD_CODEC_MPEG4)
+ enc->level.level = VCD_LEVEL_MPEG4_1;
+ else if (codec == VCD_CODEC_H264)
+ enc->level.level = VCD_LEVEL_H264_1;
+ else
+ enc->level.level = VCD_LEVEL_H263_10;
+}
+
+static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data *enc)
+{
+ enc->vop_timing.vop_time_resolution = (2 *
+ enc->frame_rate.fps_numerator) /
+ enc->frame_rate.fps_denominator;
+}
+
+static void ddl_set_default_enc_intra_period(struct ddl_encoder_data *enc)
+{
+ switch (enc->rc_type.rate_control) {
+ default:
+ case VCD_RATE_CONTROL_VBR_VFR:
+ case VCD_RATE_CONTROL_VBR_CFR:
+ case VCD_RATE_CONTROL_CBR_VFR:
+ case VCD_RATE_CONTROL_OFF:
+ enc->period.frames = ((enc->frame_rate.fps_numerator << 1) /
+ enc->frame_rate.fps_denominator) - 1;
+ break;
+ case VCD_RATE_CONTROL_CBR_CFR:
+ enc->period.frames = ((enc->frame_rate.fps_numerator >> 1) /
+ enc->frame_rate.fps_denominator) - 1;
+ break;
+ }
+ enc->period.bframes = 0;
+}
+
+static void ddl_set_default_enc_rc_params(struct ddl_encoder_data *enc)
+{
+ enum vcd_codec codec = enc->codec_type.codec;
+
+ enc->rc_level.frame_level_rc = true;
+ enc->qp_range.min_qp = 0x1;
+
+ if (codec == VCD_CODEC_H264) {
+ enc->qp_range.max_qp = 0x33;
+ enc->session_qp.iframe_qp = 0x19;
+ enc->session_qp.frame_qp = 0x19;
+
+ enc->rc_level.mb_level_rc = true;
+ enc->adaptive_rc.activity_region_flag = true;
+ enc->adaptive_rc.dark_region_as_flag = true;
+ enc->adaptive_rc.smooth_region_as_flag = true;
+ enc->adaptive_rc.static_region_as_flag = true;
+ } else {
+ enc->qp_range.max_qp = 0x1f;
+ enc->session_qp.iframe_qp = 0x14;
+ enc->session_qp.frame_qp = 0x14;
+ enc->rc_level.mb_level_rc = false;
+ }
+
+ switch (enc->rc_type.rate_control) {
+ default:
+ case VCD_RATE_CONTROL_VBR_VFR:
+ enc->r_cframe_skip = 1;
+ enc->frame_level_rc.reaction_coeff = 0x1f4;
+ break;
+ case VCD_RATE_CONTROL_VBR_CFR:
+ enc->r_cframe_skip = 0;
+ enc->frame_level_rc.reaction_coeff = 0x1f4;
+ break;
+ case VCD_RATE_CONTROL_CBR_VFR:
+ enc->r_cframe_skip = 1;
+ if (codec != VCD_CODEC_H264) {
+ enc->session_qp.iframe_qp = 0xf;
+ enc->session_qp.frame_qp = 0xf;
+ }
+
+ enc->frame_level_rc.reaction_coeff = 0x6;
+ break;
+ case VCD_RATE_CONTROL_CBR_CFR:
+ enc->r_cframe_skip = 0;
+ enc->frame_level_rc.reaction_coeff = 0x6;
+ break;
+ case VCD_RATE_CONTROL_OFF:
+ enc->r_cframe_skip = 0;
+ enc->rc_level.frame_level_rc = false;
+ enc->rc_level.mb_level_rc = false;
+ break;
+ }
+}
+
+void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *enc)
+{
+ u32 y_cb_cr_size;
+
+ y_cb_cr_size = ddl_get_yuv_buffer_size(&enc->frame_size,
+ &enc->buf_format, false);
+
+ memset(&enc->input_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+ enc->input_buf_req.min_count = 1;
+ enc->input_buf_req.actual_count = enc->input_buf_req.min_count;
+ enc->input_buf_req.max_count = DDL_MAX_BUFFER_COUNT;
+ enc->input_buf_req.size = y_cb_cr_size;
+ enc->input_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+ enc->client_input_buf_req = enc->input_buf_req;
+
+ memset(&enc->output_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+ enc->output_buf_req.min_count = 2;
+ enc->output_buf_req.actual_count = enc->output_buf_req.min_count;
+ enc->output_buf_req.max_count = DDL_MAX_BUFFER_COUNT;
+ enc->output_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+ enc->output_buf_req.size = y_cb_cr_size;
+ ddl_set_default_encoder_metadata_buffer_size(enc);
+ enc->client_output_buf_req = enc->output_buf_req;
+}
+
+void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *dec,
+ u32 estimate)
+{
+ size_t y_cb_cr_size;
+ u32 min_dpb;
+ struct vcd_property_frame_size *frame_size;
+ struct vcd_buffer_requirement *output_buf_req, *input_buf_req;
+
+ if (!dec->codec_type.codec)
+ return;
+
+ if (estimate) {
+ frame_size = &dec->client_frame_size;
+ output_buf_req = &dec->client_output_buf_req;
+ input_buf_req = &dec->client_input_buf_req;
+ min_dpb = ddl_decoder_min_num_dpb(dec);
+ y_cb_cr_size = ddl_get_yuv_buffer_size(frame_size,
+ &dec->buf_format, !dec->progressive_only);
+ } else {
+ frame_size = &dec->frame_size;
+ output_buf_req = &dec->actual_output_buf_req;
+ input_buf_req = &dec->actual_input_buf_req;
+ y_cb_cr_size = dec->y_cb_cr_size;
+ min_dpb = dec->min_dpb_num;
+ }
+
+ memset(output_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+ output_buf_req->min_count = min_dpb;
+ output_buf_req->actual_count = output_buf_req->min_count;
+ output_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+ output_buf_req->size = y_cb_cr_size;
+ if (dec->buf_format.buffer_format != VCD_BUFFER_FORMAT_NV12)
+ output_buf_req->align = DDL_TILE_BUFFER_ALIGN_BYTES;
+ else
+ output_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+ ddl_set_default_decoder_metadata_buffer_size(dec, frame_size,
+ output_buf_req);
+
+ dec->min_output_buf_req = *output_buf_req;
+
+ memset(input_buf_req, 0, sizeof(struct vcd_buffer_requirement));
+
+ input_buf_req->min_count = 1;
+ input_buf_req->actual_count = input_buf_req->min_count;
+ input_buf_req->max_count = DDL_MAX_BUFFER_COUNT;
+ input_buf_req->size = y_cb_cr_size;
+
+ if (input_buf_req->size >= (1280 * 720 * 3) >> 1)
+ input_buf_req->size >>= 1;
+
+ input_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES;
+
+ dec->min_input_buf_req = *input_buf_req;
+}
+
+size_t ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size,
+ struct vcd_property_buffer_format *buf_format, u32 interlace)
+{
+ u32 width = frame_size->stride;
+ u32 height = frame_size->scan_lines;
+ size_t sz;
+
+ if (buf_format->buffer_format != VCD_BUFFER_FORMAT_NV12) {
+ size_t component_sz;
+ u32 width_round_up;
+ u32 height_round_up;
+ u32 height_chroma = (height >> 1);
+
+ width_round_up = DDL_TILE_ALIGN(width, DDL_TILE_ALIGN_WIDTH);
+ height_round_up = DDL_TILE_ALIGN(height, DDL_TILE_ALIGN_HEIGHT);
+
+ component_sz = width_round_up * height_round_up;
+ component_sz = DDL_TILE_ALIGN(component_sz,
+ DDL_TILE_MULTIPLY_FACTOR);
+
+ sz = (component_sz + DDL_TILE_BUF_ALIGN_GUARD_BYTES) &
+ DDL_TILE_BUF_ALIGN_MASK;
+
+ height_round_up = DDL_TILE_ALIGN(height_chroma,
+ DDL_TILE_ALIGN_HEIGHT);
+ component_sz = width_round_up * height_round_up;
+ component_sz = DDL_TILE_ALIGN(component_sz,
+ DDL_TILE_MULTIPLY_FACTOR);
+ sz += component_sz;
+ } else {
+ sz = height * width;
+ sz += sz >> 1;
+ }
+ return sz;
+}
+
+void ddl_calculate_stride(struct vcd_property_frame_size *frame_size,
+ u32 interlace)
+{
+ frame_size->stride = ((frame_size->width + 15) >> 4) << 4;
+
+ if (interlace)
+ frame_size->scan_lines = ((frame_size->height + 31) >> 5) << 5;
+ else
+ frame_size->scan_lines = ((frame_size->height + 15) >> 4) << 4;
+}
+
+static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement
+ *orig, struct vcd_buffer_requirement *req)
+{
+ u32 status = false;
+ if (orig->max_count >= req->actual_count &&
+ orig->actual_count <= req->actual_count &&
+ orig->align <= req->align && orig->size <= req->size) {
+ status = true;
+ } else {
+ pr_err("ddl_valid_buf_req:Failed\n");
+ }
+ return status;
+}
+
+static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *dec)
+{
+ u32 min_dpb = 0;
+ switch (dec->codec_type.codec) {
+ default:
+ case VCD_CODEC_MPEG4:
+ case VCD_CODEC_MPEG2:
+ case VCD_CODEC_DIVX_4:
+ case VCD_CODEC_DIVX_5:
+ case VCD_CODEC_DIVX_6:
+ case VCD_CODEC_XVID:
+ min_dpb = 3;
+ break;
+ case VCD_CODEC_H263:
+ min_dpb = 2;
+ break;
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ min_dpb = 4;
+ break;
+ case VCD_CODEC_H264:
+ {
+ u32 yuv_size = (dec->client_frame_size.height *
+ dec->client_frame_size.width * 3) >> 1;
+ min_dpb = 6912000 / yuv_size;
+ if (min_dpb > 16)
+ min_dpb = 16;
+
+ min_dpb += 2;
+ break;
+ }
+ }
+ return min_dpb;
+}
+
+static u32 ddl_set_dec_buffers(struct ddl_decoder_data *dec,
+ struct ddl_property_dec_pic_buffers *dpb)
+{
+ u32 vcd_status = VCD_S_SUCCESS;
+ u32 i;
+ for (i = 0; !vcd_status && i < dpb->no_of_dec_pic_buf; ++i) {
+ if (!IS_ALIGNED(dpb->dec_pic_buffers[i].vcd_frm.phys_addr,
+ dec->client_output_buf_req.align) ||
+ dpb->dec_pic_buffers[i].vcd_frm.alloc_len <
+ dec->client_output_buf_req.size) {
+ vcd_status = VCD_ERR_ILLEGAL_PARM;
+ pr_err("ddl_set_prop:"
+ "Dpb_align_fail_or_alloc_size_small\n");
+ return vcd_status;
+ }
+ }
+
+ if (dec->dp_buf.no_of_dec_pic_buf) {
+ kfree(dec->dp_buf.dec_pic_buffers);
+ dec->dp_buf.dec_pic_buffers = NULL;
+ dec->dp_buf.no_of_dec_pic_buf = 0;
+ }
+ dec->dp_buf.dec_pic_buffers = kmalloc(dpb->no_of_dec_pic_buf *
+ sizeof(struct ddl_frame_data_tag), GFP_KERNEL);
+
+ if (!dec->dp_buf.dec_pic_buffers) {
+ pr_err("ddl_dec_set_prop:"
+ "Dpb_container_alloc_failed\n");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ dec->dp_buf.no_of_dec_pic_buf = dpb->no_of_dec_pic_buf;
+ for (i = 0; i < dpb->no_of_dec_pic_buf; ++i)
+ dec->dp_buf.dec_pic_buffers[i] = dpb->dec_pic_buffers[i];
+
+ dec->dpb_mask.client_mask = 0;
+ dec->dpb_mask.hw_mask = 0;
+ dec->dynamic_prop_change = 0;
+ return VCD_S_SUCCESS;
+}
+
+void ddl_set_initial_default_values(struct ddl_client_context *ddl)
+{
+ if (ddl->decoding) {
+ ddl->codec_data.decoder.codec_type.codec = VCD_CODEC_MPEG4;
+ ddl_set_default_dec_property(ddl);
+ } else {
+ struct ddl_encoder_data *enc = &(ddl->codec_data.encoder);
+ enc->codec_type.codec = VCD_CODEC_MPEG4;
+
+ enc->target_bit_rate.target_bitrate = 64000;
+ enc->frame_size.width = 176;
+ enc->frame_size.height = 144;
+ enc->frame_size.stride = 176;
+ enc->frame_size.scan_lines = 144;
+ enc->frame_rate.fps_numerator = 30;
+ enc->frame_rate.fps_denominator = 1;
+ ddl_set_default_enc_property(ddl);
+ }
+}
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c
new file mode 100644
index 0000000..922f071
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_ddl_utils.h"
+#include "vcd_ddl_metadata.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define ERR(x...) printk(KERN_ERR x)
+
+#ifdef CORE_TIMING_INFO
+static unsigned int g_ddl_dec_t1, g_ddl_enc_t1;
+static unsigned int g_ddl_dec_ttotal, g_ddl_enc_ttotal;
+static unsigned int g_ddl_dec_count, g_ddl_enc_count;
+#endif
+
+size_t npelly_size[] = {
+ 0x100000,
+ 0x080000,
+ 0x51c00,
+ DDL_CONTEXT_MEMORY,
+ DDL_DB_LINE_BUF_SIZE,
+ DDL_MPEG4_DATA_PARTITION_BUF_SIZE,
+ DDL_METADATA_TOTAL_INPUTBUFSIZE,
+ DDL_DBG_CORE_DUMP_SIZE,
+ 0x040000,
+ DDL_ENC_SEQHEADER_SIZE,
+};
+
+struct ddl_dma_buffer npelly_b[30];
+
+u32 npelly_init(void) {
+ int i;
+ printk("\nnpelly npelly max_key = %d\n", npelly_max_key);
+ for (i=0; i<npelly_max_key; i++) {
+ struct ddl_dma_buffer *b = &npelly_b[i];
+ b->size = npelly_size[i];
+ b->virt_addr = dma_alloc_coherent(NULL, b->size,
+ &b->phys_addr, GFP_KERNEL);
+ if (!b->virt_addr) {
+ printk("\nnpelly %s: Could not allocate %d for %d\n",
+ __FUNCTION__, b->size, i);
+ return -1;
+ }
+ printk("\nnpelly ALLOC %d for %d\n", b->size, i);
+ memset(b->virt_addr, 0, b->size);
+ }
+ return 0;
+}
+
+void *ddl_dma_alloc(struct ddl_dma_buffer *b, size_t sz, enum npelly_key key)
+{
+ printk("\nnpelly RETRIEVE %d for %d\n", sz, key);
+
+ if (sz > npelly_b[key].size) {
+ printk("\nnpelly OH SHIT, %d > %d for %d\n", sz, npelly_b[key].size, key);
+ BUG_ON(true);
+ }
+ *b = npelly_b[key];
+ b->size = sz;
+ memset(b->virt_addr, 0, sz);
+
+ return b->virt_addr;
+}
+
+void ddl_dma_free(struct ddl_dma_buffer *b)
+{
+ printk("\nnpelly RELEASE %d\n", b->size);
+
+ b->virt_addr = NULL;
+ b->size = 0;
+}
+
+#ifdef CORE_TIMING_INFO
+void ddl_get_core_start_time(u8 codec_type)
+{
+ u32 *ddl_t1 = NULL;
+ if (!codec_type)
+ ddl_t1 = &g_ddl_dec_t1;
+ else if (codec_type == 1)
+ ddl_t1 = &g_ddl_enc_t1;
+
+ if (!*ddl_t1) {
+ struct timeval ddl_tv;
+ do_gettimeofday(&ddl_tv);
+ *ddl_t1 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+ }
+}
+
+void ddl_calc_core_time(u8 codec_type)
+{
+ u32 *ddl_t1 = NULL, *ddl_ttotal = NULL,
+ *ddl_count = NULL;
+ if (!codec_type) {
+ DBG("\n720p Core Decode ");
+ ddl_t1 = &g_ddl_dec_t1;
+ ddl_ttotal = &g_ddl_dec_ttotal;
+ ddl_count = &g_ddl_dec_count;
+ } else if (codec_type == 1) {
+ DBG("\n720p Core Encode ");
+ ddl_t1 = &g_ddl_enc_t1;
+ ddl_ttotal = &g_ddl_enc_ttotal;
+ ddl_count = &g_ddl_enc_count;
+ }
+
+ if (*ddl_t1) {
+ int ddl_t2;
+ struct timeval ddl_tv;
+ do_gettimeofday(&ddl_tv);
+ ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000);
+ *ddl_ttotal += (ddl_t2 - *ddl_t1);
+ *ddl_count = *ddl_count + 1;
+ DBG("time %u, average time %u, count %u",
+ ddl_t2 - *ddl_t1, (*ddl_ttotal)/(*ddl_count),
+ *ddl_count);
+ *ddl_t1 = 0;
+ }
+}
+
+void ddl_reset_time_variables(u8 codec_type)
+{
+ if (!codec_type) {
+ DBG("\n Reset Decoder time variables");
+ g_ddl_dec_t1 = 0;
+ g_ddl_dec_ttotal = 0;
+ g_ddl_dec_count = 0;
+ } else if (codec_type == 1) {
+ DBG("\n Reset Encoder time variables ");
+ g_ddl_enc_t1 = 0;
+ g_ddl_enc_ttotal = 0;
+ g_ddl_enc_count = 0;
+ }
+}
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h
new file mode 100644
index 0000000..bf83a73
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DDL_UTILS_H_
+#define _VCD_DDL_UTILS_H_
+
+#include "vcd_ddl_core.h"
+#include "vcd_ddl.h"
+
+//TODO get rid of this hack
+enum npelly_key {
+ npelly_dec_dpb = 0,
+ npelly_dec_ref,
+ npelly_dec_h264,
+ npelly_context,
+ npelly_dbl,
+ npelly_mpeg4,
+ npelly_meta,
+ npelly_debug,
+ npelly_enc_dpb,
+ npelly_enc_seq,
+ npelly_max_key,
+};
+
+void *ddl_dma_alloc(struct ddl_dma_buffer *, size_t, enum npelly_key key);
+void ddl_dma_free(struct ddl_dma_buffer *);
+
+#ifdef CORE_TIMING_INFO
+void ddl_get_core_start_time(u8 codec_type);
+
+void ddl_calc_core_time(u8 codec_type);
+
+void ddl_reset_time_variables(u8 codec_type);
+#endif
+
+#endif
diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.c b/drivers/misc/video_core/720p/ddl/video_core_720p.c
new file mode 100644
index 0000000..053a3a8
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/video_core_720p.c
@@ -0,0 +1,754 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/unistd.h>
+
+#include "video_core_type.h"
+#include "video_core_720p.h"
+
+#define VIDC_720P_VERSION_STRING "VIDC_V1.0"
+
+unsigned long vid_c_base_addr;
+
+void vidc_720p_set_device_virtual_base(void *virt_addr)
+{
+ vid_c_base_addr = (unsigned long)virt_addr;
+}
+
+static inline void vidc_720p_write(unsigned long offset, unsigned long val)
+{
+ pr_debug("REG 0x%08lx: write 0x%08lx\n", offset, val);
+ mb();
+ iowrite32(val, vid_c_base_addr + offset);
+}
+
+static inline unsigned long vidc_720p_read(unsigned long offset)
+{
+ unsigned long val;
+ mb();
+ val = ioread32(vid_c_base_addr + offset);
+ pr_debug("REG 0x%08lx: read 0x%08lx\n", offset, val);
+ return val;
+}
+
+void vidc_720p_init(char **ppsz_version, size_t sz, phys_addr_t phys_addr,
+ enum vidc_720p_endian_type dma_endian, u32 interrupt_off,
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel,
+ u32 interrupt_mask)
+{
+ if (ppsz_version)
+ *ppsz_version = VIDC_720P_VERSION_STRING;
+
+ if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
+ vidc_720p_write(0x0504, 0);
+ else
+ vidc_720p_write(0x0504, 1);
+
+ if (interrupt_off)
+ vidc_720p_write(0x0500, 1);
+ else
+ vidc_720p_write(0x0500, 0);
+
+ vidc_720p_write(0x0508, 1);
+
+ vidc_720p_write(0x0518, 0);
+
+ vidc_720p_write(0x0518, interrupt_mask);
+
+ vidc_720p_write(0x0044, dma_endian);
+
+ vidc_720p_write(0x0138, 0);
+
+ vidc_720p_write(0x0110, 1);
+
+ vidc_720p_write(0x000C, sz / 4); /* word size */
+
+ vidc_720p_write(0x0014, phys_addr);
+
+ vidc_720p_write(0x0020, 0);
+
+ vidc_720p_write(0x0000, 1);
+}
+
+u32 vidc_720p_do_sw_reset(void)
+{
+ u32 fw_start;
+ udelay(5);
+ vidc_720p_write(0x0108, 0);
+ udelay(5);
+ vidc_720p_write(0x0134, 0);
+ udelay(5);
+ vidc_720p_write(0x0130, 1);
+ udelay(15);
+ vidc_720p_write(0x0130, 0);
+ udelay(5);
+ fw_start = vidc_720p_read(0x0134);
+
+ if (!fw_start) {
+ pr_debug("VIDC-SW-RESET-FAILS!\n");
+ return false;
+ }
+ return true;
+}
+
+u32 vidc_720p_reset_is_success()
+{
+ u32 stagecounter;
+ stagecounter = vidc_720p_read(0x0414);
+ stagecounter &= 0xff;
+ if (stagecounter != 0xe5) {
+ pr_debug("VIDC-CPU_RESET-FAILS!\n");
+ vidc_720p_write(0x0108, 0);
+ msleep(10);
+ return false;
+ }
+ return true;
+}
+
+void vidc_720p_start_cpu(enum vidc_720p_endian_type dma_endian,
+ phys_addr_t icontext_bufferstart, phys_addr_t debug_core_dump_addr,
+ size_t debug_buffer_size)
+{
+ u32 dbg_info_input0_reg = 0x1;
+
+ vidc_720p_write(0x0110, 0);
+ vidc_720p_write(0x0230, icontext_bufferstart);
+ vidc_720p_write(0x0044, dma_endian);
+ if (debug_buffer_size) {
+ dbg_info_input0_reg = (debug_buffer_size << 0x10)
+ | (0x2 << 1) | 0x1;
+ vidc_720p_write(0x0D10, debug_core_dump_addr);
+ }
+ vidc_720p_write(0x0D0C, dbg_info_input0_reg);
+ vidc_720p_write(0x0108, 1);
+}
+
+u32 vidc_720p_cpu_start()
+{
+ u32 fw_status;
+
+ fw_status = vidc_720p_read(0x0C14);
+ if (fw_status != 0x02)
+ return false;
+ return true;
+}
+
+
+void vidc_720p_stop_fw(void)
+{
+ vidc_720p_write(0x0134, 0);
+ vidc_720p_write(0x0108, 0);
+}
+
+void vidc_720p_get_interrupt_status(u32 *interrupt_status,
+ u32 *cmd_err_status, u32 *disp_pic_err_status, u32 *op_failed)
+{
+ u32 err_status;
+
+ *interrupt_status = vidc_720p_read(0x0514);
+ err_status = vidc_720p_read(0x0E9C);
+ *cmd_err_status = err_status & 0xffff;
+ *disp_pic_err_status = (err_status & 0xffff0000) >> 16;
+ *op_failed = (vidc_720p_read(0x0EC0) & 0x2) >> 1;
+}
+
+void vidc_720p_interrupt_done_clear(void)
+{
+ vidc_720p_write(0x0508, 1);
+ vidc_720p_write(0x0104, 4);
+}
+
+void vidc_720p_submit_command(u32 ch_id, u32 cmd_id)
+{
+ u32 fw_status;
+
+ vidc_720p_write(0x0104, ch_id);
+ vidc_720p_write(0x0D00, cmd_id);
+
+ fw_status = vidc_720p_read(0x0C14);
+ vidc_720p_write(0x0D1C, fw_status);
+}
+
+u32 vidc_720p_engine_reset(u32 ch_id, enum vidc_720p_endian_type dma_endian,
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel,
+ u32 interrupt_mask)
+{
+ u32 op_done;
+ u32 counter = 0;
+
+ pr_debug("ENG-RESET!!\n");
+ /* issue the engine reset command */
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET);
+
+ do {
+ udelay(20);
+ op_done = vidc_720p_read(0x050C);
+ counter++;
+ } while (!op_done && counter < 10);
+
+ if (!op_done)
+ return false; /* reset fails */
+
+ /* write invalid channel id */
+ vidc_720p_write(0x0104, 4);
+
+ /* Set INT_PULSE_SEL */
+ if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
+ vidc_720p_write(0x0504, 0);
+ else
+ vidc_720p_write(0x0504, 1);
+
+ if (!interrupt_mask) {
+ /* Disable interrupt */
+ vidc_720p_write(0x0500, 1);
+ } else {
+ /* Enable interrupt */
+ vidc_720p_write(0x0500, 0);
+ }
+
+ /* Clear any pending interrupt */
+ vidc_720p_write(0x0508, 1);
+
+ /* Set INT_ENABLE_REG */
+ vidc_720p_write(0x0518, interrupt_mask);
+
+ /* Sets the DMA endianness */
+ vidc_720p_write(0x0044, dma_endian);
+
+ /* return engine reset success */
+ return true ;
+}
+
+void vidc_720p_set_channel(u32 ch_id, enum vidc_720p_enc_dec_selection_type
+ enc_dec_sel, enum vidc_720p_codec_type codec, phys_addr_t pi_fw,
+ size_t firmware_size)
+{
+ u32 std_sel = codec;
+
+ vidc_720p_write(0x012C, 0);
+
+ if (enc_dec_sel)
+ std_sel |= 0x10;
+
+ vidc_720p_write(0x0100, std_sel);
+
+ switch (codec) {
+ default:
+ case VIDC_720P_DIVX:
+ case VIDC_720P_XVID:
+ case VIDC_720P_MPEG4:
+ if (enc_dec_sel == VIDC_720P_ENCODER)
+ vidc_720p_write(0x0200, pi_fw);
+ else
+ vidc_720p_write(0x0204, pi_fw);
+ break;
+ case VIDC_720P_H264:
+ if (enc_dec_sel == VIDC_720P_ENCODER)
+ vidc_720p_write(0x0208, pi_fw);
+ else
+ vidc_720p_write(0x020C, pi_fw);
+ break;
+ case VIDC_720P_H263:
+ if (enc_dec_sel == VIDC_720P_ENCODER)
+ vidc_720p_write(0x0200, pi_fw);
+ else
+ vidc_720p_write(0x0218, pi_fw);
+ break;
+ case VIDC_720P_VC1:
+ vidc_720p_write(0x0210, pi_fw);
+ break;
+ case VIDC_720P_MPEG2:
+ vidc_720p_write(0x40293, pi_fw);
+ break;
+ }
+ vidc_720p_write(0x000C, firmware_size / 4); /* word size */
+
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_CHSET);
+}
+
+void vidc_720p_encode_set_profile(u32 profile, u32 level)
+{
+ u32 profile_level = profile|(level << 0x8);
+
+ vidc_720p_write(0x0300, profile_level);
+}
+
+void vidc_720p_set_frame_size(u32 size_x, u32 size_y)
+{
+ vidc_720p_write(0x0118, size_x);
+
+ vidc_720p_write(0x011C, size_y);
+}
+
+void vidc_720p_encode_set_fps(u32 rc_frame_rate)
+{
+ vidc_720p_write(0x0D14, rc_frame_rate);
+}
+
+void vidc_720p_encode_set_short_header(u32 short_header)
+{
+ vidc_720p_write(0x0318, short_header);
+}
+
+void vidc_720p_encode_set_vop_time(u32 vop_time_resolution,
+ u32 vop_time_increment)
+{
+ u32 enable_vop, vop_timing_reg;
+
+ if (!vop_time_resolution)
+ vidc_720p_write(0x0E00, 0x0);
+ else {
+ enable_vop = 0x1;
+ vop_timing_reg = (enable_vop << 0x1f) |
+ (vop_time_resolution << 0x10) | vop_time_increment;
+ vidc_720p_write(0x0E00, vop_timing_reg);
+ }
+}
+
+void vidc_720p_encode_set_hec_period(u32 hec_period)
+{
+ vidc_720p_write(0x0EB0, hec_period);
+}
+
+void vidc_720p_encode_set_qp_params(u32 max_qp, u32 min_qp)
+{
+ u32 qp = min_qp | (max_qp << 0x8);
+
+ vidc_720p_write(0x0A0C, qp);
+}
+
+void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc,
+ u32 enable_mb_level_rc_flag, u32 iframe_qp, u32 pframe_qp)
+{
+ u32 rc_config = iframe_qp;
+
+ if (enable_frame_level_rc)
+ rc_config |= (0x1 << 0x9);
+
+ if (enable_mb_level_rc_flag)
+ rc_config |= (0x1 << 0x8);
+
+ vidc_720p_write(0x0A00, rc_config);
+ vidc_720p_write(0x0A04, pframe_qp);
+}
+
+void vidc_720p_encode_set_bit_rate(u32 target_bitrate)
+{
+ vidc_720p_write(0x0A08, target_bitrate);
+}
+
+void vidc_720p_encoder_set_param_change(u32 enc_param_change)
+{
+ vidc_720p_write(0x0E08, enc_param_change);
+}
+
+void vidc_720p_encode_set_control_param(u32 param_val)
+{
+ vidc_720p_write(0x0EC8, param_val);
+}
+
+void vidc_720p_encode_set_frame_level_rc_params(u32 reaction_coeff)
+{
+ vidc_720p_write(0x0A10, reaction_coeff);
+}
+
+void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag,
+ u32 smooth_region_as_flag, u32 static_region_as_flag,
+ u32 activity_region_flag)
+{
+ u32 mb_level_rc = 0x0;
+
+ if (activity_region_flag)
+ mb_level_rc |= 0x1;
+ if (static_region_as_flag)
+ mb_level_rc |= (0x1 << 0x1);
+ if (smooth_region_as_flag)
+ mb_level_rc |= (0x1 << 0x2);
+ if (dark_region_as_flag)
+ mb_level_rc |= (0x1 << 0x3);
+ /* Write MB level rate control */
+ vidc_720p_write(0x0A14, mb_level_rc);
+}
+
+void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type
+ entropy_sel, enum vidc_720p_cabac_model_type cabac_model_number)
+{
+ u32 num;
+ u32 entropy_params = entropy_sel;
+
+ /* Set Model Number */
+ if (entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) {
+ num = (u32)cabac_model_number;
+ entropy_params |= (num << 0x2);
+ }
+ /* Set Entropy parameters */
+ vidc_720p_write(0x0310, entropy_params);
+}
+
+void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type
+ db_config, u32 slice_alpha_offset, u32 slice_beta_offset)
+{
+ u32 deblock_params;
+
+ deblock_params = db_config;
+ deblock_params |=
+ (slice_beta_offset << 0x2) | (slice_alpha_offset << 0x7);
+
+ /* Write deblocking control settings */
+ vidc_720p_write(0x0314, deblock_params);
+}
+
+void vidc_720p_encode_set_intra_refresh_mb_number(u32 cir_mb_number)
+{
+ vidc_720p_write(0x0810, cir_mb_number);
+}
+
+void vidc_720p_encode_set_multi_slice_info(enum vidc_720p_MSlice_selection_type
+ m_slice_sel, u32 multi_slice_size)
+{
+ switch (m_slice_sel) {
+ case VIDC_720P_MSLICE_BY_MB_COUNT:
+ vidc_720p_write(0x0EA8, 0x1);
+ vidc_720p_write(0x1517, m_slice_sel);
+ vidc_720p_write(0x0324, multi_slice_size);
+ break;
+ case VIDC_720P_MSLICE_BY_BYTE_COUNT:
+ vidc_720p_write(0x0EA8, 0x1);
+ vidc_720p_write(0x1517, m_slice_sel);
+ vidc_720p_write(0x0328, multi_slice_size);
+ break;
+ case VIDC_720P_MSLICE_BY_GOB:
+ vidc_720p_write(0x0EA8, 0x1);
+ break;
+ default:
+ case VIDC_720P_MSLICE_OFF:
+ vidc_720p_write(0x0EA8, 0x0);
+ break;
+ }
+}
+
+void vidc_720p_encode_set_dpb_buffer(dma_addr_t pi_enc_dpb_addr,
+ size_t alloc_len)
+{
+ vidc_720p_write(0x080C, pi_enc_dpb_addr);
+ vidc_720p_write(0x0ED4, alloc_len);
+}
+
+void vidc_720p_encode_set_i_period(u32 period)
+{
+ vidc_720p_write(0x0308, period);
+}
+
+void vidc_720p_encode_init_codec(u32 ch_id,
+ enum vidc_720p_memory_access_method_type memory_access_model)
+{
+ vidc_720p_write(0x0600, memory_access_model);
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC);
+}
+
+void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word,
+ u32 lower_unalign_word)
+{
+ vidc_720p_write(0x0EA0, upper_unalign_word);
+ vidc_720p_write(0x0EA4, lower_unalign_word);
+}
+
+void vidc_720p_encode_set_seq_header_buffer(phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, u32 start_byte_num)
+{
+ vidc_720p_write(0x0018, ext_buffer_start);
+
+ vidc_720p_write(0x0024, ext_buffer_start);
+
+ vidc_720p_write(0x001C, ext_buffer_end);
+
+ vidc_720p_write(0x005C, start_byte_num);
+}
+
+void vidc_720p_encode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, u32 start_byte_number, phys_addr_t y_addr,
+ phys_addr_t c_addr)
+{
+ vidc_720p_write(0x0018, ext_buffer_start);
+
+ vidc_720p_write(0x001C, ext_buffer_end);
+
+ vidc_720p_write(0x0024, ext_buffer_start);
+
+ vidc_720p_write(0x005C, start_byte_number);
+
+ vidc_720p_write(0x99105, y_addr);
+
+ vidc_720p_write(0x0804, c_addr);
+
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_encode_get_header(u32 *pi_enc_header_size)
+{
+ *pi_enc_header_size = vidc_720p_read(0x0060);
+}
+
+void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info *enc_frame_info)
+{
+ enc_frame_info->enc_size = vidc_720p_read(0x0058);
+
+ enc_frame_info->frame_type = vidc_720p_read(0x0EBC);
+
+ enc_frame_info->frame_type &= 0x03;
+
+ enc_frame_info->metadata_exists = vidc_720p_read(0x0EB8);
+}
+
+void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size,
+ u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end,
+ enum vidc_720p_memory_access_method_type memory_access_model)
+{
+ vidc_720p_write(0x0E04, 0x0);
+
+ vidc_720p_write(0x0018, ext_buffer_start);
+
+ vidc_720p_write(0x001C, ext_buffer_end);
+
+ vidc_720p_write(0x0024, ext_buffer_end);
+
+ vidc_720p_write(0x0054, dec_unit_size);
+
+ vidc_720p_write(0x005C, start_byte_num);
+
+ vidc_720p_write(0x0600, memory_access_model);
+
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC);
+}
+
+void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type
+ *seq_hdr_info)
+{
+ unsigned long tmp;
+
+ seq_hdr_info->img_size_x = vidc_720p_read(0x0118);
+
+ seq_hdr_info->img_size_y = vidc_720p_read(0x011C);
+
+ seq_hdr_info->min_num_dpb = vidc_720p_read(0x0E10);
+
+ seq_hdr_info->min_dpb_size = vidc_720p_read(0x0C10);
+
+ seq_hdr_info->dec_frm_size = vidc_720p_read(0x0C08);
+
+ tmp = vidc_720p_read(0x0C0C);
+ seq_hdr_info->profile = tmp & 0x1f;
+ seq_hdr_info->level = (tmp & 0xff00) >> 8;
+
+ tmp = vidc_720p_read(0x0408);
+ seq_hdr_info->progressive = (tmp & 0x4) >> 2;
+ /* bit 3 is for crop existence */
+ seq_hdr_info->crop_exists = (tmp & 0x8) >> 3;
+
+ if (seq_hdr_info->crop_exists) {
+ /* read the cropping information */
+ tmp = vidc_720p_read(0x0C00);
+ seq_hdr_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10;
+ seq_hdr_info->crop_left_offset = tmp & 0xffff;
+ tmp = vidc_720p_read(0x0C04);
+ seq_hdr_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10;
+ seq_hdr_info->crop_top_offset = tmp & 0xffff;
+ }
+ /* Read the MPEG4 data partitioning indication */
+ seq_hdr_info->data_partitioned = (vidc_720p_read(0x0EBC) & 0x8) >> 3;
+}
+
+void vidc_720p_decode_set_dpb_release_buffer_mask(u32 dpb_release_buffer_mask)
+{
+ vidc_720p_write(0x0E98, dpb_release_buffer_mask);
+}
+
+void vidc_720p_decode_set_dpb_buffers(u32 i, phys_addr_t dpb_buffer)
+{
+ vidc_720p_write(0x0E18 + sizeof(i) * i, dpb_buffer);
+}
+
+void vidc_720p_decode_set_comv_buffer(phys_addr_t pi_dpb_comv_buffer,
+ size_t alloc_len)
+{
+ vidc_720p_write(0x0904, pi_dpb_comv_buffer);
+
+ vidc_720p_write(0x0D08, alloc_len);
+}
+
+void vidc_720p_decode_set_dpb_details(u32 num_dpb, size_t alloc_len,
+ phys_addr_t ref_buffer)
+{
+ vidc_720p_write(0x0900, ref_buffer);
+
+ vidc_720p_write(0x0908, 0);
+
+ vidc_720p_write(0x0E14, num_dpb);
+
+ vidc_720p_write(0x0ED4, alloc_len);
+}
+
+void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter)
+{
+ if (enable_post_filter)
+ vidc_720p_write(0x0124, 0x1);
+ else
+ vidc_720p_write(0x0124, 0x0);
+}
+
+void vidc_720p_decode_set_error_control(u32 enable_error_control)
+{
+ if (enable_error_control)
+ vidc_720p_write(0x013C, 0);
+ else
+ vidc_720p_write(0x013C, 1);
+}
+
+void vidc_720p_set_deblock_line_buffer(dma_addr_t pi_deblock_line_buffer_start,
+ size_t alloc_len)
+{
+ vidc_720p_write(0x0234, pi_deblock_line_buffer_start);
+
+ vidc_720p_write(0x0D04, alloc_len);
+}
+
+void vidc_720p_decode_set_mpeg4_data_partitionbuffer(dma_addr_t vsp_buf_start)
+{
+ vidc_720p_write(0x0230, vsp_buf_start);
+}
+
+void vidc_720p_decode_setH264VSPBuffer(dma_addr_t pi_vsp_temp_buffer_start)
+{
+ vidc_720p_write(0x0230, pi_vsp_temp_buffer_start);
+}
+
+void vidc_720p_decode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, size_t dec_unit_size, u32 start_byte_num,
+ u32 input_frame_tag)
+{
+ vidc_720p_write(0x0018, ext_buffer_start);
+
+ vidc_720p_write(0x001C, ext_buffer_end);
+
+ vidc_720p_write(0x0024, ext_buffer_end);
+
+ vidc_720p_write(0x005C, start_byte_num);
+
+ vidc_720p_write(0x0EE0, input_frame_tag);
+
+ vidc_720p_write(0x0054, dec_unit_size);
+
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_issue_eos(u32 ch_id)
+{
+ vidc_720p_write(0x0028, 0x1);
+
+ vidc_720p_write(0x0054, 0);
+
+ vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
+}
+
+void vidc_720p_eos_info(u32 *disp_status)
+{
+ *disp_status = vidc_720p_read(0x0408) & 0x3;
+}
+
+void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info *disp_info)
+{
+ unsigned long tmp;
+
+ tmp = vidc_720p_read(0x0408);
+
+ disp_info->disp_status = (enum vidc_720p_display_status_type)
+ (tmp & 0x3);
+
+ disp_info->disp_is_interlace = (tmp & 0x4) >> 2;
+ disp_info->crop_exists = (tmp & 0x8) >> 3;
+
+ disp_info->resl_change = (tmp & 0x30) >> 4;
+
+ disp_info->reconfig_flush_done = vidc_720p_read(0x0EC0) & 0x1;
+
+ disp_info->img_size_x = vidc_720p_read(0x0118);
+ disp_info->img_size_y = vidc_720p_read(0x011C);
+ disp_info->y_addr = vidc_720p_read(0x0400);
+ disp_info->c_addr = vidc_720p_read(0x0404);
+ disp_info->tag_top = vidc_720p_read(0x0EA8);
+ disp_info->tag_bottom = vidc_720p_read(0x0EE4);
+ disp_info->pic_time_top = vidc_720p_read(0x0ED8);
+ disp_info->pic_time_bottom = vidc_720p_read(0x0EDC);
+
+ if (disp_info->crop_exists) {
+ tmp = vidc_720p_read(0x0C00);
+ disp_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10;
+ disp_info->crop_left_offset = tmp & 0xffff;
+ tmp = vidc_720p_read(0x0C04);
+ disp_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10;
+ disp_info->crop_top_offset = tmp & 0xffff;
+ }
+ disp_info->metadata_exists = vidc_720p_read(0x0EB8);
+
+ disp_info->input_bytes_consumed = vidc_720p_read(0x0C08);
+
+ disp_info->input_frame_num = vidc_720p_read(0x0410);
+
+ disp_info->input_frame_type = vidc_720p_read(0x0EBC) & 0x7;
+
+ disp_info->input_is_interlace = (disp_info->input_frame_type & 0x4) >>
+ 2;
+
+ disp_info->input_frame_type &= 0x3;
+}
+
+void vidc_720p_decode_skip_frm_details(phys_addr_t *free_luma_dpb)
+{
+ u32 disp_frm_type;
+
+ disp_frm_type = vidc_720p_read(0x0EB4);
+
+ if (disp_frm_type == VIDC_720P_NOTCODED)
+ *free_luma_dpb = vidc_720p_read(0x0C18);
+}
+
+void vidc_720p_metadata_enable(u32 flag, phys_addr_t input_buffer)
+{
+ vidc_720p_write(0x0EC4, flag);
+ vidc_720p_write(0x0ED0, input_buffer);
+}
+
+void vidc_720p_decode_dynamic_req_reset(void)
+{
+ vidc_720p_write(0x0EE8, 0x0);
+ vidc_720p_write(0x0EAC, 0x0);
+ vidc_720p_write(0x0028, 0x0);
+}
+
+void vidc_720p_decode_dynamic_req_set(u32 property)
+{
+ if (property == VIDC_720P_FLUSH_REQ)
+ vidc_720p_write(0x0EE8, 0x1);
+ else if (property == VIDC_720P_EXTRADATA)
+ vidc_720p_write(0x0EAC, 0x1);
+}
+
+void vidc_720p_decode_setpassthrough_start(phys_addr_t pass_startaddr)
+{
+ vidc_720p_write(0x0D18, pass_startaddr);
+}
diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.h b/drivers/misc/video_core/720p/ddl/video_core_720p.h
new file mode 100644
index 0000000..14853f9
--- /dev/null
+++ b/drivers/misc/video_core/720p/ddl/video_core_720p.h
@@ -0,0 +1,395 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef VID_C_720P_H
+#define VID_C_720P_H
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+
+/** List all the levels and their register values */
+
+#define VIDC_720P_PROFILE_MPEG4_SP 0
+#define VIDC_720P_PROFILE_MPEG4_ASP 1
+#define VIDC_720P_PROFILE_H264_BASELINE 0
+#define VIDC_720P_PROFILE_H264_MAIN 1
+#define VIDC_720P_PROFILE_H264_HIGH 2
+#define VIDC_720P_PROFILE_H263_BASELINE 0
+
+#define VIDC_720P_PROFILE_VC1_SP 0
+#define VIDC_720P_PROFILE_VC1_MAIN 1
+#define VIDC_720P_PROFILE_VC1_ADV 2
+#define VIDC_720P_PROFILE_MPEG2_MAIN 4
+#define VIDC_720P_PROFILE_MPEG2_SP 5
+
+#define VIDC_720P_MPEG4_LEVEL0 0
+#define VIDC_720P_MPEG4_LEVEL0b 9
+#define VIDC_720P_MPEG4_LEVEL1 1
+#define VIDC_720P_MPEG4_LEVEL2 2
+#define VIDC_720P_MPEG4_LEVEL3 3
+#define VIDC_720P_MPEG4_LEVEL3b 7
+#define VIDC_720P_MPEG4_LEVEL4a 4
+#define VIDC_720P_MPEG4_LEVEL5 5
+#define VIDC_720P_MPEG4_LEVEL6 6
+
+#define VIDC_720P_H264_LEVEL1 10
+#define VIDC_720P_H264_LEVEL1b 9
+#define VIDC_720P_H264_LEVEL1p1 11
+#define VIDC_720P_H264_LEVEL1p2 12
+#define VIDC_720P_H264_LEVEL1p3 13
+#define VIDC_720P_H264_LEVEL2 20
+#define VIDC_720P_H264_LEVEL2p1 21
+#define VIDC_720P_H264_LEVEL2p2 22
+#define VIDC_720P_H264_LEVEL3 30
+#define VIDC_720P_H264_LEVEL3p1 31
+#define VIDC_720P_H264_LEVEL3p2 32
+
+#define VIDC_720P_H263_LEVEL10 10
+#define VIDC_720P_H263_LEVEL20 20
+#define VIDC_720P_H263_LEVEL30 30
+#define VIDC_720P_H263_LEVEL40 40
+#define VIDC_720P_H263_LEVEL45 45
+#define VIDC_720P_H263_LEVEL50 50
+#define VIDC_720P_H263_LEVEL60 60
+#define VIDC_720P_H263_LEVEL70 70
+
+#define VIDC_720P_VC1_LEVEL_LOW 0
+#define VIDC_720P_VC1_LEVEL_MED 2
+#define VIDC_720P_VC1_LEVEL_HIGH 4
+#define VIDC_720P_VC1_LEVEL0 0
+#define VIDC_720P_VC1_LEVEL1 1
+#define VIDC_720P_VC1_LEVEL2 2
+#define VIDC_720P_VC1_LEVEL3 3
+#define VIDC_720P_VC1_LEVEL4 4
+
+#define VIDCL_720P_MPEG2_LEVEL_LOW 10
+#define VIDCL_720P_MPEG2_LEVEL_MAIN 8
+#define VIDCL_720P_MPEG2_LEVEL_HIGH14 6
+
+#define VIDC_720P_CMD_CHSET 0x0
+#define VIDC_720P_CMD_CHEND 0x2
+#define VIDC_720P_CMD_INITCODEC 0x3
+#define VIDC_720P_CMD_FRAMERUN 0x4
+#define VIDC_720P_CMD_INITBUFFERS 0x5
+#define VIDC_720P_CMD_FRAMERUN_REALLOCATE 0x6
+#define VIDC_720P_CMD_MFC_ENGINE_RESET 0x7
+
+enum vidc_720p_endian_type {
+ VIDC_720P_BIG_ENDIAN = 0x0,
+ VIDC_720P_LITTLE_ENDIAN = 0x1
+};
+
+enum vidc_720p_memory_access_method_type {
+ VIDC_720P_TILE_LINEAR = 0,
+ VIDC_720P_TILE_16x16 = 2,
+ VIDC_720P_TILE_64x32 = 3
+};
+
+enum vidc_720p_interrupt_control_mode_type {
+ VIDC_720P_INTERRUPT_MODE = 0,
+ VIDC_720P_POLL_MODE = 1
+};
+
+enum vidc_720p_interrupt_level_selection_type {
+ VIDC_720P_INTERRUPT_LEVEL_SEL = 0,
+ VIDC_720P_INTERRUPT_PULSE_SEL = 1
+};
+
+#define VIDC_720P_INTR_BUFFER_FULL 0x002
+#define VIDC_720P_INTR_FW_DONE 0x020
+#define VIDC_720P_INTR_HEADER_DONE 0x040
+#define VIDC_720P_INTR_DMA_DONE 0x080
+#define VIDC_720P_INTR_FRAME_DONE 0x100
+
+enum vidc_720p_enc_dec_selection_type {
+ VIDC_720P_DECODER = 0,
+ VIDC_720P_ENCODER = 1
+};
+
+enum vidc_720p_codec_type {
+ VIDC_720P_MPEG4 = 0,
+ VIDC_720P_H264 = 1,
+ VIDC_720P_DIVX = 2,
+ VIDC_720P_XVID = 3,
+ VIDC_720P_H263 = 4,
+ VIDC_720P_MPEG2 = 5,
+ VIDC_720P_VC1 = 6
+};
+
+enum vidc_720p_frame_type {
+ VIDC_720P_NOTCODED = 0,
+ VIDC_720P_IFRAME = 1,
+ VIDC_720P_PFRAME = 2,
+ VIDC_720P_BFRAME = 3
+};
+
+enum vidc_720p_entropy_sel_type {
+ VIDC_720P_ENTROPY_SEL_CAVLC = 0,
+ VIDC_720P_ENTROPY_SEL_CABAC = 1
+};
+
+enum vidc_720p_cabac_model_type {
+ VIDC_720P_CABAC_MODEL_NUMBER_0 = 0,
+ VIDC_720P_CABAC_MODEL_NUMBER_1 = 1,
+ VIDC_720P_CABAC_MODEL_NUMBER_2 = 2
+};
+
+enum vidc_720p_DBConfig_type {
+ VIDC_720P_DB_ALL_BLOCKING_BOUNDARY = 0,
+ VIDC_720P_DB_DISABLE = 1,
+ VIDC_720P_DB_SKIP_SLICE_BOUNDARY = 2
+};
+
+enum vidc_720p_MSlice_selection_type {
+ VIDC_720P_MSLICE_BY_MB_COUNT = 0,
+ VIDC_720P_MSLICE_BY_BYTE_COUNT = 1,
+ VIDC_720P_MSLICE_BY_GOB = 2,
+ VIDC_720P_MSLICE_OFF = 3
+};
+
+enum vidc_720p_display_status_type {
+ VIDC_720P_DECODE_ONLY = 0,
+ VIDC_720P_DECODE_AND_DISPLAY = 1,
+ VIDC_720P_DISPLAY_ONLY = 2,
+ VIDC_720P_EMPTY_BUFFER = 3
+};
+
+#define VIDC_720P_ENC_IFRAME_REQ 0x1
+#define VIDC_720P_ENC_IPERIOD_CHANGE 0x1
+#define VIDC_720P_ENC_FRAMERATE_CHANGE 0x2
+#define VIDC_720P_ENC_BITRATE_CHANGE 0x4
+
+#define VIDC_720P_FLUSH_REQ 0x1
+#define VIDC_720P_EXTRADATA 0x2
+
+#define VIDC_720P_METADATA_ENABLE_QP 0x01
+#define VIDC_720P_METADATA_ENABLE_CONCEALMB 0x02
+#define VIDC_720P_METADATA_ENABLE_VC1 0x04
+#define VIDC_720P_METADATA_ENABLE_SEI 0x08
+#define VIDC_720P_METADATA_ENABLE_VUI 0x10
+#define VIDC_720P_METADATA_ENABLE_ENCSLICE 0x20
+#define VIDC_720P_METADATA_ENABLE_PASSTHROUGH 0x40
+
+struct vidc_720p_dec_disp_info {
+ enum vidc_720p_display_status_type disp_status;
+ u32 resl_change;
+ u32 reconfig_flush_done;
+ u32 img_size_x;
+ u32 img_size_y;
+ phys_addr_t y_addr;
+ phys_addr_t c_addr;
+ u32 tag_top;
+ u32 pic_time_top;
+ u32 disp_is_interlace;
+ u32 tag_bottom;
+ u32 pic_time_bottom;
+ u32 metadata_exists;
+ u32 crop_exists;
+ u32 crop_right_offset;
+ u32 crop_left_offset;
+ u32 crop_bottom_offset;
+ u32 crop_top_offset;
+ u32 input_frame_type;
+ u32 input_bytes_consumed;
+ u32 input_is_interlace;
+ u32 input_frame_num;
+};
+
+struct vidc_720p_seq_hdr_info_type {
+ u32 img_size_x;
+ u32 img_size_y;
+ u32 dec_frm_size;
+ u32 min_num_dpb;
+ u32 min_dpb_size;
+ u32 profile;
+ u32 level;
+ u32 progressive;
+ u32 data_partitioned;
+ u32 crop_exists;
+ u32 crop_right_offset;
+ u32 crop_left_offset;
+ u32 crop_bottom_offset;
+ u32 crop_top_offset;
+};
+
+struct vidc_720p_enc_frame_info {
+ u32 enc_size;
+ u32 frame_type;
+ u32 metadata_exists;
+};
+
+void vidc_720p_set_device_virtual_base(void *virt_addr);
+
+void vidc_720p_init(char **ppsz_version, size_t sz, phys_addr_t phys_addr,
+ enum vidc_720p_endian_type dma_endian, u32 interrupt_off,
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel,
+ u32 interrupt_mask);
+
+u32 vidc_720p_do_sw_reset(void);
+
+u32 vidc_720p_reset_is_success(void);
+
+void vidc_720p_start_cpu(enum vidc_720p_endian_type dma_endian,
+ phys_addr_t icontext_bufferstart, phys_addr_t debug_core_dump_addr,
+ size_t debug_buffer_size);
+
+u32 vidc_720p_cpu_start(void);
+
+void vidc_720p_stop_fw(void);
+
+void vidc_720p_get_interrupt_status(u32 *interrupt_status, u32 *cmd_err_status,
+ u32 *disp_pic_err_status, u32 *op_failed);
+
+void vidc_720p_interrupt_done_clear(void);
+
+void vidc_720p_submit_command(u32 ch_id, u32 cmd_id);
+
+
+void vidc_720p_set_channel(u32 ch_id,
+ enum vidc_720p_enc_dec_selection_type enc_dec_sel,
+ enum vidc_720p_codec_type codec, dma_addr_t pi_fw,
+ size_t firmware_size);
+
+u32 vidc_720p_engine_reset(u32 ch_id,
+ enum vidc_720p_endian_type dma_endian,
+ enum vidc_720p_interrupt_level_selection_type interrupt_sel,
+ u32 interrupt_mask
+);
+
+void vidc_720p_encode_set_profile(u32 profile, u32 level);
+
+void vidc_720p_set_frame_size(u32 size_x, u32 size_y);
+
+void vidc_720p_encode_set_fps(u32 rc_frame_rate);
+
+void vidc_720p_encode_set_vop_time(u32 vop_time_resolution,
+ u32 vop_time_increment);
+
+void vidc_720p_encode_set_hec_period(u32 hec_period);
+
+void vidc_720p_encode_set_short_header(u32 short_header);
+
+void vidc_720p_encode_set_qp_params(u32 max_qp, u32 min_qp);
+
+void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc,
+ u32 enable_mb_level_rc_flag, u32 iframe_qp, u32 pframe_qp);
+
+void vidc_720p_encode_set_bit_rate(u32 target_bitrate);
+
+void vidc_720p_encoder_set_param_change(u32 enc_param_change);
+
+void vidc_720p_encode_set_control_param(u32 param_val);
+
+void vidc_720p_encode_set_frame_level_rc_params(u32 reaction_coeff);
+
+void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag,
+ u32 smooth_region_as_flag, u32 static_region_as_flag,
+ u32 activity_region_flag);
+
+void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type
+ entropy_sel, enum vidc_720p_cabac_model_type cabac_model_number);
+
+void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type
+ db_config, u32 slice_alpha_offset, u32 slice_beta_offset);
+
+void vidc_720p_encode_set_intra_refresh_mb_number(u32 cir_mb_number);
+
+void vidc_720p_encode_set_multi_slice_info(enum vidc_720p_MSlice_selection_type
+ m_slice_sel, u32 multi_slice_size);
+
+void vidc_720p_encode_set_dpb_buffer(phys_addr_t pi_enc_dpb_addr,
+ size_t alloc_len);
+
+void vidc_720p_set_deblock_line_buffer(phys_addr_t pi_deblock_line_buffer_start,
+ size_t alloc_len);
+
+void vidc_720p_encode_set_i_period(u32 period);
+
+void vidc_720p_encode_init_codec(u32 ch_id,
+ enum vidc_720p_memory_access_method_type memory_access_model);
+
+void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word,
+ u32 lower_unalign_word);
+
+void vidc_720p_encode_set_seq_header_buffer(phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, u32 start_byte_num);
+
+void vidc_720p_encode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, u32 start_byte_number, phys_addr_t y_addr,
+ phys_addr_t c_addr);
+
+void vidc_720p_encode_get_header(u32 *pi_enc_header_size);
+
+void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info *enc_frame_info);
+
+void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size,
+ u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end,
+ enum vidc_720p_memory_access_method_type memory_access_model);
+
+void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type
+ *seq_hdr_info);
+
+void vidc_720p_decode_set_dpb_release_buffer_mask(u32 dpb_release_buffer_mask);
+
+void vidc_720p_decode_set_dpb_buffers(u32 buf_index, phys_addr_t pi_dpb_buffer);
+
+void vidc_720p_decode_set_comv_buffer(dma_addr_t pi_dpb_comv_buffer,
+ size_t alloc_len);
+
+void vidc_720p_decode_set_dpb_details(u32 num_dpb, size_t alloc_len,
+ phys_addr_t ref_buffer);
+
+void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter);
+
+void vidc_720p_decode_set_error_control(u32 enable_error_control);
+
+void vidc_720p_decode_set_mpeg4_data_partitionbuffer(dma_addr_t vsp_buf_start);
+
+void vidc_720p_decode_setH264VSPBuffer(dma_addr_t pi_vsp_temp_buffer_start);
+
+void vidc_720p_decode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
+ phys_addr_t ext_buffer_end, size_t dec_unit_size, u32 start_byte_num,
+ u32 input_frame_tag);
+
+void vidc_720p_issue_eos(u32 ch_id);
+void vidc_720p_eos_info(u32 *disp_status);
+
+void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info *disp_info);
+
+void vidc_720p_decode_skip_frm_details(phys_addr_t *free_luma_dpb);
+
+void vidc_720p_metadata_enable(u32 flag, phys_addr_t input_buffer);
+
+void vidc_720p_decode_dynamic_req_reset(void);
+
+void vidc_720p_decode_dynamic_req_set(u32 property);
+
+void vidc_720p_decode_setpassthrough_start(phys_addr_t pass_startaddr);
+
+#endif
diff --git a/drivers/misc/video_core/720p/dec/vdec.c b/drivers/misc/video_core/720p/dec/vdec.c
new file mode 100644
index 0000000..36ca556
--- /dev/null
+++ b/drivers/misc/video_core/720p/dec/vdec.c
@@ -0,0 +1,1525 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+
+#include "vcd_ddl_firmware.h"
+#include "video_core_type.h"
+#include "vcd_api.h"
+#include "vdec_internal.h"
+#include "video_core_init.h"
+
+
+#define VID_C_HCLK_RATE 170667000
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define INFO(x...) printk(KERN_INFO x)
+#define ERR(x...) printk(KERN_ERR x)
+
+#define VID_DEC_NAME "msm_vidc_dec"
+
+static struct vid_dec_dev *vidc_dec_dev;
+static dev_t vidc_dec_dev_num;
+static struct class *vidc_dec_class;
+
+static s32 vid_dec_get_empty_client_index(void)
+{
+ u32 i, found = false;
+
+ for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) {
+ if (!vidc_dec_dev->vdec_clients[i].vcd_handle) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ERR("%s():ERROR No space for new client\n", __func__);
+ return -1;
+ }
+ DBG("%s(): available client index = %u\n", __func__, i);
+ return i;
+}
+
+u32 vid_dec_get_status(u32 status)
+{
+ u32 vdec_status;
+
+ switch (status) {
+ case VCD_ERR_BITSTREAM_ERR:
+ case VCD_S_SUCCESS:
+ vdec_status = VDEC_S_SUCCESS;
+ break;
+ case VCD_ERR_FAIL:
+ vdec_status = VDEC_S_EFAIL;
+ break;
+ case VCD_ERR_ALLOC_FAIL:
+ vdec_status = VDEC_S_ENOSWRES;
+ break;
+ case VCD_ERR_ILLEGAL_OP:
+ vdec_status = VDEC_S_EINVALCMD;
+ break;
+ case VCD_ERR_ILLEGAL_PARM:
+ vdec_status = VDEC_S_EBADPARAM;
+ break;
+ case VCD_ERR_BAD_POINTER:
+ case VCD_ERR_BAD_HANDLE:
+ vdec_status = VDEC_S_EFATAL;
+ break;
+ case VCD_ERR_NOT_SUPPORTED:
+ vdec_status = VDEC_S_ENOTSUPP;
+ break;
+ case VCD_ERR_BAD_STATE:
+ vdec_status = VDEC_S_EINVALSTATE;
+ break;
+ case VCD_ERR_BUSY:
+ vdec_status = VDEC_S_BUSY;
+ break;
+ case VCD_ERR_MAX_CLIENT:
+ vdec_status = VDEC_S_ENOHWRES;
+ break;
+ default:
+ vdec_status = VDEC_S_EFAIL;
+ break;
+ }
+
+ return vdec_status;
+}
+
+static void vid_dec_notify_client(struct video_client_ctx *client_ctx)
+{
+ if (client_ctx)
+ complete(&client_ctx->event);
+}
+
+void vid_dec_vcd_open_done(struct video_client_ctx *client_ctx,
+ struct vcd_handle_container *handle_container)
+{
+ DBG("vid_dec_vcd_open_done\n");
+
+ if (!client_ctx) {
+ ERR("%s(): ERROR. client_ctx is NULL\n", __func__);
+ return;
+ }
+
+ if (handle_container)
+ client_ctx->vcd_handle = handle_container->handle;
+ else
+ ERR("%s(): ERROR. handle_container is NULL\n", __func__);
+
+ vid_dec_notify_client(client_ctx);
+}
+
+static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx,
+ u32 event, u32 status, struct vcd_frame_data *vcd_frame_data)
+{
+ struct vid_dec_msg *vdec_msg;
+
+ if (!client_ctx || !vcd_frame_data) {
+ ERR("vid_dec_input_frame_done() NULL pointer\n");
+ return;
+ }
+
+ vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+ if (!vdec_msg) {
+ ERR("%s: cannot allocate vid_dec_msg buffer\n", __func__);
+ return;
+ }
+
+ vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+ if (event == VCD_EVT_RESP_INPUT_DONE) {
+ vdec_msg->vdec_msg_info.msgcode =
+ VDEC_MSG_RESP_INPUT_BUFFER_DONE;
+ DBG("Send INPUT_DON message to client = %p\n", client_ctx);
+
+ } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) {
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_INPUT_FLUSHED;
+ DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx);
+ } else {
+ ERR("%s: invalid event type\n", __func__);
+ return;
+ }
+
+ vdec_msg->vdec_msg_info.msgdata.input_frame_clientdata =
+ vcd_frame_data->client_data;
+ vdec_msg->vdec_msg_info.msgdatasize = sizeof(void *);
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx,
+ u32 event, u32 status, struct vcd_frame_data *vcd_frame_data)
+{
+ struct vid_dec_msg *vdec_msg;
+
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+ struct vdec_output_frameinfo *frm;
+
+ if (!client_ctx || !vcd_frame_data) {
+ ERR("%s: NULL pointer\n", __func__);
+ return;
+ }
+
+ vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+ if (!vdec_msg) {
+ ERR("%s: cannot allocate vid_dec_msg buffer\n", __func__);
+ return;
+ }
+
+ vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+ if (event == VCD_EVT_RESP_OUTPUT_DONE) {
+ vdec_msg->vdec_msg_info.msgcode =
+ VDEC_MSG_RESP_OUTPUT_BUFFER_DONE;
+ } else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) {
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_OUTPUT_FLUSHED;
+ } else {
+ ERR("QVD: vid_dec_output_frame_done invalid cmd type\n");
+ return;
+ }
+
+ kern_addr = vcd_frame_data->virt_addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, false,
+ &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ ERR("vid_dec_output_frame_done UVA can not be found\n");
+ vdec_msg->vdec_msg_info.status_code = VDEC_S_EFATAL;
+ goto out;
+ }
+
+ frm = &vdec_msg->vdec_msg_info.msgdata.output_frame;
+ /* Buffer address in user space */
+ frm->user_addr = user_addr;
+ frm->phys_addr = vcd_frame_data->phys_addr;
+ /* Data length */
+ frm->len = vcd_frame_data->data_len;
+ frm->flags = vcd_frame_data->flags;
+ /* timestamp pass-through from input frame */
+ frm->time_stamp = vcd_frame_data->time_stamp;
+ /* Output frame client data */
+ frm->client_data = vcd_frame_data->client_data;
+ /* Associated input frame client data */
+ frm->input_frame_clientdata = (void *)vcd_frame_data->ip_frm_tag;
+ /* Decoded picture width and height */
+ frm->framesize.bottom = vcd_frame_data->dec_op_prop.disp_frm.bottom;
+ frm->framesize.left = vcd_frame_data->dec_op_prop.disp_frm.left;
+ frm->framesize.right = vcd_frame_data->dec_op_prop.disp_frm.right;
+ frm->framesize.top = vcd_frame_data->dec_op_prop.disp_frm.top;
+ vdec_msg->vdec_msg_info.msgdatasize = sizeof(*frm);
+
+out:
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_dec_lean_event(struct video_client_ctx *client_ctx,
+ u32 event, u32 status)
+{
+ struct vid_dec_msg *vdec_msg;
+
+ if (!client_ctx) {
+ ERR("%s(): !client_ctx pointer\n", __func__);
+ return;
+ }
+
+ vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
+ if (!vdec_msg) {
+ ERR("%s(): cannot allocate vid_dec_msg buffer\n", __func__);
+ return;
+ }
+
+ vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status);
+
+ switch (event) {
+ case VCD_EVT_IND_RECONFIG:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_CONFIG_CHANGED;
+ break;
+ case VCD_EVT_IND_RESOURCES_LOST:
+ INFO("msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_EVT_RESOURCES_LOST;
+ break;
+ case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode =
+ VDEC_MSG_RESP_FLUSH_INPUT_DONE;
+ break;
+ case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode =
+ VDEC_MSG_RESP_FLUSH_OUTPUT_DONE;
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_HW_ERROR;
+ break;
+ case VCD_EVT_RESP_START:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE;
+ break;
+ case VCD_EVT_RESP_STOP:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_STOP_DONE;
+ break;
+ case VCD_EVT_RESP_PAUSE:
+ INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE"
+ " to client\n");
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_PAUSE_DONE;
+ break;
+ default:
+ ERR("%s() : unknown event type\n", __func__);
+ break;
+ }
+
+ vdec_msg->vdec_msg_info.msgdatasize = 0;
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+
+void vid_dec_vcd_cb(u32 event, u32 status, void *info, u32 size, void *handle,
+ void *const client_data)
+{
+ struct video_client_ctx *client_ctx = (struct video_client_ctx *)
+ client_data;
+
+ DBG("Entering %s()\n", __func__);
+
+ if (!client_ctx) {
+ ERR("%s(): client_ctx is NULL\n", __func__);
+ return;
+ }
+
+ client_ctx->event_status = status;
+
+ switch (event) {
+ case VCD_EVT_RESP_OPEN:
+ vid_dec_vcd_open_done(client_ctx, info);
+ break;
+ case VCD_EVT_RESP_INPUT_DONE:
+ case VCD_EVT_RESP_INPUT_FLUSHED:
+ vid_dec_input_frame_done(client_ctx, event, status, info);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ case VCD_EVT_RESP_OUTPUT_FLUSHED:
+ vid_dec_output_frame_done(client_ctx, event, status, info);
+ break;
+ case VCD_EVT_RESP_PAUSE:
+ case VCD_EVT_RESP_STOP:
+ case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+ case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+ case VCD_EVT_IND_RECONFIG:
+ case VCD_EVT_IND_HWERRFATAL:
+ case VCD_EVT_IND_RESOURCES_LOST:
+ vid_dec_lean_event(client_ctx, event, status);
+ break;
+ case VCD_EVT_RESP_START:
+ if (!client_ctx->seq_header_set)
+ vid_dec_lean_event(client_ctx, event, status);
+ else
+ vid_dec_notify_client(client_ctx);
+ break;
+ default:
+ ERR("%s(): Error - Invalid event type %u\n", __func__, event);
+ break;
+ }
+}
+
+static u32 vid_dec_set_codec(struct video_client_ctx *client_ctx,
+ enum vdec_codec *vdec_codec_type)
+{
+ u32 result = true;
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_codec codec_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !vdec_codec_type)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_CODEC;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+ switch (*vdec_codec_type) {
+ case VDEC_CODECTYPE_MPEG4:
+ codec_type.codec = VCD_CODEC_MPEG4;
+ break;
+ case VDEC_CODECTYPE_H264:
+ codec_type.codec = VCD_CODEC_H264;
+ break;
+ case VDEC_CODECTYPE_DIVX_3:
+ codec_type.codec = VCD_CODEC_DIVX_3;
+ break;
+ case VDEC_CODECTYPE_XVID:
+ codec_type.codec = VCD_CODEC_XVID;
+ break;
+ case VDEC_CODECTYPE_H263:
+ codec_type.codec = VCD_CODEC_H263;
+ break;
+ case VDEC_CODECTYPE_MPEG2:
+ codec_type.codec = VCD_CODEC_MPEG2;
+ break;
+ case VDEC_CODECTYPE_VC1:
+ codec_type.codec = VCD_CODEC_VC1;
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (result) {
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &codec_type);
+ if (vcd_status)
+ result = false;
+ }
+ return result;
+}
+
+static u32 vid_dec_set_output_format(struct video_client_ctx *client_ctx,
+ enum vdec_output_format *output_format)
+{
+ u32 result = true;
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_property_buffer_format buffer_format;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !output_format)
+ return false;
+
+ prop_hdr.id = VCD_I_BUFFER_FORMAT;;
+ prop_hdr.sz = sizeof(struct vcd_property_buffer_format);
+
+ switch (*output_format) {
+ case VDEC_YUV_FORMAT_NV12:
+ buffer_format.buffer_format = VCD_BUFFER_FORMAT_NV12;
+ break;
+ case VDEC_YUV_FORMAT_TILE_4x2:
+ buffer_format.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2;
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (!result)
+ return false;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr,
+ &buffer_format);
+
+ //TODO fix false/true silliness
+ if (vcd_status)
+ return false;
+ else
+ return true;
+}
+
+static u32 vid_dec_set_frame_resolution(struct video_client_ctx *client_ctx,
+ struct vdec_picsize *video_resoultion)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_property_frame_size res;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !video_resoultion)
+ return false;
+
+ prop_hdr.id = VCD_I_FRAME_SIZE;
+ prop_hdr.sz = sizeof(struct vcd_property_frame_size);
+ res.width = video_resoultion->frame_width;
+ res.height = video_resoultion->frame_height;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr, &res);
+
+ if (vcd_status)
+ return false;
+ else
+ return true;
+}
+
+static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx,
+ struct vdec_picsize *video_res)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_property_frame_size frame_res;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !video_res)
+ return false;
+
+ prop_hdr.id = VCD_I_FRAME_SIZE;
+ prop_hdr.sz = sizeof(struct vcd_property_frame_size);
+
+ vcd_status = vcd_get_property(client_ctx->vcd_handle, &prop_hdr,
+ &frame_res);
+
+ video_res->frame_width = frame_res.width;
+ video_res->frame_height = frame_res.height;
+ video_res->scan_lines = frame_res.scan_lines;
+ video_res->stride = frame_res.stride;
+
+ if (vcd_status)
+ return false;
+ else
+ return true;
+}
+
+static u32 vid_dec_get_buffer_req(struct video_client_ctx *client_ctx,
+ struct vdec_allocatorproperty *vdec_buf_req)
+{
+ u32 vcd_status = VCD_ERR_FAIL;
+ struct vcd_buffer_requirement vcd_buf_req;
+
+ if (!client_ctx || !vdec_buf_req)
+ return false;
+
+ if (vdec_buf_req->buffer_type == VDEC_BUFFER_TYPE_INPUT) {
+ vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+ VCD_BUFFER_INPUT, &vcd_buf_req);
+ } else {
+ vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+ VCD_BUFFER_OUTPUT, &vcd_buf_req);
+ }
+
+ if (vcd_status)
+ return false;
+
+ vdec_buf_req->mincount = vcd_buf_req.min_count;
+ vdec_buf_req->maxcount = vcd_buf_req.max_count;
+ vdec_buf_req->actualcount = vcd_buf_req.actual_count;
+ vdec_buf_req->buffer_size = vcd_buf_req.size;
+ vdec_buf_req->alignment = vcd_buf_req.align;
+ vdec_buf_req->buf_poolid = vcd_buf_req.buf_pool_id;
+
+ return true;
+}
+
+static u32 vid_dec_set_buffer(struct video_client_ctx *client_ctx,
+ struct vdec_setbuffer_cmd *b_info)
+{
+ enum vcd_buffer_type buffer_type;
+ enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ unsigned long len;
+ int pmem_fd;
+ struct file *file;
+ struct buf_addr_table *addr_table;
+ s32 buffer_index = -1;
+
+ if (!client_ctx || !b_info)
+ return false;
+
+ user_addr = b_info->buffer.addr;
+
+ if (b_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT)
+ dir_buffer = BUFFER_TYPE_OUTPUT;
+
+ /* if buffer already set, ignore */
+ if (vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr,
+ &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ DBG("%s: user_addr = %p is already set\n", __func__, user_addr);
+ return true;
+ }
+
+ if (get_pmem_file(b_info->buffer.pmem_fd, (unsigned long *)&phys_addr,
+ (unsigned long *)&kern_addr, &len, &file)) {
+ ERR("%s: get_pmem_file failed\n", __func__);
+ return false;
+ }
+ put_pmem_file(file);
+ if (b_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) {
+ buffer_type = VCD_BUFFER_INPUT;
+ client_ctx->num_of_input_buffers++;
+ if (client_ctx->num_of_input_buffers > MAX_VIDEO_NUM_OF_BUFF) {
+ ERR("%s(): num_of_input_buffers reached max value"
+ " MAX_VIDEO_NUM_OF_BUFF\n", __func__);
+ client_ctx->num_of_input_buffers--;
+ return false;
+ }
+ buffer_index = client_ctx->num_of_input_buffers - 1;
+ addr_table = &client_ctx->input_buf_addr_table[buffer_index];
+ addr_table->user_addr = b_info->buffer.addr;
+ addr_table->kern_addr = kern_addr;
+ addr_table->phys_addr = phys_addr;
+ addr_table->pmem_fd = b_info->buffer.pmem_fd;
+ addr_table->file = file;
+ } else {
+ buffer_type = VCD_BUFFER_OUTPUT;
+ client_ctx->num_of_output_buffers++;
+ if (client_ctx->num_of_output_buffers > MAX_VIDEO_NUM_OF_BUFF) {
+ ERR("%s(): num_of_outut_buffers reached max value"
+ " MAX_VIDEO_NUM_OF_BUFF\n", __func__);
+ client_ctx->num_of_output_buffers--;
+ return false;
+ }
+ buffer_index = client_ctx->num_of_output_buffers - 1;
+ addr_table = &client_ctx->output_buf_addr_table[buffer_index];
+ kern_addr = (u8 *)kern_addr + b_info->buffer.offset;
+ phys_addr += b_info->buffer.offset;
+ addr_table->user_addr = b_info->buffer.addr;
+ addr_table->kern_addr = kern_addr;
+ addr_table->phys_addr = phys_addr;
+ addr_table->pmem_fd = b_info->buffer.pmem_fd;
+ addr_table->file = file;
+ }
+
+ vcd_status = vcd_set_buffer(client_ctx->vcd_handle, buffer_type,
+ kern_addr, b_info->buffer.sz);
+
+ if (!vcd_status)
+ return true;
+ else
+ return false;
+}
+
+static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx,
+ struct vdec_setbuffer_cmd *buffer_info)
+{
+ enum vcd_buffer_type buffer_type;
+ enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+ u32 vcd_status = VCD_ERR_FAIL;
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+
+ if (!client_ctx || !buffer_info)
+ return false;
+
+ user_addr = buffer_info->buffer.addr;
+
+ if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT)
+ dir_buffer = BUFFER_TYPE_OUTPUT;
+
+ /*If buffer already set, ignore */
+ if (!vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr,
+ &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ DBG("%s: user_addr = %p is already set\n", __func__, user_addr);
+ return true;
+ }
+
+ if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_INPUT)
+ buffer_type = VCD_BUFFER_INPUT;
+ else
+ buffer_type = VCD_BUFFER_OUTPUT;
+ vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer_type,
+ kern_addr);
+
+ if (!vcd_status)
+ return true;
+ else
+ return false;
+}
+
+static u32 vid_dec_pause_resume(struct video_client_ctx *client_ctx, u32 pause)
+{
+ u32 vcd_status;
+
+ if (!client_ctx) {
+ ERR("%s: Invalid client_ctx\n", __func__);
+ return false;
+ }
+
+ if (pause) {
+ INFO("msm_vidc_dec: PAUSE command from client = %p\n",
+ client_ctx);
+ vcd_status = vcd_pause(client_ctx->vcd_handle);
+ } else {
+ INFO("msm_vidc_dec: RESUME command from client = %p\n",
+ client_ctx);
+ vcd_status = vcd_resume(client_ctx->vcd_handle);
+ }
+
+ if (vcd_status)
+ return false;
+
+ return true;
+
+}
+static u32 vid_dec_start(struct video_client_ctx *client_ctx)
+{
+ struct vid_dec_msg *vdec_msg = NULL;
+ u32 vcd_status;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ if (!client_ctx) {
+ ERR("\n Invalid client_ctx");
+ return false;
+ }
+
+ if (!client_ctx->seq_header_set) {
+ INFO("%s: Calling decode_start()\n", __func__);
+ vcd_status = vcd_decode_start(client_ctx->vcd_handle, NULL);
+
+ if (vcd_status) {
+ ERR("%s: vcd_decode_start failed vcd_status = %u\n",
+ __func__, vcd_status);
+ return false;
+ }
+ return true;
+ }
+
+ INFO("%s(): Seq Hdr set: Send START_DONE to client\n", __func__);
+ vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL);
+ if (!vdec_msg) {
+ ERR("%s: cannot allocate buffer\n", __func__);
+ return false;
+ }
+ vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE;
+ vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS;
+ vdec_msg->vdec_msg_info.msgdatasize = 0;
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&vdec_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+
+ wake_up(&client_ctx->msg_wait);
+
+ DBG("Send START_DONE message to client = %p\n", client_ctx);
+
+ return true;
+}
+
+static u32 vid_dec_stop(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ if (!client_ctx) {
+ ERR("Invalid client_ctx\n");
+ return false;
+ }
+
+ INFO("%s: Calling vcd_stop()\n", __func__);
+ vcd_status = vcd_stop(client_ctx->vcd_handle);
+ if (vcd_status) {
+ ERR("%s: vcd_stop failed %u\n", __func__, vcd_status);
+ return false;
+ }
+ DBG("Send STOP_DONE message to client = %p\n", client_ctx);
+ return true;
+}
+
+static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx,
+ struct vdec_input_frameinfo *frm_info)
+{
+ struct vcd_frame_data vcd_input_buffer;
+ void *kern_addr;
+ void __user *user_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !frm_info)
+ return false;
+
+ user_addr = frm_info->user_addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, true,
+ &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ ERR("%s: kern_addr not found\n", __func__);
+ return false;
+ }
+
+ /* kernel_vaddr is found. send the frame to VCD */
+ memset((void *)&vcd_input_buffer, 0, sizeof(vcd_input_buffer));
+ vcd_input_buffer.virt_addr = (u8 *)kern_addr + frm_info->pmem_offset;
+ vcd_input_buffer.offset = frm_info->offset;
+ vcd_input_buffer.client_data = frm_info->client_data;
+ vcd_input_buffer.ip_frm_tag = (u32)frm_info->client_data;
+ vcd_input_buffer.data_len = frm_info->data_len;
+ vcd_input_buffer.time_stamp = frm_info->timestamp;
+ /* Rely on VCD using the same flags as OMX */
+ vcd_input_buffer.flags = frm_info->flags;
+
+ vcd_status = vcd_decode_frame(client_ctx->vcd_handle,
+ &vcd_input_buffer);
+
+ if (vcd_status) {
+ ERR("%s: vcd_decode_frame failed = %u\n", __func__, vcd_status);
+ return false;
+ }
+ return true;
+}
+
+static u32 vid_dec_fill_output_buffer(struct video_client_ctx *client_ctx,
+ struct vdec_fillbuffer_cmd *fill_buffer_cmd)
+{
+ void *kern_addr;
+ void __user *user_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+ u32 vcd_status = VCD_ERR_FAIL;
+ struct vcd_frame_data vcd_frame;
+
+ if (!client_ctx || !fill_buffer_cmd)
+ return false;
+
+ user_addr = fill_buffer_cmd->buffer.addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, true,
+ &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ ERR("%s: kern_addr not found\n", __func__);
+ return false;
+ }
+
+ memset((void *)&vcd_frame, 0, sizeof(vcd_frame));
+ vcd_frame.virt_addr = kern_addr;
+ vcd_frame.client_data = fill_buffer_cmd->client_data;
+ vcd_frame.alloc_len = fill_buffer_cmd->buffer.sz;
+
+ vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, &vcd_frame);
+ if (vcd_status) {
+ ERR("%s: vcd_fill_output_buffer failed = %u\n", __func__,
+ vcd_status);
+ return false;
+ }
+ return true;
+}
+
+static u32 vid_dec_flush(struct video_client_ctx *client_ctx,
+ enum vdec_bufferflush flush_dir)
+{
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ INFO("msm_vidc_dec: %s called with dir = %u\n", __func__, flush_dir);
+ if (!client_ctx) {
+ ERR("Invalid client_ctx\n");
+ return false;
+ }
+
+ switch (flush_dir) {
+ case VDEC_FLUSH_TYPE_INPUT:
+ vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_INPUT);
+ break;
+ case VDEC_FLUSH_TYPE_OUTPUT:
+ vcd_status = vcd_flush(client_ctx->vcd_handle,
+ VCD_FLUSH_OUTPUT);
+ break;
+ case VDEC_FLUSH_TYPE_ALL:
+ vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_ALL);
+ break;
+ default:
+ ERR("%s: Invalid flush cmd. flush_dir = %u\n", __func__,
+ flush_dir);
+ return false;
+ }
+
+ if (vcd_status) {
+ ERR("%s: vcd_flush failed. vcd_status = %u flush_dir = %u\n",
+ __func__, vcd_status, flush_dir);
+ return false;
+ }
+ return true;
+}
+
+static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx)
+{
+ u32 islist_empty = 0;
+ mutex_lock(&client_ctx->msg_queue_lock);
+ islist_empty = list_empty(&client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+
+ if (islist_empty) {
+ DBG("%s: vid_dec msg queue empty\n", __func__);
+ if (client_ctx->stop_msg) {
+ DBG("%s: List empty and Stop Msg set\n", __func__);
+ return client_ctx->stop_msg;
+ }
+ } else {
+ DBG("%s: vid_dec msg queue Not empty\n", __func__);
+ }
+
+ return !islist_empty;
+}
+
+static u32 vid_dec_get_next_msg(struct video_client_ctx *client_ctx,
+ struct vdec_msginfo *vdec_msg_info)
+{
+ int rc;
+ struct vid_dec_msg *vid_dec_msg = NULL;
+
+ if (!client_ctx)
+ return false;
+
+ rc = wait_event_interruptible(client_ctx->msg_wait,
+ vid_dec_msg_pending(client_ctx));
+ if (rc < 0 || client_ctx->stop_msg) {
+ DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
+ return false;
+ }
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+ if (!list_empty(&client_ctx->msg_queue)) {
+ DBG("%s(): After Wait\n", __func__);
+ vid_dec_msg = list_first_entry(&client_ctx->msg_queue,
+ struct vid_dec_msg, list);
+ list_del(&vid_dec_msg->list);
+ memcpy(vdec_msg_info, &vid_dec_msg->vdec_msg_info,
+ sizeof(struct vdec_msginfo));
+ kfree(vid_dec_msg);
+ }
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ return true;
+}
+
+static int vid_dec_ioctl(struct inode *inode, struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ struct video_client_ctx *client_ctx = NULL;
+ struct vdec_ioctl_msg vdec_msg;
+ u32 vcd_status;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ struct file *pmem_file;
+ u32 result = true;
+ void __user *u_arg = (void __user *)arg;
+
+ DBG("%s\n", __func__);
+
+ if (_IOC_TYPE(cmd) != VDEC_IOCTL_MAGIC)
+ return -ENOTTY;
+
+ client_ctx = (struct video_client_ctx *)file->private_data;
+ if (!client_ctx) {
+ ERR("!client_ctx. Cannot attach to device handle\n");
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case VDEC_IOCTL_SET_CODEC:
+ {
+ enum vdec_codec codec_type;
+ struct vcd_property_meta_data_enable metdata_disable;
+ struct vcd_property_hdr header_type;
+ DBG("VDEC_IOCTL_SET_CODEC\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&codec_type, vdec_msg.in,
+ sizeof(codec_type)))
+ return -EFAULT;
+ DBG("setting code type = %u\n", codec_type);
+ result = vid_dec_set_codec(client_ctx, &codec_type);
+ if (!result)
+ return -EIO;
+ metdata_disable.meta_data_enable_flag = 0;
+ header_type.sz = sizeof(metdata_disable);
+ header_type.id = VCD_I_METADATA_ENABLE;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &header_type, (void *)&metdata_disable);
+
+ if (vcd_status) {
+ ERR("%s: vcd_set_property Failed for Meta Data Disable"
+ "\n", __func__);
+ return -ENODEV;
+ }
+ DBG("Disabled Meta Data\n");
+ break;
+ }
+ case VDEC_IOCTL_SET_OUTPUT_FORMAT:
+ {
+ enum vdec_output_format out_format;
+ DBG("VDEC_IOCTL_SET_OUTPUT_FORMAT\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&out_format, vdec_msg.in,
+ sizeof(out_format)))
+ return -EFAULT;
+
+ result = vid_dec_set_output_format(client_ctx, &out_format);
+
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_SET_PICRES:
+ {
+ struct vdec_picsize video_res;
+ DBG("VDEC_IOCTL_SET_PICRES\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&video_res, vdec_msg.in, sizeof(video_res)))
+ return -EFAULT;
+ result = vid_dec_set_frame_resolution(client_ctx,
+ &video_res);
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_GET_PICRES:
+ {
+ struct vdec_picsize video_res;
+ DBG("VDEC_IOCTL_GET_PICRES\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&video_res, vdec_msg.out, sizeof(video_res)))
+ return -EFAULT;
+
+ result = vid_dec_get_frame_resolution(client_ctx, &video_res);
+
+ if (!result)
+ return -EIO;
+
+ if (copy_to_user(vdec_msg.out, &video_res,sizeof(video_res)))
+ return -EFAULT;
+ break;
+ }
+ case VDEC_IOCTL_SET_BUFFER_REQ:
+ {
+ //TODO unify these types
+ struct vdec_allocatorproperty vdec_buf_req;
+ struct vcd_buffer_requirement vcd_buf_req;
+
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+
+ if (copy_from_user(&vdec_buf_req, vdec_msg.in,
+ sizeof(vdec_buf_req)))
+ return -EFAULT;
+
+ vcd_buf_req.actual_count = vdec_buf_req.actualcount;
+ vcd_buf_req.align = vdec_buf_req.alignment;
+ vcd_buf_req.max_count = vdec_buf_req.maxcount;
+ vcd_buf_req.min_count = vdec_buf_req.mincount;
+ vcd_buf_req.size = vdec_buf_req.buffer_size;
+
+ switch (vdec_buf_req.buffer_type) {
+ case VDEC_BUFFER_TYPE_INPUT:
+ vcd_status = vcd_set_buffer_requirements(
+ client_ctx->vcd_handle, VCD_BUFFER_INPUT,
+ &vcd_buf_req);
+ break;
+ case VDEC_BUFFER_TYPE_OUTPUT:
+ vcd_status = vcd_set_buffer_requirements(
+ client_ctx->vcd_handle, VCD_BUFFER_OUTPUT,
+ &vcd_buf_req);
+ break;
+ default:
+ vcd_status = VCD_ERR_BAD_POINTER;
+ break;
+ }
+
+ if (vcd_status)
+ return -EFAULT;
+ break;
+ }
+ case VDEC_IOCTL_GET_BUFFER_REQ:
+ {
+ struct vdec_allocatorproperty vdec_buf_req;
+ DBG("VDEC_IOCTL_GET_BUFFER_REQ\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&vdec_buf_req, vdec_msg.out,
+ sizeof(vdec_buf_req)))
+ return -EFAULT;
+
+ result = vid_dec_get_buffer_req(client_ctx, &vdec_buf_req);
+ if (!result)
+ return -EIO;
+
+ if (copy_to_user(vdec_msg.out, &vdec_buf_req,
+ sizeof(vdec_buf_req)))
+ return -EFAULT;
+ break;
+ }
+ case VDEC_IOCTL_SET_BUFFER:
+ {
+ struct vdec_setbuffer_cmd setbuffer;
+ DBG("VDEC_IOCTL_SET_BUFFER\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&setbuffer, vdec_msg.in, sizeof(setbuffer)))
+ return -EFAULT;
+ result = vid_dec_set_buffer(client_ctx, &setbuffer);
+ break;
+ }
+ case VDEC_IOCTL_FREE_BUFFER:
+ {
+ struct vdec_setbuffer_cmd setbuffer;
+ DBG("VDEC_IOCTL_FREE_BUFFER\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&setbuffer, vdec_msg.in, sizeof(setbuffer)))
+ return -EFAULT;
+
+ result = vid_dec_free_buffer(client_ctx, &setbuffer);
+
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_CMD_START:
+ DBG("VDEC_IOCTL_CMD_START\n");
+ result = vid_dec_start(client_ctx);
+
+ if (!result)
+ return -EIO;
+ break;
+ case VDEC_IOCTL_CMD_STOP:
+ DBG("VDEC_IOCTL_CMD_STOP\n");
+ result = vid_dec_stop(client_ctx);
+
+ if (!result)
+ return -EIO;
+ break;
+ case VDEC_IOCTL_CMD_PAUSE:
+ DBG("VDEC_IOCTL_CMD_PAUSE\n");
+ result = vid_dec_pause_resume(client_ctx, true);
+
+ if (!result)
+ return -EIO;
+ break;
+ case VDEC_IOCTL_CMD_RESUME:
+ DBG("VDEC_IOCTL_CMD_RESUME\n");
+ result = vid_dec_pause_resume(client_ctx, false);
+
+ if (!result)
+ return -EIO;
+ break;
+ case VDEC_IOCTL_DECODE_FRAME:
+ {
+ struct vdec_input_frameinfo frm_info;
+ DBG("VDEC_IOCTL_DECODE_FRAME\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&frm_info, vdec_msg.in, sizeof(frm_info)))
+ return -EFAULT;
+
+ result = vid_dec_decode_frame(client_ctx, &frm_info);
+
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_FILL_OUTPUT_BUFFER:
+ {
+ struct vdec_fillbuffer_cmd fill_cmd;
+
+ DBG("VDEC_IOCTL_FILL_OUTPUT_BUFFER\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&fill_cmd, vdec_msg.in, sizeof(fill_cmd)))
+ return -EFAULT;
+ result = vid_dec_fill_output_buffer(client_ctx, &fill_cmd);
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_CMD_FLUSH:
+ {
+ enum vdec_bufferflush flush_dir;
+ DBG("VDEC_IOCTL_CMD_FLUSH\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_from_user(&flush_dir, vdec_msg.in, sizeof(flush_dir)))
+ return -EFAULT;
+
+ result = vid_dec_flush(client_ctx, flush_dir);
+
+ if (!result)
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_GET_NEXT_MSG:
+ {
+ struct vdec_msginfo msg_info;
+ DBG("VDEC_IOCTL_GET_NEXT_MSG\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ result = vid_dec_get_next_msg(client_ctx, &msg_info);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(vdec_msg.out, &msg_info, sizeof(msg_info)))
+ return -EFAULT;
+ break;
+ }
+ case VDEC_IOCTL_STOP_NEXT_MSG:
+ DBG("VDEC_IOCTL_STOP_NEXT_MSG\n");
+ client_ctx->stop_msg = 1;
+ wake_up(&client_ctx->msg_wait);
+ break;
+ case VDEC_IOCTL_SET_SEQUENCE_HEADER:
+ {
+ struct vdec_seqheader vdec_seq_hdr;
+ struct vcd_sequence_hdr vcd_seq_hdr;
+ unsigned long sz;
+ DBG("VDEC_IOCTL_SET_SEQUENCE_HEADER\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) {
+ ERR("Copy from user vdec_msg failed\n");
+ return -EFAULT;
+ }
+ if (copy_from_user(&vdec_seq_hdr, vdec_msg.in,
+ sizeof(vdec_seq_hdr))) {
+ ERR("Copy from user seq_header failed\n");
+ return -EFAULT;
+ }
+ if (!vdec_seq_hdr.sz) {
+ ERR("Seq len is zero\n");
+ return -EFAULT;
+ }
+
+ if (get_pmem_file(vdec_seq_hdr.pmem_fd,
+ (unsigned long *)&phys_addr,
+ (unsigned long *)&kern_addr, &sz, &pmem_file)) {
+ ERR("%s: get_pmem_file failed\n", __func__);
+ return false;
+ }
+ put_pmem_file(pmem_file);
+
+ vcd_seq_hdr.sz = vdec_seq_hdr.sz;
+ kern_addr = (u8 *)kern_addr + vdec_seq_hdr.pmem_offset;
+ vcd_seq_hdr.addr = kern_addr;
+ if (!vcd_seq_hdr.addr) {
+ ERR("Sequence Header pointer failed\n");
+ return -EFAULT;
+ }
+ client_ctx->seq_header_set = true;
+ if (vcd_decode_start(client_ctx->vcd_handle, &vcd_seq_hdr)) {
+ ERR("Decode start Failed\n");
+ client_ctx->seq_header_set = false;
+ return -EFAULT;
+ }
+ DBG("Wait Client completion Sequence Header\n");
+ wait_for_completion(&client_ctx->event);
+ vcd_seq_hdr.addr = NULL;
+ if (client_ctx->event_status) {
+ ERR("Set Seq Header status is failed");
+ return -EFAULT;
+ }
+ break;
+ }
+ case VDEC_IOCTL_GET_NUMBER_INSTANCES:
+ DBG("VDEC_IOCTL_GET_NUMBER_INSTANCES\n");
+ if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ if (copy_to_user(vdec_msg.out, &vidc_dec_dev->num_clients,
+ sizeof(vidc_dec_dev->num_clients)))
+ return -EFAULT;
+ break;
+ default:
+ ERR("%s(): Unsupported ioctl\n", __func__);
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static u32 vid_dec_close_client(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+ int rc;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ if (!client_ctx || !client_ctx->vcd_handle) {
+ ERR("Invalid client_ctx\n");
+ return false;
+ }
+
+ mutex_lock(&vidc_dec_dev->lock);
+ vcd_status = vcd_stop(client_ctx->vcd_handle);
+
+ if (!vcd_status) {
+ rc = wait_for_completion_timeout(&client_ctx->event,
+ (5 * HZ) / 10);
+ if (!rc)
+ DBG("%s:ERROR vcd_stop time out rc = %d\n", __func__,
+ rc);
+
+ if (client_ctx->event_status)
+ ERR("%s:ERROR vcd_stop event_status failure\n",
+ __func__);
+ }
+ vcd_status = vcd_close(client_ctx->vcd_handle);
+
+ if (vcd_status) {
+ mutex_unlock(&vidc_dec_dev->lock);
+ return false;
+ }
+ memset((void *)client_ctx, 0, sizeof(*client_ctx));
+ vidc_dec_dev->num_clients--;
+ mutex_unlock(&vidc_dec_dev->lock);
+ return true;
+}
+
+static int vid_dec_open(struct inode *inode, struct file *file)
+{
+ int rc;
+ s32 client_index;
+ struct video_client_ctx *client_ctx;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ mutex_lock(&vidc_dec_dev->lock);
+
+ if (vidc_dec_dev->num_clients == VID_DEC_MAX_DECODER_CLIENTS) {
+ ERR("%s: ERROR: max number of clients limit reached\n",
+ __func__);
+ mutex_unlock(&vidc_dec_dev->lock);
+ return -ENODEV;
+ }
+
+#ifndef USE_RES_TRACKER
+ DBG("Resource Tracker not in use");
+ if (!vid_c_enable_clk(VID_C_HCLK_RATE)) {
+ ERR("%s: ERROR: clock enabled failed\n", __func__);
+ mutex_unlock(&vidc_dec_dev->lock);
+ return -ENODEV;
+ }
+#endif
+
+ DBG("Virtual Address of ioremap is %p\n", vidc_dec_dev->virt_base);
+
+ if (!vidc_dec_dev->num_clients) {
+ rc = vcd_fw_prepare_all();
+ if (rc)
+ return rc;
+ }
+
+ client_index = vid_dec_get_empty_client_index();
+ if (client_index == -1) {
+ ERR("%s: No free clients client_index == -1\n", __func__);
+ return -ENODEV;
+ }
+ client_ctx = &vidc_dec_dev->vdec_clients[client_index];
+ vidc_dec_dev->num_clients++;
+ init_completion(&client_ctx->event);
+ mutex_init(&client_ctx->msg_queue_lock);
+ INIT_LIST_HEAD(&client_ctx->msg_queue);
+ init_waitqueue_head(&client_ctx->msg_wait);
+ client_ctx->stop_msg = 0;
+
+ vcd_status = vcd_open(vidc_dec_dev->device_handle, true,
+ vid_dec_vcd_cb, client_ctx);
+ wait_for_completion(&client_ctx->event);
+ client_ctx->seq_header_set = false;
+ file->private_data = client_ctx;
+ mutex_unlock(&vidc_dec_dev->lock);
+ return 0;
+}
+
+static int vid_dec_release(struct inode *inode, struct file *file)
+{
+ struct video_client_ctx *client_ctx = file->private_data;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ vid_dec_close_client(client_ctx);
+#ifndef USE_RES_TRACKER
+ vid_c_disable_clk();
+#endif
+ INFO("msm_vidc_dec: Return from %s\n", __func__);
+ return 0;
+}
+
+static const struct file_operations vid_dec_fops = {
+ .owner = THIS_MODULE,
+ .open = vid_dec_open,
+ .release = vid_dec_release,
+ .ioctl = vid_dec_ioctl,
+};
+
+void vid_dec_interrupt_deregister(void)
+{
+}
+
+void vid_dec_interrupt_register(void *device_name)
+{
+}
+
+void vid_dec_interrupt_clear(void)
+{
+}
+
+void *vid_dec_map_dev_base_addr(void *device_name)
+{
+ return vidc_dec_dev->virt_base;
+}
+
+static int vid_dec_vcd_init(void)
+{
+ int rc;
+ struct vcd_init_config vcd_init_config;
+ u32 i;
+
+ /* init_timer(&hw_timer); */
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ vidc_dec_dev->num_clients = 0;
+
+ for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) {
+ memset((void *)&vidc_dec_dev->vdec_clients[i], 0,
+ sizeof(vidc_dec_dev->vdec_clients[i]));
+ }
+
+ mutex_init(&vidc_dec_dev->lock);
+ vidc_dec_dev->virt_base = vid_c_get_ioaddr();
+ DBG("%s: base address for VIDC core %p\n", __func__,
+ vidc_dec_dev->virt_base);
+
+ if (!vidc_dec_dev->virt_base) {
+ ERR("%s: ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ vcd_init_config.device_name = "VID_C";
+ vcd_init_config.pf_map_dev_base_addr = vid_dec_map_dev_base_addr;
+ vcd_init_config.pf_interrupt_clr = vid_dec_interrupt_clear;
+ vcd_init_config.pf_register_isr = vid_dec_interrupt_register;
+ vcd_init_config.pf_deregister_isr = vid_dec_interrupt_deregister;
+ vcd_init_config.pf_timer_create = vid_c_timer_create;
+ vcd_init_config.pf_timer_release = vid_c_timer_release;
+ vcd_init_config.pf_timer_start = vid_c_timer_start;
+ vcd_init_config.pf_timer_stop = vid_c_timer_stop;
+
+ rc = vcd_init(&vcd_init_config, &vidc_dec_dev->device_handle);
+ if (rc) {
+ ERR("%s: vcd_init failed\n", __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __init vid_dec_init(void)
+{
+ int rc = 0;
+ struct device *class_devp;
+
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ vidc_dec_dev = kzalloc(sizeof(*vidc_dec_dev), GFP_KERNEL);
+ if (!vidc_dec_dev) {
+ ERR("%s Unable to allocate memory for vid_dec_dev\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = alloc_chrdev_region(&vidc_dec_dev_num, 0, 1, VID_DEC_NAME);
+ if (rc < 0) {
+ ERR("%s: alloc_chrdev_region failed rc = %d\n", __func__, rc);
+ goto error_vid_dec_alloc_chrdev_region;
+ }
+
+ vidc_dec_class = class_create(THIS_MODULE, VID_DEC_NAME);
+ if (IS_ERR(vidc_dec_class)) {
+ rc = PTR_ERR(vidc_dec_class);
+ ERR("%s: couldn't create vid_dec_class %d\n", __func__, rc);
+ goto error_vid_dec_class_create;
+ }
+
+ class_devp = device_create(vidc_dec_class, NULL, vidc_dec_dev_num, NULL,
+ VID_DEC_NAME);
+
+ if (IS_ERR(class_devp)) {
+ rc = PTR_ERR(class_devp);
+ ERR("%s: class device_create failed %d\n", __func__, rc);
+ goto error_vid_dec_class_device_create;
+ }
+
+ vidc_dec_dev->device = class_devp;
+
+ cdev_init(&vidc_dec_dev->cdev, &vid_dec_fops);
+ vidc_dec_dev->cdev.owner = THIS_MODULE;
+ rc = cdev_add(&vidc_dec_dev->cdev, vidc_dec_dev_num, 1);
+
+ if (rc < 0) {
+ ERR("%s: cdev_add failed %d\n", __func__, rc);
+ goto error_vid_dec_cdev_add;
+ }
+
+ return vid_dec_vcd_init();
+
+error_vid_dec_cdev_add:
+ device_destroy(vidc_dec_class, vidc_dec_dev_num);
+error_vid_dec_class_device_create:
+ class_destroy(vidc_dec_class);
+error_vid_dec_class_create:
+ unregister_chrdev_region(vidc_dec_dev_num, 1);
+error_vid_dec_alloc_chrdev_region:
+ kfree(vidc_dec_dev);
+
+ return rc;
+}
+
+static void __exit vid_dec_exit(void)
+{
+ INFO("msm_vidc_dec: Inside %s\n", __func__);
+ cdev_del(&(vidc_dec_dev->cdev));
+ device_destroy(vidc_dec_class, vidc_dec_dev_num);
+ class_destroy(vidc_dec_class);
+ unregister_chrdev_region(vidc_dec_dev_num, 1);
+ kfree(vidc_dec_dev);
+ INFO("msm_vidc_dec: Return from %s\n", __func__);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video decoder driver");
+MODULE_VERSION("1.0");
+
+module_init(vid_dec_init);
+module_exit(vid_dec_exit);
diff --git a/drivers/misc/video_core/720p/dec/vdec_internal.h b/drivers/misc/video_core/720p/dec/vdec_internal.h
new file mode 100644
index 0000000..f6ce510
--- /dev/null
+++ b/drivers/misc/video_core/720p/dec/vdec_internal.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef VDEC_INTERNAL_H
+#define VDEC_INTERNAL_H
+
+#include <linux/msm_vidc_dec.h>
+#include <linux/cdev.h>
+#include "video_core_init.h"
+
+#define VID_DEC_MAX_DECODER_CLIENTS 16
+
+struct vid_dec_msg {
+ struct list_head list;
+ struct vdec_msginfo vdec_msg_info;
+};
+
+struct vid_dec_dev {
+ struct cdev cdev;
+ struct device *device;
+ resource_size_t phys_base;
+ void __iomem *virt_base;
+ unsigned int irq;
+ struct clk *hclk;
+ struct clk *hclk_div2;
+ struct clk *pclk;
+ unsigned long hclk_rate;
+ struct mutex lock;
+ s32 device_handle;
+ struct video_client_ctx vdec_clients[VID_DEC_MAX_DECODER_CLIENTS];
+ u32 num_clients;
+ void(*pf_timer_handler)(void *);
+};
+
+#endif
diff --git a/drivers/misc/video_core/720p/enc/venc.c b/drivers/misc/video_core/720p/enc/venc.c
new file mode 100644
index 0000000..6b81018
--- /dev/null
+++ b/drivers/misc/video_core/720p/enc/venc.c
@@ -0,0 +1,1486 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+
+#include "vcd_ddl_firmware.h"
+#include "video_core_type.h"
+#include "vcd_api.h"
+#include "venc_internal.h"
+#include "video_core_init.h"
+
+#define VID_ENC_NAME "msm_vidc_enc"
+#define VID_C_HCLK_RATE 170667000
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define INFO(x...) printk(KERN_INFO x)
+#define ERR(x...) printk(KERN_ERR x)
+
+static struct vid_enc_dev *vidc_enc_dev;
+static dev_t vidc_enc_dev_num;
+static struct class *vid_enc_class;
+static int vid_enc_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+ unsigned long arg);
+//TOOD stop_cmd wtf
+static int stop_cmd;
+
+static s32 vid_enc_get_empty_client_index(void)
+{
+ u32 i;
+ u32 found = false;
+
+ for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) {
+ if (!vidc_enc_dev->venc_clients[i].vcd_handle) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ERR("%s: ERROR No space for new client\n", __func__);
+ return -1;
+ }
+ DBG("%s: available client index = %u\n", __func__, i);
+ return i;
+}
+
+//TODO collapse this crap
+u32 vid_enc_get_status(u32 status)
+{
+ u32 venc_status;
+
+ switch (status) {
+ case VCD_S_SUCCESS:
+ venc_status = VEN_S_SUCCESS;
+ break;
+ case VCD_ERR_FAIL:
+ venc_status = VEN_S_EFAIL;
+ break;
+ case VCD_ERR_ALLOC_FAIL:
+ venc_status = VEN_S_ENOSWRES;
+ break;
+ case VCD_ERR_ILLEGAL_OP:
+ venc_status = VEN_S_EINVALCMD;
+ break;
+ case VCD_ERR_ILLEGAL_PARM:
+ venc_status = VEN_S_EBADPARAM;
+ break;
+ case VCD_ERR_BAD_POINTER:
+ case VCD_ERR_BAD_HANDLE:
+ venc_status = VEN_S_EFATAL;
+ break;
+ case VCD_ERR_NOT_SUPPORTED:
+ venc_status = VEN_S_ENOTSUPP;
+ break;
+ case VCD_ERR_BAD_STATE:
+ venc_status = VEN_S_EINVALSTATE;
+ break;
+ case VCD_ERR_MAX_CLIENT:
+ venc_status = VEN_S_ENOHWRES;
+ break;
+ default:
+ venc_status = VEN_S_EFAIL;
+ break;
+ }
+ return venc_status;
+}
+
+static void vid_enc_notify_client(struct video_client_ctx *client_ctx)
+{
+ if (client_ctx)
+ complete(&client_ctx->event);
+}
+
+void vid_enc_vcd_open_done(struct video_client_ctx *client_ctx,
+ struct vcd_handle_container *handle_container)
+{
+ DBG("vid_enc_vcd_open_done\n");
+
+ if (!client_ctx) {
+ ERR("%s(): ERROR. client_ctx is NULL\n", __func__);
+ return;
+ }
+ if (handle_container)
+ client_ctx->vcd_handle = handle_container->handle;
+ else
+ ERR("%s: ERROR. handle_container is NULL\n", __func__);
+ vid_enc_notify_client(client_ctx);
+}
+
+static void vid_enc_input_frame_done(struct video_client_ctx *client_ctx,
+ u32 event, u32 status, struct vcd_frame_data *vcd_frame_data)
+{
+ struct vid_enc_msg *venc_msg;
+
+ if (!client_ctx || !vcd_frame_data) {
+ ERR("%s: NULL pointer\n", __func__);
+ return;
+ }
+
+ venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL);
+ if (!venc_msg) {
+ ERR("%s: cannot allocate vid_enc_msg buffer\n", __func__);
+ return;
+ }
+
+ venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status);
+
+ if (event == VCD_EVT_RESP_INPUT_DONE) {
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE;
+ DBG("Send INPUT_DON message to client = %p\n", client_ctx);
+ } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) {
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE;
+ DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx);
+ } else {
+ ERR("vid_enc_input_frame_done(): invalid event type\n");
+ return;
+ }
+
+ venc_msg->venc_msg_info.buf.clientdata = vcd_frame_data->client_data;
+ venc_msg->venc_msg_info.msgdata_size = sizeof(struct vid_enc_msg);
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_enc_output_frame_done(struct video_client_ctx *client_ctx,
+ u32 event, u32 status, struct vcd_frame_data *frm_data)
+{
+ struct vid_enc_msg *venc_msg;
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buf_index = -1;
+
+ if (!client_ctx || !frm_data) {
+ ERR("%s: NULL pointer\n", __func__);
+ return;
+ }
+
+ venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL);
+ if (!venc_msg) {
+ ERR("%s: cannot allocate vid_enc_msg buffer\n", __func__);
+ return;
+ }
+
+ venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status);
+
+ if (event == VCD_EVT_RESP_OUTPUT_DONE) {
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_OUTPUT_BUFFER_DONE;
+ } else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) {
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_OUTPUT_BUFFER_DONE;
+ } else {
+ ERR("QVD: vid_enc_output_frame_done invalid cmd type\n");
+ return;
+ }
+
+ kern_addr = frm_data->virt_addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, false,
+ &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buf_index)) {
+ ERR("vid_enc_output_frame_done UVA can not be found\n");
+ venc_msg->venc_msg_info.statuscode = VEN_S_EFATAL;
+ goto out;
+ }
+
+
+ /* Buffer address in user space */
+ venc_msg->venc_msg_info.buf.addr = user_addr;
+ venc_msg->venc_msg_info.buf.clientdata = frm_data->client_data;
+ /* Data length */
+ venc_msg->venc_msg_info.buf.len = frm_data->data_len;
+ venc_msg->venc_msg_info.buf.flags = frm_data->flags;
+ /* time-stamp pass-through from input frame */
+ venc_msg->venc_msg_info.buf.timestamp = frm_data->time_stamp;
+ /* Decoded picture width and height */
+ venc_msg->venc_msg_info.msgdata_size = sizeof(struct venc_buffer);
+
+out:
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+static void vid_enc_lean_event(struct video_client_ctx *client_ctx,
+ u32 event, u32 status)
+{
+ struct vid_enc_msg *venc_msg;
+ if (!client_ctx) {
+ ERR("%s(): !client_ctx pointer\n", __func__);
+ return;
+ }
+
+ venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL);
+ if (!venc_msg) {
+ ERR("%s(): cannot allocate vid_enc_msg buffer\n", __func__);
+ return;
+ }
+
+ venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status);
+
+ switch (event) {
+ case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+ INFO("%s: Sending VCD_EVT_RESP_FLUSH_INPUT_DONE to client\n",
+ __func__);
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_FLUSH_INPUT_DONE;
+ break;
+ case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+ INFO("%s: Sending VCD_EVT_RESP_FLUSH_OUTPUT_DONE to client\n",
+ __func__);
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_FLUSH_OUPUT_DONE;
+ break;
+ case VCD_EVT_RESP_START:
+ INFO("%s: Sending VCD_EVT_RESP_START to client\n", __func__);
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_START;
+ break;
+ case VCD_EVT_RESP_STOP:
+ INFO("%s: Sending VCD_EVT_RESP_STOP to client\n", __func__);
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_STOP;
+ break;
+ case VCD_EVT_RESP_PAUSE:
+ INFO("%s: Sending VCD_EVT_RESP_PAUSE to client\n", __func__);
+ venc_msg->venc_msg_info.msgcode = VEN_MSG_PAUSE;
+ break;
+ default:
+ ERR("%s: unknown event type\n", __func__);
+ break;
+ }
+
+ venc_msg->venc_msg_info.msgdata_size = 0;
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+ list_add_tail(&venc_msg->list, &client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ wake_up(&client_ctx->msg_wait);
+}
+
+
+void vid_enc_vcd_cb(u32 event, u32 status, void *info, u32 size, void *handle,
+ void *const client_data)
+{
+ struct video_client_ctx *client_ctx = client_data;
+
+ DBG("Entering %s\n", __func__);
+
+ if (!client_ctx) {
+ ERR("%s: client_ctx is NULL\n", __func__);
+ return;
+ }
+
+ client_ctx->event_status = status;
+
+ switch (event) {
+ case VCD_EVT_RESP_OPEN:
+ vid_enc_vcd_open_done(client_ctx, info);
+ break;
+ case VCD_EVT_RESP_INPUT_DONE:
+ case VCD_EVT_RESP_INPUT_FLUSHED:
+ vid_enc_input_frame_done(client_ctx, event, status, info);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ case VCD_EVT_RESP_OUTPUT_FLUSHED:
+ vid_enc_output_frame_done(client_ctx, event, status, info);
+ break;
+ case VCD_EVT_RESP_PAUSE:
+ case VCD_EVT_RESP_START:
+ case VCD_EVT_RESP_STOP:
+ case VCD_EVT_RESP_FLUSH_INPUT_DONE:
+ case VCD_EVT_RESP_FLUSH_OUTPUT_DONE:
+ case VCD_EVT_IND_RECONFIG:
+ case VCD_EVT_IND_HWERRFATAL:
+ case VCD_EVT_IND_RESOURCES_LOST:
+ vid_enc_lean_event(client_ctx, event, status);
+ break;
+ default:
+ ERR("%s: Error invalid event type %u\n", __func__, event);
+ break;
+ }
+}
+
+static u32 vid_enc_msg_pending(struct video_client_ctx *client_ctx)
+{
+ u32 islist_empty = 0;
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+ islist_empty = list_empty(&client_ctx->msg_queue);
+ mutex_unlock(&client_ctx->msg_queue_lock);
+
+ if (islist_empty) {
+ DBG("%s: vid_enc msg queue empty\n", __func__);
+ if (client_ctx->stop_msg) {
+ DBG("%s: List empty and Stop Msg set\n", __func__);
+ return client_ctx->stop_msg;
+ }
+ } else
+ DBG("%s: vid_enc msg queue Not empty\n", __func__);
+
+ return !islist_empty;
+}
+
+static u32 vid_enc_get_next_msg(struct video_client_ctx *client_ctx,
+ struct venc_msg *venc_msg_info)
+{
+ int rc;
+ struct vid_enc_msg *vid_enc_msg = NULL;
+
+ if (!client_ctx)
+ return false;
+
+ rc = wait_event_interruptible(client_ctx->msg_wait,
+ vid_enc_msg_pending(client_ctx));
+
+ if (rc < 0 || client_ctx->stop_msg) {
+ DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg);
+ return false;
+ }
+
+ mutex_lock(&client_ctx->msg_queue_lock);
+
+ if (!list_empty(&client_ctx->msg_queue)) {
+ DBG("%s: After Wait\n", __func__);
+ vid_enc_msg = list_first_entry(&client_ctx->msg_queue,
+ struct vid_enc_msg, list);
+ list_del(&vid_enc_msg->list);
+ memcpy(venc_msg_info, &vid_enc_msg->venc_msg_info,
+ sizeof(struct venc_msg));
+ kfree(vid_enc_msg);
+ }
+ mutex_unlock(&client_ctx->msg_queue_lock);
+ return true;
+}
+
+static u32 vid_enc_close_client(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ int rc;
+
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+ if (!client_ctx || !client_ctx->vcd_handle) {
+ ERR("%s: Invalid client_ctx\n", __func__);
+ return false;
+ }
+
+ mutex_lock(&vidc_enc_dev->lock);
+
+ if (!stop_cmd) {
+ vcd_status = vcd_stop(client_ctx->vcd_handle);
+ DBG("Waiting for VCD_STOP: Before Timeout\n");
+ if (!vcd_status) {
+ rc = wait_for_completion_timeout(&client_ctx->event,
+ 5 * HZ);
+ if (!rc) {
+ ERR("%s: ERROR vcd_stop time out %d\n",
+ __func__, rc);
+ }
+
+ if (client_ctx->event_status) {
+ ERR("%s :ERROR vcd_stop Not success\n",
+ __func__);
+ }
+ }
+ }
+ DBG("VCD_STOPPED: After Timeout, calling VCD_CLOSE\n");
+ vcd_status = vcd_close(client_ctx->vcd_handle);
+
+ if (vcd_status) {
+ mutex_unlock(&vidc_enc_dev->lock);
+ return false;
+ }
+
+ memset((void *)client_ctx, 0, sizeof(struct video_client_ctx));
+
+ vidc_enc_dev->num_clients--;
+ stop_cmd = 0;
+ mutex_unlock(&vidc_enc_dev->lock);
+ return true;
+}
+
+
+static int vid_enc_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ s32 client_index;
+ struct video_client_ctx *client_ctx;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+
+ mutex_lock(&vidc_enc_dev->lock);
+
+ stop_cmd = 0;
+ if (vidc_enc_dev->num_clients == VID_ENC_MAX_ENCODER_CLIENTS) {
+ ERR("ERROR: vid_enc_open() max number of clients limit reached"
+ "\n");
+ rc = -ENODEV;
+ goto out;
+ }
+
+#ifndef USE_RES_TRACKER
+ DBG("Resource Tracker not in use");
+ if (!vid_c_enable_clk(VID_C_HCLK_RATE)) {
+ ERR("ERROR: vid_enc_open() clock enabled failed\n");
+ rc = -ENODEV;
+ goto out;
+ }
+#endif
+
+ DBG("Virtual Address of ioremap is %p\n", vidc_enc_dev->virt_base);
+
+ if (!vidc_enc_dev->num_clients) {
+ rc = vcd_fw_prepare_all();
+ if (rc)
+ goto out;
+ }
+
+ client_index = vid_enc_get_empty_client_index();
+
+ if (client_index == -1) {
+ ERR("%s: No free clients client_index == -1\n", __func__);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ client_ctx = &vidc_enc_dev->venc_clients[client_index];
+ vidc_enc_dev->num_clients++;
+
+ init_completion(&client_ctx->event);
+ mutex_init(&client_ctx->msg_queue_lock);
+ INIT_LIST_HEAD(&client_ctx->msg_queue);
+ init_waitqueue_head(&client_ctx->msg_wait);
+ vcd_status = vcd_open(vidc_enc_dev->device_handle, false,
+ vid_enc_vcd_cb, client_ctx);
+ client_ctx->stop_msg = 0;
+
+ wait_for_completion(&client_ctx->event);
+ file->private_data = client_ctx;
+
+out:
+ mutex_unlock(&vidc_enc_dev->lock);
+ return rc;
+}
+
+static int vid_enc_release(struct inode *inode, struct file *file)
+{
+ struct video_client_ctx *client_ctx = file->private_data;
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+ vid_enc_close_client(client_ctx);
+#ifndef USE_RES_TRACKER
+ vid_c_disable_clk();
+#endif
+ INFO("msm_vidc_enc: Return from %s\n", __func__);
+ return 0;
+}
+
+static const struct file_operations vid_enc_fops = {
+ .owner = THIS_MODULE,
+ .open = vid_enc_open,
+ .release = vid_enc_release,
+ .ioctl = vid_enc_ioctl
+};
+
+void vid_enc_interrupt_deregister(void)
+{
+}
+
+void vid_enc_interrupt_register(void *device_name)
+{
+}
+
+void vid_enc_interrupt_clear(void)
+{
+}
+
+void *vid_enc_map_dev_base_addr(void *device_name)
+{
+ return vidc_enc_dev->virt_base;
+}
+
+static int vid_enc_vcd_init(void)
+{
+ int rc;
+ struct vcd_init_config vcd_init_config;
+ u32 i;
+
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+ vidc_enc_dev->num_clients = 0;
+
+ for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++)
+ memset((void *)&vidc_enc_dev->venc_clients[i], 0,
+ sizeof(vidc_enc_dev->venc_clients[i]));
+
+ mutex_init(&vidc_enc_dev->lock);
+ vidc_enc_dev->virt_base = vid_c_get_ioaddr();
+
+ if (!vidc_enc_dev->virt_base) {
+ ERR("%s: ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ vcd_init_config.device_name = "VID_C";
+ vcd_init_config.pf_map_dev_base_addr = vid_enc_map_dev_base_addr;
+ vcd_init_config.pf_interrupt_clr = vid_enc_interrupt_clear;
+ vcd_init_config.pf_register_isr = vid_enc_interrupt_register;
+ vcd_init_config.pf_deregister_isr = vid_enc_interrupt_deregister;
+
+ rc = vcd_init(&vcd_init_config, &vidc_enc_dev->device_handle);
+
+ if (rc) {
+ ERR("%s: vcd_init failed\n", __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __init vid_enc_init(void)
+{
+ int rc = 0;
+ struct device *class_devp;
+
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+ vidc_enc_dev = kzalloc(sizeof(struct vid_enc_dev), GFP_KERNEL);
+ if (!vidc_enc_dev) {
+ ERR("%s Unable to allocate memory for vid_enc_dev\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = alloc_chrdev_region(&vidc_enc_dev_num, 0, 1, VID_ENC_NAME);
+ if (rc < 0) {
+ ERR("%s: alloc_chrdev_region Failed rc = %d\n", __func__, rc);
+ goto error_vid_enc_alloc_chrdev_region;
+ }
+
+ vid_enc_class = class_create(THIS_MODULE, VID_ENC_NAME);
+ if (IS_ERR(vid_enc_class)) {
+ rc = PTR_ERR(vid_enc_class);
+ ERR("%s: couldn't create vid_enc_class %d\n", __func__, rc);
+ goto error_vid_enc_class_create;
+ }
+
+ class_devp = device_create(vid_enc_class, NULL, vidc_enc_dev_num, NULL,
+ VID_ENC_NAME);
+ if (IS_ERR(class_devp)) {
+ rc = PTR_ERR(class_devp);
+ ERR("%s: class device_create failed %d\n", __func__, rc);
+ goto error_vid_enc_class_device_create;
+ }
+
+ vidc_enc_dev->device = class_devp;
+
+ cdev_init(&vidc_enc_dev->cdev, &vid_enc_fops);
+ vidc_enc_dev->cdev.owner = THIS_MODULE;
+ rc = cdev_add(&vidc_enc_dev->cdev, vidc_enc_dev_num, 1);
+
+ if (rc < 0) {
+ ERR("%s: cdev_add failed %d\n", __func__, rc);
+ goto error_vid_enc_cdev_add;
+ }
+ vid_enc_vcd_init();
+ return 0;
+
+error_vid_enc_cdev_add:
+ device_destroy(vid_enc_class, vidc_enc_dev_num);
+error_vid_enc_class_device_create:
+ class_destroy(vid_enc_class);
+error_vid_enc_class_create:
+ unregister_chrdev_region(vidc_enc_dev_num, 1);
+error_vid_enc_alloc_chrdev_region:
+ kfree(vidc_enc_dev);
+
+ return rc;
+}
+
+static void __exit vid_enc_exit(void)
+{
+ INFO("msm_vidc_enc: Inside %s\n", __func__);
+ cdev_del(&vidc_enc_dev->cdev);
+ device_destroy(vid_enc_class, vidc_enc_dev_num);
+ class_destroy(vid_enc_class);
+ unregister_chrdev_region(vidc_enc_dev_num, 1);
+ kfree(vidc_enc_dev);
+ INFO("msm_vidc_enc: Return from %s\n", __func__);
+}
+
+static int vid_enc_ioctl(struct inode *inode, struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ void __user *u_arg = (void __user *)arg;
+ struct video_client_ctx *client_ctx;
+ struct venc_ioctl_msg venc_msg;
+ u32 result = true;
+
+ DBG("%s\n", __func__);
+
+ client_ctx = file->private_data;
+ if (!client_ctx) {
+ ERR("!client_ctx. Cannot attach to device handle\n");
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case VEN_IOCTL_CMD_READ_NEXT_MSG:
+ {
+ struct venc_msg cb_msg;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+ DBG("VEN_IOCTL_CMD_READ_NEXT_MSG\n");
+ result = vid_enc_get_next_msg(client_ctx, &cb_msg);
+ if (!result) {
+ ERR("VEN_IOCTL_CMD_READ_NEXT_MSG failed\n");
+ return -EIO;
+ }
+ if (copy_to_user(venc_msg.out, &cb_msg, sizeof(cb_msg)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_CMD_STOP_READ_MSG:
+ DBG("VEN_IOCTL_CMD_STOP_READ_MSG\n");
+ client_ctx->stop_msg = 1;
+ wake_up(&client_ctx->msg_wait);
+ break;
+ case VEN_IOCTL_CMD_ENCODE_FRAME:
+ case VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER:
+ {
+ struct venc_buffer enc_buf;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_CMD_ENCODE_FRAME/"
+ "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER\n");
+
+ if (copy_from_user(&enc_buf, venc_msg.in, sizeof(enc_buf)))
+ return -EFAULT;
+
+ if (cmd == VEN_IOCTL_CMD_ENCODE_FRAME)
+ result = vid_enc_encode_frame(client_ctx, &enc_buf);
+ else
+ result = vid_enc_fill_output_buffer(client_ctx,
+ &enc_buf);
+
+ if (!result) {
+ DBG("VEN_IOCTL_CMD_ENCODE_FRAME/"
+ "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_SET_INPUT_BUFFER:
+ case VEN_IOCTL_SET_OUTPUT_BUFFER:
+ {
+ struct venc_bufferpayload buf_info;
+ enum venc_buffer_dir buf_dir;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_INPUT_BUFFER/VEN_IOCTL_SET_OUTPUT_BUFFER\n");
+
+ if (copy_from_user(&buf_info, venc_msg.in, sizeof(buf_info)))
+ return -EFAULT;
+
+ buf_dir = VEN_BUFFER_TYPE_INPUT;
+ if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER)
+ buf_dir = VEN_BUFFER_TYPE_OUTPUT;
+
+ result = vid_enc_set_buffer(client_ctx, &buf_info, buf_dir);
+ if (!result) {
+ DBG("VEN_IOCTL_SET_INPUT_BUFFER"
+ "/VEN_IOCTL_SET_OUTPUT_BUFFER failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_SET_INPUT_BUFFER_REQ:
+ case VEN_IOCTL_SET_OUTPUT_BUFFER_REQ:
+ {
+ struct venc_allocatorproperty alloc;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_INPUT_BUFFER_REQ"
+ "/VEN_IOCTL_SET_OUTPUT_BUFFER_REQ\n");
+
+ if (copy_from_user(&alloc, venc_msg.in, sizeof(alloc)))
+ return -EFAULT;
+
+ if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER_REQ)
+ result = vid_enc_set_buffer_req(client_ctx, &alloc,
+ false);
+ else
+ result = vid_enc_set_buffer_req(client_ctx, &alloc,
+ true);
+ if (!result) {
+ DBG("setting VEN_IOCTL_SET_OUTPUT_BUFFER_REQ/"
+ "VEN_IOCTL_SET_INPUT_BUFFER_REQ failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_INPUT_BUFFER_REQ:
+ case VEN_IOCTL_GET_OUTPUT_BUFFER_REQ:
+ {
+ struct venc_allocatorproperty alloc;
+
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_INPUT_BUFFER_REQ/"
+ "VEN_IOCTL_GET_OUTPUT_BUFFER_REQ\n");
+
+ if (cmd == VEN_IOCTL_GET_OUTPUT_BUFFER_REQ)
+ result = vid_enc_get_buffer_req(client_ctx, &alloc,
+ false);
+ else
+ result = vid_enc_get_buffer_req(client_ctx, &alloc,
+ true);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &alloc, sizeof(alloc)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_CMD_FLUSH:
+ {
+ struct venc_bufferflush buf_flush;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_CMD_FLUSH\n");
+
+ if (copy_from_user(&buf_flush, venc_msg.in, sizeof(buf_flush)))
+ return -EFAULT;
+
+ INFO("%s: Calling vid_enc_flush with mode = %lu\n", __func__,
+ buf_flush.flush_mode);
+ result = vid_enc_flush(client_ctx, &buf_flush);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_FLUSH failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_CMD_START:
+ INFO("%s: Executing VEN_IOCTL_CMD_START\n", __func__);
+ result = vid_enc_start(client_ctx);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_START failed\n");
+ return -EIO;
+ }
+ break;
+ case VEN_IOCTL_CMD_STOP:
+ INFO("%s: Executing VEN_IOCTL_CMD_STOP", __func__);
+ result = vid_enc_stop(client_ctx);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_STOP failed\n");
+ return -EIO;
+ }
+ stop_cmd = 1;
+ break;
+ case VEN_IOCTL_CMD_PAUSE:
+ INFO("%s: Executing VEN_IOCTL_CMD_PAUSE\n", __func__);
+ result = vid_enc_pause(client_ctx);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_PAUSE failed\n");
+ return -EIO;
+ }
+ break;
+ case VEN_IOCTL_CMD_RESUME:
+ INFO("%s: Executing VEN_IOCTL_CMD_RESUME\n", __func__);
+ result = vid_enc_resume(client_ctx);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_RESUME failed\n");
+ return -EIO;
+ }
+ break;
+ case VEN_IOCTL_SET_QP_RANGE:
+ {
+ struct venc_qprange qprange;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_QP_RANGE\n");
+
+ if (copy_from_user(&qprange, venc_msg.in, sizeof(qprange)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_qprange(client_ctx, &qprange, true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_QP_RANGE failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_QP_RANGE:
+ {
+ struct venc_qprange qprange;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_QP_RANGE\n");
+ result = vid_enc_set_get_qprange(client_ctx, &qprange, false);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &qprange, sizeof(qprange)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_HEC:
+ {
+ struct venc_headerextension ext;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_HEC\n");
+
+ if (copy_from_user(&ext, venc_msg.in, sizeof(ext)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_headerextension(client_ctx, &ext,
+ true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_HEC failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_HEC:
+ {
+ struct venc_headerextension ext;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_HEC\n");
+ result = vid_enc_set_get_headerextension(client_ctx, &ext,
+ false);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &ext, sizeof(ext)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_TARGET_BITRATE:
+ {
+ struct venc_targetbitrate rate;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_TARGET_BITRATE\n");
+
+ if (copy_from_user(&rate, venc_msg.in, sizeof(rate)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_bitrate(client_ctx, &rate, true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_TARGET_BITRATE failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_TARGET_BITRATE:
+ {
+ struct venc_targetbitrate rate;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_TARGET_BITRATE\n");
+ result = vid_enc_set_get_bitrate(client_ctx, &rate, false);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &rate, sizeof(rate)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_FRAME_RATE:
+ {
+ struct venc_framerate frm_rate;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_FRAME_RATE\n");
+
+ if (copy_from_user(&frm_rate, venc_msg.in, sizeof(frm_rate)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_framerate(client_ctx, &frm_rate,
+ true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_FRAME_RATE failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_FRAME_RATE:
+ {
+ struct venc_framerate frm_rate;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_FRAME_RATE\n");
+ result = vid_enc_set_get_framerate(client_ctx, &frm_rate,
+ false);
+
+ if (result) {
+ if (copy_to_user(venc_msg.out,
+ &frm_rate, sizeof(frm_rate)))
+ return -EFAULT;
+ } else
+ return -EIO;
+ break;
+ }
+ case VEN_IOCTL_SET_VOP_TIMING_CFG:
+ {
+ struct venc_voptimingcfg timing;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_VOP_TIMING_CFG\n");
+
+ if (copy_from_user(&timing, venc_msg.in, sizeof(timing)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_voptimingcfg(client_ctx, &timing,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_VOP_TIMING_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_VOP_TIMING_CFG:
+ {
+ struct venc_voptimingcfg timing;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_VOP_TIMING_CFG\n");
+ result = vid_enc_set_get_voptimingcfg(client_ctx, &timing,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &timing, sizeof(timing)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_RATE_CTRL_CFG:
+ {
+ struct venc_ratectrlcfg rate_ctrl;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n");
+
+ if (copy_from_user(&rate_ctrl, venc_msg.in, sizeof(rate_ctrl)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_ratectrlcfg(client_ctx, &rate_ctrl,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_RATE_CTRL_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_RATE_CTRL_CFG:
+ {
+ struct venc_ratectrlcfg rate_ctrl;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n");
+ result = vid_enc_set_get_ratectrlcfg(client_ctx, &rate_ctrl,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &rate_ctrl, sizeof(rate_ctrl)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_MULTI_SLICE_CFG:
+ {
+ struct venc_multiclicecfg slice;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_MULTI_SLICE_CFG\n");
+
+ if (copy_from_user(&slice, venc_msg.in, sizeof(slice)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_multiclicecfg(client_ctx, &slice,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_MULTI_SLICE_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_MULTI_SLICE_CFG:
+ {
+ struct venc_multiclicecfg slice;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_MULTI_SLICE_CFG\n");
+ result = vid_enc_set_get_multiclicecfg(client_ctx, &slice,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &slice, sizeof(slice)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_INTRA_REFRESH:
+ {
+ struct venc_intrarefresh refresh;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_INTRA_REFRESH\n");
+
+ if (copy_from_user(&refresh, venc_msg.in, sizeof(refresh)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_intrarefresh(client_ctx, &refresh,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_INTRA_REFRESH failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_INTRA_REFRESH:
+ {
+ struct venc_intrarefresh refresh;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n");
+ result = vid_enc_set_get_intrarefresh(client_ctx, &refresh,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &refresh, sizeof(refresh)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_DEBLOCKING_CFG:
+ {
+ struct venc_dbcfg dbcfg;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_DEBLOCKING_CFG\n");
+
+ if (copy_from_user(&dbcfg, venc_msg.in, sizeof(dbcfg)))
+ return -EFAULT;
+ result = vid_enc_set_get_dbcfg(client_ctx, &dbcfg, true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_DEBLOCKING_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_DEBLOCKING_CFG:
+ {
+ struct venc_dbcfg dbcfg;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n");
+ result = vid_enc_set_get_dbcfg(client_ctx, &dbcfg, false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &dbcfg, sizeof(dbcfg)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_ENTROPY_CFG:
+ {
+ struct venc_entropycfg entropy;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_ENTROPY_CFG\n");
+
+ if (copy_from_user(&entropy, venc_msg.in, sizeof(entropy)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_entropy_cfg(client_ctx, &entropy,
+ true);
+
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_ENTROPY_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_ENTROPY_CFG:
+ {
+ struct venc_entropycfg entropy;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_ENTROPY_CFG\n");
+
+ result = vid_enc_set_get_entropy_cfg(client_ctx, &entropy,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &entropy, sizeof(entropy)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_GET_SEQUENCE_HDR:
+ {
+ int rc = 0;
+ struct venc_seqheader hdr;
+ struct venc_seqheader hdr_user;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_SEQUENCE_HDR\n");
+
+ if (copy_from_user(&hdr, venc_msg.in, sizeof(hdr)))
+ return -EFAULT;
+ if (copy_from_user(&hdr_user, venc_msg.in, sizeof(hdr_user)))
+ return -EFAULT;
+
+ hdr.buf = NULL;
+ result = vid_enc_get_sequence_header(client_ctx, &hdr);
+ if (!result)
+ rc = -EIO;
+ if (!rc || copy_to_user(hdr_user.buf, hdr.buf, hdr.hdr_len))
+ rc = -EFAULT;
+ if (!rc || copy_to_user(&hdr_user.hdr_len, &hdr.hdr_len,
+ sizeof(hdr.hdr_len)))
+ rc = -EFAULT;
+
+ kfree(hdr.buf);
+ hdr.buf = NULL;
+ if (rc)
+ return rc;
+ break;
+ }
+ case VEN_IOCTL_GET_CAPABILITY:
+ return -EIO;
+ case VEN_IOCTL_CMD_REQUEST_IFRAME:
+ result = vid_enc_request_iframe(client_ctx);
+ if (!result) {
+ ERR("setting VEN_IOCTL_CMD_REQUEST_IFRAME failed\n");
+ return -EIO;
+ }
+ break;
+ case VEN_IOCTL_SET_INTRA_PERIOD:
+ {
+ struct venc_intraperiod period;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_INTRA_PERIOD\n");
+
+ if (copy_from_user(&period, venc_msg.in, sizeof(period)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_intraperiod(client_ctx, &period,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_INTRA_PERIOD failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_INTRA_PERIOD:
+ {
+ struct venc_intraperiod period;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_SESSION_QP\n");
+
+ result = vid_enc_set_get_intraperiod(client_ctx, &period,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &period, sizeof(period)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_SESSION_QP:
+ {
+ struct venc_sessionqp qp;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_SESSION_QP\n");
+
+ if (copy_from_user(&qp, venc_msg.in, sizeof(qp)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_session_qp(client_ctx, &qp, true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_SESSION_QP failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_SESSION_QP:
+ {
+ struct venc_sessionqp qp;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_SESSION_QP\n");
+
+ result = vid_enc_set_get_session_qp(client_ctx, &qp, false);
+
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &qp, sizeof(qp)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_PROFILE_LEVEL:
+ {
+ struct ven_profilelevel level;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_PROFILE_LEVEL\n");
+
+ if (copy_from_user(&level, venc_msg.in, sizeof(level)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_profile_level(client_ctx, &level,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_PROFILE_LEVEL failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_PROFILE_LEVEL:
+ {
+ struct ven_profilelevel level;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_CODEC_PROFILE\n");
+
+ result = vid_enc_set_get_profile_level(client_ctx, &level,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &level, sizeof(level)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_CODEC_PROFILE:
+ {
+ struct venc_profile profile;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_SET_CODEC_PROFILE\n");
+
+ if (copy_from_user(&profile, venc_msg.in, sizeof(profile)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_profile(client_ctx, &profile, true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_CODEC_PROFILE failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_CODEC_PROFILE:
+ {
+ struct venc_profile profile;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_CODEC_PROFILE\n");
+
+ result = vid_enc_set_get_profile(client_ctx, &profile, false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &profile, sizeof(profile)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_SHORT_HDR:
+ {
+ struct venc_switch enc_switch;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("Getting VEN_IOCTL_SET_SHORT_HDR\n");
+
+ if (copy_from_user(&enc_switch, venc_msg.in,
+ sizeof(enc_switch)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_short_header(client_ctx, &enc_switch,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_SHORT_HDR failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_SHORT_HDR:
+ {
+ struct venc_switch enc_switch;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_LIVE_MODE\n");
+
+ result = vid_enc_set_get_short_header(client_ctx, &enc_switch,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &enc_switch, sizeof(enc_switch)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_BASE_CFG:
+ {
+ struct venc_basecfg base;
+ DBG("VEN_IOCTL_SET_BASE_CFG\n");
+
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ if (copy_from_user(&base, venc_msg.in, sizeof(base)))
+ return -EFAULT;
+
+ DBG("setting VEN_IOCTL_SET_BASE_CFG\n");
+
+ result = vid_enc_set_get_base_cfg(client_ctx, &base, true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_BASE_CFG failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_BASE_CFG:
+ {
+ struct venc_basecfg base;
+ DBG("VEN_IOCTL_GET_BASE_CFG\n");
+
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("Getting VEN_IOCTL_SET_BASE_CFG\n");
+
+ result = vid_enc_set_get_base_cfg(client_ctx, &base, false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &base, sizeof(base)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_LIVE_MODE:
+ {
+ struct venc_switch enc_switch;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("Getting VEN_IOCTL_SET_LIVE_MODE\n");
+
+ if (copy_from_user(&enc_switch, venc_msg.in, sizeof(enc_switch)))
+ return -EFAULT;
+
+ result = vid_enc_set_get_live_mode(client_ctx, &enc_switch,
+ true);
+ if (!result) {
+ ERR("setting VEN_IOCTL_SET_LIVE_MODE failed\n");
+ return -EIO;
+ }
+ break;
+ }
+ case VEN_IOCTL_GET_LIVE_MODE:
+ {
+ struct venc_switch enc_switch;
+ if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg)))
+ return -EFAULT;
+
+ DBG("VEN_IOCTL_GET_LIVE_MODE\n");
+
+ result = vid_enc_set_get_live_mode(client_ctx, &enc_switch,
+ false);
+ if (!result)
+ return -EIO;
+ if (copy_to_user(venc_msg.out, &enc_switch, sizeof(enc_switch)))
+ return -EFAULT;
+ break;
+ }
+ case VEN_IOCTL_SET_AC_PREDICTION:
+ case VEN_IOCTL_GET_AC_PREDICTION:
+ case VEN_IOCTL_SET_RVLC:
+ case VEN_IOCTL_GET_RVLC:
+ case VEN_IOCTL_SET_ROTATION:
+ case VEN_IOCTL_GET_ROTATION:
+ case VEN_IOCTL_SET_DATA_PARTITION:
+ case VEN_IOCTL_GET_DATA_PARTITION:
+ default:
+ ERR("%s: Unsupported ioctl %d\n", __func__, cmd);
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video encoder driver");
+MODULE_VERSION("1.0");
+
+module_init(vid_enc_init);
+module_exit(vid_enc_exit);
diff --git a/drivers/misc/video_core/720p/enc/venc_internal.c b/drivers/misc/video_core/720p/enc/venc_internal.c
new file mode 100644
index 0000000..11e8d66
--- /dev/null
+++ b/drivers/misc/video_core/720p/enc/venc_internal.c
@@ -0,0 +1,1576 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+
+#include "video_core_type.h"
+#include "vcd_api.h"
+#include "venc_internal.h"
+#include "video_core_init.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define ERR(x...) printk(KERN_ERR x)
+
+u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx,
+ struct venc_basecfg *config, u32 set_flag)
+{
+ struct venc_targetbitrate venc_bitrate;
+ struct venc_framerate frame_rate;
+ u32 current_codec_type;
+
+ if (!client_ctx || !config)
+ return false;
+
+ if (!vid_enc_set_get_codec(client_ctx, ¤t_codec_type, false))
+ return false;
+
+ DBG("%s: Current Codec Type = %u\n", __func__, current_codec_type);
+ if (current_codec_type != config->codectype) {
+ if (!vid_enc_set_get_codec(client_ctx, &config->codectype,
+ set_flag))
+ return false;
+ }
+
+ if (!vid_enc_set_get_inputformat(client_ctx, &config->inputformat,
+ set_flag))
+ return false;
+
+ if (!vid_enc_set_get_framesize(client_ctx, &config->input_height,
+ &config->input_width, set_flag))
+ return false;
+
+ if (set_flag)
+ venc_bitrate.target_bitrate = config->targetbitrate;
+
+ if (!vid_enc_set_get_bitrate(client_ctx, &venc_bitrate, set_flag))
+ return false;
+
+ if (!set_flag)
+ config->targetbitrate = venc_bitrate.target_bitrate;
+
+ if (set_flag) {
+ frame_rate.fps_denominator = config->fps_den;
+ frame_rate.fps_numerator = config->fps_num;
+ }
+
+ if (!vid_enc_set_get_framerate(client_ctx, &frame_rate, set_flag))
+ return false;
+
+ if (!set_flag) {
+ config->fps_den = frame_rate.fps_denominator;
+ config->fps_num = frame_rate.fps_numerator;
+ }
+
+ return true;
+}
+
+u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx,
+ u32 *input_format, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_buffer_format format_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+ u32 status = true;
+
+ if (!client_ctx || !input_format)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_BUFFER_FORMAT;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_format);
+
+ if (set_flag) {
+ switch (*input_format) {
+ case VEN_INPUTFMT_NV12:
+ format_type.buffer_format = VCD_BUFFER_FORMAT_NV12;
+ break;
+ case VEN_INPUTFMT_NV21:
+ format_type.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2;
+ break;
+ default:
+ status = false;
+ break;
+ }
+
+ if (!status)
+ return status;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &format_type);
+ if (vcd_status) {
+ status = false;
+ ERR("%s(): Set VCD_I_BUFFER_FORMAT Failed\n", __func__);
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &format_type);
+
+ if (vcd_status) {
+ status = false;
+ ERR("%s(): Get VCD_I_BUFFER_FORMAT Failed\n", __func__);
+ return status;
+ }
+ switch (format_type.buffer_format) {
+ case VCD_BUFFER_FORMAT_NV12:
+ *input_format = VEN_INPUTFMT_NV12;
+ break;
+ case VCD_BUFFER_FORMAT_TILE_4x2:
+ *input_format = VEN_INPUTFMT_NV21;
+ break;
+ default:
+ status = false;
+ break;
+ }
+ }
+ return status;
+}
+
+u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type,
+ u32 set_flag)
+{
+ struct vcd_property_codec vcd_property_codec;
+ struct vcd_property_hdr vcd_property_hdr;
+ u32 vcd_status = VCD_ERR_FAIL;
+ u32 status = true;
+
+ if (!client_ctx || !codec_type)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_CODEC;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_codec);
+
+ if (set_flag) {
+ switch (*codec_type) {
+ case VEN_CODEC_MPEG4:
+ vcd_property_codec.codec = VCD_CODEC_MPEG4;
+ break;
+ case VEN_CODEC_H263:
+ vcd_property_codec.codec = VCD_CODEC_H263;
+ break;
+ case VEN_CODEC_H264:
+ vcd_property_codec.codec = VCD_CODEC_H264;
+ break;
+ default:
+ status = false;
+ break;
+ }
+
+ if (!status)
+ return status;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_property_codec);
+ if (vcd_status) {
+ status = false;
+ ERR("%s: Set VCD_I_CODEC Failed\n", __func__);
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_property_codec);
+
+ if (vcd_status) {
+ status = false;
+ ERR("%s(): Get VCD_I_CODEC Failed\n", __func__);
+ return status;
+ }
+ switch (vcd_property_codec.codec) {
+ case VCD_CODEC_H263:
+ *codec_type = VEN_CODEC_H263;
+ break;
+ case VCD_CODEC_H264:
+ *codec_type = VEN_CODEC_H264;
+ break;
+ case VCD_CODEC_MPEG4:
+ *codec_type = VEN_CODEC_MPEG4;
+ break;
+ case VCD_CODEC_DIVX_3:
+ case VCD_CODEC_DIVX_4:
+ case VCD_CODEC_DIVX_5:
+ case VCD_CODEC_DIVX_6:
+ case VCD_CODEC_MPEG1:
+ case VCD_CODEC_MPEG2:
+ case VCD_CODEC_VC1:
+ case VCD_CODEC_VC1_RCV:
+ case VCD_CODEC_XVID:
+ default:
+ status = false;
+ break;
+ }
+ }
+ return status;
+}
+
+u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, u32 *height,
+ u32 *width, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_frame_size frame_size;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !height || !width)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_FRAME_SIZE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
+
+ if (set_flag) {
+ frame_size.height = *height;
+ frame_size.width = *width;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &frame_size);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_FRAME_SIZE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &frame_size);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_FRAME_SIZE Failed\n", __func__);
+ return false;
+ }
+ *height = frame_size.height;
+ *width = frame_size.width;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx,
+ struct venc_targetbitrate *venc_bitrate, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_target_bitrate bit_rate;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !venc_bitrate)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_TARGET_BITRATE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_target_bitrate);
+ if (set_flag) {
+ bit_rate.target_bitrate = venc_bitrate->target_bitrate;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &bit_rate);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_TARGET_BITRATE Failed\n",
+ __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &bit_rate);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_TARGET_BITRATE Failed\n",
+ __func__);
+ return false;
+ }
+ venc_bitrate->target_bitrate = bit_rate.target_bitrate;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx,
+ struct venc_framerate *frame_rate, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_frame_rate vcd_frame_rate;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !frame_rate)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_FRAME_RATE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_frame_rate);
+
+ if (set_flag) {
+ vcd_frame_rate.fps_denominator = frame_rate->fps_denominator;
+ vcd_frame_rate.fps_numerator = frame_rate->fps_numerator;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_frame_rate);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_FRAME_RATE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_frame_rate);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_FRAME_RATE Failed\n", __func__);
+ return false;
+ }
+ frame_rate->fps_denominator = vcd_frame_rate.fps_denominator;
+ frame_rate->fps_numerator = vcd_frame_rate.fps_numerator;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx,
+ struct venc_switch *encoder_switch, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_live live_mode;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_LIVE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_live);
+
+ if (set_flag) {
+ live_mode.live = 1;
+ if (!encoder_switch->status)
+ live_mode.live = 0;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &live_mode);
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_LIVE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &live_mode);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_LIVE Failed\n", __func__);
+ return false;
+ }
+ encoder_switch->status = 1;
+ if (!live_mode.live)
+ encoder_switch->status = 0;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx,
+ struct venc_switch *encoder_switch, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_short_header short_header;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !encoder_switch)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_SHORT_HEADER;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_short_header);
+
+ if (set_flag) {
+ short_header.short_header = (u32) encoder_switch->status;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &short_header);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_SHORT_HEADER Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &short_header);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_SHORT_HEADER Failed\n", __func__);
+ return false;
+ }
+ encoder_switch->status = (u8)short_header.short_header;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx,
+ struct venc_profile *profile, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_profile profile_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+ u32 status = true;
+
+ if (!client_ctx || !profile)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_PROFILE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_profile);
+
+ if (set_flag) {
+ switch (profile->profile) {
+ case VEN_PROFILE_MPEG4_SP:
+ profile_type.profile = VCD_PROFILE_MPEG4_SP;
+ break;
+ case VEN_PROFILE_MPEG4_ASP:
+ profile_type.profile = VCD_PROFILE_MPEG4_ASP;
+ break;
+ case VEN_PROFILE_H264_BASELINE:
+ profile_type.profile = VCD_PROFILE_H264_BASELINE;
+ break;
+ case VEN_PROFILE_H264_MAIN:
+ profile_type.profile = VCD_PROFILE_H264_MAIN;
+ break;
+ case VEN_PROFILE_H264_HIGH:
+ profile_type.profile = VCD_PROFILE_H264_HIGH;
+ break;
+ case VEN_PROFILE_H263_BASELINE:
+ profile_type.profile = VCD_PROFILE_H263_BASELINE;
+ break;
+ default:
+ status = false;
+ break;
+ }
+
+ if (!status)
+ return status;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &profile_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_PROFILE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &profile_type);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_PROFILE Failed\n", __func__);
+ return false;
+ }
+ switch (profile_type.profile) {
+ case VCD_PROFILE_H263_BASELINE:
+ profile->profile = VEN_PROFILE_H263_BASELINE;
+ break;
+ case VCD_PROFILE_H264_BASELINE:
+ profile->profile = VEN_PROFILE_H264_BASELINE;
+ break;
+ case VCD_PROFILE_H264_HIGH:
+ profile->profile = VEN_PROFILE_H264_HIGH;
+ break;
+ case VCD_PROFILE_H264_MAIN:
+ profile->profile = VEN_PROFILE_H264_MAIN;
+ break;
+ case VCD_PROFILE_MPEG4_ASP:
+ profile->profile = VEN_PROFILE_MPEG4_ASP;
+ break;
+ case VCD_PROFILE_MPEG4_SP:
+ profile->profile = VEN_PROFILE_MPEG4_SP;
+ break;
+ default:
+ status = false;
+ break;
+ }
+ }
+ return status;
+}
+
+u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx,
+ struct ven_profilelevel *profile_level, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_level level_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !profile_level)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_LEVEL;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_level);
+
+ if (set_flag) {
+ switch (profile_level->level) {
+ //TODO: collapse this crap
+ case VEN_LEVEL_MPEG4_0:
+ level_type.level = VCD_LEVEL_MPEG4_0;
+ break;
+ case VEN_LEVEL_MPEG4_1:
+ level_type.level = VCD_LEVEL_MPEG4_1;
+ break;
+ case VEN_LEVEL_MPEG4_2:
+ level_type.level = VCD_LEVEL_MPEG4_2;
+ break;
+ case VEN_LEVEL_MPEG4_3:
+ level_type.level = VCD_LEVEL_MPEG4_3;
+ break;
+ case VEN_LEVEL_MPEG4_4:
+ level_type.level = VCD_LEVEL_MPEG4_4;
+ break;
+ case VEN_LEVEL_MPEG4_5:
+ level_type.level = VCD_LEVEL_MPEG4_5;
+ break;
+ case VEN_LEVEL_MPEG4_3b:
+ level_type.level = VCD_LEVEL_MPEG4_3b;
+ break;
+ case VEN_LEVEL_MPEG4_6:
+ level_type.level = VCD_LEVEL_MPEG4_6;
+ break;
+ case VEN_LEVEL_H264_1:
+ level_type.level = VCD_LEVEL_H264_1;
+ break;
+ case VEN_LEVEL_H264_1b:
+ level_type.level = VCD_LEVEL_H264_1b;
+ break;
+ case VEN_LEVEL_H264_1p1:
+ level_type.level = VCD_LEVEL_H264_1p1;
+ break;
+ case VEN_LEVEL_H264_1p2:
+ level_type.level = VCD_LEVEL_H264_1p2;
+ break;
+ case VEN_LEVEL_H264_1p3:
+ level_type.level = VCD_LEVEL_H264_1p3;
+ break;
+ case VEN_LEVEL_H264_2:
+ level_type.level = VCD_LEVEL_H264_2;
+ break;
+ case VEN_LEVEL_H264_2p1:
+ level_type.level = VCD_LEVEL_H264_2p1;
+ break;
+ case VEN_LEVEL_H264_2p2:
+ level_type.level = VCD_LEVEL_H264_2p2;
+ break;
+ case VEN_LEVEL_H264_3:
+ level_type.level = VCD_LEVEL_H264_3;
+ break;
+ case VEN_LEVEL_H264_3p1:
+ level_type.level = VCD_LEVEL_H264_3p1;
+ break;
+ case VEN_LEVEL_H263_10:
+ level_type.level = VCD_LEVEL_H263_10;
+ break;
+ case VEN_LEVEL_H263_20:
+ level_type.level = VCD_LEVEL_H263_20;
+ break;
+ case VEN_LEVEL_H263_30:
+ level_type.level = VCD_LEVEL_H263_30;
+ break;
+ case VEN_LEVEL_H263_40:
+ level_type.level = VCD_LEVEL_H263_40;
+ break;
+ case VEN_LEVEL_H263_45:
+ level_type.level = VCD_LEVEL_H263_45;
+ break;
+ case VEN_LEVEL_H263_50:
+ level_type.level = VCD_LEVEL_H263_50;
+ break;
+ case VEN_LEVEL_H263_60:
+ level_type.level = VCD_LEVEL_H263_60;
+ break;
+ case VEN_LEVEL_H263_70:
+ level_type.level = VCD_LEVEL_H263_70;
+ break;
+ default:
+ return false;
+ }
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &level_type);
+
+ if (vcd_status) {
+ ERR("%s: Set VCD_I_LEVEL Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &level_type);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_LEVEL Failed\n", __func__);
+ return false;
+ }
+ switch (level_type.level) {
+ case VCD_LEVEL_MPEG4_0:
+ profile_level->level = VEN_LEVEL_MPEG4_0;
+ break;
+ case VCD_LEVEL_MPEG4_1:
+ profile_level->level = VEN_LEVEL_MPEG4_1;
+ break;
+ case VCD_LEVEL_MPEG4_2:
+ profile_level->level = VEN_LEVEL_MPEG4_2;
+ break;
+ case VCD_LEVEL_MPEG4_3:
+ profile_level->level = VEN_LEVEL_MPEG4_3;
+ break;
+ case VCD_LEVEL_MPEG4_4:
+ profile_level->level = VEN_LEVEL_MPEG4_4;
+ break;
+ case VCD_LEVEL_MPEG4_5:
+ profile_level->level = VEN_LEVEL_MPEG4_5;
+ break;
+ case VCD_LEVEL_MPEG4_3b:
+ profile_level->level = VEN_LEVEL_MPEG4_3b;
+ break;
+ case VCD_LEVEL_H264_1:
+ profile_level->level = VEN_LEVEL_H264_1;
+ break;
+ case VCD_LEVEL_H264_1b:
+ profile_level->level = VEN_LEVEL_H264_1b;
+ break;
+ case VCD_LEVEL_H264_1p1:
+ profile_level->level = VEN_LEVEL_H264_1p1;
+ break;
+ case VCD_LEVEL_H264_1p2:
+ profile_level->level = VEN_LEVEL_H264_1p2;
+ break;
+ case VCD_LEVEL_H264_1p3:
+ profile_level->level = VEN_LEVEL_H264_1p3;
+ break;
+ case VCD_LEVEL_H264_2:
+ profile_level->level = VEN_LEVEL_H264_2;
+ break;
+ case VCD_LEVEL_H264_2p1:
+ profile_level->level = VEN_LEVEL_H264_2p1;
+ break;
+ case VCD_LEVEL_H264_2p2:
+ profile_level->level = VEN_LEVEL_H264_2p2;
+ break;
+ case VCD_LEVEL_H264_3:
+ profile_level->level = VEN_LEVEL_H264_3;
+ break;
+ case VCD_LEVEL_H264_3p1:
+ profile_level->level = VEN_LEVEL_H264_3p1;
+ break;
+ case VCD_LEVEL_H264_3p2:
+ case VCD_LEVEL_H264_4:
+ return false;
+ case VCD_LEVEL_H263_10:
+ profile_level->level = VEN_LEVEL_H263_10;
+ break;
+ case VCD_LEVEL_H263_20:
+ profile_level->level = VEN_LEVEL_H263_20;
+ break;
+ case VCD_LEVEL_H263_30:
+ profile_level->level = VEN_LEVEL_H263_30;
+ break;
+ case VCD_LEVEL_H263_40:
+ profile_level->level = VEN_LEVEL_H263_40;
+ break;
+ case VCD_LEVEL_H263_45:
+ profile_level->level = VEN_LEVEL_H263_45;
+ break;
+ case VCD_LEVEL_H263_50:
+ profile_level->level = VEN_LEVEL_H263_50;
+ break;
+ case VCD_LEVEL_H263_60:
+ profile_level->level = VEN_LEVEL_H263_60;
+ break;
+ case VCD_LEVEL_H263_70:
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx,
+ struct venc_sessionqp *session_qp, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_session_qp qp_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !session_qp)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_SESSION_QP;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_session_qp);
+
+ if (set_flag) {
+ qp_type.iframe_qp = session_qp->iframeqp;
+ qp_type.frame_qp = session_qp->pframqp;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &qp_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_SESSION_QP Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &qp_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_SESSION_QP Failed\n", __func__);
+ return false;
+ }
+ session_qp->iframeqp = qp_type.iframe_qp;
+ session_qp->pframqp = qp_type.frame_qp;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx,
+ struct venc_intraperiod *intraperiod, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_i_period period_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !intraperiod)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_INTRA_PERIOD;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_i_period);
+
+ if (set_flag) {
+ period_type.frames = intraperiod->num_pframes;
+ period_type.bframes = 0;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &period_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_INTRA_PERIOD Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &period_type);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_INTRA_PERIOD Failed\n", __func__);
+ return false;
+ }
+ intraperiod->num_pframes = period_type.frames;
+ }
+ return true;
+}
+
+u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_req_i_frame request;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_REQ_IFRAME;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_req_i_frame);
+ request.req_i_frame = 1;
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+ &request);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_REQ_IFRAME Failed\n", __func__);
+ return false;
+ }
+ return true;
+}
+
+u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx,
+ struct venc_seqheader *seq_header)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_sequence_hdr hdr_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !seq_header || !seq_header->buf_sz)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_SEQ_HEADER;
+ vcd_property_hdr.sz = sizeof(struct vcd_sequence_hdr);
+
+ hdr_type.addr = kzalloc(seq_header->buf_sz, GFP_KERNEL);
+ seq_header->buf = hdr_type.addr;
+ if (!hdr_type.addr)
+ return false;
+
+ hdr_type.sz = seq_header->buf_sz;
+ vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+ &hdr_type);
+
+ if (vcd_status) {
+ ERR("%s: Get VCD_I_SEQ_HEADER Failed\n", __func__);
+ return false;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx,
+ struct venc_entropycfg *entropy_cfg, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_entropy_control control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !entropy_cfg)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_ENTROPY_CTRL;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_entropy_control);
+
+ if (set_flag) {
+ switch (entropy_cfg->cabacmodel) {
+ case VEN_ENTROPY_MODEL_CAVLC:
+ control_type.entropy_sel = VCD_ENTROPY_SEL_CAVLC;
+ break;
+ case VEN_ENTROPY_MODEL_CABAC:
+ control_type.entropy_sel = VCD_ENTROPY_SEL_CABAC;
+ break;
+ default:
+ return false;
+ }
+
+ if (entropy_cfg->cabacmodel == VCD_ENTROPY_SEL_CABAC) {
+ switch (entropy_cfg->cabacmodel) {
+ case VEN_CABAC_MODEL_0:
+ control_type.cabac_model =
+ VCD_CABAC_MODEL_NUMBER_0;
+ break;
+ case VEN_CABAC_MODEL_1:
+ control_type.cabac_model =
+ VCD_CABAC_MODEL_NUMBER_1;
+ break;
+ case VEN_CABAC_MODEL_2:
+ control_type.cabac_model =
+ VCD_CABAC_MODEL_NUMBER_2;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_ENTROPY_CTRL Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_ENTROPY_CTRL Failed\n", __func__);
+ return false;
+ }
+
+ switch (control_type.entropy_sel) {
+ case VCD_ENTROPY_SEL_CABAC:
+ entropy_cfg->cabacmodel = VEN_ENTROPY_MODEL_CABAC;
+ break;
+ case VCD_ENTROPY_SEL_CAVLC:
+ entropy_cfg->cabacmodel = VEN_ENTROPY_MODEL_CAVLC;
+ break;
+ default:
+ return false;
+ }
+
+ if (control_type.entropy_sel == VCD_ENTROPY_SEL_CABAC) {
+ switch (control_type.cabac_model) {
+ case VCD_CABAC_MODEL_NUMBER_0:
+ entropy_cfg->cabacmodel = VEN_CABAC_MODEL_0;
+ break;
+ case VCD_CABAC_MODEL_NUMBER_1:
+ entropy_cfg->cabacmodel = VEN_CABAC_MODEL_1;
+ break;
+ case VCD_CABAC_MODEL_NUMBER_2:
+ entropy_cfg->cabacmodel = VEN_CABAC_MODEL_2;
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx,
+ struct venc_dbcfg *dbcfg, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_db_config control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !dbcfg)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_DEBLOCKING;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_db_config);
+
+ if (set_flag) {
+ switch (dbcfg->db_mode) {
+ case VEN_DB_DISABLE:
+ control_type.db_config = VCD_DB_DISABLE;
+ break;
+ case VEN_DB_ALL_BLKG_BNDRY:
+ control_type.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY;
+ break;
+ case VEN_DB_SKIP_SLICE_BNDRY:
+ control_type.db_config = VCD_DB_SKIP_SLICE_BOUNDARY;
+ break;
+ default:
+ return false;
+ }
+
+ control_type.slice_alpha_offset = dbcfg->slicealpha_offset;
+ control_type.slice_beta_offset = dbcfg->slicebeta_offset;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s: Set VCD_I_DEBLOCKING Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s: Get VCD_I_DEBLOCKING Failed\n", __func__);
+ return false;
+ }
+ switch (control_type.db_config) {
+ case VCD_DB_ALL_BLOCKING_BOUNDARY:
+ dbcfg->db_mode = VEN_DB_ALL_BLKG_BNDRY;
+ break;
+ case VCD_DB_DISABLE:
+ dbcfg->db_mode = VEN_DB_DISABLE;
+ break;
+ case VCD_DB_SKIP_SLICE_BOUNDARY:
+ dbcfg->db_mode = VEN_DB_SKIP_SLICE_BNDRY;
+ break;
+ default:
+ return false;
+ }
+ dbcfg->slicealpha_offset = control_type.slice_alpha_offset;
+ dbcfg->slicebeta_offset = control_type.slice_beta_offset;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx,
+ struct venc_intrarefresh *intrarefresh, u32 set_flag)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_property_intra_refresh_mb_number control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !intrarefresh)
+ return false;
+
+ prop_hdr.id = VCD_I_INTRA_REFRESH;
+ prop_hdr.sz = sizeof(struct vcd_property_intra_refresh_mb_number);
+
+ if (set_flag) {
+ control_type.cir_mb_number = intrarefresh->mbcount;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr,
+ &control_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle, &prop_hdr,
+ &control_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", __func__);
+ return false;
+ }
+ intrarefresh->mbcount = control_type.cir_mb_number;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx,
+ struct venc_multiclicecfg *multiclicecfg, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_multi_slice control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !multiclicecfg)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_MULTI_SLICE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_multi_slice);
+
+ if (set_flag) {
+ switch (multiclicecfg->mslice_mode) {
+ case VEN_MSLICE_OFF:
+ control_type.m_slice_sel = VCD_MSLICE_OFF;
+ break;
+ case VEN_MSLICE_CNT_MB:
+ control_type.m_slice_sel = VCD_MSLICE_BY_MB_COUNT;
+ break;
+ case VEN_MSLICE_CNT_BYTE:
+ control_type.m_slice_sel = VCD_MSLICE_BY_BYTE_COUNT;
+ break;
+ case VEN_MSLICE_GOB:
+ control_type.m_slice_sel = VCD_MSLICE_BY_GOB;
+ break;
+ default:
+ return false;
+ }
+
+ control_type.m_slice_size = multiclicecfg->mslice_size;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+
+ if (vcd_status) {
+ ERR("%s: Set VCD_I_MULTI_SLICE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+
+ if (vcd_status) {
+ ERR("%s: Get VCD_I_MULTI_SLICE Failed\n", __func__);
+ return false;
+ }
+ multiclicecfg->mslice_size = control_type.m_slice_size;
+ switch (control_type.m_slice_sel) {
+ case VCD_MSLICE_OFF:
+ multiclicecfg->mslice_mode = VEN_MSLICE_OFF;
+ break;
+ case VCD_MSLICE_BY_MB_COUNT:
+ multiclicecfg->mslice_mode = VEN_MSLICE_CNT_MB;
+ break;
+ case VCD_MSLICE_BY_BYTE_COUNT:
+ multiclicecfg->mslice_mode = VEN_MSLICE_CNT_BYTE;
+ break;
+ case VCD_MSLICE_BY_GOB:
+ multiclicecfg->mslice_mode = VEN_MSLICE_GOB;
+ break;
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx,
+ struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_rate_control control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !ratectrlcfg)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_RATE_CONTROL;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_rate_control);
+
+ if (set_flag) {
+ switch (ratectrlcfg->rcmode) {
+ case VEN_RC_OFF:
+ control_type.rate_control = VCD_RATE_CONTROL_OFF;
+ break;
+ case VEN_RC_CBR_VFR:
+ control_type.rate_control = VCD_RATE_CONTROL_CBR_VFR;
+ break;
+ case VEN_RC_VBR_CFR:
+ control_type.rate_control = VCD_RATE_CONTROL_VBR_CFR;
+ break;
+ case VEN_RC_VBR_VFR:
+ control_type.rate_control = VCD_RATE_CONTROL_VBR_VFR;
+ break;
+ default:
+ return false;
+ }
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_RATE_CONTROL Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_RATE_CONTROL Failed\n", __func__);
+ return false;
+ }
+
+ switch (control_type.rate_control) {
+ case VCD_RATE_CONTROL_OFF:
+ ratectrlcfg->rcmode = VEN_RC_OFF;
+ break;
+ case VCD_RATE_CONTROL_CBR_VFR:
+ ratectrlcfg->rcmode = VEN_RC_CBR_VFR;
+ break;
+ case VCD_RATE_CONTROL_VBR_CFR:
+ ratectrlcfg->rcmode = VEN_RC_VBR_CFR;
+ break;
+ case VCD_RATE_CONTROL_VBR_VFR:
+ ratectrlcfg->rcmode = VEN_RC_VBR_VFR;
+ break;
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx,
+ struct venc_voptimingcfg *venc_timing, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_vop_timing vcd_timing;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !venc_timing)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_VOP_TIMING;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_vop_timing);
+
+ if (set_flag) {
+ vcd_timing.vop_time_resolution =
+ venc_timing->voptime_resolution;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_timing);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_VOP_TIMING Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &vcd_timing);
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_VOP_TIMING Failed\n", __func__);
+ return false;
+ }
+ venc_timing->voptime_resolution =
+ vcd_timing.vop_time_resolution;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx,
+ struct venc_headerextension *headerextension, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ u32 control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !headerextension)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_HEADER_EXTENSION;
+ vcd_property_hdr.sz = sizeof(u32);
+
+ if (set_flag) {
+ control_type = headerextension->header_extension;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s: Set VCD_I_HEADER_EXTENSION Fail\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s: Get VCD_I_HEADER_EXTENSION Fail\n", __func__);
+ return false;
+ }
+ headerextension->header_extension = control_type;
+ }
+ return true;
+}
+
+u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx,
+ struct venc_qprange *qprange, u32 set_flag)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_qp_range control_type;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !qprange)
+ return false;
+
+ vcd_property_hdr.id = VCD_I_QP_RANGE;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_qp_range);
+
+ if (set_flag) {
+ control_type.max_qp = qprange->maxqp;
+ control_type.min_qp = qprange->minqp;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+
+ if (vcd_status) {
+ ERR("%s(): Set VCD_I_QP_RANGE Failed\n", __func__);
+ return false;
+ }
+ } else {
+ vcd_status = vcd_get_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &control_type);
+ if (vcd_status) {
+ ERR("%s(): Get VCD_I_QP_RANGE Failed\n", __func__);
+ return false;
+ }
+ qprange->maxqp = control_type.max_qp;
+ qprange->minqp = control_type.min_qp;
+ }
+ return true;
+}
+
+u32 vid_enc_start(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ if (!client_ctx)
+ return false;
+
+ vcd_status = vcd_encode_start(client_ctx->vcd_handle);
+ if (vcd_status) {
+ ERR("%s: vcd_encode_start failed %u\n", __func__, vcd_status);
+ return false;
+ }
+ return true;
+}
+
+
+u32 vid_enc_stop(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ if (!client_ctx)
+ return false;
+ vcd_status = vcd_stop(client_ctx->vcd_handle);
+ if (vcd_status) {
+ ERR("%s: vcd_stop failed %u\n", __func__, vcd_status);
+ return false;
+ }
+ DBG("Send STOP_DONE message to client = %p\n", client_ctx);
+ return true;
+}
+
+u32 vid_enc_pause(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ if (!client_ctx)
+ return false;
+
+ DBG("PAUSE command from client = %p\n", client_ctx);
+ vcd_status = vcd_pause(client_ctx->vcd_handle);
+ if (vcd_status)
+ return false;
+ return true;
+}
+
+u32 vid_enc_resume(struct video_client_ctx *client_ctx)
+{
+ u32 vcd_status;
+
+ if (!client_ctx)
+ return false;
+
+ DBG("Resume command from client = %p\n", client_ctx);
+ vcd_status = vcd_resume(client_ctx->vcd_handle);
+ if (vcd_status)
+ return false;
+ return true;
+}
+
+u32 vid_enc_flush(struct video_client_ctx *client_ctx,
+ struct venc_bufferflush *bufferflush)
+{
+ u32 mode;
+ u32 vcd_status;
+
+ if (!client_ctx || !bufferflush)
+ return false;
+
+ switch (bufferflush->flush_mode) {
+ case VEN_FLUSH_INPUT:
+ mode = VCD_FLUSH_INPUT;
+ break;
+ case VEN_FLUSH_OUTPUT:
+ mode = VCD_FLUSH_OUTPUT;
+ break;
+ case VEN_FLUSH_ALL:
+ mode = VCD_FLUSH_ALL;
+ break;
+ default:
+ return false;
+ break;
+ }
+ vcd_status = vcd_flush(client_ctx->vcd_handle, mode);
+ if (vcd_status)
+ return false;
+ return true;
+}
+
+u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx,
+ struct venc_allocatorproperty *venc_buf_req, u32 input_dir)
+{
+ enum vcd_buffer_type buffer;
+ struct vcd_buffer_requirement buffer_req;
+ u32 vcd_status;
+
+ if (!client_ctx || !venc_buf_req)
+ return false;
+
+ buffer = VCD_BUFFER_OUTPUT;
+ if (input_dir)
+ buffer = VCD_BUFFER_INPUT;
+
+ vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle,
+ buffer, &buffer_req);
+ if (vcd_status)
+ return false;
+
+ venc_buf_req->actualcount = buffer_req.actual_count;
+ venc_buf_req->alignment = buffer_req.align;
+ venc_buf_req->datasize = buffer_req.size;
+ venc_buf_req->mincount = buffer_req.min_count;
+ venc_buf_req->maxcount = buffer_req.max_count;
+ venc_buf_req->alignment = buffer_req.align;
+ venc_buf_req->bufpoolid = buffer_req.buf_pool_id;
+ venc_buf_req->suffixsize = 0;
+
+ return true;
+}
+
+u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx,
+ struct venc_allocatorproperty *venc_buf_req, u32 input_dir)
+{
+ enum vcd_buffer_type buffer;
+ struct vcd_buffer_requirement buffer_req;
+ u32 vcd_status;
+
+ if (!client_ctx || !venc_buf_req)
+ return false;
+
+ buffer = VCD_BUFFER_OUTPUT;
+ if (input_dir)
+ buffer = VCD_BUFFER_INPUT;
+
+ buffer_req.actual_count = venc_buf_req->actualcount;
+ buffer_req.align = venc_buf_req->alignment;
+ buffer_req.size = venc_buf_req->datasize;
+ buffer_req.min_count = venc_buf_req->mincount;
+ buffer_req.max_count = venc_buf_req->maxcount;
+ buffer_req.align = venc_buf_req->alignment;
+ buffer_req.buf_pool_id = 0;
+
+ vcd_status = vcd_set_buffer_requirements(client_ctx->vcd_handle,
+ buffer, &buffer_req);
+
+ if (vcd_status)
+ return false;
+ return true;
+}
+
+u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx,
+ struct venc_bufferpayload *buf_info, enum venc_buffer_dir buf_type)
+{
+ u32 vcd_status = VCD_ERR_FAIL;
+ enum vcd_buffer_type buffer_vcd_type;
+ enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT;
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ unsigned long len;
+ int pmem_fd;
+ struct file *file;
+ struct buf_addr_table *buf_addr_table;
+
+ s32 buf_index = -1;
+
+ if (!client_ctx || !buf_info)
+ return false;
+
+ user_addr = buf_info->buffer;
+
+ if (buf_type == VEN_BUFFER_TYPE_OUTPUT)
+ dir_buffer = BUFFER_TYPE_OUTPUT;
+
+ /* if buffer already set, ignore */
+ if (vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr,
+ &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buf_index)) {
+
+ DBG("%s: user_addr = %p is already set\n", __func__, user_addr);
+ return true;
+ }
+
+ if (get_pmem_file(buf_info->fd, (unsigned long *)&phys_addr,
+ (unsigned long *)&kern_addr,
+ &len, &file)) {
+ ERR("%s: get_pmem_file failed\n", __func__);
+ return false;
+ }
+ put_pmem_file(file);
+ if (buf_type == VEN_BUFFER_TYPE_INPUT) {
+ buffer_vcd_type = VCD_BUFFER_INPUT;
+ client_ctx->num_of_input_buffers++;
+
+ if (client_ctx->num_of_input_buffers > VID_ENC_MAX_NUM_OF_BUFF
+ ) {
+ ERR("%s: num_of_input_buffers reached max value"
+ " VID_ENC_MAX_NUM_OF_BUFF\n", __func__);
+ client_ctx->num_of_input_buffers--;
+ return false;
+ }
+
+ buf_index = client_ctx->num_of_input_buffers - 1;
+ buf_addr_table = &client_ctx->input_buf_addr_table[buf_index];
+ buf_addr_table->user_addr = buf_info->buffer;
+ kern_addr = (u8 *)kern_addr + buf_info->offset;
+ phys_addr += buf_info->offset;
+ buf_addr_table->kern_addr = kern_addr;
+ buf_addr_table->phys_addr = phys_addr;
+ buf_addr_table->pmem_fd = buf_info->fd;
+ buf_addr_table->file = file;
+ } else {
+ buffer_vcd_type = VCD_BUFFER_OUTPUT;
+
+ client_ctx->num_of_output_buffers++;
+
+ if (client_ctx->num_of_output_buffers >
+ VID_ENC_MAX_NUM_OF_BUFF) {
+ ERR("%s: num_of_outut_buffers reached max value"
+ " VID_ENC_MAX_NUM_OF_BUFF\n", __func__);
+ client_ctx->num_of_output_buffers--;
+ return false;
+ }
+
+ buf_index = client_ctx->num_of_output_buffers - 1;
+
+ buf_addr_table = &client_ctx->output_buf_addr_table[buf_index];
+ kern_addr = (u8 *)kern_addr + buf_info->offset;
+ phys_addr += buf_info->offset;
+ buf_addr_table->user_addr = buf_info->buffer;
+ buf_addr_table->kern_addr = kern_addr;
+ buf_addr_table->phys_addr = phys_addr;
+ buf_addr_table->pmem_fd = buf_info->fd;
+ buf_addr_table->file = file;
+ }
+
+ vcd_status = vcd_set_buffer(client_ctx->vcd_handle, buffer_vcd_type,
+ kern_addr, buf_info->sz);
+
+ if (!vcd_status)
+ return true;
+ else
+ return false;
+}
+
+u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx,
+ struct venc_buffer *input_frame_info)
+{
+ struct vcd_frame_data vcd_input_buffer;
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ if (!client_ctx || !input_frame_info)
+ return false;
+
+ user_addr = input_frame_info->addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, true,
+ &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file,
+ &buffer_index)) {
+ ERR("%s: kernel_vaddr not found\n", __func__);
+ return false;
+ }
+
+ /* kern_addr is found. send the frame to VCD */
+ memset((void *)&vcd_input_buffer, 0, sizeof(vcd_input_buffer));
+
+ vcd_input_buffer.virt_addr = (u8 *)kern_addr + input_frame_info->offset;
+ vcd_input_buffer.offset = input_frame_info->offset;
+ vcd_input_buffer.client_data = input_frame_info->clientdata;
+ vcd_input_buffer.ip_frm_tag = (u32)input_frame_info->clientdata;
+ vcd_input_buffer.data_len = input_frame_info->len;
+ vcd_input_buffer.time_stamp = input_frame_info->timestamp;
+
+ /* Rely on VCD using the same flags as OMX */
+ vcd_input_buffer.flags = input_frame_info->flags;
+
+ vcd_status = vcd_encode_frame(client_ctx->vcd_handle,
+ &vcd_input_buffer);
+
+ if (vcd_status) {
+ ERR("%s: vcd_encode_frame failed = %u\n", __func__, vcd_status);
+ return false;
+ }
+ return true;
+}
+
+u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx,
+ struct venc_buffer *output_frame_info)
+{
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+ u32 vcd_status = VCD_ERR_FAIL;
+
+ struct vcd_frame_data vcd_frame;
+
+ if (!client_ctx || !output_frame_info)
+ return false;
+
+ user_addr = output_frame_info->addr;
+
+ if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+ true, &user_addr, &kern_addr, &phys_addr, &pmem_fd,
+ &file, &buffer_index)) {
+ ERR("%s: kernel_vaddr not found\n", __func__);
+ return false;
+ }
+
+ memset((void *)&vcd_frame, 0, sizeof(vcd_frame));
+ vcd_frame.virt_addr = kern_addr;
+ vcd_frame.client_data = output_frame_info->clientdata;
+ vcd_frame.alloc_len = output_frame_info->sz;
+
+ vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, &vcd_frame);
+ if (vcd_status) {
+ ERR("%s: vcd_fill_output_buffer %u\n", __func__, vcd_status);
+ return false;
+ }
+ return true;
+}
diff --git a/drivers/misc/video_core/720p/enc/venc_internal.h b/drivers/misc/video_core/720p/enc/venc_internal.h
new file mode 100644
index 0000000..517783a
--- /dev/null
+++ b/drivers/misc/video_core/720p/enc/venc_internal.h
@@ -0,0 +1,160 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef VENC_INTERNAL_H
+#define VENC_INTERNAL_H
+
+#include <linux/msm_vidc_enc.h>
+#include <linux/cdev.h>
+
+#include "video_core_init.h"
+
+#define VID_ENC_MAX_ENCODER_CLIENTS 16
+#define VID_ENC_MAX_NUM_OF_BUFF 100
+
+enum venc_buffer_dir {
+ VEN_BUFFER_TYPE_INPUT,
+ VEN_BUFFER_TYPE_OUTPUT
+};
+
+struct vid_enc_msg {
+ struct list_head list;
+ struct venc_msg venc_msg_info;
+};
+
+struct vid_enc_dev {
+
+ struct cdev cdev;
+ struct device *device;
+ resource_size_t phys_base;
+ void __iomem *virt_base;
+ unsigned int irq;
+ struct clk *hclk;
+ struct clk *hclk_div2;
+ struct clk *pclk;
+ unsigned long hclk_rate;
+ struct mutex lock;
+ s32 device_handle;
+ struct video_client_ctx venc_clients[VID_ENC_MAX_ENCODER_CLIENTS];
+ u32 num_clients;
+};
+
+u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx,
+ struct venc_basecfg *base_config, u32 set_flag);
+
+u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx,
+ u32 *input_format, u32 set_flag);
+
+u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type,
+ u32 set_flag);
+
+u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx,
+ u32 *height, u32 *width, u32 set_flag);
+
+u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx,
+ struct venc_targetbitrate *venc_bitrate, u32 set_flag);
+
+u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx,
+ struct venc_framerate *frame_rate, u32 set_flag);
+
+u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx,
+ struct venc_switch *encoder_switch, u32 set_flag);
+
+u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx,
+ struct venc_switch *encoder_switch, u32 set_flag);
+
+u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx,
+ struct venc_profile *profile, u32 set_flag);
+
+u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx,
+ struct ven_profilelevel *profile_level, u32 set_flag);
+
+u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx,
+ struct venc_sessionqp *session_qp, u32 set_flag);
+
+u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx,
+ struct venc_intraperiod *intraperiod, u32 set_flag);
+
+u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx,
+ struct venc_seqheader *seq_header);
+
+u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx,
+ struct venc_entropycfg *entropy_cfg, u32 set_flag);
+
+u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx,
+ struct venc_dbcfg *dbcfg, u32 set_flag);
+
+u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx,
+ struct venc_intrarefresh *intrarefresh, u32 set_flag);
+
+u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx,
+ struct venc_multiclicecfg *multiclicecfg, u32 set_flag);
+
+u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx,
+ struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag);
+
+u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx,
+ struct venc_voptimingcfg *voptimingcfg, u32 set_flag);
+
+u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx,
+ struct venc_headerextension *headerextension, u32 set_flag);
+
+u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx,
+ struct venc_qprange *qprange, u32 set_flag);
+
+u32 vid_enc_start(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_stop(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_pause(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_resume(struct video_client_ctx *client_ctx);
+
+u32 vid_enc_flush(struct video_client_ctx *client_ctx,
+ struct venc_bufferflush *bufferflush);
+
+u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx,
+ struct venc_allocatorproperty *venc_buf_req, u32 input_dir);
+
+u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx,
+ struct venc_allocatorproperty *venc_buf_req, u32 input_dir);
+
+u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx,
+ struct venc_bufferpayload *buffer_info,
+ enum venc_buffer_dir buffer_type);
+
+u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx,
+ struct venc_buffer *input_frame_info);
+
+u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx,
+ struct venc_buffer *output_frame_info);
+
+#endif
diff --git a/drivers/misc/video_core/720p/init/video_core_init.c b/drivers/misc/video_core/720p/init/video_core_init.c
new file mode 100644
index 0000000..9af634c
--- /dev/null
+++ b/drivers/misc/video_core/720p/init/video_core_init.c
@@ -0,0 +1,766 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/android_pmem.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <mach/clk.h>
+
+#include "vcd_ddl_firmware.h"
+#include "vcd_api.h"
+#include "video_core_init_internal.h"
+#include "video_core_init.h"
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+#define VID_C_NAME "msm_vidc_reg"
+
+#define ERR(x...) printk(KERN_ERR x)
+
+static struct vid_c_dev *vidc_dev;
+static dev_t vidc_dev_num;
+static struct class *vidc_class;
+
+static const struct file_operations vid_c_fops = {
+ .owner = THIS_MODULE,
+ .open = NULL,
+ .release = NULL,
+ .ioctl = NULL,
+};
+
+struct workqueue_struct *vid_c_wq;
+struct workqueue_struct *vidc_timer_wq;
+static irqreturn_t vid_c_isr(int irq, void *dev);
+static spinlock_t vidc_spin_lock;
+
+
+static void vid_c_timer_fn(unsigned long data)
+{
+ unsigned long flag;
+ struct vid_c_timer *hw_timer = NULL;
+
+ DBG("%s: Timer expired\n", __func__);
+ spin_lock_irqsave(&vidc_spin_lock, flag);
+ hw_timer = (struct vid_c_timer *)data;
+ list_add_tail(&hw_timer->list, &vidc_dev->vidc_timer_queue);
+ spin_unlock_irqrestore(&vidc_spin_lock, flag);
+ DBG("Queue the work for timer\n");
+ queue_work(vidc_timer_wq, &vidc_dev->vidc_timer_worker);
+}
+
+static void vid_c_timer_handler(struct work_struct *work)
+{
+ unsigned long flag = 0;
+ u32 islist_empty = 0;
+ struct vid_c_timer *hw_timer = NULL;
+
+ DBG("%s: Timer expired\n", __func__);
+ do {
+ spin_lock_irqsave(&vidc_spin_lock, flag);
+ islist_empty = list_empty(&vidc_dev->vidc_timer_queue);
+ if (!islist_empty) {
+ hw_timer = list_first_entry(&vidc_dev->vidc_timer_queue,
+ struct vid_c_timer, list);
+ list_del(&hw_timer->list);
+ }
+ spin_unlock_irqrestore(&vidc_spin_lock, flag);
+ if (!islist_empty && hw_timer && hw_timer->cb_func)
+ hw_timer->cb_func(hw_timer->userdata);
+ } while (!islist_empty);
+}
+
+static void vid_c_work_handler(struct work_struct *work)
+{
+ DBG("vid_c_work_handler()");
+ vcd_read_and_clear_interrupt();
+ vcd_response_handler();
+ enable_irq(vidc_dev->irq);
+ DBG("vid_c_work_handler() done");
+}
+
+static DECLARE_WORK(vid_c_work, vid_c_work_handler);
+
+static int __init vid_c_720p_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct resource *resource;
+ DBG("Enter %s\n", __func__);
+
+ if (pdev->id) {
+ ERR("Invalid platform device ID = %d\n", pdev->id);
+ return -EINVAL;
+ }
+ vidc_dev->irq = platform_get_irq(pdev, 0);
+ if (unlikely(vidc_dev->irq < 0)) {
+ ERR("%s: Invalid irq = %d\n", __func__, vidc_dev->irq);
+ return -ENXIO;
+ }
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource)) {
+ ERR("%s: Invalid resource\n", __func__);
+ return -ENXIO;
+ }
+
+ vidc_dev->phys_base = resource->start;
+ vidc_dev->virt_base = ioremap(resource->start,
+ resource->end - resource->start + 1);
+
+ if (!vidc_dev->virt_base) {
+ ERR("%s: ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+ vidc_dev->device = &pdev->dev;
+ mutex_init(&vidc_dev->lock);
+
+ vid_c_wq = create_singlethread_workqueue("vid_c_worker_queue");
+ if (!vid_c_wq) {
+ ERR("%s: create workqueue failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = vcd_fw_init(vidc_dev->device);
+ if (rc)
+ ERR("%s: failed to prepare firmware %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int __devexit vid_c_720p_remove(struct platform_device *pdev)
+{
+ if (pdev->id) {
+ ERR("Invalid plaform device ID = %d\n", pdev->id);
+ return -EINVAL;
+ }
+ vcd_fw_exit();
+ return 0;
+}
+
+static struct platform_driver msm_vid_c_720p_platform_driver = {
+ .probe = vid_c_720p_probe,
+ .remove = vid_c_720p_remove,
+ .driver = {
+ .name = "msm_vidc_720p",
+ },
+};
+
+static void __exit vid_c_exit(void)
+{
+ platform_driver_unregister(&msm_vid_c_720p_platform_driver);
+}
+
+static irqreturn_t vid_c_isr(int irq, void *dev)
+{
+ DBG("vid_c_isr() %d\n", irq);
+ disable_irq_nosync(irq);
+ queue_work(vid_c_wq, &vid_c_work);
+ return IRQ_HANDLED;
+}
+
+static int __init vid_c_init(void)
+{
+ int rc = 0;
+ struct device *class_devp;
+
+ vidc_dev = kzalloc(sizeof(struct vid_c_dev), GFP_KERNEL);
+ if (!vidc_dev) {
+ ERR("%s Unable to allocate memory for vid_c_dev\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rc = alloc_chrdev_region(&vidc_dev_num, 0, 1, VID_C_NAME);
+ if (rc < 0) {
+ ERR("%s: alloc_chrdev_region failed %d\n", __func__, rc);
+ goto error_vid_c_alloc_chrdev_region;
+ }
+
+ vidc_class = class_create(THIS_MODULE, VID_C_NAME);
+ if (IS_ERR(vidc_class)) {
+ rc = PTR_ERR(vidc_class);
+ ERR("%s: couldn't create vid_c_class %d\n", __func__, rc);
+ goto error_vid_c_class_create;
+ }
+
+ class_devp = device_create(vidc_class, NULL, vidc_dev_num, NULL,
+ VID_C_NAME);
+
+ if (IS_ERR(class_devp)) {
+ rc = PTR_ERR(class_devp);
+ ERR("%s: class device_create failed %d\n", __func__, rc);
+ goto error_vid_c_class_device_create;
+ }
+
+ cdev_init(&vidc_dev->cdev, &vid_c_fops);
+ vidc_dev->cdev.owner = THIS_MODULE;
+ rc = cdev_add(&(vidc_dev->cdev), vidc_dev_num, 1);
+
+ if (rc < 0) {
+ ERR("%s: cdev_add failed %d\n", __func__, rc);
+ goto error_vid_c_cdev_add;
+ }
+
+ rc = platform_driver_register(&msm_vid_c_720p_platform_driver);
+ if (rc) {
+ ERR("%s failed to load\n", __func__);
+ goto error_vid_c_platfom_register;
+ }
+
+ rc = request_irq(vidc_dev->irq, vid_c_isr, IRQF_TRIGGER_HIGH,
+ "vid_c", vidc_dev->device);
+ if (unlikely(rc)) {
+ ERR("%s:request_irq failed\n", __func__);
+ goto error_vid_c_platfom_register;
+ }
+
+ vidc_timer_wq = create_singlethread_workqueue("vidc_timer_wq");
+ if (!vidc_timer_wq) {
+ ERR("%s: create workqueue failed\n", __func__);
+ rc = -ENOMEM;
+ goto error_vid_c_platfom_register;
+ }
+
+ DBG("Disabling IRQ in %s\n", __func__);
+ disable_irq_nosync(vidc_dev->irq);
+ INIT_WORK(&vidc_dev->vidc_timer_worker, vid_c_timer_handler);
+ spin_lock_init(&vidc_spin_lock);
+ INIT_LIST_HEAD(&vidc_dev->vidc_timer_queue);
+ vidc_dev->clock_enabled = 0;
+ vidc_dev->ref_count = 0;
+ vidc_dev->firmware_refcount = 0;
+ vidc_dev->get_firmware = 0;
+
+ return 0;
+
+error_vid_c_platfom_register:
+ cdev_del(&(vidc_dev->cdev));
+error_vid_c_cdev_add:
+ device_destroy(vidc_class, vidc_dev_num);
+error_vid_c_class_device_create:
+ class_destroy(vidc_class);
+error_vid_c_class_create:
+ unregister_chrdev_region(vidc_dev_num, 1);
+error_vid_c_alloc_chrdev_region:
+ kfree(vidc_dev);
+
+ return rc;
+}
+
+void __iomem *vid_c_get_ioaddr()
+{
+ return vidc_dev->virt_base;
+}
+EXPORT_SYMBOL(vid_c_get_ioaddr);
+#ifdef USE_RES_TRACKER
+
+u32 vid_c_enable_pwr_rail()
+{
+ int rc;
+
+ mutex_lock(&vidc_dev->lock);
+
+ if (vidc_dev->rail_enabled) {
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+ }
+
+ //TODO: internal_pwr_rail_mode(MFC_CLK_ID, MANUAL)
+
+ vidc_dev->pclk = clk_get(vidc_dev->device, "mfc_pclk");
+ if (IS_ERR(vidc_dev->pclk)) {
+ ERR("%s: mfc_pclk get failed\n", __func__);
+ goto err;
+ }
+
+ vidc_dev->hclk = clk_get(vidc_dev->device, "mfc_clk");
+ if (IS_ERR(vidc_dev->hclk)) {
+ ERR("%s: mfc_clk get failed\n", __func__);
+ goto err;
+ }
+
+ vidc_dev->hclk_div2 = clk_get(vidc_dev->device, "mfc_div2_clk");
+ if (IS_ERR(vidc_dev->hclk_div2)) {
+ ERR("%s: mfc_div2_clk get failed\n", __func__);
+ goto err;
+ }
+
+ //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 1)
+
+ //TODO msleep must die
+ msleep(20);
+
+ rc = clk_reset(vidc_dev->pclk, CLK_RESET_DEASSERT);
+ if (rc) {
+ ERR("clk_reset failed %d\n", rc);
+ goto err;
+ }
+ //TODO msleep must die
+ msleep(20);
+
+ vidc_dev->rail_enabled = 1;
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+
+err:
+ if (!IS_ERR(vidc_dev->pclk))
+ clk_put(vidc_dev->pclk);
+ if (!IS_ERR(vidc_dev->hclk))
+ clk_put(vidc_dev->hclk);
+ if (!IS_ERR(vidc_dev->hclk_div2))
+ clk_put(vidc_dev->hclk_div2);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+}
+EXPORT_SYMBOL(vid_c_enable_pwr_rail);
+
+u32 vid_c_disable_pwr_rail()
+{
+ int rc = -1;
+ mutex_lock(&vidc_dev->lock);
+
+ if (vidc_dev->clock_enabled) {
+ mutex_unlock(&vidc_dev->lock);
+ DBG("Calling CLK disable in power down\n");
+ vid_c_disable_clk();
+ mutex_lock(&vidc_dev->lock);
+ }
+
+ if (!vidc_dev->rail_enabled) {
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ vidc_dev->rail_enabled = 0;
+ rc = clk_reset(vidc_dev->pclk, CLK_RESET_ASSERT);
+ if (rc) {
+ ERR("clk_reset failed %d\n", rc);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+ msleep(20);
+
+ //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 0)
+
+ clk_put(vidc_dev->hclk_div2);
+ clk_put(vidc_dev->hclk);
+ clk_put(vidc_dev->pclk);
+
+ mutex_unlock(&vidc_dev->lock);
+
+ return true;
+}
+EXPORT_SYMBOL(vid_c_disable_pwr_rail);
+
+u32 vid_c_enable_clk()
+{
+ mutex_lock(&vidc_dev->lock);
+
+ if (!vidc_dev->rail_enabled) {
+ goto err;
+ }
+ if (vidc_dev->clock_enabled) {
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+ }
+
+ DBG("Enabling IRQ in %s\n", __func__);
+ enable_irq(vidc_dev->irq);
+ DBG("%s: Enabling the clocks ...\n", __func__);
+
+ if (clk_enable(vidc_dev->pclk)) {
+ ERR("vidc pclk enable failed\n");
+ goto err;
+ }
+
+ if (clk_enable(vidc_dev->hclk)) {
+ ERR("vidc hclk enable failed\n");
+ goto err;
+ }
+
+ if (clk_enable(vidc_dev->hclk_div2)) {
+ ERR("vidc hclk_div2 enable failed\n");
+ goto err;
+ }
+
+ vidc_dev->clock_enabled = 1;
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+err:
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+}
+EXPORT_SYMBOL(vid_c_enable_clk);
+
+u32 vid_c_sel_clk_rate(unsigned long hclk_rate)
+{
+ mutex_lock(&vidc_dev->lock);
+ if (clk_set_rate(vidc_dev->hclk, hclk_rate)) {
+ ERR("vidc hclk set rate failed\n");
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+ vidc_dev->hclk_rate = hclk_rate;
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+}
+EXPORT_SYMBOL(vid_c_sel_clk_rate);
+
+u32 vid_c_get_clk_rate(unsigned long *phclk_rate)
+{
+ if (!phclk_rate) {
+ ERR("vid_c_get_clk_rate(): phclk_rate is NULL\n");
+ return false;
+ }
+ mutex_lock(&vidc_dev->lock);
+ *phclk_rate = clk_get_rate(vidc_dev->hclk);
+ if (!(*phclk_rate)) {
+ ERR("vidc hclk get rate failed\n");
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+}
+EXPORT_SYMBOL(vid_c_get_clk_rate);
+
+u32 vid_c_disable_clk(void)
+{
+ mutex_lock(&vidc_dev->lock);
+
+ if (!vidc_dev->clock_enabled) {
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ DBG("Disabling IRQ in %s\n", __func__);
+ disable_irq_nosync(vidc_dev->irq);
+ DBG("%s: Disabling the clocks ...\n", __func__);
+
+ vidc_dev->clock_enabled = 0;
+ clk_disable(vidc_dev->hclk);
+ clk_disable(vidc_dev->hclk_div2);
+ clk_disable(vidc_dev->pclk);
+
+ mutex_unlock(&vidc_dev->lock);
+
+ return true;
+}
+EXPORT_SYMBOL(vid_c_disable_clk);
+
+//TODO: consider deleting USE_RES_TRACKER
+#else
+
+u32 vid_c_enable_clk(unsigned long hclk_rate)
+{
+ int rc = -1;
+ mutex_lock(&vidc_dev->lock);
+ vidc_dev->ref_count++;
+
+ if (!vidc_dev->clock_enabled) {
+ DBG("Enabling IRQ in %s()\n", __func__);
+ enable_irq(vidc_dev->irq);
+
+ rc = internal_pwr_rail_mode
+ (PWR_RAIL_MFC_CLK, PWR_RAIL_CTL_MANUAL);
+ if (rc) {
+ ERR("%s(): internal_pwr_rail_mode failed %d\n",
+ __func__, rc);
+ return false;
+ }
+ DBG("%s(): internal_pwr_rail_mode Success %d\n",
+ __func__, rc);
+
+ vidc_dev->pclk =
+ clk_get(vidc_dev->device, "mfc_pclk");
+
+ if (IS_ERR(vidc_dev->pclk)) {
+ ERR("%s(): mfc_pclk get failed\n", __func__);
+
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ vidc_dev->hclk =
+ clk_get(vidc_dev->device, "mfc_clk");
+
+ if (IS_ERR(vidc_dev->hclk)) {
+ ERR("%s(): mfc_clk get failed\n", __func__);
+
+ clk_put(vidc_dev->pclk);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ vidc_dev->hclk_div2 =
+ clk_get(vidc_dev->device, "mfc_div2_clk");
+
+ if (IS_ERR(vidc_dev->pclk)) {
+ ERR("%s(): mfc_div2_clk get failed\n", __func__);
+
+ clk_put(vidc_dev->pclk);
+ clk_put(vidc_dev->hclk);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ vidc_dev->hclk_rate = hclk_rate;
+
+ if (clk_set_rate(vidc_dev->hclk,
+ vidc_dev->hclk_rate)) {
+ ERR("vid_c hclk set rate failed\n");
+ clk_put(vidc_dev->pclk);
+ clk_put(vidc_dev->hclk);
+ clk_put(vidc_dev->hclk_div2);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ if (clk_enable(vidc_dev->pclk)) {
+ ERR("vid_c pclk Enable failed\n");
+
+ clk_put(vidc_dev->hclk);
+ clk_put(vidc_dev->hclk_div2);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ if (clk_enable(vidc_dev->hclk)) {
+ ERR("vid_c hclk Enable failed\n");
+ clk_put(vidc_dev->pclk);
+ clk_put(vidc_dev->hclk_div2);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+
+ if (clk_enable(vidc_dev->hclk_div2)) {
+ ERR("vid_c hclk Enable failed\n");
+ clk_put(vidc_dev->hclk);
+ clk_put(vidc_dev->pclk);
+ mutex_unlock(&vidc_dev->lock);
+ return false;
+ }
+ msleep(20);
+ rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 1);
+ if (rc) {
+ ERR("\n internal_pwr_rail_ctl failed %d\n", rc);
+ return false;
+ }
+ DBG("%s(): internal_pwr_rail_ctl Success %d\n",
+ __func__, rc);
+ msleep(20);
+ rc = clk_reset(vidc_dev->pclk, CLK_RESET_DEASSERT);
+ if (rc) {
+ ERR("\n clk_reset failed %d\n", rc);
+ return false;
+ }
+ msleep(20);
+ }
+ vidc_dev->clock_enabled = 1;
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+}
+EXPORT_SYMBOL(vid_c_enable_clk);
+
+u32 vid_c_disable_clk(void)
+{
+ int rc = -1;
+ mutex_lock(&vidc_dev->lock);
+
+ if (!vidc_dev->ref_count ||
+ !vidc_dev->clock_enabled) {
+ return false;
+ }
+
+ if (vidc_dev->ref_count > 0)
+ vidc_dev->ref_count--;
+
+ if (!vidc_dev->ref_count) {
+ DBG("Disabling IRQ in %s()\n", __func__);
+ disable_irq_nosync(vidc_dev->irq);
+ rc = clk_reset(vidc_dev->pclk, CLK_RESET_ASSERT);
+ if (rc) {
+ ERR("\n clk_reset failed %d\n", rc);
+ return false;
+ }
+ msleep(20);
+
+ rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 0);
+ if (rc) {
+ ERR("\n internal_pwr_rail_ctl failed %d\n", rc);
+ return false;
+ }
+
+ vidc_dev->clock_enabled = 0;
+ clk_disable(vidc_dev->hclk);
+ clk_disable(vidc_dev->hclk_div2);
+ clk_disable(vidc_dev->pclk);
+
+ clk_put(vidc_dev->hclk_div2);
+ clk_put(vidc_dev->hclk);
+ clk_put(vidc_dev->pclk);
+
+ }
+ mutex_unlock(&vidc_dev->lock);
+ return true;
+}
+EXPORT_SYMBOL(vid_c_disable_clk);
+
+#endif
+
+u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx,
+ enum buffer_dir buffer_type, u32 search_with_user_vaddr,
+ void __user **user_addr, void **kern_addr, phys_addr_t *phys_addr,
+ int *pmem_fd, struct file **file, s32 *buffer_index)
+{
+ u32 num_of_buffers;
+ u32 i;
+ struct buf_addr_table *buf_addr_table;
+ u32 found = false;
+
+ if (!client_ctx)
+ return false;
+
+ if (buffer_type == BUFFER_TYPE_INPUT) {
+ buf_addr_table = client_ctx->input_buf_addr_table;
+ num_of_buffers = client_ctx->num_of_input_buffers;
+ DBG("%s: buffer_type = INPUT\n", __func__);
+ } else {
+ buf_addr_table = client_ctx->output_buf_addr_table;
+ num_of_buffers = client_ctx->num_of_output_buffers;
+ DBG("%s: buffer_type = OUTPUT\n", __func__);
+ }
+
+ for (i = 0; i < num_of_buffers; ++i) {
+ if (search_with_user_vaddr) {
+ if (*user_addr == buf_addr_table[i].user_addr) {
+ *kern_addr = buf_addr_table[i].kern_addr;
+ found = true;
+ DBG("%s: client_ctx=%p user_addr=%p is found\n",
+ __func__, client_ctx, *user_addr);
+ break;
+ }
+ } else {
+ if (*kern_addr == buf_addr_table[i].kern_addr) {
+ *user_addr = buf_addr_table[i].user_addr;
+ found = true;
+ DBG("%s: client_ctx=%p kern_addr=%p is found",
+ __func__, client_ctx, *kern_addr);
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ if (search_with_user_vaddr)
+ DBG("%s: client_ctx=%p user_addr=%p not found\n",
+ __func__, client_ctx, *user_addr);
+ else
+ DBG("%s: client_ctx=%p kern_addr=%p not found\n",
+ __func__, client_ctx, *kern_addr);
+ return false;
+ }
+
+ *phys_addr = buf_addr_table[i].phys_addr;
+ *pmem_fd = buf_addr_table[i].pmem_fd;
+ *file = buf_addr_table[i].file;
+ *buffer_index = i;
+
+ if (search_with_user_vaddr)
+ DBG("kern_addr=%p phys_addr=%X pmem_fd=%d "
+ "struct *file=%p buffer_index=%d\n", *kern_addr,
+ *phys_addr, *pmem_fd, *file, *buffer_index);
+ else
+ DBG("user_addr=%p phys_addr=%X pmem_fd=%d, "
+ "struct *file=%p buffer_index=%d\n", *user_addr,
+ *phys_addr, *pmem_fd, *file, *buffer_index);
+ return true;
+}
+EXPORT_SYMBOL(vid_c_lookup_addr_table);
+
+u32 vid_c_timer_create(void (*pf_timer_handler)(void *), void *user_data,
+ void **pp_timer_handle)
+{
+ struct vid_c_timer *hw_timer = NULL;
+ if (!pf_timer_handler || !pp_timer_handle) {
+ DBG("%s: timer creation failed\n", __func__);
+ return false;
+ }
+ hw_timer = kzalloc(sizeof(struct vid_c_timer), GFP_KERNEL);
+ if (!hw_timer) {
+ DBG("%s: timer creation failed in allocation\n", __func__);
+ return false;
+ }
+ init_timer(&hw_timer->hw_timeout);
+ hw_timer->hw_timeout.data = (unsigned long)hw_timer;
+ hw_timer->hw_timeout.function = vid_c_timer_fn;
+ hw_timer->cb_func = pf_timer_handler;
+ hw_timer->userdata = user_data;
+ *pp_timer_handle = hw_timer;
+ return true;
+}
+EXPORT_SYMBOL(vid_c_timer_create);
+
+void vid_c_timer_release(void *timer_handle)
+{
+ kfree(timer_handle);
+}
+EXPORT_SYMBOL(vid_c_timer_release);
+
+void vid_c_timer_start(void *timer_handle, u32 time_out)
+{
+ struct vid_c_timer *hw_timer = timer_handle;
+ DBG("%s: start timer\n ", __func__);
+ if (hw_timer) {
+ hw_timer->hw_timeout.expires = jiffies + 1 * HZ;
+ add_timer(&hw_timer->hw_timeout);
+ }
+}
+EXPORT_SYMBOL(vid_c_timer_start);
+
+void vid_c_timer_stop(void *timer_handle)
+{
+ struct vid_c_timer *hw_timer = timer_handle;
+ DBG("%s: stop timer\n ", __func__);
+ if (hw_timer)
+ del_timer(&hw_timer->hw_timeout);
+}
+EXPORT_SYMBOL(vid_c_timer_stop);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Video decoder/encoder driver Init Module");
+MODULE_VERSION("1.0");
+module_init(vid_c_init);
+module_exit(vid_c_exit);
diff --git a/drivers/misc/video_core/720p/init/video_core_init.h b/drivers/misc/video_core/720p/init/video_core_init.h
new file mode 100644
index 0000000..d8e40f3
--- /dev/null
+++ b/drivers/misc/video_core/720p/init/video_core_init.h
@@ -0,0 +1,95 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef VIDEO_CORE_INIT_H
+#define VIDEO_CORE_INIT_H
+
+#include "video_core_type.h"
+
+#define MAX_VIDEO_NUM_OF_BUFF 100
+
+enum buffer_dir {
+ BUFFER_TYPE_INPUT,
+ BUFFER_TYPE_OUTPUT
+};
+
+struct buf_addr_table {
+ void __user *user_addr;
+ void *kern_addr;
+ phys_addr_t phys_addr;
+ int pmem_fd;
+ struct file *file;
+};
+
+struct video_client_ctx {
+ void *vcd_handle;
+ u32 num_of_input_buffers;
+ u32 num_of_output_buffers;
+ struct buf_addr_table input_buf_addr_table[MAX_VIDEO_NUM_OF_BUFF];
+ struct buf_addr_table output_buf_addr_table[MAX_VIDEO_NUM_OF_BUFF];
+ struct list_head msg_queue;
+ struct mutex msg_queue_lock;
+ wait_queue_head_t msg_wait;
+ struct completion event;
+ u32 event_status;
+ u32 seq_header_set;
+ u32 stop_msg;
+};
+
+void __iomem *vid_c_get_ioaddr(void);
+
+#ifdef USE_RES_TRACKER
+
+u32 vid_c_sel_clk_rate(unsigned long hclk_rate);
+u32 vid_c_get_clk_rate(unsigned long *phclk_rate);
+u32 vid_c_enable_clk(void);
+u32 vid_c_disable_clk(void);
+u32 vid_c_enable_pwr_rail(void);
+u32 vid_c_disable_pwr_rail(void);
+
+#else
+u32 vid_c_enable_clk(unsigned long hclk_rate);
+u32 vid_c_disable_clk(void);
+#endif
+
+int vid_c_load_firmware(void);
+void vid_c_release_firmware(void);
+u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx,
+ enum buffer_dir buffer_type, u32 search_with_user_vaddr,
+ void __user **user_addr, void **kernel_addr, phys_addr_t *phys_addr,
+ int *pmem_fd, struct file **file, s32 *buffer_index);
+
+u32 vid_c_timer_create(void (*pf_timer_handler)(void *),
+ void *user_data, void **pp_timer_handle);
+void vid_c_timer_release(void *timer_handle);
+void vid_c_timer_start(void *timer_handle, u32 time_out);
+void vid_c_timer_stop(void *timer_handle);
+
+
+#endif
diff --git a/drivers/misc/video_core/720p/init/video_core_init_internal.h b/drivers/misc/video_core/720p/init/video_core_init_internal.h
new file mode 100644
index 0000000..b72e9f2
--- /dev/null
+++ b/drivers/misc/video_core/720p/init/video_core_init_internal.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef VIDEO_CORE_INIT_INTERNAL_H
+#define VIDEO_CORE_INIT_INTERNAL_H
+
+#include <linux/cdev.h>
+
+struct vid_c_timer {
+ struct list_head list;
+ struct timer_list hw_timeout;
+ void (*cb_func)(void *);
+ void *userdata;
+};
+
+struct vid_c_dev {
+ struct cdev cdev;
+ struct device *device;
+ resource_size_t phys_base;
+ void __iomem *virt_base;
+ unsigned int irq;
+ struct clk *hclk;
+ struct clk *hclk_div2;
+ struct clk *pclk;
+ unsigned long hclk_rate;
+ unsigned int clock_enabled;
+ unsigned int rail_enabled;
+ unsigned int ref_count;
+ unsigned int firmware_refcount;
+ unsigned int get_firmware;
+ struct mutex lock;
+ s32 device_handle;
+ struct list_head vidc_timer_queue;
+ struct work_struct vidc_timer_worker;
+};
+
+#endif
diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c
new file mode 100644
index 0000000..7ad199b
--- /dev/null
+++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c
@@ -0,0 +1,275 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_res_tracker.h"
+#include "video_core_init.h"
+
+#include <linux/pm_qos_params.h>
+#ifdef AXI_CLK_SCALING
+#include <mach/msm_reqs.h>
+#endif
+
+#define MSM_AXI_QOS_NAME "msm_vidc_reg"
+
+#define QVGA_PERF_LEVEL (300 * 30)
+#define VGA_PERF_LEVEL (1200 * 30)
+#define WVGA_PERF_LEVEL (1500 * 30)
+
+static unsigned int mfc_clk_freq_table[3] = {
+ 61440000, 122880000, 170667000
+};
+
+#ifndef CONFIG_MSM_NPA_SYSTEM_BUS
+static unsigned int axi_clk_freq_table_enc[2] = {
+ 122880, 192000
+};
+static unsigned int axi_clk_freq_table_dec[2] = {
+ 122880, 192000
+};
+#else
+static unsigned int axi_clk_freq_table_enc[2] = {
+ MSM_AXI_FLOW_VIDEO_RECORDING_720P,
+ MSM_AXI_FLOW_VIDEO_RECORDING_720P
+};
+static unsigned int axi_clk_freq_table_dec[2] = {
+ MSM_AXI_FLOW_VIDEO_PLAYBACK_720P,
+ MSM_AXI_FLOW_VIDEO_PLAYBACK_720P
+};
+#endif
+static u32 res_trk_convert_freq_to_perf_lvl(u64 freq)
+{
+ u64 perf_lvl;
+ u64 temp;
+
+ pr_debug("\n %s():: freq = %u\n", __func__, (u32)freq);
+
+ if (!freq)
+ return 0;
+
+ temp = freq * 1000;
+ do_div(temp, VCD_RESTRK_HZ_PER_1000_PERFLVL);
+ perf_lvl = (u32)temp;
+ pr_debug("\n %s(): perf_lvl = %u\n", __func__, (u32)perf_lvl);
+
+ return (u32)perf_lvl;
+}
+
+static u32 res_trk_convert_perf_lvl_to_freq(u64 perf_lvl)
+{
+ u64 freq, temp;
+
+ pr_debug("\n %s():: perf_lvl = %u\n", __func__,
+ (u32)perf_lvl);
+ temp = (perf_lvl * VCD_RESTRK_HZ_PER_1000_PERFLVL) + 999;
+ do_div(temp, 1000);
+ freq = (u32)temp;
+ pr_debug("\n %s(): freq = %u\n", __func__, (u32)freq);
+
+ return (u32)freq;
+}
+
+u32 res_trk_power_up(void)
+{
+ pr_debug("clk_regime_rail_enable\n");
+ pr_debug("clk_regime_sel_rail_control\n");
+#ifdef AXI_CLK_SCALING
+{
+ int rc;
+ pr_debug("\n res_trk_power_up():: "
+ "Calling AXI add requirement\n");
+ rc = pm_qos_add_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+ MSM_AXI_QOS_NAME, PM_QOS_DEFAULT_VALUE);
+ if (rc < 0) {
+ pr_err("Request AXI bus QOS fails. rc = %d\n", rc);
+ return false;
+ }
+}
+#endif
+
+#ifdef USE_RES_TRACKER
+ pr_debug("\n res_trk_power_up():: Calling "
+ "vid_c_enable_pwr_rail()\n");
+ return vid_c_enable_pwr_rail();
+#endif
+ return true;
+}
+
+u32 res_trk_power_down(void)
+{
+ pr_debug("clk_regime_rail_disable\n");
+#ifdef AXI_CLK_SCALING
+ pr_debug("\n res_trk_power_down()::"
+ "Calling AXI remove requirement\n");
+ pm_qos_remove_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+ MSM_AXI_QOS_NAME);
+#endif
+
+#ifdef USE_RES_TRACKER
+ pr_debug("\n res_trk_power_down():: Calling "
+ "vid_c_disable_pwr_rail()\n");
+ return vid_c_disable_pwr_rail();
+#endif
+ return true;
+}
+
+u32 res_trk_enable_clocks(void)
+{
+ pr_debug("clk_regime_msm_enable\n");
+#ifdef USE_RES_TRACKER
+ pr_debug("\n res_trk_enable_clocks():: Calling "
+ "vid_c_enable_clk()\n");
+ return vid_c_enable_clk();
+#endif
+ return true;
+}
+
+u32 res_trk_disable_clocks(void)
+{
+ pr_debug("clk_regime_msm_disable\n");
+
+#ifdef USE_RES_TRACKER
+ pr_debug("\n res_trk_disable_clocks():: Calling "
+ "vid_c_disable_clk()\n");
+ return vid_c_disable_clk();
+#endif
+ return true;
+}
+
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl)
+{
+ if (!pn_max_perf_lvl) {
+ pr_err("%s(): pn_max_perf_lvl is NULL\n", __func__);
+ return false;
+ }
+
+ *pn_max_perf_lvl = VCD_RESTRK_MAX_PERF_LEVEL;
+ return true;
+}
+
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+ struct vcd_clnt_ctxt *cctxt)
+{
+ u32 axi_freq = 0, mfc_freq = 0, calc_mfc_freq = 0;
+
+ if (!pn_set_perf_lvl) {
+ pr_err("%s(): pn_perf_lvl is NULL\n", __func__);
+ return false;
+ }
+
+ pr_debug("%s(), req_perf_lvl = %d\n", __func__, req_perf_lvl);
+ if (cctxt) {
+ calc_mfc_freq = res_trk_convert_perf_lvl_to_freq(
+ (u64)req_perf_lvl);
+
+ if (calc_mfc_freq < VCD_RESTRK_MIN_FREQ_POINT)
+ calc_mfc_freq = VCD_RESTRK_MIN_FREQ_POINT;
+ else if (calc_mfc_freq > VCD_RESTRK_MAX_FREQ_POINT)
+ calc_mfc_freq = VCD_RESTRK_MAX_FREQ_POINT;
+
+ if (!cctxt->decoding) {
+ if (req_perf_lvl >= VGA_PERF_LEVEL) {
+ mfc_freq = mfc_clk_freq_table[2];
+ axi_freq = axi_clk_freq_table_enc[1];
+ } else {
+ mfc_freq = mfc_clk_freq_table[0];
+ axi_freq = axi_clk_freq_table_enc[0];
+ }
+ pr_debug("\n ENCODER: axi_freq = %u"
+ ", mfc_freq = %u, calc_mfc_freq = %u,"
+ " req_perf_lvl = %u", axi_freq,
+ mfc_freq, calc_mfc_freq,
+ req_perf_lvl);
+ } else {
+ if (req_perf_lvl <= QVGA_PERF_LEVEL) {
+ mfc_freq = mfc_clk_freq_table[0];
+ axi_freq = axi_clk_freq_table_dec[0];
+ } else {
+ axi_freq = axi_clk_freq_table_dec[0];
+ if (req_perf_lvl <= VGA_PERF_LEVEL)
+ mfc_freq = mfc_clk_freq_table[0];
+ else if (req_perf_lvl <= WVGA_PERF_LEVEL)
+ mfc_freq = mfc_clk_freq_table[1];
+ else {
+ mfc_freq = mfc_clk_freq_table[2];
+ axi_freq = axi_clk_freq_table_dec[1];
+ }
+ }
+ pr_debug("\n DECODER: axi_freq = %u"
+ ", mfc_freq = %u, calc_mfc_freq = %u,"
+ " req_perf_lvl = %u", axi_freq,
+ mfc_freq, calc_mfc_freq,
+ req_perf_lvl);
+ }
+ } else {
+ pr_debug("%s() WARNING:: cctxt is NULL\n", __func__);
+ return true;
+ }
+
+#ifdef AXI_CLK_SCALING
+ if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) {
+ int rc = -1;
+ pr_debug("\n %s(): Setting AXI freq to %u",
+ __func__, axi_freq);
+ rc = pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ,
+ MSM_AXI_QOS_NAME, axi_freq);
+
+ if (rc < 0) {
+ pr_err("\n Update AXI bus QOS fails,rc = %d\n", rc);
+ return false;
+ }
+ }
+#endif
+
+#ifdef USE_RES_TRACKER
+ if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) {
+ pr_debug("\n %s(): Setting MFC freq to %u",
+ __func__, mfc_freq);
+ if (!vid_c_sel_clk_rate(mfc_freq)) {
+ pr_err("%s(): vid_c_sel_clk_rate FAILED\n", __func__);
+ *pn_set_perf_lvl = 0;
+ return false;
+ }
+ }
+#endif
+
+ *pn_set_perf_lvl =
+ res_trk_convert_freq_to_perf_lvl((u64) mfc_freq);
+ return true;
+}
+
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl)
+{
+ unsigned long freq;
+
+ if (!pn_perf_lvl) {
+ pr_err("%s(): pn_perf_lvl is NULL\n", __func__);
+ return false;
+ }
+ pr_debug("clk_regime_msm_get_clk_freq_hz\n");
+ if (!vid_c_get_clk_rate(&freq)) {
+ pr_err("%s(): vid_c_get_clk_rate FAILED\n", __func__);
+ *pn_perf_lvl = 0;
+ return false;
+ }
+
+ *pn_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) freq);
+ pr_debug("%s(): freq = %lu, *pn_perf_lvl = %u\n", __func__,
+ freq, *pn_perf_lvl);
+ return true;
+}
diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h
new file mode 100644
index 0000000..f937e79
--- /dev/null
+++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_H_
+
+#include "vcd_res_tracker_api.h"
+
+#define VCD_RESTRK_MIN_PERF_LEVEL 37900
+#define VCD_RESTRK_MAX_PERF_LEVEL 108000
+#define VCD_RESTRK_MIN_FREQ_POINT 61440000
+#define VCD_RESTRK_MAX_FREQ_POINT 170667000
+#define VCD_RESTRK_HZ_PER_1000_PERFLVL 1580250
+
+#endif
diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h
new file mode 100644
index 0000000..b099d09
--- /dev/null
+++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VIDEO_720P_RESOURCE_TRACKER_API_H_
+#define _VIDEO_720P_RESOURCE_TRACKER_API_H_
+
+#include "vcd_core.h"
+
+u32 res_trk_power_up(void);
+u32 res_trk_power_down(void);
+u32 res_trk_enable_clocks(void);
+u32 res_trk_disable_clocks(void);
+u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl);
+u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl,
+ struct vcd_clnt_ctxt *cctxt);
+u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl);
+
+#endif
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c
new file mode 100644
index 0000000..169f082
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c
@@ -0,0 +1,1247 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+
+#include "vid_frame_scheduler_api.h"
+#include "vid_frame_scheduler.h"
+
+static const u32 SCHED_TKNBKT_SIZE_FACTOR = 5;
+static const u32 SCHED_TKNBKT_FILL_NORMLZ_SCALE = 100;
+static const u32 SCHED_TIME_MAX = 0xffffffff;
+
+
+SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status status)
+{
+ SCHED_MSG_LOW("SCHED_SUCCEEDED check: status = %d", status);
+
+ if (status == SCHED_S_OK)
+ return true;
+ else
+ return false;
+
+}
+
+SCHED_INLINE u32 SCHED_FAILED(enum sched_status status)
+{
+ SCHED_MSG_LOW("SCHED_FAILED check: status = %d", status);
+
+ if (status >= SCHED_S_EFAIL)
+ return true;
+ else
+ return false;
+
+}
+
+static void sched_clear_clnt_ctx(struct sched_clnt_ctx *ctx)
+{
+ if (ctx->clnt_frm_q)
+ SCHED_FREE(ctx->clnt_frm_q);
+ (void)SCHED_CRITSEC_RELEASE(ctx->clnt_cs);
+}
+
+SCHED_INLINE void sched_free_clnt_node(
+ struct _sched_clnt_list_node *clnt_node)
+{
+ sched_clear_clnt_ctx(&clnt_node->data);
+ SCHED_FREE(clnt_node);
+
+}
+
+enum sched_status sched_clear_clnt_list(
+ struct _sched_clnt_list_node *clnt_lst) {
+ struct _sched_clnt_list_node *clnt_node;
+
+ while (clnt_lst) {
+ (void)SCHED_CRITSEC_ENTER(clnt_lst->data.clnt_cs);
+ clnt_node = clnt_lst;
+ clnt_lst = clnt_lst->next;
+ sched_free_clnt_node(clnt_node);
+ }
+ return SCHED_S_OK;
+}
+
+static SCHED_INLINE enum sched_status sched_alloc_frm_q(
+ struct sched_clnt_ctx *ctx)
+{
+ ctx->clnt_frm_q = (struct sched_clnt_q_elem *)
+ SCHED_MALLOC(sizeof(struct sched_clnt_q_elem) *
+ ctx->max_queue_len);
+
+ if (!ctx->clnt_frm_q) {
+ SCHED_MSG_ERR("Could not allocate clnt frm Q. Out of memory");
+ return SCHED_S_ENOMEM;
+ }
+
+ SCHED_MEMSET(ctx->clnt_frm_q,
+ 0, sizeof(struct sched_clnt_q_elem) * ctx->max_queue_len);
+ ctx->q_head = 0;
+ ctx->q_tail = -1;
+ ctx->q_len = 0;
+ SCHED_MSG_MED("Clnt frm Q allocted & initialized");
+ return SCHED_S_OK;
+
+}
+
+static SCHED_INLINE void sched_de_q_head_frm
+ (struct sched_clnt_ctx *ctx,
+ struct sched_clnt_q_elem *q_elem) {
+ *q_elem = ctx->clnt_frm_q[ctx->q_head];
+
+ memset(&ctx->clnt_frm_q[ctx->q_head], 0,
+ sizeof(struct sched_clnt_q_elem));
+
+ /*Update the circular queue head index.*/
+ ctx->q_head = (ctx->q_head + 1) % ctx->max_queue_len;
+ ctx->q_len--;
+}
+
+static SCHED_INLINE void sched_tkn_bkt_fill_normalize
+ (struct sched_clnt_ctx *ctx)
+{
+ ctx->bkt_curr_tkns_nmlzd =
+ (ctx->bkt_curr_tkns * SCHED_TKNBKT_FILL_NORMLZ_SCALE) /
+ ctx->tkn_per_frm;
+}
+
+static void sched_tkn_bkt_config(struct sched_clnt_ctx *ctx)
+{
+ ctx->bkt_size = ctx->tkn_per_frm * SCHED_TKNBKT_SIZE_FACTOR;
+ ctx->bkt_quies_cap = ctx->bkt_size;
+ ctx->bkt_curr_tkns =
+ SCHED_MIN(ctx->bkt_curr_tkns, ctx->bkt_size);
+}
+
+static void sched_tkn_bkt_supply(
+ struct sched_clnt_ctx *ctx, u32 curr_time)
+{
+ u32 delta;
+ u32 num_tkns;
+
+ /*Check if there's time wrap-around since last token supply time.*/
+ if (curr_time < ctx->bkt_lst_sup_time) {
+ SCHED_MSG_HIGH("Current time wrap around detected");
+ delta =
+ SCHED_TIME_MAX - ctx->bkt_lst_sup_time + curr_time;
+ } else
+ delta = curr_time - ctx->bkt_lst_sup_time;
+
+ /*Proceed only if there is any time elapsed since our last supply
+ time.*/
+ if (delta > 0) {
+ /*Calculate the number of tokens that we can supply based on
+ time elapsed and the client's token supply rate.*/
+ num_tkns = delta * ctx->curr_p_tkn_rate / 1000;
+
+ if (num_tkns > 0) {
+ ctx->bkt_curr_tkns = SCHED_MIN(ctx->bkt_size,
+ ctx->bkt_curr_tkns + num_tkns);
+
+ if ((delta * ctx->curr_p_tkn_rate % 1000)) {
+ delta = (num_tkns * 1000 +
+ (ctx->curr_p_tkn_rate >> 1))
+ / ctx->curr_p_tkn_rate;
+ if ((SCHED_TIME_MAX -
+ ctx->bkt_lst_sup_time) < delta) {
+ SCHED_MSG_HIGH
+ ("Handling for current time wrap "
+ "around");
+
+ ctx->bkt_lst_sup_time = delta -
+ (SCHED_TIME_MAX -
+ ctx->bkt_lst_sup_time);
+ } else
+ ctx->bkt_lst_sup_time += delta;
+ } else
+ ctx->bkt_lst_sup_time = curr_time;
+
+ if (ctx->bkt_curr_tkns >
+ (s32) ctx->bkt_quies_cap) {
+ SCHED_MSG_HIGH
+ ("Client Quiesence detected. Capping "
+ "bkt_curr_tkns");
+ ctx->bkt_curr_tkns = ctx->tkn_per_frm;
+ }
+ sched_tkn_bkt_fill_normalize(ctx);
+ }
+ }
+}
+
+static SCHED_INLINE void sched_tkn_bkt_consume(
+ struct sched_clnt_ctx *ctx) {
+ ctx->bkt_curr_tkns -= ctx->tkn_per_frm;
+}
+
+static SCHED_INLINE u32 sched_clnt_frm_is_cnfmnt
+ (struct sched_clnt_ctx *ctx)
+{
+ if (ctx->bkt_curr_tkns >= (s32) ctx->tkn_per_frm)
+ return true;
+ else
+ return false;
+} /* end of sched_clnt_frm_is_conformant */
+
+static struct sched_clnt_ctx *sched_elect_cnfmnt
+ (struct sched_clnt_ctx *prov_elect,
+ struct sched_clnt_ctx *new_cand) {
+
+ /*If there is no provisional elect client then the new candidate
+ becomes the first one.*/
+ if (!prov_elect)
+ return new_cand;
+
+
+ /*Here we want to pick the client who has accumulated the most tokens
+ from the time of attaining single frame conformance.
+ Since we are comparing between clients we use the available normalized
+ token bucket occupancy value.*/
+ if (prov_elect->bkt_curr_tkns_nmlzd >=
+ new_cand->bkt_curr_tkns_nmlzd) {
+ return prov_elect;
+ } else {
+ /*We had held on to this provisional elect conformant
+ client critical section. Since new candidate has won the
+ election leave critical section of earlier provisional
+ elect.
+ */
+ (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs);
+ return new_cand;
+ }
+}
+
+static struct sched_clnt_ctx *sched_elect_non_cnfmnt
+ (struct sched_clnt_ctx *prov_elect,
+ struct sched_clnt_ctx *new_cand) {
+
+ /*If there is no provisional elect client then the new candidate
+ becomes the first one.*/
+ if (!prov_elect)
+ return new_cand;
+ /*Here we want to pick the client who is closest to attaining a single
+ frame conformance.
+ Since we are comparing between clients we use the available
+ normalized token bucket occupancy value.
+ Also if the provisional elect or the new contender (in that order)
+ have an end of frame marker set we give it priority over deciding
+ by frame conformance method mentiond earlier.*/
+ if (prov_elect->eof_marker > 0) {
+ return prov_elect;
+ } else if (new_cand->eof_marker > 0) {
+ /*We had held on to this provisional elect non conformant client
+ critical section. Since new candidate has won the election
+ leave critical section of earlier provisional elect.
+ */
+ (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs);
+
+ return new_cand;
+ } else if (prov_elect->bkt_curr_tkns_nmlzd >=
+ new_cand->bkt_curr_tkns_nmlzd) {
+ return prov_elect;
+ } else {
+ /*Similar to above case leave critical section of earlier
+ provisional elect.*/
+ (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs);
+ return new_cand;
+ }
+
+}
+
+static struct sched_clnt_ctx *sched_elect_non_rt
+ (struct sched_ctx *sched_ctx) {
+ struct _sched_clnt_list_node *node = NULL;
+ struct _sched_clnt_list_node *start_node = NULL;
+ u32 found = false;
+
+ /*For non real time clients we are using a round robin election
+ algorithm.
+ Based on the last scheduled client we find the next to schedule
+ and return its context.
+ We also need to skip the client if certain conditions (mentioned below)
+ are not met*/
+ if (!sched_ctx->non_rt_last_sched)
+ start_node = node = sched_ctx->non_rt_head;
+ else {
+ if (!sched_ctx->non_rt_last_sched->next)
+ start_node = sched_ctx->non_rt_head;
+ else
+ start_node = sched_ctx->non_rt_last_sched->next;
+
+ node = start_node;
+ }
+
+ do {
+
+ (void)SCHED_CRITSEC_ENTER(node->data.clnt_cs);
+
+ /*Check if the client can be considered for this round of scheduling.*/
+ if (sched_consider_clnt_for_sched(&node->data)) {
+ found = true;
+ sched_ctx->non_rt_last_sched = node;
+ }
+
+ /*If this client is not the election winner then leave its critical
+ section.
+ If we have found a winner we want to hold on to its critical
+ section. We would leave its critical section after we are done
+ with dequeueing a frame from the client context.*/
+ if (!found)
+ (void)SCHED_CRITSEC_LEAVE(node->data.clnt_cs);
+
+ if (!node->next)
+ node = sched_ctx->non_rt_head;
+ else
+ node = node->next;
+
+ } while (node != start_node);
+
+ if (found) {
+ SCHED_MSG_LOW("Non real time client selected");
+
+ return &sched_ctx->non_rt_last_sched->data;
+ } else {
+ SCHED_MSG_MED
+ ("No non-real time client available for scheduling");
+
+ return NULL;
+ }
+
+}
+
+static enum sched_status sched_process_set_p_tkn_rate(
+ struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx *clnt_ctx,
+ union sched_value_type *param_value) {
+ u32 curr_time = 0;
+
+ if (param_value->un_value == clnt_ctx->curr_p_tkn_rate)
+ return SCHED_S_OK;
+
+
+ if ((sched_ctx->total_clnt_bw - clnt_ctx->curr_p_tkn_rate +
+ param_value->un_value) > sched_ctx->perf_lvl) {
+ SCHED_MSG_HIGH
+ ("Perf level insufficient for requested P Tkn rate");
+
+ }
+
+ /*Get current time. We need this for token supply.
+ If we didn't get a valid current time value just return*/
+ if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&curr_time))) {
+ SCHED_MSG_ERR("Get current time failed");
+
+ return SCHED_S_EFAIL;
+ }
+
+ /*Before we go ahead and update the Current tkn rate, we fill
+ the token bucket upto current time instance.*/
+ sched_tkn_bkt_supply(clnt_ctx, curr_time);
+
+ /*Next, update the current value of total client bandwidth with
+ the new tkn rate of the client.*/
+ sched_ctx->total_clnt_bw = sched_ctx->total_clnt_bw -
+ clnt_ctx->curr_p_tkn_rate + param_value->un_value;
+ clnt_ctx->curr_p_tkn_rate = param_value->un_value;
+
+ /*Since the current Ptkn rate (i.e. current alloted bandwidth)
+ of the client has changed we need to update client's token
+ bucket configuration*/
+ sched_tkn_bkt_config(clnt_ctx);
+ return SCHED_S_OK;
+}
+
+static enum sched_status sched_process_add_rt_clnt(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node) {
+ enum sched_status status;
+ struct sched_clnt_ctx *clnt_ctx = &clnt_node->data;
+ struct _sched_clnt_list_node *tmp_node;
+
+ /*Validate real time client specific parameters.*/
+ if (!clnt_ctx->curr_p_tkn_rate)
+ SCHED_MSG_HIGH("Allocated token rate is zero");
+
+ /*Check if our performance level setting can sustain the new client*/
+ if (sched_ctx->total_clnt_bw + clnt_ctx->curr_p_tkn_rate >
+ sched_ctx->perf_lvl) {
+ SCHED_MSG_HIGH("Not enough bandwidth to support client");
+ SCHED_MSG_HIGH
+ ("curr_perflvl=%d, curr_bw=%d, newclnt_ptknrate=%d",
+ sched_ctx->perf_lvl, sched_ctx->total_clnt_bw,
+ clnt_ctx->curr_p_tkn_rate);
+
+ }
+ /*Allocate the client frame queue*/
+ status = sched_alloc_frm_q(clnt_ctx);
+
+ if (SCHED_SUCCEEDED(status)) {
+ /*Allocate the token bucket*/
+ sched_tkn_bkt_config(clnt_ctx);
+ /*We start with empty token bucket*/
+ clnt_ctx->bkt_curr_tkns = 0;
+ clnt_ctx->bkt_curr_tkns_nmlzd = 0;
+ /*Add the client to the real time client list and increase the
+ total client bandwidth.*/
+ tmp_node = sched_ctx->rt_head;
+ sched_ctx->rt_head = clnt_node;
+ sched_ctx->rt_head->next = tmp_node;
+ sched_ctx->rt_clnts++;
+ sched_ctx->total_clnt_bw += clnt_ctx->curr_p_tkn_rate;
+ }
+ return status;
+}
+
+static enum sched_status sched_process_add_non_rt_clnt(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node) {
+ enum sched_status status;
+ struct sched_clnt_ctx *clnt_ctx = &clnt_node->data;
+ struct _sched_clnt_list_node *tmp_node;
+
+ /*Allocate the client frame queue*/
+ status = sched_alloc_frm_q(clnt_ctx);
+ if (SCHED_SUCCEEDED(status)) {
+ /*Add the client to the real time client list and increase the
+ total client bandwidth.*/
+ tmp_node = sched_ctx->non_rt_head;
+ sched_ctx->non_rt_head = clnt_node;
+ sched_ctx->non_rt_head->next = tmp_node;
+ sched_ctx->non_rt_clnts++;
+ }
+ return status;
+}
+
+enum sched_status sched_process_add_clnt(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node,
+ struct sched_client_init_param *init_param) {
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MEMSET(clnt_node, 0, sizeof(struct _sched_clnt_list_node));
+
+ /*Validate all initialization parameters*/
+ if (!init_param->tkn_per_frm ||
+ !init_param->frm_rate.numer ||
+ !init_param->frm_rate.denom ||
+ !init_param->max_queue_len ||
+ !init_param->o_tkn_max ||
+ !init_param->o_tkn_per_ip_frm ||
+ init_param->o_tkn_init > init_param->o_tkn_max ||
+ init_param->o_tkn_per_ip_frm > init_param->o_tkn_max) {
+ SCHED_MSG_ERR("Bad initialization parameters");
+ return SCHED_S_EBADPARM;
+ }
+
+ /*Store all initialization parameters*/
+ clnt_node->data.client_ctgy = init_param->client_ctgy;
+ clnt_node->data.curr_p_tkn_rate = init_param->alloc_p_tkn_rate;
+ clnt_node->data.frm_rate = init_param->frm_rate;
+ clnt_node->data.max_queue_len = init_param->max_queue_len;
+ clnt_node->data.o_tkn_max = init_param->o_tkn_max;
+ clnt_node->data.o_tkn_per_ip_frm = init_param->o_tkn_per_ip_frm;
+ clnt_node->data.curr_o_tkns = init_param->o_tkn_init;
+ clnt_node->data.tkn_per_frm = init_param->tkn_per_frm;
+ clnt_node->data.client_data = init_param->client_data;
+ clnt_node->data.sched_state = true;
+
+ SCHED_MSG_HIGH("Adding new client of category %d",
+ clnt_node->data.client_ctgy);
+ SCHED_MSG_MED("Allocated P token rate (per sec) = %d",
+ clnt_node->data.curr_p_tkn_rate);
+ SCHED_MSG_MED("Frame rate = %d / %d",
+ clnt_node->data.frm_rate.numer,
+ clnt_node->data.frm_rate.denom);
+ SCHED_MSG_MED("Max_queue_len = %d", clnt_node->data.max_queue_len);
+ SCHED_MSG_MED("Max O tokens = %d", clnt_node->data.o_tkn_max);
+ SCHED_MSG_MED("O tokens threshold = %d",
+ clnt_node->data.o_tkn_per_ip_frm);
+ SCHED_MSG_MED("P tokens per frame = %d",
+ clnt_node->data.tkn_per_frm);
+ SCHED_MSG_MED("Client data ptr = %p", clnt_node->data.client_data);
+
+ if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&clnt_node->data.clnt_cs)))
+ return SCHED_S_EFAIL;
+
+ /*Configure the client context based on client category.*/
+ switch (clnt_node->data.client_ctgy) {
+ case SCHED_CLNT_RT_BUFF:
+ case SCHED_CLNT_RT_NOBUFF:
+ {
+ status =
+ sched_process_add_rt_clnt(sched_ctx, clnt_node);
+ break;
+ }
+
+ case SCHED_CLNT_NONRT:
+ {
+ status =
+ sched_process_add_non_rt_clnt(sched_ctx,
+ clnt_node);
+ break;
+ }
+
+ default:
+ {
+ status = SCHED_S_EBADPARM;
+ break;
+ }
+
+ }
+ return status;
+}
+
+enum sched_status sched_process_remove_clnt(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node) {
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+
+ /*Handling if the client frame queue is not empty. Just return
+ and let Codec driver dequeue all frames for this client
+ before calling remove client*/
+ if (clnt_node->data.q_len) {
+ SCHED_MSG_ERR("Cannot remove client. Queue is not empty");
+ return SCHED_S_EINVALST;
+ }
+
+ /*Based on client category, remove the client node from the
+ appropriate scheduler client list*/
+ switch (clnt_node->data.client_ctgy) {
+ case SCHED_CLNT_RT_BUFF:
+ case SCHED_CLNT_RT_NOBUFF:
+ {
+
+ sched_remove_node_from_list(&sched_ctx->rt_head,
+ clnt_node);
+ sched_ctx->rt_clnts--;
+ sched_ctx->total_clnt_bw -=
+ clnt_node->data.curr_p_tkn_rate;
+ break;
+ }
+
+ case SCHED_CLNT_NONRT:
+ {
+ sched_remove_node_from_list(&sched_ctx->non_rt_head,
+ clnt_node);
+ sched_ctx->non_rt_clnts--;
+ break;
+ }
+
+ default:
+ {
+ SCHED_ASSERT(0);
+ break;
+ }
+ }
+
+ /*Now that client node is off the scheduler client list free up
+ resources that its been using.*/
+ SCHED_MSG_HIGH("Removing new client of category %d",
+ clnt_node->data.client_ctgy);
+ SCHED_MSG_MED("Allocated P token rate (per sec) = %d",
+ clnt_node->data.curr_p_tkn_rate);
+ SCHED_MSG_MED("Frame rate = %d / %d",
+ clnt_node->data.frm_rate.numer,
+ clnt_node->data.frm_rate.denom);
+ SCHED_MSG_MED("Max_queue_len = %d", clnt_node->data.max_queue_len);
+ SCHED_MSG_MED("Max O tokens = %d", clnt_node->data.o_tkn_max);
+ SCHED_MSG_MED("P tokens per frame = %d",
+ clnt_node->data.tkn_per_frm);
+ SCHED_MSG_MED("Client data ptr = %p", clnt_node->data.client_data);
+ sched_free_clnt_node(clnt_node);
+ return SCHED_S_OK;
+}
+
+enum sched_status sched_process_flush_clnt_buff(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, void **pp_frm_data) {
+ struct sched_clnt_ctx *clnt_ctx;
+ enum sched_status status = SCHED_S_OK;
+ struct sched_clnt_q_elem q_elem;
+
+ clnt_ctx = &clnt_node->data;
+
+ /*If the client queue is empty just return an QEMPTY status*/
+ if (!clnt_ctx->q_len) {
+ status = SCHED_S_QEMPTY;
+ } else {
+ clnt_ctx->flushing = true;
+
+ /*If the client queue is not empty just remove and return the
+ element at the front of the queue.*/
+ sched_de_q_head_frm(clnt_ctx, &q_elem);
+ *pp_frm_data = q_elem.frm_data;
+ }
+
+ /*If the Queue was orginially empty OR if it got empty after latest
+ De_queue we reset the flushing and First_frame flags.
+ Token bucket contents are also emptied.Queue pointers are reset.
+ o_tkns are restored.*/
+ if (!clnt_ctx->q_len) {
+ clnt_ctx->flushing = false;
+ clnt_ctx->first_frm = false;
+ clnt_ctx->bkt_curr_tkns = 0;
+ clnt_ctx->bkt_curr_tkns_nmlzd = 0;
+ clnt_ctx->bkt_lst_sup_time = 0;
+ clnt_ctx->q_head = 0;
+ clnt_ctx->q_tail = -1;
+ SCHED_MSG_HIGH
+ ("Client flushed and re-initialized. Client category %d",
+ clnt_ctx->client_ctgy);
+ SCHED_MSG_MED("Client allocated P token rate (per sec) = %d",
+ clnt_ctx->curr_p_tkn_rate);
+ SCHED_MSG_MED("Client frame rate = %d / %d",
+ clnt_ctx->frm_rate.numer,
+ clnt_ctx->frm_rate.denom);
+ SCHED_MSG_MED("Client P tokens per frame = %d",
+ clnt_ctx->tkn_per_frm);
+ }
+ return status;
+}
+
+SCHED_INLINE enum sched_status sched_process_mark_clnt_eof(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node) {
+
+ if (!clnt_node->data.q_len)
+ return SCHED_S_QEMPTY;
+
+
+ if (!clnt_node->data.clnt_frm_q[clnt_node->data.q_tail].eof) {
+ /*Just increment the EOF marker count in the client context.*/
+ clnt_node->data.eof_marker++;
+ clnt_node->data.clnt_frm_q[clnt_node->data.q_tail].
+ eof = true;
+ } else
+ SCHED_MSG_HIGH("Current frame is already marked EOF");
+
+ SCHED_MSG_HIGH("Client marked for end of frames. Client category %d",
+ clnt_node->data.client_ctgy);
+ SCHED_MSG_MED("Client allocated P token rate (per sec) = %d",
+ clnt_node->data.curr_p_tkn_rate);
+ SCHED_MSG_MED("Client frame rate = %d / %d",
+ clnt_node->data.frm_rate.numer,
+ clnt_node->data.frm_rate.denom);
+ SCHED_MSG_MED("Client P tokens per frame = %d",
+ clnt_node->data.tkn_per_frm);
+ return SCHED_S_OK;
+}
+
+enum sched_status sched_process_update_clnt_o_tkn(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node,
+ u32 type, u32 o_tkn) {
+
+ /*Act based on the type of update.*/
+
+ if (type) {
+ /*Just replenish the output tokens the client currently has with
+ the provided number while not going over the max value.*/
+ clnt_node->data.curr_o_tkns =
+ SCHED_MIN(clnt_node->data.curr_o_tkns + o_tkn,
+ clnt_node->data.o_tkn_max);
+ } else {
+ /*Just subtract the give number of output tokens from the count
+ the client currently has while not going less than 0.*/
+ if (o_tkn >= clnt_node->data.curr_o_tkns)
+ clnt_node->data.curr_o_tkns = 0;
+ else
+ clnt_node->data.curr_o_tkns -= o_tkn;
+
+ }
+
+ SCHED_MSG_LOW("%d O tokens restored for client", o_tkn);
+ SCHED_MSG_LOW("Client Curr_o_tkns = %d",
+ clnt_node->data.curr_o_tkns);
+ SCHED_MSG_LOW("Client category = %d", clnt_node->data.client_ctgy);
+ SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d",
+ clnt_node->data.curr_p_tkn_rate);
+ SCHED_MSG_LOW("Client frame rate = %d / %d",
+ clnt_node->data.frm_rate.numer,
+ clnt_node->data.frm_rate.denom);
+ SCHED_MSG_LOW("Client P tokens per frame = %d",
+ clnt_node->data.tkn_per_frm);
+ return SCHED_S_OK;
+}
+
+enum sched_status sched_process_en_q_frm(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, void *frm_data) {
+ struct sched_clnt_ctx *clnt_ctx;
+ u32 curr_time = 0;
+
+ clnt_ctx = &clnt_node->data;
+
+ /*Check if the client queue is full already*/
+ if (clnt_ctx->q_len == clnt_ctx->max_queue_len) {
+ SCHED_MSG_HIGH("Cannot enqueue. Client queue is full");
+
+ return SCHED_S_QFULL;
+ }
+
+ /*Check if the client queue is being flushed.*/
+ if (clnt_ctx->flushing) {
+ SCHED_MSG_ERR("Cannot enqueue. Client queue is being flushed");
+
+ return SCHED_S_EINVALST;
+ }
+
+ /*Reposition tail, increase Q length and add the frame data to Q*/
+ clnt_ctx->q_tail =
+ (clnt_ctx->q_tail + 1) % clnt_ctx->max_queue_len;
+
+ clnt_ctx->q_len++;
+
+ clnt_ctx->clnt_frm_q[clnt_ctx->q_tail].frm_data = frm_data;
+ clnt_ctx->clnt_frm_q[clnt_ctx->q_tail].eof = false;
+
+ /*If this is the first frame being queued for this client then,
+ get current time. We now start the token supply clock for the client.
+ Supply tokens required for a single frame processing while storing
+ the current time as the last supply time and marking that first
+ frame is received.*/
+ if (!clnt_ctx->first_frm) {
+ SCHED_MSG_HIGH("Client first frame enqueued");
+ if (clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) {
+ if (SCHED_SUCCEEDED
+ (SCHED_GET_CURRENT_TIME(&curr_time))) {
+ clnt_ctx->bkt_curr_tkns =
+ clnt_ctx->tkn_per_frm;
+ clnt_ctx->bkt_lst_sup_time = curr_time;
+ clnt_ctx->first_frm = true;
+ }
+ } else
+ clnt_ctx->first_frm = true;
+ }
+
+ SCHED_MSG_LOW("Client frame enqueued. Queue fill status = %d / %d",
+ clnt_ctx->q_len, clnt_ctx->max_queue_len);
+ SCHED_MSG_LOW("Client category = %d", clnt_ctx->client_ctgy);
+ SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d",
+ clnt_ctx->curr_p_tkn_rate);
+ SCHED_MSG_LOW("Client frame rate = %d / %d",
+ clnt_ctx->frm_rate.numer,
+ clnt_ctx->frm_rate.denom);
+ SCHED_MSG_LOW("Client P tokens per frame = %d",
+ clnt_ctx->tkn_per_frm);
+
+ return SCHED_S_OK;
+
+}
+
+enum sched_status sched_process_re_en_q_frm(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node,
+ void *frm_data)
+{
+ struct sched_clnt_ctx *clnt_ctx;
+ u32 curr_time = 0;
+
+ clnt_ctx = &clnt_node->data;
+
+ if (clnt_ctx->q_len == clnt_ctx->max_queue_len) {
+ SCHED_MSG_ERR("Cannot re-enqueue. Client queue is full");
+ return SCHED_S_QFULL;
+ }
+
+ if (clnt_ctx->flushing) {
+ SCHED_MSG_ERR("Cannot re-enqueue. Client"
+ " queue is being flushed");
+ return SCHED_S_EINVALST;
+ }
+
+ clnt_ctx->q_head =
+ (clnt_ctx->q_head + clnt_ctx->max_queue_len - 1) %
+ clnt_ctx->max_queue_len;
+
+ clnt_ctx->q_len++;
+
+ clnt_ctx->clnt_frm_q[clnt_ctx->q_head].frm_data =
+ frm_data;
+ clnt_ctx->clnt_frm_q[clnt_ctx->q_head].eof =
+ false;
+
+ if (clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) {
+ if (!clnt_ctx->first_frm) {
+ SCHED_MSG_HIGH("Client frame "
+ "re-enqueued as first frame");
+ if (SCHED_SUCCEEDED
+ (SCHED_GET_CURRENT_TIME(&curr_time))) {
+ clnt_ctx->bkt_curr_tkns =
+ clnt_ctx->tkn_per_frm;
+ clnt_ctx->bkt_lst_sup_time =
+ curr_time;
+ clnt_ctx->first_frm =
+ true;
+ }
+ } else
+ clnt_ctx->bkt_curr_tkns +=
+ clnt_ctx->tkn_per_frm;
+ } else
+ clnt_ctx->first_frm = true;
+
+
+ SCHED_MSG_LOW("Client frame re-enqueued. Queue fill status = %d / %d",
+ clnt_ctx->q_len, clnt_ctx->max_queue_len);
+ SCHED_MSG_LOW("Client category = %d", clnt_ctx->client_ctgy);
+ SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d",
+ clnt_ctx->curr_p_tkn_rate);
+ SCHED_MSG_LOW("Client frame rate = %d / %d",
+ clnt_ctx->frm_rate.numer,
+ clnt_ctx->frm_rate.denom);
+ SCHED_MSG_LOW("Client P tokens per frame = %d",
+ clnt_ctx->tkn_per_frm);
+
+ return SCHED_S_OK;
+
+}
+
+enum sched_status sched_process_de_q_frm_rt_clnt(
+ struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx **pp_conf_elect_ctx,
+ struct sched_clnt_ctx **pp_non_conf_elect_ctx) {
+ u32 curr_time = 0;
+ struct _sched_clnt_list_node *clnt_node;
+ struct sched_clnt_ctx *clnt_ctx;
+
+ *pp_conf_elect_ctx = NULL;
+ *pp_non_conf_elect_ctx = NULL;
+
+ /*Get current time. We need this for token supply.
+ If we didn't get a valid current time value just return*/
+ if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&curr_time))) {
+ SCHED_MSG_ERR("Get current time failed");
+
+ return SCHED_S_EFAIL;
+ }
+
+ /*Run through the list of real time clients.
+ Consider only the clients that have queued atleast one frame since
+ being admitted into the scheduler.
+ Supply tokens equivalent to elapsed time since last supply.
+ Also in this same pass, check if each client has a conformant
+ frame or not.*/
+ clnt_node = sched_ctx->rt_head;
+ while (clnt_node) {
+ clnt_ctx = &clnt_node->data;
+
+ (void)SCHED_CRITSEC_ENTER(clnt_ctx->clnt_cs);
+
+ if (sched_consider_clnt_for_sched(clnt_ctx)) {
+ sched_tkn_bkt_supply(clnt_ctx, curr_time);
+ if (sched_clnt_frm_is_cnfmnt(clnt_ctx)) {
+ *pp_conf_elect_ctx =
+ sched_elect_cnfmnt(*pp_conf_elect_ctx,
+ clnt_ctx);
+ } else {
+ if (!*pp_conf_elect_ctx) {
+ *pp_non_conf_elect_ctx =
+ sched_elect_non_cnfmnt
+ (*pp_non_conf_elect_ctx,
+ clnt_ctx);
+ } else if (*pp_non_conf_elect_ctx) {
+ (void)
+ SCHED_CRITSEC_LEAVE(
+ (*pp_non_conf_elect_ctx)->clnt_cs);
+ *pp_non_conf_elect_ctx = NULL;
+
+ }
+ }
+ }
+ if (clnt_ctx != *pp_conf_elect_ctx
+ && clnt_ctx != *pp_non_conf_elect_ctx)
+ (void)SCHED_CRITSEC_LEAVE(clnt_ctx->clnt_cs);
+ clnt_node = clnt_node->next;
+ }
+
+ return SCHED_S_OK;
+
+}
+
+enum sched_status sched_process_de_q_frm(
+ struct sched_ctx *sched_ctx,
+ void **pp_frm_data, void **pp_client_data) {
+ enum sched_status status;
+ struct sched_clnt_ctx *sched_clnt_ctx = NULL;
+ struct sched_clnt_ctx *conf_elect_ctx;
+ struct sched_clnt_ctx *non_conf_elect_ctx;
+ struct sched_clnt_q_elem q_elem;
+
+ status = sched_process_de_q_frm_rt_clnt(sched_ctx,
+ &conf_elect_ctx,
+ &non_conf_elect_ctx);
+ if (SCHED_FAILED(status)) {
+ SCHED_MSG_ERR("sched_process_de_q_frm_rt_clnt ret err=%d",
+ status);
+
+ return status;
+ }
+
+ /*At this point we have looked at all real time clients in the
+ scheduler list and have run their elections.
+ We used the following frame service order to pick the client to
+ schedule:
+ a) client with conformant frame
+ b) client with non-conformant frame
+ c) non real-time client*/
+ if (conf_elect_ctx) {
+ SCHED_MSG_LOW("Conformant frame client selected");
+ sched_tkn_bkt_consume(conf_elect_ctx);
+ sched_clnt_ctx = conf_elect_ctx;
+ } else if (non_conf_elect_ctx) {
+ SCHED_MSG_LOW("Non-Conformant frame client selected");
+ sched_tkn_bkt_consume(non_conf_elect_ctx);
+ sched_clnt_ctx = non_conf_elect_ctx;
+ } else if (sched_ctx->non_rt_clnts)
+ sched_clnt_ctx = sched_elect_non_rt(sched_ctx);
+
+ /*If we have a client that we can schedule, then dequeue the frame
+ at the head of its queue.*/
+ if (sched_clnt_ctx) {
+ *pp_client_data = sched_clnt_ctx->client_data;
+
+ sched_de_q_head_frm(sched_clnt_ctx, &q_elem);
+
+ *pp_frm_data = q_elem.frm_data;
+
+ sched_clnt_ctx->curr_o_tkns -=
+ sched_clnt_ctx->o_tkn_per_ip_frm;
+
+ /*If the dequeued frame was marked EOF we need to decrement the
+ eof_marker count.*/
+ if (q_elem.eof) {
+ SCHED_MSG_MED
+ ("Last frame for EOF marked client dequeued");
+
+ sched_clnt_ctx->eof_marker--;
+
+ status = SCHED_S_EOF;
+ }
+
+ SCHED_MSG_LOW
+ ("Client frame Dequeued. Queue fill status = %d / %d",
+ sched_clnt_ctx->q_len,
+ sched_clnt_ctx->max_queue_len);
+ SCHED_MSG_LOW("Client category = %d",
+ sched_clnt_ctx->client_ctgy);
+ SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d",
+ sched_clnt_ctx->curr_p_tkn_rate);
+ SCHED_MSG_LOW("Client frame rate = %d / %d",
+ sched_clnt_ctx->frm_rate.numer,
+ sched_clnt_ctx->frm_rate.denom);
+ SCHED_MSG_LOW("Client P tokens per frame = %d",
+ sched_clnt_ctx->tkn_per_frm);
+
+ /*We had held on to the election winning client critical
+ section. Leave client critical section before we exit.*/
+ (void)SCHED_CRITSEC_LEAVE(sched_clnt_ctx->clnt_cs);
+ } else {
+ status = SCHED_S_QEMPTY;
+ }
+
+ return status;
+
+}
+
+enum sched_status sched_process_sched_lvl_get_param(
+ struct sched_ctx *sched_ctx,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ enum sched_status status = SCHED_S_OK;
+
+ switch (param_index) {
+ case SCHED_I_PERFLEVEL:
+ {
+ param_value->un_value = sched_ctx->perf_lvl;
+ break;
+ }
+
+ default:
+ {
+ status = SCHED_S_EBADPARM;
+ break;
+ }
+ }
+ return status;
+}
+
+enum sched_status sched_process_sched_lvl_set_param(
+ struct sched_ctx *sched_ctx,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("Set_sched_param index = %u, value = %p",
+ param_index, (void *)param_value);
+
+ switch (param_index) {
+ case SCHED_I_PERFLEVEL:
+ {
+ if (sched_ctx->total_clnt_bw >
+ param_value->un_value) {
+ SCHED_MSG_HIGH
+ ("Perf level being lowered than current "
+ "bandwidth");
+ SCHED_MSG_HIGH
+ ("curr_perflvl=%d, new_perflvl=%d, "
+ "curr_bw=%d",
+ sched_ctx->perf_lvl,
+ param_value->un_value,
+ sched_ctx->total_clnt_bw);
+ }
+
+ sched_ctx->perf_lvl = param_value->un_value;
+
+ break;
+ }
+
+ default:
+ {
+ status = SCHED_S_EBADPARM;
+ break;
+ }
+ }
+ return status;
+}
+
+enum sched_status sched_process_clnt_lvl_get_param(
+ struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx *clnt_ctx,
+ enum sched_index param_index,
+ union sched_value_type *param_value) {
+ enum sched_status status = SCHED_S_OK;
+
+ switch (param_index) {
+ case SCHED_I_CLNT_CURRQLEN:
+ {
+ param_value->un_value = clnt_ctx->q_len;
+ break;
+ }
+
+ case SCHED_I_CLNT_PTKNRATE:
+ {
+ param_value->un_value = clnt_ctx->curr_p_tkn_rate;
+ break;
+ }
+
+ case SCHED_I_CLNT_PTKNPERFRM:
+ {
+ param_value->un_value = clnt_ctx->tkn_per_frm;
+ break;
+ }
+
+ case SCHED_I_CLNT_FRAMERATE:
+ {
+ param_value->frm_rate = clnt_ctx->frm_rate;
+ break;
+ }
+
+ case SCHED_I_CLNT_OTKNMAX:
+ {
+ param_value->un_value = clnt_ctx->o_tkn_max;
+ break;
+ }
+
+ case SCHED_I_CLNT_OTKNPERIPFRM:
+ {
+ param_value->un_value =
+ clnt_ctx->o_tkn_per_ip_frm;
+ break;
+ }
+
+ case SCHED_I_CLNT_OTKNCURRENT:
+ {
+ param_value->un_value = clnt_ctx->curr_o_tkns;
+ break;
+ }
+
+ default:
+ {
+ status = SCHED_S_EBADPARM;
+ break;
+ }
+ }
+ return status;
+}
+
+enum sched_status sched_process_clnt_lvl_set_param(
+ struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx *clnt_ctx,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("Set_clnt_param index = %u, value = %p",
+ param_index, (void *)param_value);
+
+ switch (param_index) {
+ case SCHED_I_CLNT_CURRQLEN:
+ case SCHED_I_CLNT_OTKNCURRENT:
+ {
+ status = SCHED_S_EINVALOP;
+ break;
+ }
+
+ case SCHED_I_CLNT_PTKNRATE:
+ {
+ status =
+ sched_process_set_p_tkn_rate(sched_ctx,
+ clnt_ctx,
+ param_value);
+ break;
+ }
+
+ case SCHED_I_CLNT_PTKNPERFRM:
+ {
+
+ clnt_ctx->tkn_per_frm = param_value->un_value;
+ sched_tkn_bkt_config(clnt_ctx);
+ break;
+ }
+
+ case SCHED_I_CLNT_FRAMERATE:
+ {
+ clnt_ctx->frm_rate = param_value->frm_rate;
+ break;
+ }
+
+ case SCHED_I_CLNT_OTKNMAX:
+ {
+ if (param_value->un_value <
+ clnt_ctx->o_tkn_per_ip_frm) {
+ status = SCHED_S_EBADPARM;
+ } else {
+ clnt_ctx->o_tkn_max =
+ param_value->un_value;
+
+ clnt_ctx->curr_o_tkns =
+ SCHED_MIN(clnt_ctx->curr_o_tkns,
+ clnt_ctx->o_tkn_max);
+ }
+ break;
+ }
+
+ case SCHED_I_CLNT_OTKNPERIPFRM:
+ {
+ if (param_value->un_value > clnt_ctx->o_tkn_max) {
+ status = SCHED_S_EBADPARM;
+ } else {
+ clnt_ctx->o_tkn_per_ip_frm =
+ param_value->un_value;
+ }
+ break;
+ }
+
+ default:
+ {
+ status = SCHED_S_EBADPARM;
+ break;
+ }
+ }
+
+ return status;
+
+}
+
+enum sched_status sched_process_suspend_resume_clnt(
+ struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, u32 state) {
+ u32 curr_time;
+ struct sched_clnt_ctx *clnt_ctx = &clnt_node->data;
+
+ SCHED_MSG_HIGH("Current client sched_state=%d. Requested state=%d",
+ clnt_ctx->sched_state, state);
+
+ if (clnt_ctx->sched_state == state)
+ return SCHED_S_OK;
+
+
+ clnt_ctx->sched_state = state;
+
+ if (!SCHED_SUCCEEDED(SCHED_GET_CURRENT_TIME(&curr_time))) {
+ SCHED_MSG_ERR("Get current time failed");
+
+ return SCHED_S_OK;
+ }
+
+ /* RESUME */
+ if (state) {
+ clnt_ctx->bkt_lst_sup_time = curr_time;
+ } else { /* SUSPEND */
+ /*As we are suspending the client we fill the token bucket upto
+ current time instance.*/
+ sched_tkn_bkt_supply(clnt_ctx, curr_time);
+ }
+
+ SCHED_MSG_MED("Client category %d", clnt_ctx->client_ctgy);
+ SCHED_MSG_MED("Client allocated P token rate (per sec) = %d",
+ clnt_ctx->curr_p_tkn_rate);
+ SCHED_MSG_MED("Client frame rate = %d / %d",
+ clnt_ctx->frm_rate.numer,
+ clnt_ctx->frm_rate.denom);
+ SCHED_MSG_MED("Client P tokens per frame = %d",
+ clnt_ctx->tkn_per_frm);
+
+ return SCHED_S_OK;
+
+}
+
+void sched_remove_node_from_list(
+ struct _sched_clnt_list_node **pp_head,
+ struct _sched_clnt_list_node *node)
+{
+ u32 found = false;
+ struct _sched_clnt_list_node *curr = *pp_head;
+
+ if (!*pp_head || !node) {
+ SCHED_MSG_ERR("Bad params. head %p, node %p", *pp_head,
+ node);
+ return;
+ }
+
+ if (node == *pp_head) {
+ *pp_head = node->next;
+ return;
+ }
+
+ while (!found && curr) {
+ if (node == curr->next) {
+ curr->next = node->next;
+ found = true;
+ }
+
+ curr = curr->next;
+ }
+
+}
+
+SCHED_INLINE u32 sched_consider_clnt_for_sched(
+ struct sched_clnt_ctx *clnt_ctx)
+{
+ if (clnt_ctx->first_frm &&
+ clnt_ctx->sched_state &&
+ !clnt_ctx->flushing &&
+ clnt_ctx->q_len &&
+ clnt_ctx->curr_o_tkns >= clnt_ctx->o_tkn_per_ip_frm) {
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h
new file mode 100644
index 0000000..e38e9af
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h
@@ -0,0 +1,138 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VID_FRAME_SCHEDULER_H_
+#define _VID_FRAME_SCHEDULER_H_
+#include "vid_frame_scheduler_utils.h"
+
+struct sched_clnt_q_elem {
+ void *frm_data;
+ u32 eof;
+
+};
+
+struct sched_clnt_ctx {
+ enum sched_client_ctgy client_ctgy;
+ struct sched_client_frm_rate frm_rate;
+ u32 tkn_per_frm;
+ u32 curr_p_tkn_rate;
+ u32 o_tkn_max;
+ u32 o_tkn_per_ip_frm;
+ u32 curr_o_tkns;
+ u32 bkt_size;
+ u32 bkt_quies_cap;
+ s32 bkt_curr_tkns;
+ s32 bkt_curr_tkns_nmlzd;
+ u32 bkt_lst_sup_time;
+ u32 max_queue_len;
+ struct sched_clnt_q_elem *clnt_frm_q;
+ s32 q_head;
+ s32 q_tail;
+ u32 q_len;
+ u32 first_frm;
+ u32 eof_marker;
+ u32 flushing;
+ u32 sched_state;
+ void *client_data;
+ u32 *clnt_cs;
+};
+
+struct _sched_clnt_list_node {
+ struct sched_clnt_ctx data;
+ struct _sched_clnt_list_node *next;
+
+};
+
+struct _sched_clnt_list_node;
+
+struct sched_ctx {
+ u32 perf_lvl;
+ struct _sched_clnt_list_node *rt_head;
+ u32 rt_clnts;
+ struct _sched_clnt_list_node *non_rt_head;
+ u32 non_rt_clnts;
+ struct _sched_clnt_list_node *non_rt_last_sched;
+ u32 total_clnt_bw;
+ u32 *sched_cs;
+};
+
+SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status status);
+SCHED_INLINE u32 SCHED_FAILED(enum sched_status status);
+SCHED_INLINE void sched_free_clnt_node
+ (struct _sched_clnt_list_node *clnt_node);
+enum sched_status sched_clear_clnt_list
+ (struct _sched_clnt_list_node *clnt_lst);
+enum sched_status sched_process_add_clnt
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node,
+ struct sched_client_init_param *init_param);
+enum sched_status sched_process_remove_clnt
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node);
+enum sched_status sched_process_flush_clnt_buff
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, void **pp_frm_data);
+SCHED_INLINE enum sched_status sched_process_mark_clnt_eof
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node);
+enum sched_status sched_process_update_clnt_o_tkn
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, u32 type, u32 o_tkn);
+enum sched_status sched_process_en_q_frm
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, void *frm_data);
+enum sched_status sched_process_re_en_q_frm
+(struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, void *frm_data);
+enum sched_status sched_process_de_q_frm
+ (struct sched_ctx *sched_ctx,
+ void **pp_frm_data, void **pp_client_data);
+enum sched_status sched_process_sched_lvl_get_param
+ (struct sched_ctx *sched_ctx,
+ enum sched_index param_index, union sched_value_type *param_value);
+enum sched_status sched_process_sched_lvl_set_param
+ (struct sched_ctx *sched_ctx,
+ enum sched_index param_index, union sched_value_type *param_value);
+enum sched_status sched_process_clnt_lvl_get_param
+ (struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx *clnt_ctx,
+ enum sched_index param_index, union sched_value_type *param_value);
+enum sched_status sched_process_clnt_lvl_set_param
+ (struct sched_ctx *sched_ctx,
+ struct sched_clnt_ctx *clnt_ctx,
+ enum sched_index param_index, union sched_value_type *param_value);
+enum sched_status sched_process_suspend_resume_clnt
+ (struct sched_ctx *sched_ctx,
+ struct _sched_clnt_list_node *clnt_node, u32 state);
+void sched_remove_node_from_list
+ (struct _sched_clnt_list_node **pp_head,
+ struct _sched_clnt_list_node *node);
+SCHED_INLINE u32 sched_consider_clnt_for_sched
+ (struct sched_clnt_ctx *clnt_ctx);
+
+#endif
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c
new file mode 100644
index 0000000..7c55784
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c
@@ -0,0 +1,426 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+
+#include "vid_frame_scheduler_api.h"
+#include "vid_frame_scheduler.h"
+
+enum sched_status sched_create(
+ struct sched_init_param *init_param, void **handle)
+{
+ struct sched_ctx *sched_ctx;
+
+ SCHED_MSG_HIGH("sched_create API");
+
+ if (!handle || !init_param) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: handle=%p, init_param=%p",
+ handle, init_param);
+ return SCHED_S_EBADPARM;
+ }
+
+ if (!init_param->perf_lvl) {
+ SCHED_MSG_ERR("Invalid Perf level=%u",
+ init_param->perf_lvl);
+ return SCHED_S_EBADPARM;
+ }
+
+ sched_ctx =
+ (struct sched_ctx *)
+ SCHED_MALLOC(sizeof(struct sched_ctx));
+
+ if (!sched_ctx) {
+ SCHED_MSG_ERR("Could not allocate sched ctx. Out of memory");
+ return SCHED_S_ENOMEM;
+ }
+
+ SCHED_MEMSET(sched_ctx, 0, sizeof(struct sched_ctx));
+ sched_ctx->perf_lvl = init_param->perf_lvl;
+
+ if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&sched_ctx->sched_cs))) {
+ SCHED_FREE(sched_ctx);
+ return SCHED_S_EFAIL;
+ }
+
+ *handle = sched_ctx;
+
+ SCHED_MSG_MED("Sched instance created. All went well");
+
+ return SCHED_S_OK;
+
+}
+
+enum sched_status sched_destroy(void *handle)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+
+ SCHED_MSG_HIGH("sched_destroy API");
+
+ if (!sched_ctx) {
+ SCHED_MSG_ERR("Bad input parameters");
+ return SCHED_S_EBADPARM;
+ }
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ (void)sched_clear_clnt_list(sched_ctx->rt_head);
+ (void)sched_clear_clnt_list(sched_ctx->rt_head);
+ SCHED_MSG_MED("Sched clnt lists are cleared & released");
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ (void)SCHED_CRITSEC_RELEASE(sched_ctx->sched_cs);
+ SCHED_MEMSET(sched_ctx, 0, sizeof(struct sched_ctx));
+ SCHED_FREE(sched_ctx);
+ SCHED_MSG_MED("Sched instance deleted");
+ return SCHED_S_OK;
+}
+
+enum sched_status sched_get_param(
+ void *handle,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ enum sched_status status;
+
+ SCHED_MSG_HIGH("sched_get_param API");
+
+ if (!sched_ctx || !param_value) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, param_value=%p",
+ sched_ctx, param_value);
+
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+
+ status =
+ sched_process_sched_lvl_get_param(sched_ctx, param_index,
+ param_value);
+
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ return status;
+}
+
+enum sched_status sched_set_param(
+ void *handle,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ enum sched_status status;
+
+ SCHED_MSG_HIGH("sched_set_param API");
+
+ if (!sched_ctx || !param_value) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, param_value=%p",
+ sched_ctx, param_value);
+ return SCHED_S_EBADPARM;
+ }
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ status =
+ sched_process_sched_lvl_set_param(sched_ctx, param_index,
+ param_value);
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ return status;
+}
+
+enum sched_status sched_add_client(
+ void *handle,
+ struct sched_client_init_param *init_param,
+ void **client_hdl)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ enum sched_status status = SCHED_S_OK;
+ struct _sched_clnt_list_node *new_clnt;
+
+ SCHED_MSG_HIGH("sched_add_client API");
+
+ if (!sched_ctx || !init_param ||
+ !client_hdl) {
+ SCHED_MSG_ERR("Bad input parameters");
+
+ return SCHED_S_EBADPARM;
+ }
+
+ new_clnt = (struct _sched_clnt_list_node *)
+ SCHED_MALLOC(sizeof(struct _sched_clnt_list_node));
+ if (!new_clnt) {
+ SCHED_MSG_ERR("Could not allocate client ctx. Out of memory");
+ return SCHED_S_ENOMEM;
+ }
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ status = sched_process_add_clnt(sched_ctx, new_clnt, init_param);
+
+ if (SCHED_FAILED(status)) {
+ SCHED_MSG_ERR("Add_client failed with err=%d", status);
+ sched_free_clnt_node(new_clnt);
+ new_clnt = NULL;
+ }
+
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ *client_hdl = new_clnt;
+ SCHED_MSG_MED("Sched client instance created. All went well");
+ return status;
+}
+
+enum sched_status sched_remove_client(void *handle, void *client_hdl)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_remove_client API");
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ status = sched_process_remove_clnt(sched_ctx, clnt_node);
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ return status;
+}
+
+enum sched_status sched_flush_client_buffer(
+ void *handle, void *client_hdl, void **pp_frm_data)
+{
+
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_flush_client_buffer API");
+ if (!sched_ctx || !clnt_node || !pp_frm_data) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status =
+ sched_process_flush_clnt_buff(sched_ctx, clnt_node,
+ pp_frm_data);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_mark_client_eof(void *handle, void *client_hdl)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_mark_client_eof API");
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status = sched_process_mark_clnt_eof(sched_ctx, clnt_node);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_update_client_o_tkn(
+ void *handle, void *client_hdl, u32 type, u32 o_tkn)
+{
+
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_restore_client_o_tkn API");
+
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status =
+ sched_process_update_clnt_o_tkn(sched_ctx, clnt_node, type,
+ o_tkn);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_queue_frame(
+ void *handle, void *client_hdl, void *frm_data)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_queue_frame API");
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status = sched_process_en_q_frm(sched_ctx, clnt_node, frm_data);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_re_queue_frame(
+void *handle, void *client_hdl, void *frm_data)
+{
+ struct sched_ctx* sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("\n sched_re_queue_frame API");
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR("Bad input parameters:"
+ "sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status = sched_process_re_en_q_frm(sched_ctx, clnt_node,
+ frm_data);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_de_queue_frame(
+ void *handle, void **pp_frm_data, void **pp_client_data)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ enum sched_status status = SCHED_S_OK;
+
+ SCHED_MSG_HIGH("sched_de_queue_frame API");
+
+ if (!sched_ctx || !pp_frm_data
+ || !pp_client_data) {
+ SCHED_MSG_ERR("Bad input parameters: sched_ctx=%p, "
+ "pp_frm_data=%p, pp_client_data=%p",
+ sched_ctx, pp_frm_data,
+ pp_client_data);
+ return SCHED_S_EBADPARM;
+ }
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ status =
+ sched_process_de_q_frm(sched_ctx, pp_frm_data, pp_client_data);
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ return status;
+}
+
+enum sched_status sched_get_client_param(
+ void *handle, void *client_hdl,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status;
+
+ SCHED_MSG_HIGH("sched_get_client_param API");
+
+ if (!sched_ctx || !clnt_node ||
+ !param_value) {
+ SCHED_MSG_ERR("Bad input parameters: sched_ctx=%p, "
+ "clnt_node=%p, param_value=%p",
+ sched_ctx, clnt_node,
+ param_value);
+
+ return SCHED_S_EBADPARM;
+ }
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status = sched_process_clnt_lvl_get_param(sched_ctx,
+ &clnt_node->data,
+ param_index, param_value);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
+
+enum sched_status sched_set_client_param(
+ void *handle, void *client_hdl,
+ enum sched_index param_index,
+ union sched_value_type *param_value)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status;
+
+ SCHED_MSG_HIGH("sched_set_client_param API");
+
+ if (!sched_ctx || !clnt_node ||
+ !param_value) {
+ SCHED_MSG_ERR("Bad input parameters: "
+ "sched_ctx=%p, clnt_node=%p, "
+ "param_value=%p", sched_ctx, clnt_node,
+ param_value);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs);
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+
+ status = sched_process_clnt_lvl_set_param(sched_ctx,
+ &clnt_node->data, param_index, param_value);
+
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs);
+ return status;
+}
+
+enum sched_status sched_suspend_resume_client(
+ void *handle, void *client_hdl, u32 state)
+{
+ struct sched_ctx *sched_ctx = (struct sched_ctx *)handle;
+ struct _sched_clnt_list_node *clnt_node =
+ (struct _sched_clnt_list_node *)client_hdl;
+ enum sched_status status;
+
+ SCHED_MSG_HIGH("sched_client_suspend_resume API");
+
+ if (!sched_ctx || !clnt_node) {
+ SCHED_MSG_ERR
+ ("Bad input parameters: sched_ctx=%p, clnt_node=%p",
+ sched_ctx, clnt_node);
+ return SCHED_S_EBADPARM;
+ }
+
+ (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs);
+ status =
+ sched_process_suspend_resume_clnt(sched_ctx, clnt_node,
+ state);
+ (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs);
+ return status;
+}
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h
new file mode 100644
index 0000000..29e0692
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h
@@ -0,0 +1,150 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _SCHEDULER_API_H_
+#define _SCHEDULER_API_H_
+
+enum sched_status {
+ SCHED_S_OK = 0x0,
+ SCHED_S_NOPTKN,
+ SCHED_S_NOOTKN,
+ SCHED_S_SLEEP,
+ SCHED_S_QEMPTY,
+ SCHED_S_QFULL,
+ SCHED_S_EOF,
+ SCHED_S_EFAIL = 0x64,
+ SCHED_S_ENOMEM,
+ SCHED_S_EBADPARM,
+ SCHED_S_EINVALOP,
+ SCHED_S_ENOTIMPL,
+ SCHED_S_ENORES,
+ SCHED_S_EINVALST,
+ SCHED_S_MAX = 0x7fffffff
+};
+
+enum sched_index {
+ SCHED_I_START_UNUSED = 0x0,
+ SCHED_I_PERFLEVEL,
+ SCHED_I_CLNT_START_UNUSED = 0x63,
+ SCHED_I_CLNT_CURRQLEN,
+ SCHED_I_CLNT_PTKNRATE,
+ SCHED_I_CLNT_PTKNPERFRM,
+ SCHED_I_CLNT_FRAMERATE,
+ SCHED_I_CLNT_OTKNMAX,
+ SCHED_I_CLNT_OTKNPERIPFRM,
+ SCHED_I_CLNT_OTKNCURRENT,
+ SCHED_I_MAX = 0x7fffffff
+};
+
+struct sched_client_frm_rate {
+ u32 numer;
+ u32 denom;
+
+};
+
+union sched_value_type {
+ u32 un_value;
+ struct sched_client_frm_rate frm_rate;
+
+};
+
+struct sched_init_param {
+ u32 perf_lvl;
+
+};
+
+enum sched_client_ctgy {
+ SCHED_CLNT_RT_BUFF = 0,
+ SCHED_CLNT_RT_NOBUFF,
+ SCHED_CLNT_NONRT,
+ SCHED_CLNT_MAX = 0x7fffffff
+};
+
+struct sched_client_init_param {
+ enum sched_client_ctgy client_ctgy;
+ u32 max_queue_len;
+ struct sched_client_frm_rate frm_rate;
+ u32 tkn_per_frm;
+ u32 alloc_p_tkn_rate;
+ u32 o_tkn_max;
+ u32 o_tkn_per_ip_frm;
+ u32 o_tkn_init;
+
+ void *client_data;
+
+};
+
+enum sched_status sched_create
+ (struct sched_init_param *init_param, void **handle);
+
+enum sched_status sched_destroy(void *handle);
+
+enum sched_status sched_get_param
+ (void *handle,
+ enum sched_index param_index, union sched_value_type *param_value);
+
+enum sched_status sched_set_param
+ (void *handle,
+ enum sched_index param_index, union sched_value_type *param_value);
+
+enum sched_status sched_add_client
+ (void *handle,
+ struct sched_client_init_param *init_param, void **client_hdl);
+
+enum sched_status sched_remove_client(void *handle, void *client_hdl);
+
+enum sched_status sched_flush_client_buffer
+ (void *handle, void *client_hdl, void **pp_frm_data);
+
+enum sched_status sched_mark_client_eof(void *handle, void *client_hdl);
+
+enum sched_status sched_update_client_o_tkn
+ (void *handle, void *client_hdl, u32 type, u32 o_tkn);
+
+enum sched_status sched_queue_frame
+ (void *handle, void *client_hdl, void *frm_data);
+enum sched_status sched_re_queue_frame
+(void *handle, void *client_hdl, void *frm_data);
+
+enum sched_status sched_de_queue_frame
+ (void *handle, void **pp_frm_data, void **pp_client_data);
+
+enum sched_status sched_get_client_param
+ (void *handle,
+ void *client_hdl,
+ enum sched_index param_index, union sched_value_type *param_value);
+
+enum sched_status sched_set_client_param
+ (void *handle,
+ void *client_hdl,
+ enum sched_index param_index, union sched_value_type *param_value);
+
+enum sched_status sched_suspend_resume_client
+ (void *handle, void *client_hdl, u32 state);
+
+#endif
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c
new file mode 100644
index 0000000..f6a7b83
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vid_frame_scheduler_utils.h"
+
+/**
+ * SCHED_ASSERT () - This function is a wrapper to underlying ASSERT
+ * @val: value to be checked for
+ * function.
+ * DEPENDENCIES: None
+ * Returns none
+ */
+SCHED_INLINE void SCHED_ASSERT(int val)
+{
+
+} /* end of SCHED_ASSERT */
+
+/**
+ * SCHED_MIN () - This function will find minimum of two values
+ * @x: value 1
+ * @y: value 2
+ * DEPENDENCIES: None
+ * Returns none
+ */
+SCHED_INLINE int SCHED_MIN(int x, int y)
+{
+ if (x < y)
+ return x;
+ else
+ return y;
+
+} /* end of SCHED_MIN */
+
+/**
+ * SCHED_MALLOC () - This function is a wrapper to underlying malloc
+ * @size: memory size to be allocated
+ * function
+ * DEPENDENCIES: None
+ * Returns none
+ */
+SCHED_INLINE void *SCHED_MALLOC(int size)
+{
+ return kmalloc(size, GFP_KERNEL);
+} /* end of SCHED_MALLOC */
+
+/**
+ * SCHED_FREE () - This function is a wrapper to underlying memory free
+ * @ptr: memory to be freed
+ * function
+ * DEPENDENCIES: None
+ * Returns none
+ */
+SCHED_INLINE void SCHED_FREE(void *ptr)
+{
+ kfree(ptr);
+} /* end of SCHED_FREE */
+
+/**
+ * SCHED_MEMSET () - This function is a wrapper to underlying memory set
+ * @ptr: ptr to memory
+ * @val: value to be set
+ * @size: memory size to be set
+ * function
+ * DEPENDENCIES: None
+ * Returns none
+ */
+SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size)
+{
+ return memset(ptr, val, size);
+} /* end of SCHED_MEMSET */
+
+/**
+ * SCHED_GET_CURRENT_TIME () - This function is a wrapper to underlying get time
+ * @pn_time: ptr time value in milliseconds
+ * function
+ * DEPENDENCIES: None
+ * Returns SCHED_S_OK on success
+ */
+SCHED_INLINE enum sched_status SCHED_GET_CURRENT_TIME(u32 *pn_time)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ *pn_time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ return SCHED_S_OK;
+
+} /* end of SCHED_GET_CURRENT_TIME */
+
+/**
+ * SCHED_CRITSEC_CREATE () - This function is a wrapper to creating a critical
+ * @cs: ptr to a critical section type
+ * section
+ * DEPENDENCIES: None
+ * Returns SCHED_S_OK on success
+ */
+SCHED_INLINE enum sched_status SCHED_CRITSEC_CREATE(u32 **cs)
+{
+ return SCHED_S_OK;
+
+} /* end of SCHED_CRITSEC_CREATE */
+
+/**
+ * SCHED_CRITSEC_RELEASE () - This function is a wrapper to releasing a critical
+ * @cs: critical section handle type
+ * section resource
+ * DEPENDENCIES: None
+ * Returns SCHED_S_OK on success
+ */
+SCHED_INLINE enum sched_status SCHED_CRITSEC_RELEASE(u32 *cs)
+{
+ return SCHED_S_OK;
+
+} /* end of SCHED_CRITSEC_RELEASE */
+
+/**
+ * SCHED_CRITSEC_ENTER () - This function is a wrapper to enter a critical
+ * @cs: critical section handle type
+ * section
+ * DEPENDENCIES: None
+ * Returns SCHED_S_OK on success
+ */
+SCHED_INLINE enum sched_status SCHED_CRITSEC_ENTER(u32 *cs)
+{
+ return SCHED_S_OK;
+
+} /* end of SCHED_CRITSEC_ENTER */
+
+/**
+ * SCHED_CRITSEC_LEAVE () - This function is a wrapper to leave a critical
+ * @cs: critical section handle type
+ * section
+ * DEPENDENCIES: None
+ * Returns SCHED_S_OK on success
+ */
+SCHED_INLINE enum sched_status SCHED_CRITSEC_LEAVE(u32 *cs)
+{
+ return SCHED_S_OK;
+
+} /* end of SCHED_CRITSEC_LEAVE */
diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h
new file mode 100644
index 0000000..f5e1d7d
--- /dev/null
+++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _SCHEDULER_UTILS_H_
+#define _SCHEDULER_UTILS_H_
+
+#include "vid_frame_scheduler_api.h"
+
+//TODO lots of low hanging fruit in here
+#define SCHED_INLINE
+
+#if DEBUG
+
+#define SCHED_MSG_LOW(xx_fmt, ...) printk(KERN_INFO "\n " \
+ xx_fmt, ## __VA_ARGS__)
+#define SCHED_MSG_MED(xx_fmt, ...) printk(KERN_INFO "\n" \
+ xx_fmt, ## __VA_ARGS__)
+#define SCHED_MSG_HIGH(xx_fmt, ...) printk(KERN_WARNING "\n" \
+ xx_fmt, ## __VA_ARGS__)
+
+#else
+
+#define SCHED_MSG_LOW(xx_fmt...)
+#define SCHED_MSG_MED(xx_fmt...)
+#define SCHED_MSG_HIGH(xx_fmt...)
+
+#endif
+
+#define SCHED_MSG_ERR(xx_fmt, ...) printk(KERN_ERR "\n err: " \
+ xx_fmt, ## __VA_ARGS__)
+#define SCHED_MSG_FATAL(xx_fmt, ...) printk(KERN_ERR "\n<FATAL> " \
+ xx_fmt, ## __VA_ARGS__)
+
+SCHED_INLINE void SCHED_ASSERT(int val);
+
+SCHED_INLINE int SCHED_MIN(int x, int y);
+
+SCHED_INLINE enum sched_status SCHED_CRITSEC_CREATE(u32 **cs);
+
+SCHED_INLINE enum sched_status SCHED_CRITSEC_RELEASE(u32 *cs);
+
+SCHED_INLINE enum sched_status SCHED_CRITSEC_ENTER(u32 *cs);
+
+SCHED_INLINE enum sched_status SCHED_CRITSEC_LEAVE(u32 *cs);
+
+SCHED_INLINE void *SCHED_MALLOC(int size);
+
+SCHED_INLINE void SCHED_FREE(void *ptr);
+
+SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size);
+
+SCHED_INLINE enum sched_status SCHED_GET_CURRENT_TIME(u32 *pn_time);
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd.h b/drivers/misc/video_core/720p/vcd/vcd.h
new file mode 100644
index 0000000..1367e7d
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd.h
@@ -0,0 +1,320 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_H_
+#define _VCD_H_
+
+#include "vcd_api.h"
+#include "vid_frame_scheduler_api.h"
+#include "vcd_ddl_api.h"
+#include "vcd_res_tracker_api.h"
+#include "vcd_util.h"
+#include "vcd_client_sm.h"
+#include "vcd_core.h"
+#include "vcd_device_sm.h"
+
+void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_get_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc);
+
+u32 vcd_get_command_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc);
+
+void vcd_mark_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+void vcd_release_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+void vcd_release_multiple_command_channels(struct vcd_dev_ctxt *dev_ctxt,
+ u32 channels);
+
+void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_get_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc);
+
+u32 vcd_get_frame_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc);
+
+void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_release_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt *dev_ctxt,
+ u32 channels);
+
+void vcd_release_interim_frame_channels(struct vcd_dev_ctxt *dev_ctxt);
+u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt);
+void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code);
+
+u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code);
+
+u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_check_for_client_context(struct vcd_dev_ctxt *dev_ctxt, s32 driver_id);
+
+u32 vcd_validate_driver_handle(struct vcd_dev_ctxt *dev_ctxt,
+ s32 driver_handle);
+
+void vcd_handle_for_last_clnt_close(struct vcd_dev_ctxt *dev_ctxt,
+ u32 send_deinit);
+
+u32 vcd_common_allocate_set_buffer(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type buffer, size_t sz,
+ struct vcd_buffer_pool **pp_buf_pool);
+
+u32 vcd_set_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, void *buf, size_t sz);
+
+u32 vcd_allocate_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, size_t buf_size, void **virt_addr,
+ phys_addr_t *phys_addr);
+
+u32 vcd_free_one_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, u8 *buffer);
+
+u32 vcd_free_buffers_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool);
+
+u32 vcd_alloc_buffer_pool_entries(struct vcd_buffer_pool *buf_pool,
+ struct vcd_buffer_requirement *buf_req);
+
+void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool);
+
+void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, u32 event);
+
+void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool);
+
+struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry(
+ struct vcd_buffer_pool *pool);
+
+struct vcd_buffer_entry *vcd_find_buffer_pool_entry(struct vcd_buffer_pool
+ *pool, void *virt_addr);
+
+struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q(
+ struct vcd_buffer_pool *pool);
+
+u32 vcd_buffer_pool_entry_en_q(struct vcd_buffer_pool *pool,
+ struct vcd_buffer_entry *entry);
+
+u32 vcd_client_cmd_en_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type command);
+
+void vcd_client_cmd_flush_and_en_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type command);
+
+u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type *command);
+
+u32 vcd_handle_recvd_eos(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame, u32 * pb_eos_handled);
+
+u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_input_frame(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame);
+
+u32 vcd_store_seq_hdr(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_sequence_hdr *seq_hdr);
+
+u32 vcd_set_frame_size(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_size *frm_size);
+
+u32 vcd_set_frame_rate(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_rate *fps);
+
+u32 vcd_calculate_frame_delta(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *frame);
+
+struct vcd_buffer_entry *vcd_check_fill_output_buffer(
+ struct vcd_clnt_ctxt *cctxt, struct vcd_frame_data *buffer);
+
+u32 vcd_requeue_input_frame(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *cctxt, struct vcd_buffer_entry *buf_entry);
+
+u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt **pp_cctxt,
+ struct vcd_buffer_entry **pp_ip_buf_entry);
+
+u32 vcd_map_sched_status(enum sched_status sched_status);
+
+u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc);
+
+u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc);
+
+void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_submit_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc);
+
+u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt);
+void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc);
+
+struct vcd_transc *vcd_get_free_trans_tbl_entry(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry);
+
+void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt);
+void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt *cctxt);
+void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_input_done(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 event, u32 status);
+
+void vcd_handle_input_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status);
+
+void vcd_handle_input_done_failed(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc);
+
+void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_handle_input_done_with_trans_end(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_handle_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 event, u32 status);
+
+void vcd_handle_frame_done_for_interlacing(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc_ip1, struct ddl_frame_data_tag *op_frm,
+ u32 status);
+
+u32 vcd_handle_first_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload);
+
+void vcd_handle_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status);
+
+u32 vcd_handle_first_encode_frame_done(struct vcd_clnt_ctxt *cctxt,
+ void *payload);
+
+u32 vcd_handle_output_required(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status);
+
+u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt *cctxt,
+ void *payload);
+
+u32 vcd_handle_output_req_tran_end_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_validate_io_done_pyld(void *payload, u32 status);
+
+void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_handle_eos_done(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status);
+
+void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame, u32 valid_opbuf);
+
+void vcd_send_frame_done_in_eos_for_dec(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame);
+
+void vcd_send_frame_done_in_eos_for_enc(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame);
+
+void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status);
+
+void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 status);
+
+void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status);
+
+void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt);
+
+void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode);
+void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt,
+ u32 event);
+
+u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event,
+ struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_client_power_event(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *cctxt, u32 event);
+
+u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl,
+ struct vcd_clnt_ctxt *cctxt);
+
+u32 vcd_update_clnt_perf_lvl(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_rate *fps, u32 frm_p_units);
+
+u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt);
+
+void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, u32 status);
+
+void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *cctxt);
+
+void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event);
+
+void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt, u32 status);
+
+void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status);
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.c b/drivers/misc/video_core/720p/vcd/vcd_api.c
new file mode 100644
index 0000000..202f61d
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_api.c
@@ -0,0 +1,882 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd.h"
+
+u32 vcd_init(struct vcd_init_config *config, s32 *driver_handle)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_MED("vcd_init:");
+
+ if (!config || !driver_handle || !config->pf_map_dev_base_addr) {
+ VCD_MSG_ERROR("Bad parameters");
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ if (!drv_ctxt->dev_mutex) {
+ drv_ctxt->dev_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (!drv_ctxt->dev_mutex) {
+ VCD_MSG_ERROR("Failed: vcd_critical_section_create");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+ mutex_init(drv_ctxt->dev_mutex);
+ }
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_init) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_init(drv_ctxt, config, driver_handle);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in device state %d\n",
+ drv_ctxt->dev_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_init);
+
+u32 vcd_term(s32 driver_handle)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_MED("vcd_term:");
+
+ drv_ctxt = vcd_get_drv_context();
+
+ if (!drv_ctxt->dev_mutex) {
+ VCD_MSG_ERROR("No critical section object");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_term) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_term(drv_ctxt, driver_handle);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in device state %d\n",
+ drv_ctxt->dev_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state == VCD_DEVICE_STATE_NULL) {
+ VCD_MSG_HIGH
+ ("Device in NULL state. Releasing critical section\n");
+
+ mutex_destroy(drv_ctxt->dev_mutex);
+ kfree(drv_ctxt->dev_mutex);
+ drv_ctxt->dev_mutex = NULL;
+ }
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_term);
+
+u32 vcd_open(s32 driver_handle, u32 decoding,
+ void (*callback) (u32 event, u32 status, void *info, u32 size,
+ void *handle, void *const client_data), void *client_data)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_MED("vcd_open:\n");
+
+ if (!callback) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ if (!drv_ctxt->dev_mutex) {
+ VCD_MSG_ERROR("No critical section object\n");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_open) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_open(drv_ctxt, driver_handle, decoding, callback,
+ client_data);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in device state %d\n",
+ drv_ctxt->dev_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_open);
+
+u32 vcd_close(void *handle)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_close:");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_close) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_close(drv_ctxt, cctxt);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in device state %d\n",
+ drv_ctxt->dev_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_close);
+
+u32 vcd_encode_start(void *handle)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_encode_start:");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_encode_start &&
+ drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_encode_start(cctxt);
+ } else {
+ VCD_MSG_ERROR
+ ("Unsupported API dev power state %d OR client state %d\n",
+ drv_ctxt->dev_ctxt.pwr_state,
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_encode_start);
+
+u32 vcd_encode_frame(void *handle, struct vcd_frame_data *input_frame)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_encode_frame:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!input_frame) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_encode_frame) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_encode_frame(cctxt, input_frame);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_encode_frame);
+
+u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr *seq_hdr)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_decode_start:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_decode_start &&
+ drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_decode_start(cctxt, seq_hdr);
+ } else {
+ VCD_MSG_ERROR
+ ("Unsupported API dev power state %d OR client state %d\n",
+ drv_ctxt->dev_ctxt.pwr_state,
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_decode_start);
+
+u32 vcd_decode_frame(void *handle, struct vcd_frame_data *input_frame)
+{
+ struct vcd_clnt_ctxt *cctxt =
+ (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_decode_frame:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!input_frame) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_decode_frame) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_decode_frame(cctxt, input_frame);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_decode_frame);
+
+u32 vcd_pause(void *handle)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+ struct vcd_clnt_ctxt *cctxt =
+ (struct vcd_clnt_ctxt *)handle;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_pause:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_pause) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_pause(cctxt);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_pause);
+
+u32 vcd_resume(void *handle)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_resume:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_resume &&
+ drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_resume(drv_ctxt, cctxt);
+ } else {
+ VCD_MSG_ERROR
+ ("Unsupported API dev power state %d OR client state %d\n",
+ drv_ctxt->dev_ctxt.pwr_state,
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_resume);
+
+u32 vcd_flush(void *handle, u32 mode)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_flush:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_flush) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_flush(cctxt, mode);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_flush);
+
+u32 vcd_stop(void *handle)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_stop:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_stop &&
+ drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_stop(cctxt);
+ } else {
+ VCD_MSG_ERROR
+ ("Unsupported API dev power state %d OR client state %d\n",
+ drv_ctxt->dev_ctxt.pwr_state,
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_stop);
+
+u32 vcd_set_property(void *handle, struct vcd_property_hdr *prop_hdr,
+ void *prop_val)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_set_property:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!prop_hdr || !prop_val) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_set_property) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_set_property(cctxt, prop_hdr, prop_val);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_property);
+
+u32 vcd_get_property(void *handle, struct vcd_property_hdr *prop_hdr,
+ void *prop_val)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_get_property:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!prop_hdr || !prop_val) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_get_property) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_get_property(cctxt, prop_hdr, prop_val);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_get_property);
+
+u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type,
+ struct vcd_buffer_requirement *buffer_req)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_set_buffer_requirements:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!buffer_req) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.
+ pf_set_buffer_requirements) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_set_buffer_requirements(cctxt, buffer_type, buffer_req);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_buffer_requirements);
+
+u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type,
+ struct vcd_buffer_requirement *buffer_req)
+{
+ struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_get_buffer_requirements:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!buffer_req) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.
+ pf_get_buffer_requirements) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_get_buffer_requirements(cctxt, buffer_type, buffer_req);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_get_buffer_requirements);
+
+u32 vcd_set_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buffer,
+ size_t buf_size)
+{
+ struct vcd_clnt_ctxt *cctxt = handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_set_buffer:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!buffer || !buf_size) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_set_buffer) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_set_buffer(cctxt, buffer_type, buffer, buf_size);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_buffer);
+
+u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type buffer_type,
+ size_t sz, void **virt_addr, phys_addr_t *phys_addr)
+{
+ struct vcd_clnt_ctxt *cctxt = handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_allocate_buffer:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!virt_addr || !phys_addr || !sz) {
+ VCD_MSG_ERROR("Bad parameters\n");
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_allocate_buffer) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.pf_allocate_buffer(
+ cctxt, buffer_type, sz, virt_addr, phys_addr);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_allocate_buffer);
+
+u32 vcd_free_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buf)
+{
+ struct vcd_clnt_ctxt *cctxt = handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_free_buffer:");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_free_buffer) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_free_buffer(cctxt, buffer_type, buf);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_free_buffer);
+
+u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data *buffer)
+{
+ struct vcd_clnt_ctxt *cctxt =
+ (struct vcd_clnt_ctxt *)handle;
+ struct vcd_drv_ctxt *drv_ctxt;
+ u32 rc;
+
+ VCD_MSG_MED("vcd_fill_output_buffer:\n");
+
+ if (!cctxt || cctxt->signature != VCD_SIGNATURE) {
+ VCD_MSG_ERROR("Bad client handle\n");
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (!buffer) {
+ VCD_MSG_ERROR("Bad parameters\n");
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_fill_output_buffer) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_fill_output_buffer(cctxt, buffer);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d\n",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_fill_output_buffer);
+
+u32 vcd_set_device_power(s32 driver_handle, enum vcd_power_state pwr_state)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_MED("vcd_set_device_power:\n");
+
+ drv_ctxt = vcd_get_drv_context();
+
+ if (!drv_ctxt->dev_mutex) {
+ VCD_MSG_ERROR("No critical section object\n");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_set_dev_pwr) {
+ rc = drv_ctxt->dev_state.state_table->ev_hdlr.
+ pf_set_dev_pwr(drv_ctxt, pwr_state);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in device state %d\n",
+ drv_ctxt->dev_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ mutex_unlock(drv_ctxt->dev_mutex);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(vcd_set_device_power);
+
+void vcd_read_and_clear_interrupt(void)
+{
+ VCD_MSG_LOW("vcd_read_and_clear_interrupt:\n");
+ ddl_read_and_clear_interrupt();
+}
+
+
+void vcd_response_handler(void)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_LOW("vcd_response_handler:\n");
+ drv_ctxt = vcd_get_drv_context();
+
+ mutex_lock(drv_ctxt->dev_mutex);
+
+ if (!ddl_process_core_response()) {
+ VCD_MSG_HIGH("ddl_process_core_response indicated no further"
+ "processing\n");
+ mutex_unlock(drv_ctxt->dev_mutex);
+ return;
+ }
+
+ if (drv_ctxt->dev_ctxt.cont)
+ vcd_continue();
+ mutex_unlock(drv_ctxt->dev_mutex);
+}
+EXPORT_SYMBOL(vcd_response_handler);
+
+
+
+
+
+
diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.h b/drivers/misc/video_core/720p/vcd/vcd_api.h
new file mode 100644
index 0000000..4368331
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_api.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_API_H_
+#define _VCD_API_H_
+#include "vcd_property.h"
+#include "vcd_status.h"
+
+#define VCD_FRAME_FLAG_EOS 0x00000001
+#define VCD_FRAME_FLAG_ENDOFFRAME 0x00000010
+#define VCD_FRAME_FLAG_SYNCFRAME 0x00000020
+#define VCD_FRAME_FLAG_EXTRADATA 0x00000040
+#define VCD_FRAME_FLAG_CODECCONFIG 0x00000080
+
+#define VCD_FLUSH_INPUT 0x0001
+#define VCD_FLUSH_OUTPUT 0x0002
+#define VCD_FLUSH_ALL 0x0003
+
+#define VCD_FRAMETAG_INVALID 0xffffffff
+
+struct vcd_handle_container {
+ void *handle;
+};
+struct vcd_flush_cmd {
+ u32 mode;
+};
+
+enum vcd_frame {
+ VCD_FRAME_YUV = 1,
+ VCD_FRAME_I,
+ VCD_FRAME_P,
+ VCD_FRAME_B,
+ VCD_FRAME_32BIT = 0x7fffffff
+};
+
+enum vcd_power_state {
+ VCD_PWR_STATE_ON = 1,
+ VCD_PWR_STATE_SLEEP,
+};
+
+struct vcd_frame_data {
+ void *virt_addr;
+ phys_addr_t phys_addr;
+ size_t alloc_len;
+ size_t data_len;
+ size_t offset;
+ s64 time_stamp;
+ u32 flags;
+ void *client_data;
+ struct vcd_property_dec_output_buffer dec_op_prop;
+ u32 interlaced;
+ enum vcd_frame frame_type;
+ u32 ip_frm_tag;
+};
+
+struct vcd_phys_sequence_hdr {
+ phys_addr_t addr;
+ size_t sz;
+};
+
+struct vcd_sequence_hdr {
+ void *addr;
+ size_t sz;
+};
+
+enum vcd_buffer_type {
+ VCD_BUFFER_INPUT = 0x1,
+ VCD_BUFFER_OUTPUT = 0x2,
+ VCD_BUFFER_INVALID = 0x3,
+ VCD_BUFFER_32BIT = 0x7FFFFFFF
+};
+
+struct vcd_buffer_requirement {
+ u32 min_count;
+ u32 actual_count;
+ u32 max_count;
+ size_t size;
+ u32 align;
+ u32 buf_pool_id;
+};
+
+struct vcd_init_config {
+ void *device_name;
+ void *(*pf_map_dev_base_addr) (void *device_name);
+ void (*pf_un_map_dev_base_addr) (void);
+ void (*pf_interrupt_clr) (void);
+ void (*pf_register_isr) (void *device_name);
+ void (*pf_deregister_isr) (void);
+ u32 (*pf_timer_create) (void (*pf_timer_handler)(void *),
+ void *user_data, void **pp_timer_handle);
+ void (*pf_timer_release) (void *timer_handle);
+ void (*pf_timer_start) (void *timer_handle, u32 time_out);
+ void (*pf_timer_stop) (void *timer_handle);
+};
+
+u32 vcd_init(struct vcd_init_config *config, s32 *driver_handle);
+u32 vcd_term(s32 driver_handle);
+u32 vcd_open(s32 driver_handle, u32 decoding,
+ void (*callback) (u32 event, u32 status, void *info, u32 size,
+ void *handle, void *const client_data), void *client_data);
+u32 vcd_close(void *handle);
+u32 vcd_encode_start(void *handle);
+u32 vcd_encode_frame(void *handle, struct vcd_frame_data *input_frame);
+u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr *seq_hdr);
+u32 vcd_decode_frame(void *handle, struct vcd_frame_data *input_frame);
+u32 vcd_pause(void *handle);
+u32 vcd_resume(void *handle);
+u32 vcd_flush(void *handle, u32 mode);
+u32 vcd_stop(void *handle);
+u32 vcd_set_property(void *handle, struct vcd_property_hdr *prop_hdr,
+ void *prop_val);
+u32 vcd_get_property(void *handle, struct vcd_property_hdr *prop_hdr,
+ void *prop_val);
+u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type,
+ struct vcd_buffer_requirement *buffer_req);
+u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type,
+ struct vcd_buffer_requirement *buffer_req);
+u32 vcd_set_buffer(void *handle, enum vcd_buffer_type buffer_type,
+ void *buffer, size_t buf_size);
+u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type buffer_type,
+ size_t sz, void **virt_addr, phys_addr_t *phys_addr);
+u32 vcd_free_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buf);
+u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data *buffer);
+u32 vcd_set_device_power(s32 driver_handle, enum vcd_power_state pwr_state);
+void vcd_read_and_clear_interrupt(void);
+void vcd_response_handler(void);
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.c b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c
new file mode 100644
index 0000000..4617f80
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c
@@ -0,0 +1,1499 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd.h"
+
+static const struct vcd_clnt_state_table *vcd_clnt_state_table[];
+
+void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event)
+{
+ if (cctxt->clnt_state.state != VCD_CLIENT_STATE_INVALID) {
+ cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0,
+ cctxt, cctxt->client_data);
+ vcd_flush_buffers_in_err_fatal(cctxt);
+ vcd_do_client_state_transition(cctxt,
+ VCD_CLIENT_STATE_INVALID,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ }
+}
+
+static u32 vcd_close_in_open(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_close_in_open:");
+ if (cctxt->in_buf_pool.allocated || cctxt->out_buf_pool.allocated) {
+ VCD_MSG_ERROR("Allocated buffers are not freed yet\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+ vcd_destroy_client_context(cctxt);
+ return rc;
+}
+
+static u32 vcd_close_in_invalid(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("vcd_close_in_invalid:\n");
+ if (cctxt->in_buf_pool.allocated || cctxt->out_buf_pool.allocated) {
+ VCD_MSG_ERROR("Allocated buffers are not freed yet\n");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (cctxt->status.cleaning_up)
+ cctxt->status.close_pending = true;
+ else
+ vcd_destroy_client_context(cctxt);
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_start_in_run_cmn(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("vcd_start_in_run_cmn:\n");
+ cctxt->callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0, cctxt,
+ cctxt->client_data);
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_encode_start_in_open(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_property_vop_timing timing;
+
+ VCD_MSG_LOW("vcd_encode_start_in_open:\n");
+
+ if (cctxt->decoding) {
+ VCD_MSG_ERROR("vcd_encode_init for decoder client\n");
+
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!cctxt->in_buf_pool.entries || !cctxt->out_buf_pool.entries ||
+ cctxt->in_buf_pool.validated !=
+ cctxt->in_buf_pool.count ||
+ cctxt->out_buf_pool.validated !=
+ cctxt->out_buf_pool.count) {
+ VCD_MSG_ERROR("Buffer pool is not completely setup yet\n");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ rc = vcd_add_client_to_sched(cctxt);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched\n");
+
+ prop_hdr.id = VCD_I_VOP_TIMING;
+ prop_hdr.sz = sizeof(struct vcd_property_vop_timing);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &timing);
+
+ VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_VOP_TIMING\n");
+ if (!timing.vop_time_resolution) {
+ VCD_MSG_ERROR("Vop_time_resolution value is zero\n");
+ return VCD_ERR_FAIL;
+ }
+ cctxt->time_resoln = timing.vop_time_resolution;
+
+ rc = vcd_process_cmd_sess_start(cctxt);
+
+ if (!VCD_FAILED(rc)) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STARTING,
+ CLIENT_STATE_EVENT_NUMBER(pf_encode_start));
+ }
+
+ return rc;
+}
+
+static u32 vcd_encode_start_in_run(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("vcd_encode_start_in_run:\n");
+ vcd_start_in_run_cmn(cctxt);
+ return VCD_S_SUCCESS;
+}
+
+
+static u32 vcd_encode_frame_cmn(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame)
+{
+ VCD_MSG_LOW("vcd_encode_frame_cmn in %d:", cctxt->clnt_state.state);
+
+ if (cctxt->decoding) {
+ VCD_MSG_ERROR("vcd_encode_frame for decoder client\n");
+
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ return vcd_handle_input_frame(cctxt, input_frame);
+}
+
+static u32 vcd_decode_start_in_open(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_sequence_hdr *seq_hdr)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_decode_start_in_open:\n");
+
+ if (!cctxt->decoding) {
+ VCD_MSG_ERROR("vcd_decode_init for encoder client\n");
+
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (seq_hdr) {
+ VCD_MSG_HIGH("Seq hdr supplied. len = %d\n", seq_hdr->sz);
+ rc = vcd_store_seq_hdr(cctxt, seq_hdr);
+
+ } else {
+ VCD_MSG_HIGH("Seq hdr not supplied\n");
+ cctxt->seq_hdr.sz = 0;
+ cctxt->seq_hdr.addr = NULL;
+ }
+
+ VCD_FAILED_RETURN(rc, "Err processing seq hdr\n");
+
+ rc = vcd_process_cmd_sess_start(cctxt);
+
+ if (!VCD_FAILED(rc)) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STARTING,
+ CLIENT_STATE_EVENT_NUMBER(pf_decode_start));
+ }
+
+ return rc;
+}
+
+static u32 vcd_decode_start_in_run(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_sequence_hdr *seqhdr)
+{
+ VCD_MSG_LOW("vcd_decode_start_in_run:\n");
+ vcd_start_in_run_cmn(cctxt);
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_decode_frame_cmn(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame)
+{
+ VCD_MSG_LOW("vcd_decode_frame_cmn in %d:\n", cctxt->clnt_state.state);
+
+ if (!cctxt->decoding) {
+ VCD_MSG_ERROR("Decode_frame api called for Encoder client\n");
+
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ return vcd_handle_input_frame(cctxt, input_frame);
+}
+
+static u32 vcd_pause_in_run(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_pause_in_run:\n");
+
+ if (cctxt->sched_clnt_valid) {
+ rc = vcd_map_sched_status(sched_suspend_resume_client(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ false));
+ }
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_suspend_resume_client\n");
+
+ if (cctxt->status.frame_submitted > 0) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_PAUSING,
+ CLIENT_STATE_EVENT_NUMBER(pf_pause));
+
+ } else {
+ VCD_MSG_HIGH("No client frames are currently being processed\n");
+
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_PAUSED,
+ CLIENT_STATE_EVENT_NUMBER(pf_pause));
+
+ cctxt->callback(VCD_EVT_RESP_PAUSE, VCD_S_SUCCESS, NULL, 0,
+ cctxt, cctxt->client_data);
+
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_PAUSE);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END failed\n");
+
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_resume_in_paused(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_resume_in_paused:\n");
+
+ if (cctxt->sched_clnt_valid) {
+
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_RESUME);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_RESUME failed\n");
+ } else {
+ rc = vcd_map_sched_status(sched_suspend_resume_client(
+ cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, true));
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: "
+ "sched_suspend_resume_client\n", rc);
+ }
+
+ }
+ if (!VCD_FAILED(rc))
+ vcd_try_submit_frame(dev_ctxt);
+ }
+
+ if (!VCD_FAILED(rc)) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_resume));
+ }
+
+ return rc;
+}
+
+static u32 vcd_flush_cmn(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_flush_cmn in %d:\n", cctxt->clnt_state.state);
+
+ rc = vcd_flush_buffers(cctxt, mode);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers\n");
+
+ if (cctxt->status.frame_submitted > 0) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_FLUSHING,
+ CLIENT_STATE_EVENT_NUMBER(pf_flush));
+ } else {
+ VCD_MSG_HIGH("All buffers are flushed\n");
+ cctxt->status.flush_mode = mode;
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+ }
+
+ return rc;
+}
+
+static u32 vcd_flush_inopen(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ VCD_MSG_LOW("vcd_flush_inopen:\n");
+ cctxt->status.flush_mode = mode;
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_flush_in_flushing(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_flush_in_flushing:\n");
+
+ rc = vcd_flush_buffers(cctxt, mode);
+
+ return rc;
+}
+
+static u32 vcd_flush_in_eos(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ VCD_MSG_LOW("vcd_flush_in_eos:\n");
+
+ if (mode > VCD_FLUSH_ALL || !mode) {
+ VCD_MSG_ERROR("Invalid flush mode %d\n", mode);
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ VCD_MSG_MED("Flush mode requested %d\n", mode);
+
+ cctxt->status.flush_mode |= mode;
+
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_flush_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ u32 rc = VCD_S_SUCCESS;
+ VCD_MSG_LOW("vcd_flush_in_invalid:\n");
+ if (!cctxt->status.cleaning_up) {
+ rc = vcd_flush_buffers(cctxt, mode);
+ if (!VCD_FAILED(rc)) {
+ VCD_MSG_HIGH("All buffers are flushed\n");
+ cctxt->status.flush_mode = mode;
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+ }
+ }
+ return rc;
+}
+
+static u32 vcd_stop_cmn(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_transc *transc;
+
+ VCD_MSG_LOW("vcd_stop_cmn in %d:\n", cctxt->clnt_state.state);
+
+ rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers\n");
+
+ if (!cctxt->status.frame_submitted) {
+
+ if (vcd_get_command_channel(dev_ctxt, &transc)) {
+ rc = vcd_power_event(dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+ if (!VCD_FAILED(rc)) {
+ transc->type = VCD_CMD_CODEC_STOP;
+ transc->cctxt = cctxt;
+
+ rc = vcd_submit_cmd_sess_end(transc);
+ } else {
+ VCD_MSG_ERROR("Failed:"
+ " VCD_EVT_PWR_CLNT_CMD_BEGIN\n");
+ }
+
+ if (VCD_FAILED(rc))
+ vcd_release_command_channel(dev_ctxt, transc);
+
+ } else {
+ vcd_client_cmd_flush_and_en_q(cctxt,
+ VCD_CMD_CODEC_STOP);
+ }
+ }
+
+ if (VCD_FAILED(rc)) {
+ vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL);
+ } else {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STOPPING,
+ CLIENT_STATE_EVENT_NUMBER(pf_stop));
+ }
+
+ return rc;
+}
+
+
+static u32 vcd_stop_inopen(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("vcd_stop_inopen:\n");
+
+ cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, cctxt,
+ cctxt->client_data);
+
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_stop_in_run(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_stop_in_run:\n");
+
+ rc = vcd_stop_cmn(cctxt);
+
+ if (!VCD_FAILED(rc) && cctxt->status.b1st_frame_recvd) {
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_LAST_FRAME);
+ }
+
+ return rc;
+}
+
+static u32 vcd_stop_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_stop_in_eos:\n");
+
+ cctxt->status.stop_pending = true;
+
+ return rc;
+}
+
+static u32 vcd_stop_in_invalid(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("vcd_stop_in_invalid:\n");
+ if (cctxt->status.cleaning_up) {
+ cctxt->status.stop_pending = true;
+ } else {
+ vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+ cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0,
+ cctxt, cctxt->client_data);
+ }
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_set_property_cmn(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_set_property_cmn in %d:\n", cctxt->clnt_state.state);
+ VCD_MSG_LOW("property Id = %d\n", prop_hdr->id);
+
+ if (!prop_hdr->sz || !prop_hdr->id) {
+ VCD_MSG_MED("Bad parameters\n");
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ rc = ddl_set_property(cctxt->ddl_handle, prop_hdr, prop_val);
+
+ VCD_FAILED_RETURN(rc, "Failed: ddl_set_property\n");
+
+ switch (prop_hdr->id) {
+ case VCD_I_LIVE:
+ {
+ struct vcd_property_live *live = (struct vcd_property_live *)
+ prop_val;
+ cctxt->live = live->live;
+ break;
+ }
+ case VCD_I_FRAME_RATE:
+ if (cctxt->sched_clnt_valid) {
+ rc = vcd_set_frame_rate(cctxt,
+ (struct vcd_property_frame_rate *)prop_val);
+ }
+ break;
+ case VCD_I_FRAME_SIZE:
+ if (cctxt->sched_clnt_valid) {
+ rc = vcd_set_frame_size(cctxt,
+ (struct vcd_property_frame_size *)prop_val);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static u32 vcd_get_property_cmn(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_hdr *prop_hdr, void *prop_val)
+{
+ VCD_MSG_LOW("vcd_get_property_cmn in %d:\n", cctxt->clnt_state.state);
+ VCD_MSG_LOW("property id = %d\n", prop_hdr->id);
+ if (!prop_hdr->sz || !prop_hdr->id) {
+ VCD_MSG_MED("Bad parameters\n");
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ return ddl_get_property(cctxt->ddl_handle, prop_hdr, prop_val);
+}
+
+static u32 vcd_set_buffer_requirements_cmn(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type,
+ struct vcd_buffer_requirement *buffer_req)
+{
+ struct vcd_property_hdr prop_hdr;
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_pool *buf_pool;
+
+ VCD_MSG_LOW("vcd_set_buffer_requirements_cmn in %d:\n",
+ cctxt->clnt_state.state);
+
+ if (!cctxt->decoding && cctxt->clnt_state.state !=
+ VCD_CLIENT_STATE_OPEN) {
+ VCD_MSG_ERROR("Bad state (%d) for encoder\n",
+ cctxt->clnt_state.state);
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ VCD_MSG_MED("Buffer type = %d\n", vcd_buffer_type);
+
+ if (vcd_buffer_type == VCD_BUFFER_INPUT) {
+ prop_hdr.id = DDL_I_INPUT_BUF_REQ;
+ buf_pool = &cctxt->in_buf_pool;
+ } else if (vcd_buffer_type == VCD_BUFFER_OUTPUT) {
+ prop_hdr.id = DDL_I_OUTPUT_BUF_REQ;
+ buf_pool = &cctxt->out_buf_pool;
+ } else {
+ rc = VCD_ERR_ILLEGAL_PARM;
+ }
+
+ VCD_FAILED_RETURN(rc, "Invalid buffer type provided\n");
+
+ if (buf_pool->validated > 0) {
+ VCD_MSG_ERROR("Need to free allocated buffers\n");
+
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ prop_hdr.sz = sizeof(*buffer_req);
+
+ rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, buffer_req);
+
+ VCD_FAILED_RETURN(rc, "Failed: ddl_set_property\n");
+
+ if (buf_pool->entries) {
+ VCD_MSG_MED("Resetting buffer requirements\n");
+
+ vcd_free_buffer_pool_entries(buf_pool);
+ }
+
+ rc = vcd_alloc_buffer_pool_entries(buf_pool, buffer_req);
+
+ return rc;
+}
+
+static u32 vcd_get_buffer_requirements_cmn(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type,
+ struct vcd_buffer_requirement *buffer_req)
+{
+ struct vcd_property_hdr prop_hdr;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_get_buffer_requirements_cmn in %d:\n",
+ cctxt->clnt_state.state);
+
+ VCD_MSG_MED("Buffer type = %d\n", vcd_buffer_type);
+
+ if (vcd_buffer_type == VCD_BUFFER_INPUT)
+ prop_hdr.id = DDL_I_INPUT_BUF_REQ;
+ else if (vcd_buffer_type == VCD_BUFFER_OUTPUT)
+ prop_hdr.id = DDL_I_OUTPUT_BUF_REQ;
+ else
+ rc = VCD_ERR_ILLEGAL_PARM;
+
+ VCD_FAILED_RETURN(rc, "Invalid buffer type provided\n");
+
+ prop_hdr.sz = sizeof(*buffer_req);
+
+ return ddl_get_property(cctxt->ddl_handle, &prop_hdr, buffer_req);
+
+}
+
+static u32 vcd_set_buffer_cmn(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, void *buf, size_t sz)
+{
+ u32 rc;
+ struct vcd_buffer_pool *buf_pool;
+
+ VCD_MSG_LOW("vcd_set_buffer_cmn in %d:\n", cctxt->clnt_state.state);
+
+ rc = vcd_common_allocate_set_buffer(cctxt, vcd_buffer_type, sz,
+ &buf_pool);
+
+ if (!VCD_FAILED(rc)) {
+ rc = vcd_set_buffer_internal(cctxt, buf_pool, buf, sz);
+ }
+
+ return rc;
+}
+
+static u32 vcd_allocate_buffer_cmn(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, size_t sz, void **virt_addr,
+ phys_addr_t *phys_addr)
+{
+ u32 rc;
+ struct vcd_buffer_pool *buf_pool;
+
+ VCD_MSG_LOW("vcd_allocate_buffer_cmn in %d:\n",
+ cctxt->clnt_state.state);
+
+ rc = vcd_common_allocate_set_buffer(cctxt, vcd_buffer_type, sz,
+ &buf_pool);
+
+ if (!VCD_FAILED(rc)) {
+ rc = vcd_allocate_buffer_internal(cctxt, buf_pool, sz,
+ virt_addr, phys_addr);
+ }
+
+ return rc;
+}
+
+static u32 vcd_free_buffer_cmn(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, void *buf)
+{
+ VCD_MSG_LOW("vcd_free_buffer_cmn in %d:\n", cctxt->clnt_state.state);
+
+ return vcd_free_one_buffer_internal(cctxt, vcd_buffer_type, buf);
+}
+
+static u32 vcd_fill_output_buffer_cmn(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *buffer)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_entry *buf_entry;
+ struct vcd_frame_data *frm_entry;
+ u32 q_result = true;
+
+ VCD_MSG_LOW("vcd_fill_output_buffer_cmn in %d:\n",
+ cctxt->clnt_state.state);
+
+ buf_entry = vcd_check_fill_output_buffer(cctxt, buffer);
+ if (!buf_entry)
+ return VCD_ERR_BAD_POINTER;
+
+ q_result = vcd_buffer_pool_entry_en_q(&cctxt->out_buf_pool, buf_entry);
+
+ if (!q_result && !cctxt->decoding) {
+ VCD_MSG_ERROR("Failed: vcd_buffer_pool_entry_en_q\n");
+
+ return VCD_ERR_FAIL;
+ }
+
+ frm_entry = &buf_entry->frame;
+
+ *frm_entry = *buffer;
+ frm_entry->phys_addr = buf_entry->phys_addr;
+ frm_entry->ip_frm_tag = VCD_FRAMETAG_INVALID;
+ frm_entry->data_len = 0;
+
+ if (cctxt->sched_clnt_valid) {
+ if (cctxt->decoding && cctxt->status.b1st_frame_recvd) {
+ struct vcd_property_hdr prop_hdr;
+ struct ddl_frame_data_tag ddl_frm;
+
+ prop_hdr.id = DDL_I_DPB_RELEASE;
+ prop_hdr.sz = sizeof(struct ddl_frame_data_tag);
+
+ memset(&ddl_frm, 0, sizeof(ddl_frm));
+ ddl_frm.vcd_frm = *frm_entry;
+ ddl_frm.intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID;
+
+ rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr,
+ &ddl_frm);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("Error returning output buffer to"
+ " HW. rc = 0x%x\n", rc);
+
+ buf_entry->in_use = false;
+ } else {
+ cctxt->out_buf_pool.in_use++;
+ buf_entry->in_use = true;
+ }
+ }
+
+ if (!VCD_FAILED(rc)) {
+ rc = vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm));
+ }
+
+ if (!VCD_FAILED(rc))
+ vcd_try_submit_frame(cctxt->dev_ctxt);
+
+ }
+
+ return rc;
+}
+
+static u32 vcd_fill_output_buffer_in_eos(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *buffer)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_entry *buf_entry;
+
+ VCD_MSG_LOW("vcd_fill_output_buffer_in_eos:\n");
+
+ buf_entry = vcd_check_fill_output_buffer(cctxt, buffer);
+ if (!buf_entry)
+ return VCD_ERR_BAD_POINTER;
+
+ if (cctxt->status.eos_wait_for_op_buf) {
+ VCD_MSG_HIGH("Got an output buffer we were waiting for\n");
+
+ buf_entry->frame = *buffer;
+
+ buf_entry->frame.data_len = 0;
+ buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+ buf_entry->frame.ip_frm_tag =
+ cctxt->status.eos_trig_ip_frm.ip_frm_tag;
+ buf_entry->frame.time_stamp =
+ cctxt->status.eos_trig_ip_frm.time_stamp;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+ &buf_entry->frame, sizeof(struct vcd_frame_data),
+ cctxt, cctxt->client_data);
+
+ cctxt->status.eos_wait_for_op_buf = false;
+
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_fill_output_buffer));
+
+ } else {
+ rc = vcd_fill_output_buffer_cmn(cctxt, buffer);
+ }
+
+ return rc;
+}
+
+static void vcd_clnt_cb_in_starting(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ struct vcd_transc *transc = (struct vcd_transc *)client_data;
+ VCD_MSG_LOW("vcd_clnt_cb_in_starting:\n");
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("vcd_clnt_cb_in_initing: Wrong DDL handle %p\n",
+ ddl_handle);
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_START:
+ vcd_handle_start_done(cctxt, (struct vcd_transc *)client_data,
+ status);
+ break;
+ case VCD_EVT_RESP_STOP:
+ vcd_handle_stop_done_in_starting(cctxt, (struct vcd_transc *)
+ client_data, status);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ cctxt->status.cmd_submitted--;
+ vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+ vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d "
+ "from DDL\n", event, status);
+ dev_ctxt->cont = false;
+ break;
+ }
+}
+
+static void vcd_clnt_cb_in_run(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_INPUT_DONE:
+ rc = vcd_handle_input_done(cctxt, payload, event, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ if (!cctxt->status.b1st_op_done_recvd) {
+ if (!VCD_FAILED(status)) {
+ rc = vcd_handle_first_frame_done(cctxt,
+ payload);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: "
+ "vcd_handle_first_frame_"
+ "done\n", rc);
+ status = VCD_ERR_FAIL;
+ } else {
+ cctxt->status.b1st_op_done_recvd = true;
+ }
+ }
+ }
+
+ rc = vcd_handle_frame_done(cctxt, payload, event, status);
+
+ break;
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ rc = vcd_handle_output_required(cctxt, payload, status);
+ break;
+ case VCD_EVT_IND_RECONFIG:
+ break;
+ case VCD_EVT_RESP_TRANSACTION_PENDING:
+ vcd_handle_trans_pending(cctxt);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL,
+ status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+ dev_ctxt->cont = false;
+ break;
+ }
+
+ if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_REQ)) {
+
+ if (((struct ddl_frame_data_tag *)payload)->frm_trans_end)
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+ }
+}
+
+static void vcd_clnt_cb_in_eos(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ struct vcd_transc *transc = NULL;
+
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_INPUT_DONE:
+ vcd_handle_input_done_in_eos(cctxt, payload, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ vcd_handle_frame_done_in_eos(cctxt, payload, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ vcd_handle_output_required(cctxt, payload, status);
+ break;
+ case VCD_EVT_RESP_EOS_DONE:
+ transc = (struct vcd_transc *)client_data;
+ vcd_handle_eos_done(cctxt, transc, status);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL,
+ status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+
+ dev_ctxt->cont = false;
+ break;
+ }
+
+ if (event == VCD_EVT_RESP_INPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_REQ) {
+ if (payload && ((struct ddl_frame_data_tag *)
+ payload)->frm_trans_end) {
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+ if (!cctxt->status.frame_submitted)
+ vcd_handle_eos_trans_end(cctxt);
+ }
+ }
+}
+
+static void vcd_clnt_cb_in_flushing(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_clnt_cb_in_flushing:\n");
+
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_INPUT_DONE:
+ rc = vcd_handle_input_done(cctxt, payload,
+ VCD_EVT_RESP_INPUT_FLUSHED, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ rc = vcd_handle_frame_done(cctxt, payload,
+ VCD_EVT_RESP_OUTPUT_FLUSHED, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ rc = vcd_handle_output_required_in_flushing(cctxt, payload);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL,
+ status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+ dev_ctxt->cont = false;
+ break;
+ }
+
+ if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_REQ) &&
+ ((struct ddl_frame_data_tag *)payload)->frm_trans_end) {
+
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+
+ if (!cctxt->status.frame_submitted) {
+ VCD_MSG_HIGH("All pending frames recvd from DDL\n");
+
+ if (cctxt->status.flush_mode & VCD_FLUSH_OUTPUT) {
+ vcd_flush_output_buffers(cctxt);
+
+ vcd_release_all_clnt_frm_transc(cctxt);
+ }
+
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+ vcd_release_interim_frame_channels(dev_ctxt);
+ VCD_MSG_HIGH("Flush complete\n");
+ vcd_release_all_clnt_def_frm_transc(cctxt);
+ vcd_do_client_state_transition(cctxt,
+ VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ }
+ }
+}
+
+static void vcd_clnt_cb_in_stopping(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_clnt_cb_in_stopping:\n");
+
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_INPUT_DONE:
+ rc = vcd_handle_input_done(cctxt, payload,
+ VCD_EVT_RESP_INPUT_FLUSHED, status);
+
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ rc = vcd_handle_frame_done(cctxt, payload,
+ VCD_EVT_RESP_OUTPUT_FLUSHED, status);
+
+ break;
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ rc = vcd_handle_output_required_in_flushing(cctxt, payload);
+ break;
+ case VCD_EVT_RESP_STOP:
+ vcd_handle_stop_done(cctxt, (struct vcd_transc *)client_data,
+ status);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_RESP_STOP, status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+
+ dev_ctxt->cont = false;
+ break;
+ }
+
+ if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_REQ) &&
+ ((struct ddl_frame_data_tag *)payload)->frm_trans_end) {
+
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+
+ if (!cctxt->status.frame_submitted) {
+ VCD_MSG_HIGH("All pending frames recvd from DDL\n");
+
+ vcd_flush_output_buffers(cctxt);
+
+ cctxt->status.flush_mode = 0;
+
+ vcd_release_all_clnt_frm_transc(cctxt);
+
+ VCD_MSG_HIGH("All buffers flushed. Enqueuing stop cmd\n");
+
+ vcd_client_cmd_flush_and_en_q(cctxt,
+ VCD_CMD_CODEC_STOP);
+ }
+ }
+}
+
+static void vcd_clnt_cb_in_pausing(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_clnt_cb_in_pausing:\n");
+
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+
+ return;
+ }
+
+ switch (event) {
+ case VCD_EVT_RESP_INPUT_DONE:
+ rc = vcd_handle_input_done(cctxt, payload, event, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ rc = vcd_handle_frame_done(cctxt, payload, event, status);
+ break;
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ rc = vcd_handle_output_required(cctxt, payload, status);
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_RESP_PAUSE, status);
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+
+ dev_ctxt->cont = false;
+ break;
+ }
+
+ if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_DONE ||
+ event == VCD_EVT_RESP_OUTPUT_REQ) &&
+ ((struct ddl_frame_data_tag *)payload)->frm_trans_end) {
+
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+
+ if (!cctxt->status.frame_submitted) {
+ VCD_MSG_HIGH("All pending frames recvd from DDL\n");
+
+ cctxt->callback(VCD_EVT_RESP_PAUSE, VCD_S_SUCCESS, NULL,
+ 0, cctxt, cctxt->client_data);
+
+ vcd_do_client_state_transition(cctxt,
+ VCD_CLIENT_STATE_PAUSED,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_PAUSE);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END "
+ "failed\n");
+ }
+ }
+ }
+}
+
+static void vcd_clnt_cb_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ VCD_MSG_LOW("vcd_clnt_cb_in_invalid:\n");
+ if (cctxt->ddl_handle != ddl_handle) {
+ VCD_MSG_ERROR("ddl_handle mismatch\n");
+ return;
+ }
+ switch (event) {
+ case VCD_EVT_RESP_STOP:
+ vcd_handle_stop_done_in_invalid(cctxt, status);
+ break;
+ case VCD_EVT_RESP_INPUT_DONE:
+ case VCD_EVT_RESP_OUTPUT_DONE:
+ case VCD_EVT_RESP_OUTPUT_REQ:
+ case VCD_EVT_RESP_TRANSACTION_PENDING:
+ break;
+ case VCD_EVT_IND_HWERRFATAL:
+ if (status == VCD_ERR_HW_FATAL)
+ vcd_handle_stop_done_in_invalid(cctxt, status);
+
+ break;
+ default:
+ VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n",
+ event, status);
+ dev_ctxt->cont = false;
+ break;
+ }
+}
+
+static void vcd_clnt_enter_open(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_OPEN on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_starting(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_STARTING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_run(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_RUN on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_flushing(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_FLUSHING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_stopping(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_STOPPING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_eos(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ u32 rc;
+
+ VCD_MSG_MED("Entering CLIENT_STATE_EOS on api %d\n", ev_code);
+ rc = vcd_map_sched_status(sched_suspend_resume_client(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, false));
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x\n",
+ rc);
+}
+
+static void vcd_clnt_enter_pausing(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_PAUSING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_paused(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_PAUSED on api %d\n", ev_code);
+}
+
+static void vcd_clnt_enter_invalid(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering CLIENT_STATE_INVALID on api %d\n", ev_code);
+ cctxt->ddl_hdl_valid = false;
+}
+
+static void vcd_clnt_exit_open(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_OPEN on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_starting(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_STARTING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_run(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_RUN on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_flushing(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_FLUSHING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_stopping(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_STOPPING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_eos(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ u32 rc;
+ VCD_MSG_MED("Exiting CLIENT_STATE_EOS on api %d\n", ev_code);
+ rc = vcd_map_sched_status(sched_suspend_resume_client(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true));
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x\n",
+ rc);
+}
+
+static void vcd_clnt_exit_pausing(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_PAUSING on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_paused(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_PAUSED on api %d\n", ev_code);
+}
+
+static void vcd_clnt_exit_invalid(struct vcd_clnt_ctxt *cctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting CLIENT_STATE_INVALID on api %d\n", ev_code);
+}
+
+void vcd_do_client_state_transition(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_clnt_state_enum to_state, u32 ev_code)
+{
+ struct vcd_clnt_state_ctxt *state_ctxt;
+
+ if (!cctxt || to_state >= VCD_CLIENT_STATE_MAX) {
+ VCD_MSG_ERROR("Bad parameters. cctxt=%p, to_state=%d\n", cctxt,
+ to_state);
+ }
+
+ state_ctxt = &cctxt->clnt_state;
+
+ if (state_ctxt->state == to_state) {
+ VCD_MSG_HIGH("Client already in requested to_state=%d\n",
+ to_state);
+ return;
+ }
+
+ VCD_MSG_MED("vcd_do_client_state_transition: C%d -> C%d, for api %d\n",
+ (int)state_ctxt->state, (int)to_state, ev_code);
+
+ if (state_ctxt->state_table->pf_exit)
+ state_ctxt->state_table->pf_exit(cctxt, ev_code);
+
+
+ state_ctxt->state = to_state;
+ state_ctxt->state_table = vcd_clnt_state_table[to_state];
+
+ if (state_ctxt->state_table->pf_entry)
+ state_ctxt->state_table->pf_entry(cctxt, ev_code);
+}
+
+const struct vcd_clnt_state_table *vcd_get_client_state_table(
+ enum vcd_clnt_state_enum state)
+{
+ return vcd_clnt_state_table[state];
+}
+
+static const struct vcd_clnt_state_table vcd_clnt_table_open = {
+ {
+ vcd_close_in_open,
+ vcd_encode_start_in_open,
+ NULL,
+ vcd_decode_start_in_open,
+ NULL,
+ NULL,
+ NULL,
+ vcd_flush_inopen,
+ vcd_stop_inopen,
+ vcd_set_property_cmn,
+ vcd_get_property_cmn,
+ vcd_set_buffer_requirements_cmn,
+ vcd_get_buffer_requirements_cmn,
+ vcd_set_buffer_cmn,
+ vcd_allocate_buffer_cmn,
+ vcd_free_buffer_cmn,
+ vcd_fill_output_buffer_cmn,
+ NULL,
+ },
+ vcd_clnt_enter_open,
+ vcd_clnt_exit_open
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_starting = {
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_clnt_cb_in_starting,
+ },
+ vcd_clnt_enter_starting,
+ vcd_clnt_exit_starting
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_run = {
+ {
+ NULL,
+ vcd_encode_start_in_run,
+ vcd_encode_frame_cmn,
+ vcd_decode_start_in_run,
+ vcd_decode_frame_cmn,
+ vcd_pause_in_run,
+ NULL,
+ vcd_flush_cmn,
+ vcd_stop_in_run,
+ vcd_set_property_cmn,
+ vcd_get_property_cmn,
+ vcd_set_buffer_requirements_cmn,
+ vcd_get_buffer_requirements_cmn,
+ vcd_set_buffer_cmn,
+ vcd_allocate_buffer_cmn,
+ NULL,
+ vcd_fill_output_buffer_cmn,
+ vcd_clnt_cb_in_run,
+ },
+ vcd_clnt_enter_run,
+ vcd_clnt_exit_run
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_flushing = {
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_flush_in_flushing,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_clnt_cb_in_flushing,
+ },
+ vcd_clnt_enter_flushing,
+ vcd_clnt_exit_flushing
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_stopping = {
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_get_property_cmn,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_clnt_cb_in_stopping,
+ },
+ vcd_clnt_enter_stopping,
+ vcd_clnt_exit_stopping
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_eos = {
+ {
+ NULL,
+ NULL,
+ vcd_encode_frame_cmn,
+ NULL,
+ vcd_decode_frame_cmn,
+ NULL,
+ NULL,
+ vcd_flush_in_eos,
+ vcd_stop_in_eos,
+ NULL,
+ vcd_get_property_cmn,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_fill_output_buffer_in_eos,
+ vcd_clnt_cb_in_eos,
+ },
+ vcd_clnt_enter_eos,
+ vcd_clnt_exit_eos
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_pausing = {
+ {
+ NULL,
+ NULL,
+ vcd_encode_frame_cmn,
+ NULL,
+ vcd_decode_frame_cmn,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_get_property_cmn,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_fill_output_buffer_cmn,
+ vcd_clnt_cb_in_pausing,
+ },
+ vcd_clnt_enter_pausing,
+ vcd_clnt_exit_pausing
+};
+
+static const struct vcd_clnt_state_table vcd_clnt_table_paused = {
+ {
+ NULL,
+ NULL,
+ vcd_encode_frame_cmn,
+ NULL,
+ vcd_decode_frame_cmn,
+ NULL,
+ vcd_resume_in_paused,
+ vcd_flush_cmn,
+ vcd_stop_cmn,
+ vcd_set_property_cmn,
+ vcd_get_property_cmn,
+ vcd_set_buffer_requirements_cmn,
+ vcd_get_buffer_requirements_cmn,
+ vcd_set_buffer_cmn,
+ vcd_allocate_buffer_cmn,
+ NULL,
+ vcd_fill_output_buffer_cmn,
+ NULL,
+ },
+ vcd_clnt_enter_paused,
+ vcd_clnt_exit_paused
+};
+static const struct vcd_clnt_state_table vcd_clnt_table_invalid = {
+ {
+ vcd_close_in_invalid,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_flush_in_invalid,
+ vcd_stop_in_invalid,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vcd_free_buffer_cmn,
+ NULL,
+ vcd_clnt_cb_in_invalid,
+ },
+ vcd_clnt_enter_invalid,
+ vcd_clnt_exit_invalid
+};
+
+static const struct vcd_clnt_state_table *vcd_clnt_state_table[] = {
+ NULL,
+ &vcd_clnt_table_open,
+ &vcd_clnt_table_starting,
+ &vcd_clnt_table_run,
+ &vcd_clnt_table_flushing,
+ &vcd_clnt_table_pausing,
+ &vcd_clnt_table_paused,
+ &vcd_clnt_table_stopping,
+ &vcd_clnt_table_eos,
+ &vcd_clnt_table_invalid
+};
diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.h b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h
new file mode 100644
index 0000000..8f3a975
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_CLIENT_SM_H_
+#define _VCD_CLIENT_SM_H_
+#include "vcd_api.h"
+#include "vcd_ddl_api.h"
+
+struct vcd_clnt_state_table;
+struct vcd_clnt_state_ctxt;
+struct vcd_clnt_ctxt;
+
+enum vcd_clnt_state_enum {
+ VCD_CLIENT_STATE_NULL = 0,
+ VCD_CLIENT_STATE_OPEN,
+ VCD_CLIENT_STATE_STARTING,
+ VCD_CLIENT_STATE_RUN,
+ VCD_CLIENT_STATE_FLUSHING,
+ VCD_CLIENT_STATE_PAUSING,
+ VCD_CLIENT_STATE_PAUSED,
+ VCD_CLIENT_STATE_STOPPING,
+ VCD_CLIENT_STATE_EOS,
+ VCD_CLIENT_STATE_INVALID,
+ VCD_CLIENT_STATE_MAX,
+ VCD_CLIENT_STATE_32BIT = 0x7FFFFFFF
+};
+
+#define CLIENT_STATE_EVENT_NUMBER(ppf) \
+ ((u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.ppf)) - \
+ (u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.pf_close)) + 1)
+
+struct vcd_clnt_state_table {
+ struct {
+ u32(*pf_close) (struct vcd_clnt_ctxt *cctxt);
+ u32(*pf_encode_start) (struct vcd_clnt_ctxt *cctxt);
+ u32(*pf_encode_frame) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame);
+ u32(*pf_decode_start) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_sequence_hdr *seq_hdr);
+ u32(*pf_decode_frame) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame);
+ u32(*pf_pause) (struct vcd_clnt_ctxt *cctxt);
+ u32(*pf_resume) (struct vcd_clnt_ctxt *cctxt);
+ u32(*pf_flush) (struct vcd_clnt_ctxt *cctxt, u32 mode);
+ u32(*pf_stop) (struct vcd_clnt_ctxt *cctxt);
+ u32(*pf_set_property) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_hdr *prop_hdr, void *prop);
+ u32(*pf_get_property) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_hdr *prop_hdr,
+ void *prop);
+ u32(*pf_set_buffer_requirements) (struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type,
+ struct vcd_buffer_requirement *buffer_req);
+ u32(*pf_get_buffer_requirements) (struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type,
+ struct vcd_buffer_requirement *buffer_req);
+ u32(*pf_set_buffer) (struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, void *buffer,
+ size_t buf_size);
+ u32(*pf_allocate_buffer) (struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, size_t sz,
+ void **virt_addr, phys_addr_t *phys_addr);
+ u32(*pf_free_buffer) (struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, void *buf);
+ u32(*pf_fill_output_buffer) (struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *buffer);
+ void (*pf_clnt_cb) (struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data);
+ } ev_hdlr;
+
+ void (*pf_entry) (struct vcd_clnt_ctxt *cctxt, s32 state_event_type);
+ void (*pf_exit) (struct vcd_clnt_ctxt *cctxt, s32 state_event_type);
+};
+
+struct vcd_clnt_state_ctxt {
+ const struct vcd_clnt_state_table *state_table;
+ enum vcd_clnt_state_enum state;
+};
+
+extern void vcd_do_client_state_transition(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_clnt_state_enum to_state, u32 ev_code);
+
+extern const struct vcd_clnt_state_table *vcd_get_client_state_table(
+ enum vcd_clnt_state_enum state);
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_core.h b/drivers/misc/video_core/720p/vcd/vcd_core.h
new file mode 100644
index 0000000..f855d7bd
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_core.h
@@ -0,0 +1,258 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_CORE_H_
+#define _VCD_CORE_H_
+
+#include "vcd_api.h"
+#include "vid_frame_scheduler_api.h"
+#include "vcd_ddl_api.h"
+
+#include "vcd_util.h"
+#include "vcd_client_sm.h"
+#include "vcd_power_sm.h"
+
+#define VCD_SIGNATURE 0x75017591U
+
+#define VCD_MIN_PERF_LEVEL 37900
+
+#define VCD_MAX_SCHEDULER_QUEUE_DURATION 1
+
+#define VCD_MAX_SCHEDULER_QUEUE_SIZE(fps_n, fps_d) \
+ (fps_n / fps_d * VCD_MAX_SCHEDULER_QUEUE_DURATION)
+
+#define VCD_SCHEDULER_INITIAL_PERF_LEVEL 108000
+
+#define VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM 1
+
+#define VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM 1
+
+#define VCD_DRIVER_INSTANCE_MAX 4
+
+#define VCD_MAX_CLIENT_TRANSACTIONS 32
+
+#define VCD_SEQ_HDR_PADDING_BYTES 256
+
+#define VCD_DEC_NUM_INTERLACED_FIELDS 2
+
+#define VCD_TIMESTAMP_RESOLUTION 1000000
+#define VCD_DEC_INITIAL_FRAME_RATE 30
+
+#define VCD_S_SCHED_STAT_BASE 0x20000000
+#define VCD_S_SCHED_EOS (VCD_S_SCHED_STAT_BASE + 0x1)
+#define VCD_S_SCHED_SLEEP (VCD_S_SCHED_STAT_BASE + 0x2)
+#define VCD_S_SCHED_QEMPTY (VCD_S_SCHED_STAT_BASE + 0x3)
+#define VCD_S_SCHED_QFULL (VCD_S_SCHED_STAT_BASE + 0x4)
+
+enum vcd_command_type {
+ VCD_CMD_NONE,
+ VCD_CMD_DEVICE_INIT,
+ VCD_CMD_DEVICE_TERM,
+ VCD_CMD_DEVICE_RESET,
+ VCD_CMD_CODEC_START,
+ VCD_CMD_CODEC_STOP,
+ VCD_CMD_CODE_FRAME,
+ VCD_CMD_OUTPUT_FLUSH,
+ VCD_CMD_CLIENT_CLOSE
+};
+
+//TODO: remove this
+struct vcd_cmd_q_element {
+ enum vcd_command_type pending_cmd;
+};
+
+struct vcd_dma_buffer {
+ void *virt_addr;
+ phys_addr_t phys_addr;
+ size_t size;
+};
+
+struct vcd_buffer_entry {
+ u32 valid;
+ struct vcd_dma_buffer buffer;
+ void *virt_addr;
+ phys_addr_t phys_addr;
+ size_t size;
+// u8 *alloc;
+// u8 *virtual; // aligned so == alloc
+// u8 *physical;
+// u32 size;
+ u32 allocated; // true when allocated
+ u32 in_use;
+ struct vcd_frame_data frame;
+
+};
+
+struct vcd_buffer_pool {
+ struct vcd_buffer_entry *entries;
+ u32 count;
+ struct vcd_buffer_requirement buf_req;
+ u32 validated;
+ u32 allocated;
+ u32 in_use;
+ struct vcd_buffer_entry **queue;
+ u16 q_len;
+ u16 q_head;
+ u16 q_tail;
+
+};
+
+struct vcd_transc {
+ u32 in_use;
+ enum vcd_command_type type;
+ struct vcd_clnt_ctxt *cctxt;
+
+ struct vcd_buffer_entry *ip_buf_entry;
+
+ s64 time_stamp;
+ u32 ip_frm_tag;
+ enum vcd_frame frame_type;
+
+ struct vcd_buffer_entry *op_buf_entry;
+
+ u32 input_done;
+ u32 frame_done;
+};
+
+struct vcd_dev_ctxt {
+ u32 ddl_cmd_concurrency;
+ u32 ddl_frame_ch_depth;
+ u32 ddl_cmd_ch_depth;
+ u32 ddl_frame_ch_interim;
+ u32 ddl_cmd_ch_interim;
+ u32 ddl_frame_ch_free;
+ u32 ddl_cmd_ch_free;
+
+ void *sched_hdl;
+
+ struct vcd_init_config config;
+
+ u32 driver_ids[VCD_DRIVER_INSTANCE_MAX];
+ u32 refs;
+ u8 *device_base_addr;
+ void *hw_timer_handle;
+ u32 hw_time_out;
+ struct vcd_clnt_ctxt *cctxt_list_head;
+
+ enum vcd_command_type pending_cmd;
+
+ u32 cont;
+
+ struct vcd_transc *trans_tbl;
+ u32 trans_tbl_size;
+
+ enum vcd_power_state pwr_state;
+ enum vcd_pwr_clk_state_type pwr_clk_state;
+ u32 active_clnts;
+ u32 max_perf_lvl;
+ u32 reqd_perf_lvl;
+ u32 curr_perf_lvl;
+ u32 set_perf_lvl_pending;
+
+};
+
+struct vcd_clnt_status {
+ u32 req_perf_lvl;
+
+ u32 b1st_frame_recvd;
+ u32 b1st_ip_done_recvd;
+ u32 b1st_op_done_recvd;
+
+ u32 frame_submitted;
+ u32 frame_delayed;
+ u32 cmd_submitted;
+
+ u32 int_field_cnt;
+
+ s64 first_ts;
+ s64 prev_ts;
+ u32 time_elapsed;
+
+ u32 stop_pending;
+ u32 flush_mode;
+
+ u32 eos_wait_for_op_buf;
+ struct vcd_frame_data eos_trig_ip_frm;
+
+ u32 eos_prev_valid;
+ struct ddl_frame_data_tag eos_prev_op_frm;
+ u32 last_err;
+ u32 last_evt;
+ u32 cleaning_up;
+ u32 close_pending;
+};
+
+struct vcd_clnt_ctxt {
+ u32 signature;
+ struct vcd_clnt_state_ctxt clnt_state;
+
+ s32 driver_id;
+
+ u32 live;
+ u32 decoding;
+
+ struct vcd_property_frame_rate frm_rate;
+ u32 frm_p_units;
+ u32 reqd_perf_lvl;
+ u32 time_resoln;
+
+ struct vcd_buffer_pool in_buf_pool;
+ struct vcd_buffer_pool out_buf_pool;
+
+ void (*callback) (u32 event, u32 status, void *info, u32 size,
+ void *handle, void *const client_data);
+ void *client_data;
+
+ u32 sched_clnt_valid;
+ void *sched_clnt_hdl;
+ u32 sched_o_tkn_per_ip_frm;
+ u32 ddl_hdl_valid;
+ u32 *ddl_handle;
+ struct vcd_dev_ctxt *dev_ctxt;
+ struct vcd_cmd_q_element cmd_q;
+
+ struct vcd_sequence_hdr seq_hdr;
+ phys_addr_t seq_hdr_phys_addr;
+
+ struct vcd_clnt_status status;
+
+ struct vcd_clnt_ctxt *next;
+};
+
+#define VCD_BUFFERPOOL_INUSE_DECREMENT(val) \
+do { \
+ if ((val) > 0) \
+ val--; \
+ else { \
+ VCD_MSG_ERROR("%s(): Inconsistent val given in " \
+ " VCD_BUFFERPOOL_INUSE_DECREMENT\n", __func__); \
+ vcd_assert(); \
+ } \
+} while (0)
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.c b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c
new file mode 100644
index 0000000..9e3a6a7
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c
@@ -0,0 +1,1109 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd.h"
+
+static const struct vcd_dev_state_table *vcd_dev_state_table[];
+static const struct vcd_dev_state_table vcd_dev_table_null;
+
+struct vcd_drv_ctxt *vcd_get_drv_context(void)
+{
+ static struct vcd_drv_ctxt drv_context = {
+ {&vcd_dev_table_null, VCD_DEVICE_STATE_NULL},
+ {0},
+ 0
+ };
+
+ return &drv_context;
+
+}
+
+void vcd_do_device_state_transition(struct vcd_drv_ctxt *drv_ctxt,
+ enum vcd_dev_state_enum to_state, u32 ev_code)
+{
+ struct vcd_dev_state_ctxt *state_ctxt;
+
+ if (!drv_ctxt || to_state >= VCD_DEVICE_STATE_MAX) {
+ VCD_MSG_ERROR("Bad parameters. drv_ctxt=%p, to_state=%d",
+ drv_ctxt, to_state);
+ }
+
+ state_ctxt = &drv_ctxt->dev_state;
+
+ if (state_ctxt->state == to_state) {
+ VCD_MSG_HIGH("Device already in requested to_state=%d",
+ to_state);
+
+ return;
+ }
+
+ VCD_MSG_MED("vcd_do_device_state_transition: D%d -> D%d, for api %d",
+ (int)state_ctxt->state, (int)to_state, ev_code);
+
+ if (state_ctxt->state_table->pf_exit)
+ state_ctxt->state_table->pf_exit(drv_ctxt, ev_code);
+
+
+ state_ctxt->state = to_state;
+ state_ctxt->state_table = vcd_dev_state_table[to_state];
+
+ if (state_ctxt->state_table->pf_entry)
+ state_ctxt->state_table->pf_entry(drv_ctxt, ev_code);
+}
+
+void vcd_hw_timeout_handler(void *user_data)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+
+ VCD_MSG_HIGH("vcd_hw_timeout_handler:");
+ user_data = NULL;
+ drv_ctxt = vcd_get_drv_context();
+ mutex_lock(drv_ctxt->dev_mutex);
+ if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout)
+ drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout(drv_ctxt,
+ user_data);
+ else
+ VCD_MSG_ERROR("hw_timeout unsupported in device state %d",
+ drv_ctxt->dev_state.state);
+ mutex_unlock(drv_ctxt->dev_mutex);
+}
+
+void vcd_ddl_callback(u32 event, u32 status, void *payload,
+ u32 size, u32 *ddl_handle, void *const client_data)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+ struct vcd_dev_ctxt *dev_ctxt;
+ struct vcd_dev_state_ctxt *dev_state;
+ struct vcd_clnt_ctxt *cctxt;
+ struct vcd_transc *transc;
+
+ VCD_MSG_LOW("vcd_ddl_callback:");
+
+ VCD_MSG_LOW("event=0x%x status=0x%x", event, status);
+
+ drv_ctxt = vcd_get_drv_context();
+ dev_ctxt = &drv_ctxt->dev_ctxt;
+ dev_state = &drv_ctxt->dev_state;
+
+ dev_ctxt->cont = true;
+ vcd_device_timer_stop(dev_ctxt);
+
+ switch (dev_state->state) {
+ case VCD_DEVICE_STATE_NULL:
+ VCD_MSG_HIGH("Callback unexpected in NULL state");
+ break;
+ case VCD_DEVICE_STATE_NOT_INIT:
+ VCD_MSG_HIGH("Callback unexpected in NOT_INIT state");
+ break;
+ case VCD_DEVICE_STATE_INITING:
+ if (dev_state->state_table->ev_hdlr.pf_dev_cb) {
+ dev_state->state_table->ev_hdlr.pf_dev_cb(drv_ctxt,
+ event, status, payload, size, ddl_handle,
+ client_data);
+ } else {
+ VCD_MSG_HIGH("No device handler in %d state",
+ dev_state->state);
+ }
+ break;
+ case VCD_DEVICE_STATE_READY:
+ transc = (struct vcd_transc *)client_data;
+
+ if (!transc || !transc->in_use || !transc->cctxt) {
+ VCD_MSG_ERROR("Invalid clientdata received from DDL ");
+ } else {
+ cctxt = transc->cctxt;
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_clnt_cb) {
+ cctxt->clnt_state.state_table->ev_hdlr.
+ pf_clnt_cb(cctxt, event, status,
+ payload, size, ddl_handle, client_data);
+ } else {
+ VCD_MSG_HIGH("No client handler in"
+ " (dsm:READY, csm:%d) state",
+ (int)cctxt->clnt_state.state);
+
+ if (VCD_FAILED(status)) {
+ VCD_MSG_FATAL("DDL callback"
+ " returned failure 0x%x",
+ status);
+ }
+ }
+ }
+ break;
+ default:
+ VCD_MSG_ERROR("Unknown state");
+ break;
+ }
+
+}
+
+u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ struct sched_init_param sched_init;
+ u32 rc;
+ struct ddl_init_config ddl_init;
+
+ VCD_MSG_LOW("vcd_init_device_context:");
+
+ dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+ rc = vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN);
+ VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_INIT_BEGIN failed");
+
+ VCD_MSG_HIGH("Device powered ON and clocked");
+
+ sched_init.perf_lvl = dev_ctxt->max_perf_lvl;
+ rc = vcd_map_sched_status(sched_create(&sched_init,
+ &dev_ctxt->sched_hdl));
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: sched_create", rc);
+
+ vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL);
+
+ return rc;
+ }
+
+ VCD_MSG_HIGH("Created scheduler instance.");
+
+ ddl_init.core_virtual_base_addr = dev_ctxt->device_base_addr;
+ ddl_init.pf_interrupt_clr = dev_ctxt->config.pf_interrupt_clr;
+ ddl_init.ddl_callback = vcd_ddl_callback;
+
+ rc = ddl_device_init(&ddl_init, NULL);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_init", rc);
+
+ sched_destroy(dev_ctxt->sched_hdl);
+ dev_ctxt->sched_hdl = NULL;
+
+ vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL);
+ } else {
+ vcd_device_timer_start(dev_ctxt);
+ vcd_do_device_state_transition(drv_ctxt,
+ VCD_DEVICE_STATE_INITING, ev_code);
+ }
+
+ return rc;
+}
+
+void vcd_handle_device_init_failed(struct vcd_drv_ctxt *drv_ctxt, u32 status)
+{
+ struct vcd_clnt_ctxt *client;
+ struct vcd_clnt_ctxt *tmp_client;
+
+ VCD_MSG_ERROR("Device init failed. status = %d", status);
+
+ client = drv_ctxt->dev_ctxt.cctxt_list_head;
+ while (client) {
+ client->callback(VCD_EVT_RESP_OPEN, status, NULL, 0, 0,
+ client->client_data);
+
+ tmp_client = client;
+ client = client->next;
+
+ vcd_destroy_client_context(tmp_client);
+ }
+ if (ddl_device_release(NULL))
+ VCD_MSG_ERROR("Failed: ddl_device_release");
+
+ (void)sched_destroy(drv_ctxt->dev_ctxt.sched_hdl);
+ drv_ctxt->dev_ctxt.sched_hdl = NULL;
+
+ if (vcd_power_event(&drv_ctxt->dev_ctxt, NULL,
+ VCD_EVT_PWR_DEV_INIT_FAIL))
+ VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_FAIL failed");
+
+ vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NOT_INIT,
+ DEVICE_STATE_EVENT_NUMBER(pf_dev_cb));
+}
+
+u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_deinit_device_context:");
+
+ rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL,
+ VCD_EVT_PWR_DEV_TERM_BEGIN);
+
+ VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed");
+
+ rc = ddl_device_release(NULL);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_release", rc);
+
+ vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_FAIL);
+ } else {
+ sched_destroy(dev_ctxt->sched_hdl);
+ dev_ctxt->sched_hdl = NULL;
+
+ vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END);
+
+ vcd_do_device_state_transition(drv_ctxt,
+ VCD_DEVICE_STATE_NOT_INIT, ev_code);
+ }
+ return rc;
+}
+
+void vcd_term_driver_context(struct vcd_drv_ctxt *drv_ctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+ VCD_MSG_HIGH("All driver instances terminated");
+
+ if (dev_ctxt->config.pf_deregister_isr)
+ dev_ctxt->config.pf_deregister_isr();
+
+ if (dev_ctxt->config.pf_un_map_dev_base_addr)
+ dev_ctxt->config.pf_un_map_dev_base_addr();
+
+ if (dev_ctxt->config.pf_timer_release)
+ dev_ctxt->config.pf_timer_release(dev_ctxt->hw_timer_handle);
+
+ kfree(dev_ctxt->trans_tbl);
+
+ memset(dev_ctxt, 0, sizeof(struct vcd_dev_ctxt));
+
+ vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NULL,
+ DEVICE_STATE_EVENT_NUMBER(pf_term));
+
+}
+
+u32 vcd_reset_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_reset_device_context:");
+ vcd_reset_device_channels(dev_ctxt);
+ rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL,
+ VCD_EVT_PWR_DEV_TERM_BEGIN);
+ VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed");
+ if (ddl_reset_hw(0))
+ VCD_MSG_HIGH("HW Reset done");
+ else
+ VCD_MSG_FATAL("HW Reset failed");
+
+ vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END);
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *trig_clnt)
+{
+ struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+ VCD_MSG_LOW("vcd_handle_device_err_fatal:");
+ while (cctxt) {
+ if (cctxt != trig_clnt) {
+ vcd_clnt_handle_device_err_fatal(cctxt,
+ VCD_EVT_IND_HWERRFATAL);
+ }
+ cctxt = cctxt->next;
+ }
+ dev_ctxt->pending_cmd = VCD_CMD_DEVICE_RESET;
+ vcd_do_device_state_transition(vcd_get_drv_context(),
+ VCD_DEVICE_STATE_INVALID, DEVICE_STATE_EVENT_NUMBER(pf_dev_cb));
+}
+
+void vcd_handle_for_last_clnt_close(
+ struct vcd_dev_ctxt *dev_ctxt, u32 send_deinit)
+{
+ if (!dev_ctxt->cctxt_list_head) {
+ VCD_MSG_HIGH("All clients are closed");
+ if (send_deinit)
+ vcd_deinit_device_context(vcd_get_drv_context(),
+ DEVICE_STATE_EVENT_NUMBER(pf_close));
+ else
+ dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM;
+ }
+}
+void vcd_continue(void)
+{
+ struct vcd_drv_ctxt *drv_ctxt;
+ struct vcd_dev_ctxt *dev_ctxt;
+ u32 cont;
+ struct vcd_transc *transc;
+ u32 rc;
+ VCD_MSG_LOW("vcd_continue:");
+
+ drv_ctxt = vcd_get_drv_context();
+ dev_ctxt = &drv_ctxt->dev_ctxt;
+
+ dev_ctxt->cont = false;
+
+ if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_INIT) {
+ VCD_MSG_HIGH("VCD_CMD_DEVICE_INIT is pending");
+
+ dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+ vcd_init_device_context(drv_ctxt,
+ DEVICE_STATE_EVENT_NUMBER(pf_open));
+ } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_TERM) {
+ VCD_MSG_HIGH("VCD_CMD_DEVICE_TERM is pending");
+
+ dev_ctxt->pending_cmd = VCD_CMD_NONE;
+
+ vcd_deinit_device_context(drv_ctxt,
+ DEVICE_STATE_EVENT_NUMBER(pf_close));
+ } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_RESET) {
+ VCD_MSG_HIGH("VCD_CMD_DEVICE_RESET is pending");
+ dev_ctxt->pending_cmd = VCD_CMD_NONE;
+ vcd_reset_device_context(drv_ctxt,
+ DEVICE_STATE_EVENT_NUMBER(pf_dev_cb));
+ } else {
+ if (dev_ctxt->set_perf_lvl_pending) {
+ rc = vcd_power_event(dev_ctxt, NULL,
+ VCD_EVT_PWR_DEV_SET_PERFLVL);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR
+ ("VCD_EVT_PWR_CLNT_SET_PERFLVL failed");
+ VCD_MSG_HIGH("Not running at desired perf "
+ "level.curr=%d, reqd=%d",
+ dev_ctxt->curr_perf_lvl,
+ dev_ctxt->reqd_perf_lvl);
+ } else {
+ dev_ctxt->set_perf_lvl_pending = false;
+ }
+ }
+
+ do {
+ cont = false;
+
+ if (vcd_get_command_channel_in_loop(dev_ctxt,
+ &transc)) {
+ if (vcd_submit_command_in_continue(dev_ctxt,
+ transc))
+ cont = true;
+ else {
+ VCD_MSG_MED
+ ("No more commands to submit");
+
+ vcd_release_command_channel(dev_ctxt,
+ transc);
+
+ vcd_release_interim_command_channels(
+ dev_ctxt);
+ }
+ }
+ } while (cont);
+
+ do {
+ cont = false;
+
+ if (vcd_get_frame_channel_in_loop(dev_ctxt, &transc)) {
+ if (vcd_try_submit_frame_in_continue(dev_ctxt,
+ transc)) {
+ cont = true;
+ } else {
+ VCD_MSG_MED("No more frames to submit");
+
+ vcd_release_frame_channel(dev_ctxt,
+ transc);
+
+ vcd_release_interim_frame_channels(
+ dev_ctxt);
+ }
+ }
+
+ } while (cont);
+
+ if (!vcd_core_is_busy(dev_ctxt)) {
+ rc = vcd_power_event(dev_ctxt, NULL,
+ VCD_EVT_PWR_CLNT_CMD_END);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Failed:"
+ "VCD_EVT_PWR_CLNT_CMD_END");
+ }
+ }
+}
+
+static void vcd_pause_all_sessions(struct vcd_dev_ctxt *dev_ctxt)
+{
+ struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+ u32 rc;
+
+ while (cctxt) {
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_pause) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_pause(cctxt);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Client pause failed");
+
+ }
+
+ cctxt = cctxt->next;
+ }
+}
+
+static void vcd_resume_all_sessions(struct vcd_dev_ctxt *dev_ctxt)
+{
+ struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head;
+ u32 rc;
+
+ while (cctxt) {
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_resume(cctxt);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Client resume failed");
+
+ }
+
+ cctxt = cctxt->next;
+ }
+}
+
+static u32 vcd_init_cmn(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ s32 driver_id;
+
+ if (dev_ctxt->config.pf_interrupt_clr != config->pf_interrupt_clr ||
+ dev_ctxt->config.pf_register_isr !=
+ config->pf_register_isr ||
+ dev_ctxt->config.pf_deregister_isr !=
+ config->pf_deregister_isr ||
+ dev_ctxt->config.pf_map_dev_base_addr !=
+ config->pf_map_dev_base_addr ||
+ dev_ctxt->config.pf_un_map_dev_base_addr !=
+ config->pf_un_map_dev_base_addr) {
+ VCD_MSG_ERROR("Device config mismatch");
+ VCD_MSG_HIGH("VCD will be using config from 1st vcd_init");
+ }
+
+ *driver_handle = 0;
+
+ driver_id = 0;
+ while (driver_id < VCD_DRIVER_INSTANCE_MAX &&
+ dev_ctxt->driver_ids[driver_id]) {
+ ++driver_id;
+ }
+
+ if (driver_id == VCD_DRIVER_INSTANCE_MAX) {
+ VCD_MSG_ERROR("Max driver instances reached");
+
+ return VCD_ERR_FAIL;
+ }
+
+ ++dev_ctxt->refs;
+ dev_ctxt->driver_ids[driver_id] = true;
+ *driver_handle = driver_id + 1;
+
+ VCD_MSG_HIGH("Driver_id = %d. No of driver instances = %d",
+ driver_id, dev_ctxt->refs);
+
+ return VCD_S_SUCCESS;
+
+}
+
+static u32 vcd_init_in_null(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle) {
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ u32 done_create_timer = false;
+ VCD_MSG_LOW("vcd_init_in_dev_null:");
+
+ dev_ctxt->config = *config;
+
+ dev_ctxt->device_base_addr = (u8 *)config->pf_map_dev_base_addr(
+ dev_ctxt->config.device_name);
+
+ if (!dev_ctxt->device_base_addr) {
+ VCD_MSG_ERROR("NULL Device_base_addr");
+
+ return VCD_ERR_FAIL;
+ }
+
+ if (config->pf_register_isr)
+ config->pf_register_isr(dev_ctxt->config.device_name);
+
+ if (config->pf_timer_create) {
+ if (config->pf_timer_create(vcd_hw_timeout_handler, NULL,
+ &dev_ctxt->hw_timer_handle))
+ done_create_timer = true;
+ else {
+ VCD_MSG_ERROR("timercreate failed");
+ return VCD_ERR_FAIL;
+ }
+ }
+
+
+ rc = vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+ if (!VCD_FAILED(rc)) {
+ vcd_do_device_state_transition(drv_ctxt,
+ VCD_DEVICE_STATE_NOT_INIT,
+ DEVICE_STATE_EVENT_NUMBER(pf_init));
+ } else {
+ if (dev_ctxt->config.pf_un_map_dev_base_addr)
+ dev_ctxt->config.pf_un_map_dev_base_addr();
+
+ if (dev_ctxt->config.pf_deregister_isr)
+ dev_ctxt->config.pf_deregister_isr();
+
+ if (done_create_timer && dev_ctxt->config.pf_timer_release)
+ dev_ctxt->config.pf_timer_release(
+ dev_ctxt->hw_timer_handle);
+ }
+
+ return rc;
+
+}
+
+u32 npelly_init(void);
+
+static u32 vcd_init_in_not_init(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle)
+{
+ u32 rc;
+ VCD_MSG_LOW("vcd_init_in_dev_not_init:");
+
+ rc = npelly_init();
+
+ if (rc)
+ return rc;
+ return vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+}
+
+static u32 vcd_init_in_initing(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle)
+{
+ VCD_MSG_LOW("vcd_init_in_dev_initing:");
+
+ return vcd_init_cmn(drv_ctxt, config, driver_handle);
+
+}
+
+static u32 vcd_init_in_ready(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle)
+{
+ VCD_MSG_LOW("vcd_init_in_dev_ready:");
+
+ return vcd_init_cmn(drv_ctxt, config, driver_handle);
+}
+
+static u32 vcd_term_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+ if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) {
+ VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle);
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ if (vcd_check_for_client_context(dev_ctxt, driver_handle - 1)) {
+ VCD_MSG_ERROR("Driver has active client");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ --dev_ctxt->refs;
+ dev_ctxt->driver_ids[driver_handle - 1] = false;
+
+ VCD_MSG_HIGH("Driver_id %d terminated. No of driver instances = %d",
+ driver_handle - 1, dev_ctxt->refs);
+
+ return VCD_S_SUCCESS;
+}
+
+static u32 vcd_term_in_not_init(struct vcd_drv_ctxt *drv_ctxt,
+ s32 driver_handle)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_term_in_dev_not_init:");
+
+ rc = vcd_term_cmn(drv_ctxt, driver_handle);
+
+ if (!VCD_FAILED(rc) && !dev_ctxt->refs)
+ vcd_term_driver_context(drv_ctxt);
+
+ return rc;
+}
+
+static u32 vcd_term_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+ VCD_MSG_LOW("vcd_term_in_dev_initing:");
+
+ return vcd_term_cmn(drv_ctxt, driver_handle);
+}
+
+static u32 vcd_term_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle)
+{
+ VCD_MSG_LOW("vcd_term_in_dev_ready:");
+
+ return vcd_term_cmn(drv_ctxt, driver_handle);
+}
+
+static u32 vcd_term_in_invalid(struct vcd_drv_ctxt *drv_ctxt,
+ s32 driver_handle)
+{
+ u32 rc;
+ VCD_MSG_LOW("vcd_term_in_invalid:");
+ rc = vcd_term_cmn(drv_ctxt, driver_handle);
+ if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.refs)
+ vcd_term_driver_context(drv_ctxt);
+
+ return rc;
+}
+
+static u32 vcd_open_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle,
+ u32 decoding, void (*callback) (u32 event, u32 status, void *info,
+ u32 size, void *handle, void *const client_data), void *client_data,
+ struct vcd_clnt_ctxt **pp_cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ struct vcd_clnt_ctxt *cctxt;
+ struct vcd_clnt_ctxt *client;
+
+ if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) {
+ VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle);
+
+ return VCD_ERR_BAD_HANDLE;
+ }
+
+ cctxt = kzalloc(sizeof(struct vcd_clnt_ctxt), GFP_KERNEL);
+ if (!cctxt) {
+ VCD_MSG_ERROR("No memory for client ctxt");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ cctxt->dev_ctxt = dev_ctxt;
+ cctxt->driver_id = driver_handle - 1;
+ cctxt->decoding = decoding;
+ cctxt->callback = callback;
+ cctxt->client_data = client_data;
+
+ client = dev_ctxt->cctxt_list_head;
+ dev_ctxt->cctxt_list_head = cctxt;
+ cctxt->next = client;
+
+ *pp_cctxt = cctxt;
+
+ return VCD_S_SUCCESS;
+
+}
+
+static u32 vcd_open_in_not_init(struct vcd_drv_ctxt *drv_ctxt,
+ s32 driver_handle, u32 decoding, void (*callback) (u32 event,
+ u32 status, void *info, u32 size, void *handle,
+ void *const client_data), void *client_data)
+{
+ struct vcd_clnt_ctxt *cctxt;
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_open_in_dev_not_init:");
+
+ rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+ client_data, &cctxt);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn");
+
+ rc = vcd_init_device_context(drv_ctxt,
+ DEVICE_STATE_EVENT_NUMBER(pf_open));
+
+ if (VCD_FAILED(rc))
+ vcd_destroy_client_context(cctxt);
+
+ return rc;
+}
+
+static u32 vcd_open_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle,
+ u32 decoding, void (*callback) (u32 event, u32 status, void *info,
+ u32 size, void *handle, void *const client_data), void *client_data)
+{
+ struct vcd_clnt_ctxt *cctxt;
+
+ VCD_MSG_LOW("vcd_open_in_dev_initing:");
+
+ return vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+ client_data, &cctxt);
+}
+
+static u32 vcd_open_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle,
+ u32 decoding, void (*callback) (u32 event, u32 status, void *info,
+ u32 size, void *handle, void *const client_data), void *client_data)
+{
+ struct vcd_clnt_ctxt *cctxt;
+ struct vcd_handle_container container;
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_open_in_dev_ready:");
+
+ rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback,
+ client_data, &cctxt);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn");
+
+ rc = vcd_init_client_context(cctxt);
+
+ if (!VCD_FAILED(rc)) {
+ container.handle = (void *)cctxt;
+
+ callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS, &container,
+ sizeof(container), container.handle, client_data);
+ } else {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_init_client_context", rc);
+
+ vcd_destroy_client_context(cctxt);
+ }
+
+ return rc;
+}
+
+static u32 vcd_close_in_ready(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_clnt_ctxt *cctxt) {
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_close_in_dev_ready:");
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.
+ pf_close(cctxt);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ if (!VCD_FAILED(rc))
+ vcd_handle_for_last_clnt_close(&drv_ctxt->dev_ctxt, true);
+
+ return rc;
+}
+
+static u32 vcd_close_in_dev_invalid(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc;
+ VCD_MSG_LOW("vcd_close_in_dev_invalid:");
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.pf_close(cctxt);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d",
+ cctxt->clnt_state.state);
+ rc = VCD_ERR_BAD_STATE;
+ }
+ if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.cctxt_list_head) {
+ VCD_MSG_HIGH("All INVALID clients are closed");
+ vcd_do_device_state_transition(drv_ctxt,
+ VCD_DEVICE_STATE_NOT_INIT,
+ DEVICE_STATE_EVENT_NUMBER(pf_close));
+ }
+ return rc;
+}
+
+static u32 vcd_resume_in_ready(struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_resume_in_ready:");
+
+ if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) {
+ rc = cctxt->clnt_state.state_table->ev_hdlr.pf_resume(cctxt);
+ } else {
+ VCD_MSG_ERROR("Unsupported API in client state %d",
+ cctxt->clnt_state.state);
+
+ rc = VCD_ERR_BAD_STATE;
+ }
+
+ return rc;
+}
+
+static u32 vcd_set_dev_pwr_in_ready(struct vcd_drv_ctxt *drv_ctxt,
+ enum vcd_power_state pwr_state)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+
+ VCD_MSG_LOW("vcd_set_dev_pwr_in_ready:");
+
+ switch (pwr_state) {
+ case VCD_PWR_STATE_SLEEP:
+ vcd_pause_all_sessions(dev_ctxt);
+
+ dev_ctxt->pwr_state = VCD_PWR_STATE_SLEEP;
+
+ break;
+ case VCD_PWR_STATE_ON:
+ if (dev_ctxt->pwr_state == VCD_PWR_STATE_SLEEP)
+ vcd_resume_all_sessions(dev_ctxt);
+
+ dev_ctxt->pwr_state = VCD_PWR_STATE_ON;
+ break;
+ default:
+ VCD_MSG_ERROR("Invalid power state requested %d",
+ pwr_state);
+ break;
+ }
+ return rc;
+}
+
+static void vcd_dev_cb_in_initing(struct vcd_drv_ctxt *drv_ctxt, u32 event,
+ u32 status, void *payload, u32 size, u32 *ddl_handle,
+ void *const client_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt;
+ struct vcd_clnt_ctxt *client;
+ struct vcd_clnt_ctxt *tmp_client;
+ struct vcd_handle_container container;
+ u32 rc = VCD_S_SUCCESS;
+ u32 client_inited = false;
+ u32 fail_all_open = false;
+
+ VCD_MSG_LOW("vcd_dev_cb_in_initing:");
+
+ if (event != VCD_EVT_RESP_DEVICE_INIT) {
+ VCD_MSG_ERROR("vcd_dev_cb_in_initing: Unexpected event %d",
+ (int)event);
+ return;
+ }
+
+ dev_ctxt = &drv_ctxt->dev_ctxt;
+
+ dev_ctxt->cont = false;
+
+ if (VCD_FAILED(status)) {
+ vcd_handle_device_init_failed(drv_ctxt, status);
+
+ return;
+ }
+
+ vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_READY,
+ DEVICE_STATE_EVENT_NUMBER(pf_open));
+
+ if (!dev_ctxt->cctxt_list_head) {
+ VCD_MSG_HIGH("All clients are closed");
+
+ dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM;
+
+ return;
+ }
+
+ if (!dev_ctxt->ddl_cmd_ch_depth || !dev_ctxt->trans_tbl)
+ rc = vcd_setup_with_ddl_capabilities(dev_ctxt);
+
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x: Failed "
+ "vcd_setup_with_ddl_capabilities", rc);
+
+ fail_all_open = true;
+ }
+
+ client = dev_ctxt->cctxt_list_head;
+ while (client) {
+ if (!fail_all_open)
+ rc = vcd_init_client_context(client);
+
+
+ if (!VCD_FAILED(rc)) {
+ container.handle = (void *)client;
+ client->callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS,
+ &container, sizeof(container), container.handle,
+ client->client_data);
+
+ client = client->next;
+
+ client_inited = true;
+ } else {
+ VCD_MSG_ERROR("rc = 0x%x, Failed: "
+ "vcd_init_client_context", rc);
+
+ client->callback(VCD_EVT_RESP_OPEN, rc, NULL, 0, 0,
+ client->client_data);
+
+ tmp_client = client;
+ client = client->next;
+
+ vcd_destroy_client_context(tmp_client);
+ }
+ }
+
+ if (!client_inited || fail_all_open) {
+ VCD_MSG_ERROR("All client open requests failed");
+
+ dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM;
+ } else if (vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_END)) {
+ VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed");
+ }
+}
+
+static void vcd_hw_timeout_cmn(struct vcd_drv_ctxt *drv_ctxt, void *user_data)
+{
+ struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt;
+ VCD_MSG_LOW("vcd_hw_timeout_cmn:");
+ vcd_device_timer_stop(dev_ctxt);
+
+ vcd_handle_device_err_fatal(dev_ctxt, NULL);
+
+ /* Reset HW. */
+ vcd_reset_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER(
+ pf_timeout));
+}
+
+static void vcd_dev_enter_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", ev_code);
+}
+
+static void vcd_dev_enter_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d", ev_code);
+}
+
+static void vcd_dev_enter_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d", ev_code);
+}
+
+static void vcd_dev_enter_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d", ev_code);
+}
+
+static void vcd_dev_enter_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", ev_code);
+}
+
+static void vcd_dev_exit_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", ev_code);
+}
+
+static void vcd_dev_exit_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d", ev_code);
+}
+
+static void vcd_dev_exit_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d", ev_code);
+}
+
+static void vcd_dev_exit_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", ev_code);
+}
+
+static void vcd_dev_exit_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code)
+{
+ VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", ev_code);
+}
+
+static const struct vcd_dev_state_table vcd_dev_table_null = {
+ {
+ vcd_init_in_null,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+ vcd_dev_enter_null,
+ vcd_dev_exit_null
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_not_init = {
+ {
+ vcd_init_in_not_init,
+ vcd_term_in_not_init,
+ vcd_open_in_not_init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+ vcd_dev_enter_not_init,
+ vcd_dev_exit_not_init
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_initing = {
+ {
+ vcd_init_in_initing,
+ vcd_term_in_initing,
+ vcd_open_in_initing,
+ NULL,
+ NULL,
+ NULL,
+ vcd_dev_cb_in_initing,
+ vcd_hw_timeout_cmn,
+ },
+ vcd_dev_enter_initing,
+ vcd_dev_exit_initing
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_ready = {
+ {
+ vcd_init_in_ready,
+ vcd_term_in_ready,
+ vcd_open_in_ready,
+ vcd_close_in_ready,
+ vcd_resume_in_ready,
+ vcd_set_dev_pwr_in_ready,
+ NULL,
+ vcd_hw_timeout_cmn,
+ },
+ vcd_dev_enter_ready,
+ vcd_dev_exit_ready
+};
+
+static const struct vcd_dev_state_table vcd_dev_table_in_invalid = {
+ {
+ NULL,
+ vcd_term_in_invalid,
+ NULL,
+ vcd_close_in_dev_invalid,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+ vcd_dev_enter_invalid,
+ vcd_dev_exit_invalid
+};
+
+static const struct vcd_dev_state_table *vcd_dev_state_table[] = {
+ &vcd_dev_table_null,
+ &vcd_dev_table_not_init,
+ &vcd_dev_table_initing,
+ &vcd_dev_table_ready,
+ &vcd_dev_table_in_invalid
+};
diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.h b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h
new file mode 100644
index 0000000..0ba70d2
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h
@@ -0,0 +1,106 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DEVICE_SM_H_
+#define _VCD_DEVICE_SM_H_
+
+#include "vcd_api.h"
+#include "vcd_ddl_api.h"
+#include "vcd_core.h"
+
+struct vcd_dev_state_table;
+struct vcd_dev_state_ctxt;
+struct vcd_drv_ctxt;
+
+enum vcd_dev_state_enum {
+ VCD_DEVICE_STATE_NULL = 0,
+ VCD_DEVICE_STATE_NOT_INIT,
+ VCD_DEVICE_STATE_INITING,
+ VCD_DEVICE_STATE_READY,
+ VCD_DEVICE_STATE_INVALID,
+ VCD_DEVICE_STATE_MAX,
+ VCD_DEVICE_STATE_32BIT = 0x7FFFFFFF
+};
+
+struct vcd_dev_state_table {
+ struct {
+ u32(*pf_init) (struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_init_config *config, s32 *driver_handle);
+
+ u32(*pf_term) (struct vcd_drv_ctxt *drv_ctxt,
+ s32 driver_handle);
+
+ u32(*pf_open) (struct vcd_drv_ctxt *drv_ctxt,
+ s32 driver_handle, u32 decoding,
+ void (*callback) (u32 event, u32 status, void *info,
+ u32 size, void *handle, void *const client_data),
+ void *client_data);
+
+ u32(*pf_close) (struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_clnt_ctxt *cctxt);
+
+ u32(*pf_resume) (struct vcd_drv_ctxt *drv_ctxt,
+ struct vcd_clnt_ctxt *cctxt);
+
+ u32(*pf_set_dev_pwr) (struct vcd_drv_ctxt *drv_ctxt,
+ enum vcd_power_state pwr_state);
+
+ void (*pf_dev_cb) (struct vcd_drv_ctxt *drv_ctxt,
+ u32 event, u32 status, void *payload, u32 size,
+ u32 *ddl_handle, void *const client_data);
+
+ void (*pf_timeout) (struct vcd_drv_ctxt *drv_ctxt,
+ void *user_data);
+ } ev_hdlr;
+
+ void (*pf_entry) (struct vcd_drv_ctxt *drv_ctxt, s32 state_event_type);
+ void (*pf_exit) (struct vcd_drv_ctxt *drv_ctxt, s32 state_event_type);
+};
+
+#define DEVICE_STATE_EVENT_NUMBER(ppf) \
+ ((u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.ppf)) - \
+ (u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.pf_init)) \
+ + 1)
+
+struct vcd_dev_state_ctxt {
+ const struct vcd_dev_state_table *state_table;
+
+ enum vcd_dev_state_enum state;
+};
+
+struct vcd_drv_ctxt {
+ struct vcd_dev_state_ctxt dev_state;
+ struct vcd_dev_ctxt dev_ctxt;
+ struct mutex *dev_mutex;
+};
+
+extern struct vcd_drv_ctxt *vcd_get_drv_context(void);
+
+void vcd_continue(void);
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_power_sm.c b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c
new file mode 100644
index 0000000..5685417
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c
@@ -0,0 +1,316 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "video_core_type.h"
+#include "vcd_power_sm.h"
+#include "vcd_core.h"
+#include "vcd.h"
+
+u32 vcd_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt,
+ u32 event)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_MED("Device power state = %d\n", dev_ctxt->pwr_clk_state);
+ VCD_MSG_MED("event = 0x%x\n", event);
+ switch (event) {
+ case VCD_EVT_PWR_DEV_INIT_BEGIN:
+ case VCD_EVT_PWR_DEV_INIT_END:
+ case VCD_EVT_PWR_DEV_INIT_FAIL:
+ case VCD_EVT_PWR_DEV_TERM_BEGIN:
+ case VCD_EVT_PWR_DEV_TERM_END:
+ case VCD_EVT_PWR_DEV_TERM_FAIL:
+ case VCD_EVT_PWR_DEV_SLEEP_BEGIN:
+ case VCD_EVT_PWR_DEV_SLEEP_END:
+ case VCD_EVT_PWR_DEV_SET_PERFLVL:
+ case VCD_EVT_PWR_DEV_HWTIMEOUT:
+ rc = vcd_device_power_event(dev_ctxt, event, cctxt);
+ break;
+ case VCD_EVT_PWR_CLNT_CMD_BEGIN:
+ case VCD_EVT_PWR_CLNT_CMD_END:
+ case VCD_EVT_PWR_CLNT_CMD_FAIL:
+ case VCD_EVT_PWR_CLNT_PAUSE:
+ case VCD_EVT_PWR_CLNT_RESUME:
+ case VCD_EVT_PWR_CLNT_FIRST_FRAME:
+ case VCD_EVT_PWR_CLNT_LAST_FRAME:
+ case VCD_EVT_PWR_CLNT_ERRFATAL:
+ rc = vcd_client_power_event(dev_ctxt, cctxt, event);
+ break;
+ }
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("vcd_power_event: event 0x%x failed\n", event);
+
+ return rc;
+
+}
+
+u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event,
+ struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_ERR_FAIL;
+ u32 set_perf_lvl;
+
+ switch (event) {
+ case VCD_EVT_PWR_DEV_INIT_BEGIN:
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF &&
+ res_trk_get_max_perf_level(
+ &dev_ctxt->max_perf_lvl) &&
+ res_trk_power_up()) {
+ dev_ctxt->pwr_clk_state =
+ VCD_PWRCLK_STATE_ON_NOTCLOCKED;
+ dev_ctxt->curr_perf_lvl = 0;
+ dev_ctxt->reqd_perf_lvl = 0;
+ dev_ctxt->active_clnts = 0;
+ dev_ctxt->set_perf_lvl_pending = false;
+ rc = vcd_enable_clock(dev_ctxt, cctxt);
+ if (VCD_FAILED(rc)) {
+ res_trk_power_down();
+ dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_OFF;
+ }
+ }
+ break;
+ case VCD_EVT_PWR_DEV_INIT_END:
+ case VCD_EVT_PWR_DEV_TERM_FAIL:
+ case VCD_EVT_PWR_DEV_SLEEP_BEGIN:
+ case VCD_EVT_PWR_DEV_HWTIMEOUT:
+ rc = vcd_gate_clock(dev_ctxt);
+ break;
+ case VCD_EVT_PWR_DEV_INIT_FAIL:
+ case VCD_EVT_PWR_DEV_TERM_END:
+ if (dev_ctxt->pwr_clk_state != VCD_PWRCLK_STATE_OFF) {
+ vcd_disable_clock(dev_ctxt);
+ res_trk_power_down();
+
+ dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_OFF;
+ dev_ctxt->curr_perf_lvl = 0;
+ dev_ctxt->reqd_perf_lvl = 0;
+ dev_ctxt->active_clnts = 0;
+ dev_ctxt->set_perf_lvl_pending = false;
+ rc = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_EVT_PWR_DEV_TERM_BEGIN:
+ case VCD_EVT_PWR_DEV_SLEEP_END:
+ rc = vcd_un_gate_clock(dev_ctxt);
+ break;
+ case VCD_EVT_PWR_DEV_SET_PERFLVL:
+ set_perf_lvl = dev_ctxt->reqd_perf_lvl > 0 ?
+ dev_ctxt->reqd_perf_lvl : VCD_MIN_PERF_LEVEL;
+ rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl, cctxt);
+ break;
+ }
+ return rc;
+}
+
+u32 vcd_client_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt
+ *cctxt, u32 event)
+{
+ u32 rc = VCD_ERR_FAIL;
+
+ switch (event) {
+ case VCD_EVT_PWR_CLNT_CMD_BEGIN:
+ rc = vcd_un_gate_clock(dev_ctxt);
+ break;
+ case VCD_EVT_PWR_CLNT_CMD_END:
+ rc = vcd_gate_clock(dev_ctxt);
+ break;
+ case VCD_EVT_PWR_CLNT_CMD_FAIL:
+ if (!vcd_core_is_busy(dev_ctxt))
+ rc = vcd_gate_clock(dev_ctxt);
+ break;
+ case VCD_EVT_PWR_CLNT_PAUSE:
+ case VCD_EVT_PWR_CLNT_LAST_FRAME:
+ case VCD_EVT_PWR_CLNT_ERRFATAL:
+ if (!cctxt)
+ break;
+ rc = VCD_S_SUCCESS;
+ if (cctxt->status.req_perf_lvl) {
+ dev_ctxt->reqd_perf_lvl -= cctxt->reqd_perf_lvl;
+ cctxt->status.req_perf_lvl = false;
+
+ rc = vcd_set_perf_level(dev_ctxt,
+ dev_ctxt->reqd_perf_lvl, cctxt);
+ }
+ break;
+ case VCD_EVT_PWR_CLNT_RESUME:
+ case VCD_EVT_PWR_CLNT_FIRST_FRAME:
+ if (!cctxt)
+ break;
+ rc = VCD_S_SUCCESS;
+ if (!cctxt->status.req_perf_lvl) {
+ dev_ctxt->reqd_perf_lvl += cctxt->reqd_perf_lvl;
+ cctxt->status.req_perf_lvl = true;
+
+ rc = vcd_set_perf_level(dev_ctxt,
+ dev_ctxt->reqd_perf_lvl, cctxt);
+ }
+ break;
+ }
+
+ return rc;
+}
+
+u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+ u32 set_perf_lvl;
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) {
+ VCD_MSG_ERROR("vcd_enable_clock(): Already in state "
+ "VCD_PWRCLK_STATE_OFF\n");
+ vcd_assert();
+ rc = VCD_ERR_FAIL;
+ } else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+
+ set_perf_lvl = dev_ctxt->reqd_perf_lvl > 0 ?
+ dev_ctxt->reqd_perf_lvl : VCD_MIN_PERF_LEVEL;
+
+ rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl, cctxt);
+
+ if (!VCD_FAILED(rc)) {
+ if (res_trk_enable_clocks()) {
+ dev_ctxt->pwr_clk_state =
+ VCD_PWRCLK_STATE_ON_CLOCKED;
+ }
+ } else {
+ rc = VCD_ERR_FAIL;
+ }
+
+ }
+
+ if (!VCD_FAILED(rc))
+ dev_ctxt->active_clnts++;
+
+ return rc;
+}
+
+u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) {
+ VCD_MSG_ERROR("vcd_disable_clock(): Already in state "
+ "VCD_PWRCLK_STATE_OFF\n");
+ vcd_assert();
+ rc = VCD_ERR_FAIL;
+ } else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED ||
+ dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) {
+ dev_ctxt->active_clnts--;
+
+ if (!dev_ctxt->active_clnts) {
+ if (!res_trk_disable_clocks())
+ rc = VCD_ERR_FAIL;
+
+ dev_ctxt->pwr_clk_state =
+ VCD_PWRCLK_STATE_ON_NOTCLOCKED;
+ dev_ctxt->curr_perf_lvl = 0;
+ }
+ }
+
+ return rc;
+}
+
+u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl,
+ struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ if (!vcd_core_is_busy(dev_ctxt)) {
+ if (res_trk_set_perf_level(perf_lvl, &dev_ctxt->curr_perf_lvl,
+ cctxt)) {
+ dev_ctxt->set_perf_lvl_pending = false;
+ } else {
+ rc = VCD_ERR_FAIL;
+ dev_ctxt->set_perf_lvl_pending = true;
+ }
+
+ } else {
+ dev_ctxt->set_perf_lvl_pending = true;
+ }
+
+ return rc;
+}
+
+u32 vcd_update_clnt_perf_lvl(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_rate *fps, u32 frm_p_units)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 new_perf_lvl;
+
+ new_perf_lvl = frm_p_units * fps->fps_numerator / fps->fps_denominator;
+
+ if (cctxt->status.req_perf_lvl) {
+ dev_ctxt->reqd_perf_lvl = dev_ctxt->reqd_perf_lvl -
+ cctxt->reqd_perf_lvl + new_perf_lvl;
+
+ rc = vcd_set_perf_level(cctxt->dev_ctxt,
+ dev_ctxt->reqd_perf_lvl, cctxt);
+ }
+
+ cctxt->reqd_perf_lvl = new_perf_lvl;
+
+ return rc;
+}
+
+u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF ||
+ dev_ctxt->pwr_clk_state ==
+ VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+ VCD_MSG_ERROR("%s: Clk is Off or Not Clked yet\n", __func__);
+ vcd_assert();
+ return VCD_ERR_FAIL;
+ }
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED)
+ return rc;
+
+ if (res_trk_disable_clocks())
+ dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKGATED;
+ else
+ rc = VCD_ERR_FAIL;
+
+ return rc;
+}
+
+u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF ||
+ dev_ctxt->pwr_clk_state ==
+ VCD_PWRCLK_STATE_ON_NOTCLOCKED) {
+ VCD_MSG_ERROR("%s: Clk is Off or Not Clked yet\n", __func__);
+ vcd_assert();
+ return VCD_ERR_FAIL;
+ }
+
+ if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED)
+ return rc;
+
+ if (res_trk_enable_clocks())
+ dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKED;
+ else
+ rc = VCD_ERR_FAIL;
+
+ return rc;
+}
diff --git a/drivers/misc/video_core/720p/vcd/vcd_power_sm.h b/drivers/misc/video_core/720p/vcd/vcd_power_sm.h
new file mode 100644
index 0000000..b2af5dd
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_power_sm.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_POWERSM_H_
+#define _VCD_POWERSM_H_
+
+#define VCD_EVT_PWR_BASE 0x5000
+#define VCD_EVT_PWR_DEV_INIT_BEGIN (VCD_EVT_PWR_BASE + 0x1)
+#define VCD_EVT_PWR_DEV_INIT_END (VCD_EVT_PWR_BASE + 0x2)
+#define VCD_EVT_PWR_DEV_INIT_FAIL (VCD_EVT_PWR_BASE + 0x3)
+#define VCD_EVT_PWR_DEV_TERM_BEGIN (VCD_EVT_PWR_BASE + 0x4)
+#define VCD_EVT_PWR_DEV_TERM_END (VCD_EVT_PWR_BASE + 0x5)
+#define VCD_EVT_PWR_DEV_TERM_FAIL (VCD_EVT_PWR_BASE + 0x6)
+#define VCD_EVT_PWR_DEV_SLEEP_BEGIN (VCD_EVT_PWR_BASE + 0x7)
+#define VCD_EVT_PWR_DEV_SLEEP_END (VCD_EVT_PWR_BASE + 0x8)
+#define VCD_EVT_PWR_DEV_SET_PERFLVL (VCD_EVT_PWR_BASE + 0x9)
+#define VCD_EVT_PWR_DEV_HWTIMEOUT (VCD_EVT_PWR_BASE + 0xa)
+#define VCD_EVT_PWR_CLNT_CMD_BEGIN (VCD_EVT_PWR_BASE + 0xb)
+#define VCD_EVT_PWR_CLNT_CMD_END (VCD_EVT_PWR_BASE + 0xc)
+#define VCD_EVT_PWR_CLNT_CMD_FAIL (VCD_EVT_PWR_BASE + 0xd)
+#define VCD_EVT_PWR_CLNT_PAUSE (VCD_EVT_PWR_BASE + 0xe)
+#define VCD_EVT_PWR_CLNT_RESUME (VCD_EVT_PWR_BASE + 0xf)
+#define VCD_EVT_PWR_CLNT_FIRST_FRAME (VCD_EVT_PWR_BASE + 0x10)
+#define VCD_EVT_PWR_CLNT_LAST_FRAME (VCD_EVT_PWR_BASE + 0x11)
+#define VCD_EVT_PWR_CLNT_ERRFATAL (VCD_EVT_PWR_BASE + 0x12)
+
+enum vcd_pwr_clk_state_type {
+ VCD_PWRCLK_STATE_OFF = 0,
+ VCD_PWRCLK_STATE_ON_NOTCLOCKED,
+ VCD_PWRCLK_STATE_ON_CLOCKED,
+ VCD_PWRCLK_STATE_ON_CLOCKGATED
+};
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_property.h b/drivers/misc/video_core/720p/vcd/vcd_property.h
new file mode 100644
index 0000000..4df31b6
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_property.h
@@ -0,0 +1,313 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_DRIVER_PROPERTY_H_
+#define _VCD_DRIVER_PROPERTY_H_
+
+#define VCD_START_BASE 0x0
+#define VCD_I_LIVE (VCD_START_BASE + 0x1)
+#define VCD_I_CODEC (VCD_START_BASE + 0x2)
+#define VCD_I_FRAME_SIZE (VCD_START_BASE + 0x3)
+#define VCD_I_METADATA_ENABLE (VCD_START_BASE + 0x4)
+#define VCD_I_METADATA_HEADER (VCD_START_BASE + 0x5)
+#define VCD_I_PROFILE (VCD_START_BASE + 0x6)
+#define VCD_I_LEVEL (VCD_START_BASE + 0x7)
+#define VCD_I_BUFFER_FORMAT (VCD_START_BASE + 0x8)
+#define VCD_I_FRAME_RATE (VCD_START_BASE + 0x9)
+#define VCD_I_TARGET_BITRATE (VCD_START_BASE + 0xA)
+#define VCD_I_MULTI_SLICE (VCD_START_BASE + 0xB)
+#define VCD_I_ENTROPY_CTRL (VCD_START_BASE + 0xC)
+#define VCD_I_DEBLOCKING (VCD_START_BASE + 0xD)
+#define VCD_I_RATE_CONTROL (VCD_START_BASE + 0xE)
+#define VCD_I_QP_RANGE (VCD_START_BASE + 0xF)
+#define VCD_I_SESSION_QP (VCD_START_BASE + 0x10)
+#define VCD_I_INTRA_PERIOD (VCD_START_BASE + 0x11)
+#define VCD_I_VOP_TIMING (VCD_START_BASE + 0x12)
+#define VCD_I_SHORT_HEADER (VCD_START_BASE + 0x13)
+#define VCD_I_SEQ_HEADER (VCD_START_BASE + 0x14)
+#define VCD_I_HEADER_EXTENSION (VCD_START_BASE + 0x15)
+#define VCD_I_INTRA_REFRESH (VCD_START_BASE + 0x16)
+#define VCD_I_POST_FILTER (VCD_START_BASE + 0x17)
+#define VCD_I_PROGRESSIVE_ONLY (VCD_START_BASE + 0x18)
+
+#define VCD_START_REQ (VCD_START_BASE + 0x1000)
+#define VCD_I_REQ_IFRAME (VCD_START_REQ + 0x1)
+
+#define VCD_I_RESERVED_BASE (VCD_START_BASE + 0x10000)
+
+struct vcd_property_hdr {
+ u32 id;
+ size_t sz;
+};
+
+//TODO: Remove?
+struct vcd_property_live {
+ u32 live;
+};
+
+enum vcd_codec {
+ VCD_CODEC_H264 = 0x1,
+ VCD_CODEC_H263 = 0x2,
+ VCD_CODEC_MPEG1 = 0x3,
+ VCD_CODEC_MPEG2 = 0x4,
+ VCD_CODEC_MPEG4 = 0x5,
+ VCD_CODEC_DIVX_3 = 0x6,
+ VCD_CODEC_DIVX_4 = 0x7,
+ VCD_CODEC_DIVX_5 = 0x8,
+ VCD_CODEC_DIVX_6 = 0x9,
+ VCD_CODEC_XVID = 0xA,
+ VCD_CODEC_VC1 = 0xB,
+ VCD_CODEC_VC1_RCV = 0xC
+};
+
+struct vcd_property_codec {
+ enum vcd_codec codec;
+};
+
+struct vcd_property_frame_size {
+ u32 width;
+ u32 height;
+ u32 stride;
+ u32 scan_lines;
+};
+
+
+#define VCD_METADATA_DATANONE 0x001
+#define VCD_METADATA_QCOMFILLER 0x002
+#define VCD_METADATA_QPARRAY 0x004
+#define VCD_METADATA_CONCEALMB 0x008
+#define VCD_METADATA_SEI 0x010
+#define VCD_METADATA_VUI 0x020
+#define VCD_METADATA_VC1 0x040
+#define VCD_METADATA_PASSTHROUGH 0x080
+#define VCD_METADATA_ENC_SLICE 0x100
+
+struct vcd_property_meta_data_enable {
+ u32 meta_data_enable_flag;
+};
+
+struct vcd_property_metadata_hdr {
+ u32 meta_data_id_type;
+ u32 version;
+ u32 port_index;
+ u32 type;
+};
+
+struct vcd_property_frame_rate {
+ u32 fps_denominator;
+ u32 fps_numerator;
+};
+
+struct vcd_property_target_bitrate {
+ u32 target_bitrate;
+};
+
+enum vcd_yuv_buffer_format_type {
+ VCD_BUFFER_FORMAT_NV12 = 0x1,
+ VCD_BUFFER_FORMAT_TILE_4x2 = 0x2,
+ VCD_BUFFER_FORMAT_NV12_16M2KA = 0x3
+};
+
+struct vcd_property_buffer_format {
+ enum vcd_yuv_buffer_format_type buffer_format;
+};
+
+struct vcd_property_post_filter {
+ u32 post_filter;
+};
+
+enum vcd_codec_profile_type {
+ VCD_PROFILE_UNKNOWN = 0x0,
+ VCD_PROFILE_MPEG4_SP = 0x1,
+ VCD_PROFILE_MPEG4_ASP = 0x2,
+ VCD_PROFILE_H264_BASELINE = 0x3,
+ VCD_PROFILE_H264_MAIN = 0x4,
+ VCD_PROFILE_H264_HIGH = 0x5,
+ VCD_PROFILE_H263_BASELINE = 0x6,
+ VCD_PROFILE_VC1_SIMPLE = 0x7,
+ VCD_PROFILE_VC1_MAIN = 0x8,
+ VCD_PROFILE_VC1_ADVANCE = 0x9,
+ VCD_PROFILE_MPEG2_MAIN = 0xA,
+ VCD_PROFILE_MPEG2_SIMPLE = 0xB
+};
+
+struct vcd_property_profile {
+ enum vcd_codec_profile_type profile;
+};
+
+enum vcd_codec_level_type {
+ VCD_LEVEL_UNKNOWN = 0x0,
+ VCD_LEVEL_MPEG4_0 = 0x1,
+ VCD_LEVEL_MPEG4_0b = 0x2,
+ VCD_LEVEL_MPEG4_1 = 0x3,
+ VCD_LEVEL_MPEG4_2 = 0x4,
+ VCD_LEVEL_MPEG4_3 = 0x5,
+ VCD_LEVEL_MPEG4_3b = 0x6,
+ VCD_LEVEL_MPEG4_4 = 0x7,
+ VCD_LEVEL_MPEG4_4a = 0x8,
+ VCD_LEVEL_MPEG4_5 = 0x9,
+ VCD_LEVEL_MPEG4_6 = 0xA,
+ VCD_LEVEL_MPEG4_7 = 0xB,
+ VCD_LEVEL_MPEG4_X = 0xC,
+ VCD_LEVEL_H264_1 = 0x10,
+ VCD_LEVEL_H264_1b = 0x11,
+ VCD_LEVEL_H264_1p1 = 0x12,
+ VCD_LEVEL_H264_1p2 = 0x13,
+ VCD_LEVEL_H264_1p3 = 0x14,
+ VCD_LEVEL_H264_2 = 0x15,
+ VCD_LEVEL_H264_2p1 = 0x16,
+ VCD_LEVEL_H264_2p2 = 0x17,
+ VCD_LEVEL_H264_3 = 0x18,
+ VCD_LEVEL_H264_3p1 = 0x19,
+ VCD_LEVEL_H264_3p2 = 0x1A,
+ VCD_LEVEL_H264_4 = 0x1B,
+ VCD_LEVEL_H264_X = 0x1C,
+ VCD_LEVEL_H263_10 = 0x20,
+ VCD_LEVEL_H263_20 = 0x21,
+ VCD_LEVEL_H263_30 = 0x22,
+ VCD_LEVEL_H263_40 = 0x23,
+ VCD_LEVEL_H263_45 = 0x24,
+ VCD_LEVEL_H263_50 = 0x25,
+ VCD_LEVEL_H263_60 = 0x26,
+ VCD_LEVEL_H263_70 = 0x27,
+ VCD_LEVEL_H263_X = 0x28,
+ VCD_LEVEL_MPEG2_LOW = 0x30,
+ VCD_LEVEL_MPEG2_MAIN = 0x31,
+ VCD_LEVEL_MPEG2_HIGH_14 = 0x32,
+ VCD_LEVEL_MPEG2_HIGH = 0x33,
+ VCD_LEVEL_MPEG2_X = 0x34,
+ VCD_LEVEL_VC1_LOW = 0x40,
+ VCD_LEVEL_VC1_MEDIUM = 0x41,
+ VCD_LEVEL_VC1_HIGH = 0x42,
+ VCD_LEVEL_VC1_0 = 0x43,
+ VCD_LEVEL_VC1_1 = 0x44,
+ VCD_LEVEL_VC1_2 = 0x45,
+ VCD_LEVEL_VC1_3 = 0x46,
+ VCD_LEVEL_VC1_4 = 0x47,
+ VCD_LEVEL_VC1_X = 0x48
+};
+
+struct vcd_property_level {
+ enum vcd_codec_level_type level;
+};
+
+enum vcd_m_slice_sel_type {
+ VCD_MSLICE_OFF = 0x1,
+ VCD_MSLICE_BY_MB_COUNT = 0x2,
+ VCD_MSLICE_BY_BYTE_COUNT = 0x3,
+ VCD_MSLICE_BY_GOB = 0x4
+};
+
+struct vcd_property_multi_slice {
+ enum vcd_m_slice_sel_type m_slice_sel;
+ u32 m_slice_size;
+};
+
+enum vcd_entropy_sel_type {
+ VCD_ENTROPY_SEL_CAVLC = 0x1,
+ VCD_ENTROPY_SEL_CABAC = 0x2
+};
+
+enum vcd_cabac_model_type {
+ VCD_CABAC_MODEL_NUMBER_0 = 0x1,
+ VCD_CABAC_MODEL_NUMBER_1 = 0x2,
+ VCD_CABAC_MODEL_NUMBER_2 = 0x3
+};
+
+struct vcd_property_entropy_control {
+ enum vcd_entropy_sel_type entropy_sel;
+ enum vcd_cabac_model_type cabac_model;
+};
+
+enum vcd_db_config_type {
+ VCD_DB_ALL_BLOCKING_BOUNDARY = 0x1,
+ VCD_DB_DISABLE = 0x2,
+ VCD_DB_SKIP_SLICE_BOUNDARY = 0x3
+};
+struct vcd_property_db_config {
+ enum vcd_db_config_type db_config;
+ u32 slice_alpha_offset;
+ u32 slice_beta_offset;
+};
+
+enum vcd_rate_control_type {
+ VCD_RATE_CONTROL_OFF = 0x1,
+ VCD_RATE_CONTROL_VBR_VFR = 0x2,
+ VCD_RATE_CONTROL_VBR_CFR = 0x3,
+ VCD_RATE_CONTROL_CBR_VFR = 0x4,
+ VCD_RATE_CONTROL_CBR_CFR = 0x5
+};
+
+struct vcd_property_rate_control {
+ enum vcd_rate_control_type rate_control;
+};
+
+struct vcd_property_qp_range {
+ u32 max_qp;
+ u32 min_qp;
+};
+
+struct vcd_property_session_qp {
+ u32 iframe_qp;
+ u32 frame_qp;
+ u32 bframe_qp;
+};
+
+struct vcd_property_i_period {
+ u32 frames;
+ u32 bframes;
+};
+
+struct vcd_property_vop_timing {
+ u32 vop_time_resolution;
+};
+
+struct vcd_property_short_header {
+ u32 short_header;
+};
+
+struct vcd_property_intra_refresh_mb_number {
+ u32 cir_mb_number;
+};
+
+struct vcd_property_req_i_frame {
+ u32 req_i_frame;
+};
+
+struct vcd_frame_rect {
+ u32 left;
+ u32 top;
+ u32 right;
+ u32 bottom;
+};
+
+struct vcd_property_dec_output_buffer {
+ struct vcd_frame_rect disp_frm;
+};
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_status.h b/drivers/misc/video_core/720p/vcd/vcd_status.h
new file mode 100644
index 0000000..702ed54
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_status.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _VCD_ERR_STATUS_H_
+#define _VCD_ERR_STATUS_H_
+
+#define VCD_EVT_RESP_BASE 0x1000
+#define VCD_EVT_RESP_OPEN (VCD_EVT_RESP_BASE + 0x1)
+#define VCD_EVT_RESP_START (VCD_EVT_RESP_BASE + 0x2)
+#define VCD_EVT_RESP_STOP (VCD_EVT_RESP_BASE + 0x3)
+#define VCD_EVT_RESP_PAUSE (VCD_EVT_RESP_BASE + 0x4)
+#define VCD_EVT_RESP_FLUSH_INPUT_DONE (VCD_EVT_RESP_BASE + 0x5)
+#define VCD_EVT_RESP_FLUSH_OUTPUT_DONE (VCD_EVT_RESP_BASE + 0x6)
+#define VCD_EVT_RESP_INPUT_FLUSHED (VCD_EVT_RESP_BASE + 0x7)
+#define VCD_EVT_RESP_OUTPUT_FLUSHED (VCD_EVT_RESP_BASE + 0x8)
+#define VCD_EVT_RESP_INPUT_DONE (VCD_EVT_RESP_BASE + 0x9)
+#define VCD_EVT_RESP_OUTPUT_DONE (VCD_EVT_RESP_BASE + 0xa)
+
+#define VCD_EVT_IND_BASE 0x2000
+#define VCD_EVT_IND_RECONFIG (VCD_EVT_IND_BASE + 0x1)
+#define VCD_EVT_IND_HWERRFATAL (VCD_EVT_IND_BASE + 0x2)
+#define VCD_EVT_IND_RESOURCES_LOST (VCD_EVT_IND_BASE + 0x3)
+
+#define VCD_S_SUCCESS 0x0
+
+#define VCD_S_ERR_BASE 0x80000000
+#define VCD_ERR_FAIL (VCD_S_ERR_BASE + 0x1)
+#define VCD_ERR_ALLOC_FAIL (VCD_S_ERR_BASE + 0x2)
+#define VCD_ERR_ILLEGAL_OP (VCD_S_ERR_BASE + 0x3)
+#define VCD_ERR_ILLEGAL_PARM (VCD_S_ERR_BASE + 0x4)
+#define VCD_ERR_BAD_POINTER (VCD_S_ERR_BASE + 0x5)
+#define VCD_ERR_BAD_HANDLE (VCD_S_ERR_BASE + 0x6)
+#define VCD_ERR_NOT_SUPPORTED (VCD_S_ERR_BASE + 0x7)
+#define VCD_ERR_BAD_STATE (VCD_S_ERR_BASE + 0x8)
+#define VCD_ERR_BUSY (VCD_S_ERR_BASE + 0x9)
+#define VCD_ERR_MAX_CLIENT (VCD_S_ERR_BASE + 0xa)
+#define VCD_ERR_IFRAME_EXPECTED (VCD_S_ERR_BASE + 0xb)
+#define VCD_ERR_INTRLCD_FIELD_DROP (VCD_S_ERR_BASE + 0xc)
+#define VCD_ERR_HW_FATAL (VCD_S_ERR_BASE + 0xd)
+#define VCD_ERR_BITSTREAM_ERR (VCD_S_ERR_BASE + 0xe)
+#define VCD_FAILED(rc) ((rc > VCD_S_ERR_BASE) ? true : false)
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/vcd_sub.c b/drivers/misc/video_core/720p/vcd/vcd_sub.c
new file mode 100644
index 0000000..a088cbc
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_sub.c
@@ -0,0 +1,2921 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <asm/div64.h>
+
+#include "video_core_type.h"
+#include "vcd.h"
+#include "vdec_internal.h"
+
+static phys_addr_t vcd_pmem_get_physical(struct video_client_ctx *client_ctx,
+ void *kern_addr)
+{
+ phys_addr_t phys_addr;
+ void __user *user_addr;
+ int pmem_fd;
+ struct file *file;
+ s32 buffer_index = -1;
+
+ if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT,
+ false, &user_addr, &kern_addr, &phys_addr, &pmem_fd,
+ &file, &buffer_index)) {
+ return phys_addr;
+ }
+ if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT,
+ false, &user_addr, &kern_addr, &phys_addr, &pmem_fd,
+ &file, &buffer_index)) {
+ return phys_addr;
+ }
+ VCD_MSG_ERROR("Couldn't get physical address");
+ return 0;
+}
+
+void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt)
+{
+ dev_ctxt->ddl_frame_ch_free = dev_ctxt->ddl_frame_ch_depth;
+ dev_ctxt->ddl_cmd_ch_free = dev_ctxt->ddl_cmd_ch_depth;
+ dev_ctxt->ddl_frame_ch_interim = 0;
+ dev_ctxt->ddl_cmd_ch_interim = 0;
+}
+
+u32 vcd_get_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc)
+{
+ u32 result = false;
+
+ *pp_transc = NULL;
+
+ if (dev_ctxt->ddl_cmd_ch_free > 0) {
+ if (dev_ctxt->ddl_cmd_concurrency) {
+ --dev_ctxt->ddl_cmd_ch_free;
+ result = true;
+ } else if ((dev_ctxt->ddl_frame_ch_free +
+ dev_ctxt->ddl_frame_ch_interim) ==
+ dev_ctxt->ddl_frame_ch_depth) {
+ --dev_ctxt->ddl_cmd_ch_free;
+ result = true;
+ }
+ }
+
+ if (result) {
+ *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+ if (!*pp_transc) {
+ result = false;
+
+ vcd_release_command_channel(dev_ctxt, *pp_transc);
+ }
+
+ }
+ return result;
+}
+
+u32 vcd_get_command_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc)
+{
+ u32 result = false;
+
+ *pp_transc = NULL;
+
+ if (dev_ctxt->ddl_cmd_ch_interim > 0) {
+ if (dev_ctxt->ddl_cmd_concurrency) {
+ --dev_ctxt->ddl_cmd_ch_interim;
+ result = true;
+ } else if ((dev_ctxt->ddl_frame_ch_free +
+ dev_ctxt->ddl_frame_ch_interim)
+ == dev_ctxt->ddl_frame_ch_depth) {
+ --dev_ctxt->ddl_cmd_ch_interim;
+ result = true;
+ }
+ } else {
+ result = vcd_get_command_channel(dev_ctxt, pp_transc);
+ }
+
+ if (result && !*pp_transc) {
+ *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+ if (!*pp_transc) {
+ result = false;
+
+ ++dev_ctxt->ddl_cmd_ch_interim;
+ }
+ }
+
+ return result;
+}
+
+void vcd_mark_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ ++dev_ctxt->ddl_cmd_ch_interim;
+
+ vcd_release_trans_tbl_entry(transc);
+ if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_ERROR("\n Command channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_command_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ ++dev_ctxt->ddl_cmd_ch_free;
+
+ vcd_release_trans_tbl_entry(transc);
+ if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_ERROR("\n Command channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_multiple_command_channels(struct vcd_dev_ctxt *dev_ctxt,
+ u32 channels)
+{
+ dev_ctxt->ddl_cmd_ch_free += channels;
+
+ if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_ERROR("\n Command channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt)
+{
+ dev_ctxt->ddl_cmd_ch_free += dev_ctxt->ddl_cmd_ch_interim;
+ dev_ctxt->ddl_cmd_ch_interim = 0;
+
+ if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_ERROR("\n Command channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+u32 vcd_get_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc)
+{
+ u32 result = false;
+
+ if (dev_ctxt->ddl_frame_ch_free > 0) {
+ if (dev_ctxt->ddl_cmd_concurrency) {
+ --dev_ctxt->ddl_frame_ch_free;
+ result = true;
+ } else if ((dev_ctxt->ddl_cmd_ch_free +
+ dev_ctxt->ddl_cmd_ch_interim)
+ == dev_ctxt->ddl_cmd_ch_depth) {
+ --dev_ctxt->ddl_frame_ch_free;
+ result = true;
+ }
+ }
+
+ if (result) {
+ *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+ if (!*pp_transc) {
+ result = false;
+
+ vcd_release_frame_channel(dev_ctxt, *pp_transc);
+ } else {
+ (*pp_transc)->type = VCD_CMD_CODE_FRAME;
+ }
+
+ }
+
+ return result;
+}
+
+u32 vcd_get_frame_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc **pp_transc)
+{
+ u32 result = false;
+
+ *pp_transc = NULL;
+
+ if (dev_ctxt->ddl_frame_ch_interim > 0) {
+ if (dev_ctxt->ddl_cmd_concurrency) {
+ --dev_ctxt->ddl_frame_ch_interim;
+ result = true;
+ } else if ((dev_ctxt->ddl_cmd_ch_free +
+ dev_ctxt->ddl_cmd_ch_interim) ==
+ dev_ctxt->ddl_cmd_ch_depth) {
+ --dev_ctxt->ddl_frame_ch_interim;
+ result = true;
+ }
+ } else {
+ result = vcd_get_frame_channel(dev_ctxt, pp_transc);
+ }
+
+ if (result && !*pp_transc) {
+ *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt);
+
+ if (!*pp_transc) {
+ result = false;
+ VCD_MSG_FATAL("\n%s: All transactions are busy;"
+ "Couldnt find free one\n", __func__);
+ ++dev_ctxt->ddl_frame_ch_interim;
+ }
+
+ }
+
+ return result;
+}
+
+void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt)
+{
+ ++dev_ctxt->ddl_frame_ch_interim;
+
+ if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_FATAL("Frame channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_frame_channel(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ ++dev_ctxt->ddl_frame_ch_free;
+
+ vcd_release_trans_tbl_entry(transc);
+
+ if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free >
+ dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_FATAL("Frame channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt
+ *dev_ctxt, u32 channels)
+{
+ dev_ctxt->ddl_frame_ch_free += channels;
+
+ if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free >
+ dev_ctxt->ddl_frame_ch_depth) {
+ VCD_MSG_FATAL("Frame channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+void vcd_release_interim_frame_channels(struct vcd_dev_ctxt
+ *dev_ctxt)
+{
+ dev_ctxt->ddl_frame_ch_free += dev_ctxt->ddl_frame_ch_interim;
+ dev_ctxt->ddl_frame_ch_interim = 0;
+
+ if (dev_ctxt->ddl_frame_ch_free > dev_ctxt->ddl_cmd_ch_depth) {
+ VCD_MSG_FATAL("Frame channel access counters messed up");
+ vcd_assert();
+ }
+}
+
+u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt)
+{
+ if (((dev_ctxt->ddl_cmd_ch_free + dev_ctxt->ddl_cmd_ch_interim) !=
+ dev_ctxt->ddl_cmd_ch_depth) ||
+ ((dev_ctxt->ddl_frame_ch_free +
+ dev_ctxt->ddl_frame_ch_interim) !=
+ dev_ctxt->ddl_frame_ch_depth)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt)
+{
+ if (dev_ctxt->config.pf_timer_start)
+ dev_ctxt->config.pf_timer_start(dev_ctxt->hw_timer_handle,
+ dev_ctxt->hw_time_out);
+}
+
+void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt)
+{
+ if (dev_ctxt->config.pf_timer_stop)
+ dev_ctxt->config.pf_timer_stop(dev_ctxt->hw_timer_handle);
+}
+
+
+u32 vcd_common_allocate_set_buffer(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type buffer, size_t sz,
+ struct vcd_buffer_pool **pp_buf_pool)
+{
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_requirement buf_req;
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_buffer_pool *buf_pool;
+
+ if (buffer == VCD_BUFFER_INPUT) {
+ prop_hdr.id = DDL_I_INPUT_BUF_REQ;
+ buf_pool = &cctxt->in_buf_pool;
+ } else if (buffer == VCD_BUFFER_OUTPUT) {
+ prop_hdr.id = DDL_I_OUTPUT_BUF_REQ;
+ buf_pool = &cctxt->out_buf_pool;
+ } else {
+ rc = VCD_ERR_ILLEGAL_PARM;
+ }
+
+ VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+ *pp_buf_pool = buf_pool;
+
+ if (buf_pool->count > 0 && buf_pool->validated == buf_pool->count) {
+ VCD_MSG_ERROR("Buffer pool is full");
+
+ return VCD_ERR_FAIL;
+ }
+
+ if (!buf_pool->entries) {
+ prop_hdr.sz = sizeof(buf_req);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &buf_req);
+
+ if (!VCD_FAILED(rc))
+ rc = vcd_alloc_buffer_pool_entries(buf_pool, &buf_req);
+ else
+ VCD_MSG_ERROR("rc = 0x%x. Failed ddl_get_property", rc);
+ }
+
+ if (!VCD_FAILED(rc)) {
+ if (buf_pool->buf_req.size > sz) {
+ VCD_MSG_ERROR("required buffer size %u allocated size "
+ "%u", buf_pool->buf_req.size, sz);
+ rc = VCD_ERR_ILLEGAL_PARM;
+ }
+ }
+
+ return rc;
+}
+
+u32 vcd_set_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, void *buf, size_t sz)
+{
+ struct vcd_buffer_entry *buf_entry;
+
+ buf_entry = vcd_find_buffer_pool_entry(buf_pool, buf);
+ if (buf_entry) {
+ VCD_MSG_ERROR("This buffer address already exists");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ if (!IS_ALIGNED((unsigned long)buf, buf_pool->buf_req.align)) {
+ VCD_MSG_ERROR("Provided addr is not aligned");
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ buf_entry = vcd_get_free_buffer_pool_entry(buf_pool);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Can't allocate buffer pool is full");
+ return VCD_ERR_FAIL;
+ }
+
+ printk("npelly adding %p to buf_pool %p\n", buf, buf_entry);
+ buf_entry->virt_addr = buf;
+
+ buf_entry->phys_addr = vcd_pmem_get_physical(cctxt->client_data, buf);
+
+ if (!buf_entry->phys_addr) {
+ VCD_MSG_ERROR("Couldn't get physical address");
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ if (!IS_ALIGNED((unsigned long)buf_entry->phys_addr,
+ buf_pool->buf_req.align)) {
+ VCD_MSG_ERROR("Physical addr is not aligned");
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ buf_entry->size = sz;
+ buf_entry->frame.alloc_len = sz;
+ buf_entry->allocated = false;
+
+ buf_entry->frame.virt_addr = buf_entry->virt_addr;
+ buf_entry->frame.phys_addr = buf_entry->phys_addr;
+
+ buf_pool->validated++;
+
+ return VCD_S_SUCCESS;
+
+}
+
+u32 vcd_allocate_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, size_t buf_size, void **virt_addr,
+ phys_addr_t *phys_addr)
+{
+ struct vcd_buffer_entry *buf_entry;
+ struct vcd_buffer_requirement *buf_req;
+// u32 addr;
+// int rc = 0;
+
+ buf_entry = vcd_get_free_buffer_pool_entry(buf_pool);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Can't allocate buffer pool is full");
+ return VCD_ERR_FAIL;
+ }
+
+ buf_req = &buf_pool->buf_req;
+
+ //TODO strip align crap
+// buf_size += buf_req->align;
+
+ buf_entry->buffer.virt_addr = dma_alloc_coherent(NULL, buf_size,
+ &buf_entry->buffer.phys_addr, GFP_KERNEL);
+ if (!buf_entry->buffer.virt_addr) {
+ VCD_MSG_ERROR("Buffer allocation failed");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ buf_entry->buffer.size = buf_size;
+ buf_entry->allocated = true;
+
+ buf_entry->frame.alloc_len = buf_entry->buffer.size;
+ buf_entry->frame.virt_addr = buf_entry->buffer.virt_addr;
+ buf_entry->frame.phys_addr = buf_entry->buffer.phys_addr;
+
+ *virt_addr = buf_entry->buffer.virt_addr;
+ *phys_addr = buf_entry->buffer.phys_addr;
+
+ buf_pool->allocated++;
+ buf_pool->validated++;
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_free_one_buffer_internal(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_buffer_type vcd_buffer_type, u8 *buffer)
+{
+ struct vcd_buffer_pool *buf_pool;
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_entry *buf_entry;
+
+ if (vcd_buffer_type == VCD_BUFFER_INPUT)
+ buf_pool = &cctxt->in_buf_pool;
+ else if (vcd_buffer_type == VCD_BUFFER_OUTPUT)
+ buf_pool = &cctxt->out_buf_pool;
+ else
+ rc = VCD_ERR_ILLEGAL_PARM;
+
+ VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
+
+ buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Buffer addr %p not found. Can't free buffer",
+ buffer);
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+ if (buf_entry->in_use) {
+ VCD_MSG_ERROR("\n Buffer is in use and is not flushed");
+ return VCD_ERR_ILLEGAL_OP;
+ }
+
+ VCD_MSG_LOW("Freeing buffer %p. Allocated %d", buf_entry->virt_addr,
+ buf_entry->allocated);
+
+ if (buf_entry->allocated) {
+ dma_free_coherent(NULL, buf_entry->size, buf_entry->virt_addr,
+ buf_entry->phys_addr);
+ buf_entry->virt_addr = NULL;
+ buf_pool->allocated--;
+
+ }
+
+ memset(buf_entry, 0, sizeof(struct vcd_buffer_entry));
+
+ buf_pool->validated--;
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_free_buffers_internal(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool)
+{
+ u32 rc = VCD_S_SUCCESS;
+ u32 i;
+
+ VCD_MSG_LOW("vcd_free_buffers_internal:");
+
+ if (!buf_pool->entries)
+ return rc;
+
+ for (i = 1; i <= buf_pool->count; i++) {
+ struct vcd_buffer_entry *b = &buf_pool->entries[i];
+ if (!b->valid || !b->allocated)
+ continue;
+ dma_free_coherent(NULL, b->size, b->virt_addr, b->phys_addr);
+ }
+
+ vcd_reset_buffer_pool_for_reuse(buf_pool);
+
+ return rc;
+}
+
+u32 vcd_alloc_buffer_pool_entries(struct vcd_buffer_pool *buf_pool,
+ struct vcd_buffer_requirement *buf_req)
+{
+
+ VCD_MSG_LOW("vcd_alloc_buffer_pool_entries:");
+
+ buf_pool->buf_req = *buf_req;
+
+ buf_pool->count = buf_req->actual_count;
+ buf_pool->entries = kzalloc(sizeof(struct vcd_buffer_entry) *
+ (buf_pool->count + 1), GFP_KERNEL);
+
+ if (!buf_pool->entries) {
+ VCD_MSG_ERROR("Buf_pool entries alloc failed");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ buf_pool->queue = kzalloc(sizeof(struct vcd_buffer_entry *) *
+ buf_pool->count, GFP_KERNEL);
+
+ if (!buf_pool->queue) {
+ VCD_MSG_ERROR("Buf_pool queue alloc failed");
+ kfree(buf_pool->entries);
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ buf_pool->entries[0].valid = true;
+
+ buf_pool->q_head = 0;
+ buf_pool->q_tail = (u16) (buf_pool->count - 1);
+ buf_pool->q_len = 0;
+
+ buf_pool->validated = 0;
+ buf_pool->allocated = 0;
+ buf_pool->in_use = 0;
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool)
+{
+ VCD_MSG_LOW("vcd_free_buffer_pool_entries:");
+
+ kfree(buf_pool->entries);
+ kfree(buf_pool->queue);
+
+ memset(buf_pool, 0, sizeof(struct vcd_buffer_pool));
+}
+
+void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_buffer_pool *buf_pool, u32 event)
+{
+ u32 i;
+ VCD_MSG_LOW("vcd_flush_buffer_pool_entries: event=0x%x", event);
+
+ if (!buf_pool->entries)
+ return;
+
+ for (i = 0; i <= buf_pool->count; i++) {
+ if (buf_pool->entries[i].virt_addr &&
+ buf_pool->entries[i].in_use) {
+ cctxt->callback(event, VCD_S_SUCCESS,
+ &buf_pool->entries[i].frame,
+ sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+ buf_pool->entries[i].in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(buf_pool->in_use);
+ }
+ }
+}
+
+
+void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool)
+{
+ VCD_MSG_LOW("vcd_reset_buffer_pool_for_reuse:");
+
+ memset(&buf_pool->entries[1], 0, sizeof(struct vcd_buffer_entry) *
+ buf_pool->count);
+ memset(buf_pool->queue, 0, sizeof(struct vcd_buffer_entry *) *
+ buf_pool->count);
+
+ buf_pool->q_head = 0;
+ buf_pool->q_tail = (u16) (buf_pool->count - 1);
+ buf_pool->q_len = 0;
+
+ buf_pool->validated = 0;
+ buf_pool->allocated = 0;
+ buf_pool->in_use = 0;
+
+}
+
+struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry(struct vcd_buffer_pool
+ *pool)
+{
+ int i;
+ for (i = 1; i <= pool->count; i++) {
+ if (!pool->entries[i].valid) {
+ pool->entries[i].valid = true;
+ return &pool->entries[i];
+ }
+ }
+ return NULL;
+}
+
+struct vcd_buffer_entry *vcd_find_buffer_pool_entry(struct vcd_buffer_pool
+ *pool, void *virt_addr)
+{
+ int i;
+ for (i = 0; i <= pool->count; i++)
+ if (pool->entries[i].virt_addr == virt_addr)
+ return &pool->entries[i];
+ return NULL;
+}
+
+u32 vcd_buffer_pool_entry_en_q(struct vcd_buffer_pool *pool,
+ struct vcd_buffer_entry *entry)
+{
+ u16 i;
+ u16 q_cntr;
+ u32 found = false;
+
+ if (pool->q_len == pool->count)
+ return false;
+
+ for (i = 0, q_cntr = pool->q_head; !found && i < pool->q_len;
+ i++, q_cntr = (q_cntr + 1) % pool->count) {
+ if (pool->queue[q_cntr] == entry)
+ found = true;
+ }
+
+ if (found) {
+ VCD_MSG_HIGH("this output buffer is already present in queue");
+ VCD_MSG_HIGH("virt_addr %p phys_addr %x", entry->virt_addr,
+ entry->phys_addr);
+ return false;
+ }
+
+ pool->q_tail = (pool->q_tail + 1) % pool->count;
+ pool->q_len++;
+ pool->queue[pool->q_tail] = entry;
+
+ return true;
+}
+
+struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q(struct vcd_buffer_pool
+ *pool)
+{
+ struct vcd_buffer_entry *entry;
+
+ if (!pool || !pool->q_len)
+ return NULL;
+
+ entry = pool->queue[pool->q_head];
+ pool->q_head = (pool->q_head + 1) % pool->count;
+ pool->q_len--;
+
+ return entry;
+}
+
+void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_buffer_pool *buf_pool;
+ struct vcd_buffer_entry *buf_entry;
+ u32 count = 0;
+ struct vcd_property_hdr prop_hdr;
+
+ VCD_MSG_LOW("vcd_flush_output_buffers:");
+
+ buf_pool = &cctxt->out_buf_pool;
+
+ buf_entry = vcd_buffer_pool_entry_de_q(buf_pool);
+ while (buf_entry) {
+ if (!cctxt->decoding || buf_entry->in_use) {
+ buf_entry->frame.data_len = 0;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_FLUSHED,
+ VCD_S_SUCCESS, &buf_entry->frame,
+ sizeof(struct vcd_frame_data),
+ cctxt, cctxt->client_data);
+
+ buf_entry->in_use = false;
+
+ count++;
+ }
+
+ buf_entry = vcd_buffer_pool_entry_de_q(buf_pool);
+ }
+ buf_pool->in_use = 0;
+
+ if (cctxt->sched_clnt_valid && count > 0) {
+ VCD_MSG_LOW("Updating scheduler O tkns = %u", count);
+
+ sched_update_client_o_tkn(cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, false,
+ count * cctxt->sched_o_tkn_per_ip_frm);
+ }
+
+ if (cctxt->ddl_hdl_valid && cctxt->decoding) {
+ prop_hdr.id = DDL_I_REQ_OUTPUT_FLUSH;
+ prop_hdr.sz = sizeof(u32);
+ count = 0x1;
+
+ ddl_set_property(cctxt->ddl_handle, &prop_hdr, &count);
+ }
+}
+
+u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_buffer_entry *buf_entry;
+
+ VCD_MSG_LOW("vcd_flush_buffers:");
+
+ if (mode > VCD_FLUSH_ALL || !(mode & VCD_FLUSH_ALL)) {
+ VCD_MSG_ERROR("Invalid flush mode %d", mode);
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ VCD_MSG_MED("Flush mode %d requested", mode);
+
+ if ((mode & VCD_FLUSH_INPUT) && cctxt->sched_clnt_valid) {
+ rc = vcd_map_sched_status(sched_flush_client_buffer(
+ dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ (void **)&buf_entry));
+
+ while (!VCD_FAILED(rc) && rc != VCD_S_SCHED_QEMPTY &&
+ buf_entry) {
+ if (buf_entry->virt_addr) {
+ cctxt->callback(VCD_EVT_RESP_INPUT_FLUSHED,
+ VCD_S_SUCCESS, &buf_entry->frame,
+ sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+ }
+
+ buf_entry->in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(
+ cctxt->in_buf_pool.in_use);
+ buf_entry = NULL;
+ rc = vcd_map_sched_status(sched_flush_client_buffer(
+ dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ (void **)&buf_entry));
+ }
+
+ }
+ VCD_FAILED_RETURN(rc, "Failed: sched_flush_client_buffer");
+
+ if (cctxt->status.frame_submitted > 0) {
+ cctxt->status.flush_mode |= mode;
+ } else {
+ if (mode & VCD_FLUSH_OUTPUT) {
+ vcd_flush_output_buffers(cctxt);
+ vcd_release_all_clnt_frm_transc(cctxt);
+ }
+
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt)
+{
+ VCD_MSG_LOW("\n vcd_flush_buffers_in_err_fatal:");
+ vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+ vcd_flush_in_use_buffer_pool_entries(cctxt, &cctxt->in_buf_pool,
+ VCD_EVT_RESP_INPUT_FLUSHED);
+ vcd_flush_in_use_buffer_pool_entries(cctxt, &cctxt->out_buf_pool,
+ VCD_EVT_RESP_OUTPUT_FLUSHED);
+ cctxt->status.flush_mode = VCD_FLUSH_ALL;
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+}
+
+u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_init_client_context:");
+
+ rc = ddl_open(&cctxt->ddl_handle, cctxt->decoding);
+
+ VCD_FAILED_RETURN(rc, "Failed: ddl_open");
+ cctxt->ddl_hdl_valid = true;
+
+ cctxt->clnt_state.state = VCD_CLIENT_STATE_OPEN;
+ cctxt->clnt_state.state_table = vcd_get_client_state_table(
+ VCD_CLIENT_STATE_OPEN);
+
+ cctxt->signature = VCD_SIGNATURE;
+ cctxt->live = true;
+
+ cctxt->cmd_q.pending_cmd = VCD_CMD_NONE;
+
+ return rc;
+}
+
+void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt;
+ struct vcd_clnt_ctxt *client;
+ u32 rc = VCD_S_SUCCESS;
+ int idx;
+
+ VCD_MSG_LOW("vcd_destroy_client_context:");
+
+ dev_ctxt = cctxt->dev_ctxt;
+
+ if (cctxt == dev_ctxt->cctxt_list_head) {
+ VCD_MSG_MED("Clnt list head clnt being removed");
+
+ dev_ctxt->cctxt_list_head = cctxt->next;
+ } else {
+ client = dev_ctxt->cctxt_list_head;
+ while (client && cctxt != client->next)
+ client = client->next;
+
+ if (client)
+ client->next = cctxt->next;
+
+ if (!client) {
+ rc = VCD_ERR_FAIL;
+
+ VCD_MSG_ERROR("Client not found in client list");
+ }
+ }
+
+ if (VCD_FAILED(rc))
+ return;
+
+ if (cctxt->sched_clnt_valid) {
+ rc = VCD_S_SUCCESS;
+ while (!VCD_FAILED(rc) && rc != VCD_S_SCHED_QEMPTY) {
+
+ rc = vcd_map_sched_status(sched_flush_client_buffer(
+ dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ (void *)&idx));
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("\n Failed: "
+ "sched_flush_client_buffer");
+ }
+
+ rc = vcd_map_sched_status(sched_remove_client(
+ dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl));
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("\n Failed: sched_remove_client");
+
+ cctxt->sched_clnt_valid = false;
+ }
+
+ if (cctxt->seq_hdr.addr) {
+ dma_free_coherent(NULL, cctxt->seq_hdr.sz, cctxt->seq_hdr.addr,
+ cctxt->seq_hdr_phys_addr);
+ cctxt->seq_hdr.addr = NULL;
+ }
+
+ vcd_free_buffers_internal(cctxt, &cctxt->in_buf_pool);
+ vcd_free_buffers_internal(cctxt, &cctxt->out_buf_pool);
+ vcd_free_buffer_pool_entries(&cctxt->in_buf_pool);
+ vcd_free_buffer_pool_entries(&cctxt->out_buf_pool);
+ vcd_release_all_clnt_transc(cctxt);
+
+ if (cctxt->ddl_hdl_valid) {
+ ddl_close(&cctxt->ddl_handle);
+ cctxt->ddl_hdl_valid = false;
+ }
+ kfree(cctxt);
+}
+
+u32 vcd_check_for_client_context(struct vcd_dev_ctxt *dev_ctxt, s32 driver_id)
+{
+ struct vcd_clnt_ctxt *client;
+
+ client = dev_ctxt->cctxt_list_head;
+ while (client && client->driver_id != driver_id)
+ client = client->next;
+
+ if (!client)
+ return false;
+ else
+ return true;
+}
+
+u32 vcd_validate_driver_handle(struct vcd_dev_ctxt *dev_ctxt, s32 driver_handle)
+{
+ driver_handle--;
+
+ if (driver_handle < 0 || driver_handle >= VCD_DRIVER_INSTANCE_MAX ||
+ !dev_ctxt->driver_ids[driver_handle]) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+u32 vcd_client_cmd_en_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type command)
+{
+ u32 result;
+
+ if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE) {
+ cctxt->cmd_q.pending_cmd = command;
+ result = true;
+ } else {
+ result = false;
+ }
+
+ return result;
+}
+
+void vcd_client_cmd_flush_and_en_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type command)
+{
+ cctxt->cmd_q.pending_cmd = command;
+}
+
+u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt *cctxt,
+ enum vcd_command_type *command)
+{
+ if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE)
+ return false;
+
+ *command = cctxt->cmd_q.pending_cmd;
+ cctxt->cmd_q.pending_cmd = VCD_CMD_NONE;
+
+ return true;
+}
+
+u32 vcd_get_next_queued_client_cmd(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt **cctxt, enum vcd_command_type *command)
+{
+ struct vcd_clnt_ctxt *client = dev_ctxt->cctxt_list_head;
+ u32 result = false;
+
+ while (client && !result) {
+ *cctxt = client;
+ result = vcd_client_cmd_de_q(client, command);
+ client = client->next;
+ }
+ return result;
+}
+
+u32 vcd_map_sched_status(enum sched_status sched_status)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ switch (sched_status) {
+
+ case SCHED_S_OK:
+ rc = VCD_S_SUCCESS;
+ break;
+
+ case SCHED_S_EOF:
+ rc = VCD_S_SCHED_EOS;
+ break;
+
+ case SCHED_S_QEMPTY:
+ rc = VCD_S_SCHED_QEMPTY;
+ break;
+
+ case SCHED_S_QFULL:
+ rc = VCD_S_SCHED_QFULL;
+ break;
+
+ default:
+ rc = VCD_ERR_FAIL;
+ break;
+ }
+
+ return rc;
+}
+
+u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc)
+{
+ u32 rc;
+ struct vcd_phys_sequence_hdr seq_hdr;
+
+ VCD_MSG_LOW("vcd_submit_cmd_sess_start:");
+
+ if (transc->cctxt->decoding) {
+
+ if (transc->cctxt->seq_hdr.addr) {
+ seq_hdr.sz = transc->cctxt->seq_hdr.sz;
+ seq_hdr.addr = transc->cctxt->seq_hdr_phys_addr;
+
+ rc = ddl_decode_start(transc->cctxt->ddl_handle,
+ &seq_hdr, (void *)transc);
+ } else {
+ rc = ddl_decode_start(transc->cctxt->ddl_handle, NULL,
+ (void *)transc);
+ }
+
+ } else {
+ rc = ddl_encode_start(transc->cctxt->ddl_handle,
+ (void *)transc);
+ }
+ if (!VCD_FAILED(rc)) {
+ transc->cctxt->status.cmd_submitted++;
+ vcd_device_timer_start(transc->cctxt->dev_ctxt);
+ } else
+ VCD_MSG_ERROR("rc = 0x%x. Failed: ddl start", rc);
+
+ return rc;
+}
+
+u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc)
+{
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_submit_cmd_sess_end:");
+
+ if (transc->cctxt->decoding) {
+ rc = ddl_decode_end(transc->cctxt->ddl_handle,
+ (void *)transc);
+ } else {
+ rc = ddl_encode_end(transc->cctxt->ddl_handle,
+ (void *)transc);
+ }
+ if (!VCD_FAILED(rc)) {
+ transc->cctxt->status.cmd_submitted++;
+ vcd_device_timer_start(transc->cctxt->dev_ctxt);
+ } else
+ VCD_MSG_ERROR("rc = 0x%x. Failed: ddl end", rc);
+
+ return rc;
+}
+
+void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt)
+{
+ ddl_close(&cctxt->ddl_handle);
+ cctxt->ddl_hdl_valid = false;
+ cctxt->status.cleaning_up = false;
+ if (cctxt->status.close_pending) {
+ vcd_destroy_client_context(cctxt);
+ vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, true);
+ }
+}
+
+u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_clnt_ctxt *client = NULL;
+ enum vcd_command_type cmd = VCD_CMD_NONE;
+ u32 rc = VCD_ERR_FAIL;
+ u32 result = false;
+ u32 flush = 0;
+ u32 event = 0;
+
+ VCD_MSG_LOW("\n vcd_submit_command_in_continue:");
+
+ while (1) {
+ result = vcd_get_next_queued_client_cmd(dev_ctxt, &client,
+ &cmd);
+
+ if (!result)
+ break;
+
+ transc->type = cmd;
+ transc->cctxt = client;
+
+ switch (cmd) {
+ case VCD_CMD_CODEC_START:
+ rc = vcd_submit_cmd_sess_start(transc);
+ event = VCD_EVT_RESP_START;
+ break;
+ case VCD_CMD_CODEC_STOP:
+ rc = vcd_submit_cmd_sess_end(transc);
+ event = VCD_EVT_RESP_STOP;
+ break;
+ case VCD_CMD_OUTPUT_FLUSH:
+ prop_hdr.id = DDL_I_REQ_OUTPUT_FLUSH;
+ prop_hdr.sz = sizeof(u32);
+ flush = 0x1;
+ ddl_set_property(client->ddl_handle, &prop_hdr, &flush);
+ vcd_release_command_channel(dev_ctxt, transc);
+ rc = VCD_S_SUCCESS;
+ break;
+ case VCD_CMD_CLIENT_CLOSE:
+ vcd_submit_cmd_client_close(client);
+ vcd_release_command_channel(dev_ctxt, transc);
+ rc = VCD_S_SUCCESS;
+ break;
+ default:
+ VCD_MSG_ERROR("\n vcd_submit_command: Unknown"
+ "command %d", (int)cmd);
+ vcd_assert();
+ break;
+ }
+
+ if (!VCD_FAILED(rc)) {
+ break;
+ } else {
+ VCD_MSG_ERROR("vcd_submit_command %d: failed 0x%x",
+ cmd, rc);
+ client->callback(event, rc, NULL, 0, client,
+ client->client_data);
+ }
+ }
+ return result;
+}
+
+u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt
+ **pp_cctxt, struct vcd_buffer_entry **pp_ip_buf_entry)
+{
+ u32 rc = VCD_S_SUCCESS;
+ VCD_MSG_LOW("vcd_schedule_frame:");
+
+ if (!dev_ctxt->cctxt_list_head) {
+ VCD_MSG_HIGH("Client list empty");
+ return false;
+ }
+
+ rc = vcd_map_sched_status(sched_de_queue_frame(dev_ctxt->sched_hdl,
+ (void **) pp_ip_buf_entry, (void **) pp_cctxt));
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_FATAL("vcd_submit_frame: sched_de_queue_frame"
+ "failed 0x%x", rc);
+ return false;
+ }
+
+ if (rc == VCD_S_SCHED_QEMPTY) {
+ VCD_MSG_HIGH("No frame available. Sched queues are empty");
+ return false;
+ }
+
+ if (!*pp_cctxt || !*pp_ip_buf_entry) {
+ VCD_MSG_FATAL("Sched returned invalid values. ctxt=%p,"
+ "ipbuf=%p", *pp_cctxt, *pp_ip_buf_entry);
+ return false;
+ }
+
+ if (rc == VCD_S_SCHED_EOS)
+ (*pp_ip_buf_entry)->frame.flags |= VCD_FRAME_FLAG_EOS;
+
+ return true;
+}
+
+void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt)
+{
+ struct vcd_transc *transc;
+ u32 rc = VCD_S_SUCCESS;
+ struct vcd_clnt_ctxt *cctxt = NULL;
+ struct vcd_buffer_entry *ip_buf_entry = NULL;
+ u32 result = false;
+
+ VCD_MSG_LOW("vcd_try_submit_frame:");
+
+ if (!vcd_get_frame_channel(dev_ctxt, &transc))
+ return;
+
+ if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry)) {
+ vcd_release_frame_channel(dev_ctxt, transc);
+ return;
+ }
+
+ rc = vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+ if (!VCD_FAILED(rc)) {
+ transc->cctxt = cctxt;
+ transc->ip_buf_entry = ip_buf_entry;
+
+ result = vcd_submit_frame(dev_ctxt, transc);
+ } else {
+ VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN");
+
+ vcd_requeue_input_frame(dev_ctxt, cctxt, ip_buf_entry);
+
+ vcd_map_sched_status(sched_update_client_o_tkn(
+ dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ true, cctxt->sched_o_tkn_per_ip_frm));
+ }
+
+ if (!result) {
+ vcd_release_frame_channel(dev_ctxt, transc);
+ vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL);
+ }
+}
+
+u32 vcd_submit_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc)
+{
+ struct vcd_clnt_ctxt *cctxt = NULL;
+ struct vcd_frame_data *ip_frm_entry;
+ struct vcd_buffer_entry *op_buf_entry = NULL;
+ u32 rc = VCD_S_SUCCESS;
+ u32 evcode = 0;
+ struct ddl_frame_data_tag ddl_ip_frm;
+ struct ddl_frame_data_tag ddl_op_frm;
+
+ VCD_MSG_LOW("vcd_submit_frame:");
+ cctxt = transc->cctxt;
+ ip_frm_entry = &transc->ip_buf_entry->frame;
+
+ transc->op_buf_entry = op_buf_entry;
+ transc->ip_frm_tag = ip_frm_entry->ip_frm_tag;
+ transc->time_stamp = ip_frm_entry->time_stamp;
+ ip_frm_entry->ip_frm_tag = (u32) transc;
+ memset(&ddl_ip_frm, 0, sizeof(ddl_ip_frm));
+ memset(&ddl_op_frm, 0, sizeof(ddl_op_frm));
+ if (cctxt->decoding) {
+ evcode = CLIENT_STATE_EVENT_NUMBER(pf_decode_frame);
+ ddl_ip_frm.vcd_frm = *ip_frm_entry;
+ rc = ddl_decode_frame(cctxt->ddl_handle, &ddl_ip_frm,
+ (void *) transc);
+ } else {
+ op_buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool);
+ if (!op_buf_entry) {
+ VCD_MSG_ERROR("Sched provided frame when no"
+ "op buffer was present");
+ rc = VCD_ERR_FAIL;
+ } else {
+ op_buf_entry->in_use = true;
+ cctxt->out_buf_pool.in_use++;
+ ddl_ip_frm.vcd_frm = *ip_frm_entry;
+ ddl_ip_frm.frm_delta = vcd_calculate_frame_delta(cctxt,
+ ip_frm_entry);
+
+ ddl_op_frm.vcd_frm = op_buf_entry->frame;
+
+ evcode = CLIENT_STATE_EVENT_NUMBER(pf_encode_frame);
+
+ rc = ddl_encode_frame(cctxt->ddl_handle, &ddl_ip_frm,
+ &ddl_op_frm, (void *) transc);
+ }
+ }
+ ip_frm_entry->ip_frm_tag = transc->ip_frm_tag;
+ if (!VCD_FAILED(rc)) {
+ vcd_device_timer_start(dev_ctxt);
+ cctxt->status.frame_submitted++;
+ if (ip_frm_entry->flags & VCD_FRAME_FLAG_EOS)
+ vcd_do_client_state_transition(cctxt,
+ VCD_CLIENT_STATE_EOS, evcode);
+ } else {
+ VCD_MSG_ERROR("Frame submission failed. rc = 0x%x", rc);
+ vcd_handle_submit_frame_failed(dev_ctxt, transc);
+ }
+ return true;
+}
+
+u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ struct vcd_clnt_ctxt *cctxt = NULL;
+ struct vcd_buffer_entry *ip_buf_entry = NULL;
+
+ VCD_MSG_LOW("vcd_try_submit_frame_in_continue:");
+
+ if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry))
+ return false;
+
+ transc->cctxt = cctxt;
+ transc->ip_buf_entry = ip_buf_entry;
+
+ return vcd_submit_frame(dev_ctxt, transc);
+}
+
+u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_transc *transc;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_process_cmd_sess_start:");
+ if (vcd_get_command_channel(cctxt->dev_ctxt, &transc)) {
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_CMD_BEGIN);
+
+ if (!VCD_FAILED(rc)) {
+ transc->type = VCD_CMD_CODEC_START;
+ transc->cctxt = cctxt;
+ rc = vcd_submit_cmd_sess_start(transc);
+ } else {
+ VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN");
+ }
+
+ if (VCD_FAILED(rc)) {
+ vcd_release_command_channel(cctxt->dev_ctxt,
+ transc);
+ }
+ } else {
+ u32 result;
+
+ result = vcd_client_cmd_en_q(cctxt, VCD_CMD_CODEC_START);
+ if (!result) {
+ rc = VCD_ERR_BUSY;
+ VCD_MSG_ERROR("%s(): vcd_client_cmd_en_q() "
+ "failed\n", __func__);
+ vcd_assert();
+ }
+ }
+
+ if (VCD_FAILED(rc)) {
+ vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_CMD_FAIL);
+ }
+
+ return rc;
+}
+
+void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame, u32 valid_opbuf)
+{
+ VCD_MSG_LOW("vcd_send_frame_done_in_eos:");
+
+ if (!input_frame->virt_addr && !valid_opbuf) {
+ VCD_MSG_MED("Sending NULL output with EOS");
+
+ cctxt->out_buf_pool.entries[0].frame.flags = VCD_FRAME_FLAG_EOS;
+ cctxt->out_buf_pool.entries[0].frame.data_len = 0;
+ cctxt->out_buf_pool.entries[0].frame.time_stamp =
+ input_frame->time_stamp;
+ cctxt->out_buf_pool.entries[0].frame.ip_frm_tag =
+ input_frame->ip_frm_tag;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+ &cctxt->out_buf_pool.entries[0].frame,
+ sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+
+ memset(&cctxt->out_buf_pool.entries[0].frame, 0,
+ sizeof(struct vcd_frame_data));
+ } else if (!input_frame->data_len) {
+ if (cctxt->decoding)
+ vcd_send_frame_done_in_eos_for_dec(cctxt, input_frame);
+ else
+ vcd_send_frame_done_in_eos_for_enc(cctxt, input_frame);
+ }
+}
+
+void vcd_send_frame_done_in_eos_for_dec(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame)
+{
+ struct vcd_buffer_entry *buf_entry;
+ struct vcd_property_hdr prop_hdr;
+ u32 rc;
+ struct ddl_frame_data_tag ddl_frm;
+
+ prop_hdr.id = DDL_I_DPB_RETRIEVE;
+ prop_hdr.sz = sizeof(struct ddl_frame_data_tag);
+ memset(&ddl_frm, 0, sizeof(ddl_frm));
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &ddl_frm);
+
+ if (VCD_FAILED(rc) || !ddl_frm.vcd_frm.virt_addr) {
+ cctxt->status.eos_trig_ip_frm = *input_frame;
+ cctxt->status.eos_wait_for_op_buf = true;
+
+ return;
+ }
+
+ buf_entry = vcd_find_buffer_pool_entry(&cctxt->out_buf_pool,
+ ddl_frm.vcd_frm.virt_addr);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Unrecognized buffer address provided %p",
+ ddl_frm.vcd_frm.virt_addr);
+ vcd_assert();
+ } else {
+ vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,\
+ false, cctxt->sched_o_tkn_per_ip_frm));
+
+ VCD_MSG_MED("Sending non-NULL output with EOS");
+
+ buf_entry->frame.data_len = 0;
+ buf_entry->frame.offset = 0;
+ buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+ buf_entry->frame.ip_frm_tag = input_frame->ip_frm_tag;
+ buf_entry->frame.time_stamp = input_frame->time_stamp;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+ &buf_entry->frame, sizeof(struct vcd_frame_data),
+ cctxt, cctxt->client_data);
+
+ buf_entry->in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->out_buf_pool.in_use);
+ }
+}
+
+void vcd_send_frame_done_in_eos_for_enc(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame)
+{
+ struct vcd_buffer_entry *op_buf_entry;
+
+ if (!cctxt->out_buf_pool.q_len) {
+ cctxt->status.eos_trig_ip_frm = *input_frame;
+
+ cctxt->status.eos_wait_for_op_buf = true;
+
+ return;
+ }
+
+ op_buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool);
+ if (!op_buf_entry) {
+ VCD_MSG_ERROR("%s(): vcd_buffer_pool_entry_de_q() "
+ "failed\n", __func__);
+ vcd_assert();
+ } else {
+ vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ false, cctxt->sched_o_tkn_per_ip_frm));
+
+ VCD_MSG_MED("Sending non-NULL output with EOS");
+
+ op_buf_entry->frame.data_len = 0;
+ op_buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS;
+ op_buf_entry->frame.ip_frm_tag = input_frame->ip_frm_tag;
+ op_buf_entry->frame.time_stamp = input_frame->time_stamp;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS,
+ &op_buf_entry->frame, sizeof(struct vcd_frame_data),
+ cctxt, cctxt->client_data);
+ }
+}
+
+u32 vcd_handle_recvd_eos(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame, u32 *pb_eos_handled)
+{
+ union sched_value_type sched_val;
+ u32 rc;
+
+ VCD_MSG_LOW("vcd_handle_recvd_eos:");
+
+ *pb_eos_handled = false;
+
+ if (input_frame->virt_addr && input_frame->data_len)
+ return VCD_S_SUCCESS;
+
+ input_frame->data_len = 0;
+
+ rc = vcd_map_sched_status(sched_get_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_CURRQLEN, &sched_val));
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_get_client_param");
+
+ if (sched_val.un_value > 0) {
+ rc = vcd_map_sched_status(sched_mark_client_eof(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl));
+
+ if (!VCD_FAILED(rc)) {
+ *pb_eos_handled = true;
+ } else {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: "
+ "sched_mark_client_eof", rc);
+ }
+
+ } else if (cctxt->decoding && !input_frame->virt_addr) {
+ rc = vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm));
+ } else if (!cctxt->decoding) {
+
+ vcd_send_frame_done_in_eos(cctxt, input_frame, false);
+
+ if (cctxt->status.eos_wait_for_op_buf) {
+ vcd_do_client_state_transition(cctxt,
+ VCD_CLIENT_STATE_EOS,
+ CLIENT_STATE_EVENT_NUMBER(pf_encode_frame));
+ }
+
+ *pb_eos_handled = true;
+
+ }
+
+ if (*pb_eos_handled && input_frame->virt_addr &&
+ !input_frame->data_len) {
+ cctxt->callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS,
+ input_frame, sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+ }
+ return rc;
+}
+
+u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt)
+{
+ struct ddl_property_dec_pic_buffers dpb;
+ struct vcd_property_hdr prop_hdr;
+ u32 rc;
+ u16 i;
+ u16 q_cntr;
+ struct ddl_frame_data_tag *frm_entry;
+ struct ddl_frame_data_tag ddl_frm;
+ struct vcd_buffer_pool *out_buf_pool;
+
+ VCD_MSG_LOW("vcd_handle_first_decode_frame:");
+
+ if (!cctxt->in_buf_pool.entries || !cctxt->out_buf_pool.entries ||
+ cctxt->in_buf_pool.validated !=
+ cctxt->in_buf_pool.count ||
+ cctxt->out_buf_pool.validated !=
+ cctxt->out_buf_pool.count) {
+ VCD_MSG_ERROR("Buffer pool is not completely setup yet");
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ rc = vcd_add_client_to_sched(cctxt);
+
+ VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched");
+
+ prop_hdr.id = DDL_I_DPB;
+ prop_hdr.sz = sizeof(dpb);
+
+ out_buf_pool = &cctxt->out_buf_pool;
+
+ frm_entry = kmalloc(sizeof(struct ddl_frame_data_tag) *
+ out_buf_pool->count, GFP_KERNEL);
+ if (!frm_entry) {
+ VCD_MSG_ERROR("Memory allocation failure");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ for (i = 1; i <= out_buf_pool->count; i++)
+ frm_entry[i - 1].vcd_frm = out_buf_pool->entries[i].frame;
+
+ dpb.dec_pic_buffers = frm_entry;
+ dpb.no_of_dec_pic_buf = out_buf_pool->count;
+ rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, &dpb);
+
+ kfree(frm_entry);
+
+ VCD_FAILED_RETURN(rc, "Failed: DDL set DDL_I_DPB");
+
+ if (out_buf_pool->q_len > 0) {
+ prop_hdr.id = DDL_I_DPB_RELEASE;
+ prop_hdr.sz = sizeof(struct ddl_frame_data_tag);
+
+ for (i = 0, q_cntr = out_buf_pool->q_head; !VCD_FAILED(rc) &&
+ i < out_buf_pool->q_len; i++,
+ q_cntr = (q_cntr + 1) % out_buf_pool->count) {
+
+ ddl_frm.vcd_frm = out_buf_pool->queue[q_cntr]->frame;
+
+ rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr,
+ &ddl_frm);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR
+ ("Error returning output buffer to HW");
+
+ out_buf_pool->queue[q_cntr]->in_use = false;
+ } else {
+ out_buf_pool->queue[q_cntr]->in_use = true;
+ out_buf_pool->in_use++;
+ }
+ }
+
+ if (VCD_FAILED(rc))
+ return rc;
+ rc = vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm * out_buf_pool->q_len));
+ }
+ return rc;
+}
+
+u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct ddl_property_capability capability;
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_LOW("vcd_setup_with_ddl_capabilities:");
+
+ if (dev_ctxt->ddl_cmd_ch_depth)
+ goto out;
+
+ prop_hdr.id = DDL_I_CAPABILITY;
+ prop_hdr.sz = sizeof(capability);
+
+ /*
+ * Since this is underlying core's property we don't need a
+ * ddl client handle.
+ */
+ rc = ddl_get_property(NULL, &prop_hdr, &capability);
+
+ if (VCD_FAILED(rc))
+ goto out;
+
+ /*
+ ** Allocate the transaction table.
+ */
+ dev_ctxt->trans_tbl_size = VCD_MAX_CLIENT_TRANSACTIONS *
+ capability.max_num_client + capability.general_command_depth;
+
+ dev_ctxt->trans_tbl = kzalloc(sizeof(struct vcd_transc) *
+ dev_ctxt->trans_tbl_size, GFP_KERNEL);
+ if (!dev_ctxt->trans_tbl) {
+ VCD_MSG_ERROR("Transaction table alloc failed");
+ rc = VCD_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ /*
+ ** Set the command/frame depth
+ */
+ dev_ctxt->ddl_cmd_concurrency = !capability.exclusive;
+ dev_ctxt->ddl_frame_ch_depth = capability.frame_command_depth;
+ dev_ctxt->ddl_cmd_ch_depth = capability.general_command_depth;
+
+ vcd_reset_device_channels(dev_ctxt);
+
+ dev_ctxt->hw_time_out = capability.ddl_time_out_in_ms;
+
+out:
+ return rc;
+}
+
+struct vcd_transc *vcd_get_free_trans_tbl_entry(struct vcd_dev_ctxt *dev_ctxt)
+{
+ u8 i;
+
+ if (!dev_ctxt->trans_tbl)
+ return NULL;
+
+ i = 0;
+ while (i < dev_ctxt->trans_tbl_size && dev_ctxt->trans_tbl[i].in_use)
+ i++;
+
+ if (i == dev_ctxt->trans_tbl_size) {
+ return NULL;
+ } else {
+ memset(&dev_ctxt->trans_tbl[i], 0, sizeof(struct vcd_transc));
+
+ dev_ctxt->trans_tbl[i].in_use = true;
+
+ return &dev_ctxt->trans_tbl[i];
+ }
+}
+
+void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry)
+{
+ if (trans_entry)
+ trans_entry->in_use = false;
+}
+
+u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_property_hdr prop_hdr;
+ struct sched_client_init_param sched_input_init;
+ u32 rc, seqhdr_present = 0;;
+
+ if (cctxt->sched_clnt_valid) {
+ VCD_MSG_HIGH("Schedulder client is already added ");
+ return VCD_S_SUCCESS;
+ }
+
+ prop_hdr.id = DDL_I_FRAME_PROC_UNITS;
+ prop_hdr.sz = sizeof(cctxt->frm_p_units);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+ &cctxt->frm_p_units);
+ VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS");
+
+ if (cctxt->decoding) {
+ cctxt->frm_rate.fps_numerator = VCD_DEC_INITIAL_FRAME_RATE;
+ cctxt->frm_rate.fps_denominator = 1;
+
+ sched_input_init.o_tkn_per_ip_frm =
+ VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM;
+ cctxt->sched_o_tkn_per_ip_frm =
+ VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM;
+
+ sched_input_init.o_tkn_max = cctxt->sched_o_tkn_per_ip_frm *
+ cctxt->out_buf_pool.count+1;
+ } else {
+ sched_input_init.o_tkn_per_ip_frm =
+ VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM;
+ cctxt->sched_o_tkn_per_ip_frm =
+ VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM;
+ prop_hdr.id = DDL_I_SEQHDR_PRESENT;
+ prop_hdr.sz = sizeof(seqhdr_present);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+ &seqhdr_present);
+ if (!VCD_FAILED(rc)) {
+ if (seqhdr_present == 0x1) {
+ VCD_MSG_MED("Sequence hdr present");
+ sched_input_init.o_tkn_per_ip_frm++;
+ }
+ sched_input_init.o_tkn_max = cctxt->out_buf_pool.count;
+ prop_hdr.id = VCD_I_FRAME_RATE;
+ prop_hdr.sz = sizeof(cctxt->frm_rate);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+ &cctxt->frm_rate);
+ }
+ }
+
+ VCD_FAILED_RETURN(rc, "Failed: DDL get VCD_I_FRAME_RATE");
+
+ if (cctxt->live)
+ sched_input_init.client_ctgy = SCHED_CLNT_RT_NOBUFF;
+ else
+ sched_input_init.client_ctgy = SCHED_CLNT_NONRT;
+
+ sched_input_init.max_queue_len = max(cctxt->in_buf_pool.count,
+ VCD_MAX_SCHEDULER_QUEUE_SIZE(cctxt->frm_rate.fps_numerator,
+ cctxt->frm_rate.fps_denominator));
+ cctxt->reqd_perf_lvl = cctxt->frm_p_units *
+ cctxt->frm_rate.fps_numerator / cctxt->frm_rate.fps_denominator;
+
+ sched_input_init.frm_rate.numer = cctxt->frm_rate.fps_numerator;
+ sched_input_init.frm_rate.denom = cctxt->frm_rate.fps_denominator;
+ sched_input_init.tkn_per_frm = cctxt->frm_p_units;
+ sched_input_init.alloc_p_tkn_rate = cctxt->reqd_perf_lvl;
+
+ sched_input_init.o_tkn_init = 0;
+
+ sched_input_init.client_data = cctxt;
+
+ rc = vcd_map_sched_status(sched_add_client(cctxt->dev_ctxt->sched_hdl,
+ &sched_input_init, &cctxt->sched_clnt_hdl));
+
+ if (!VCD_FAILED(rc))
+ cctxt->sched_clnt_valid = true;
+
+ return rc;
+}
+
+u32 vcd_handle_input_done(struct vcd_clnt_ctxt *cctxt, void *payload, u32 event,
+ u32 status)
+{
+ struct vcd_transc *transc;
+ struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload;
+ u32 rc;
+
+ if (!cctxt->status.frame_submitted && !cctxt->status.frame_delayed) {
+ VCD_MSG_ERROR("Input done was not expected");
+ vcd_assert();
+
+ return VCD_ERR_BAD_STATE;
+ }
+
+ rc = vcd_validate_io_done_pyld(payload, status);
+ VCD_FAILED_RETURN(rc, "Bad input done payload");
+
+ transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+
+ if (transc->ip_buf_entry->frame.virt_addr != frame->vcd_frm.virt_addr ||
+ !transc->ip_buf_entry->in_use) {
+ VCD_MSG_ERROR("Bad frm transaction state");
+ vcd_assert();
+ }
+
+ frame->vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+
+ cctxt->callback(event, status, &frame->vcd_frm,
+ sizeof(struct vcd_frame_data), cctxt, cctxt->client_data);
+
+ transc->frame_type = frame->vcd_frm.frame_type;
+
+ transc->ip_buf_entry->in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use);
+ transc->ip_buf_entry = NULL;
+ transc->input_done = true;
+
+ if (transc->input_done && transc->frame_done)
+ transc->in_use = false;
+
+ if (VCD_FAILED(status)) {
+ VCD_MSG_ERROR("INPUT_DONE returned err = 0x%x", status);
+ vcd_handle_input_done_failed(cctxt, transc);
+ }
+
+ if (cctxt->status.frame_submitted > 0)
+ cctxt->status.frame_submitted--;
+ else
+ cctxt->status.frame_delayed--;
+
+ if (!VCD_FAILED(status) && cctxt->decoding) {
+ if (frame->vcd_frm.interlaced)
+ vcd_handle_input_done_for_interlacing(cctxt);
+ if (frame->frm_trans_end)
+ vcd_handle_input_done_with_trans_end(cctxt);
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_handle_input_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status)
+{
+ struct vcd_transc *transc;
+ struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload;
+
+ if (VCD_FAILED(vcd_validate_io_done_pyld(payload, status)))
+ return;
+
+ transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+
+ vcd_handle_input_done(cctxt, payload, VCD_EVT_RESP_INPUT_DONE, status);
+
+ if ((frame->vcd_frm.flags & VCD_FRAME_FLAG_EOS)) {
+ VCD_MSG_HIGH("Got input done for EOS initiator");
+ transc->input_done = false;
+ transc->in_use = true;
+ }
+}
+
+u32 vcd_validate_io_done_pyld(void *payload, u32 status)
+{
+ struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload;
+
+ if (!frame) {
+ VCD_MSG_ERROR("Bad payload from DDL");
+ vcd_assert();
+
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ if (!frame->vcd_frm.ip_frm_tag || frame->vcd_frm.ip_frm_tag ==
+ VCD_FRAMETAG_INVALID) {
+ VCD_MSG_ERROR("bad input frame tag");
+ vcd_assert();
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ if (!frame->vcd_frm.virt_addr && status != VCD_ERR_INTRLCD_FIELD_DROP)
+ return VCD_ERR_BAD_POINTER;
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_handle_input_done_failed(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc)
+{
+ if (cctxt->decoding) {
+ vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm));
+
+ transc->in_use = false;
+ }
+}
+
+void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc;
+
+ cctxt->status.int_field_cnt++;
+
+ if (cctxt->status.int_field_cnt == 1) {
+ rc = vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm));
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("sched_update_client_o_tkn failed");
+ } else if (cctxt->status.int_field_cnt == VCD_DEC_NUM_INTERLACED_FIELDS)
+ cctxt->status.int_field_cnt = 0;
+}
+
+void vcd_handle_input_done_with_trans_end(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc;
+ union sched_value_type sched_val;
+ if (!cctxt->decoding)
+ return;
+
+ if (cctxt->out_buf_pool.in_use < cctxt->out_buf_pool.buf_req.min_count)
+ return;
+
+ rc = vcd_map_sched_status(sched_get_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_OTKNCURRENT, &sched_val));
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("sched_get_client_param:OTKNCURRENT failed");
+ return;
+ }
+
+ if (!sched_val.un_value) {
+ VCD_MSG_MED("All output buffers with core are pending display");
+
+ rc = vcd_map_sched_status(sched_update_client_o_tkn(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true,
+ cctxt->sched_o_tkn_per_ip_frm));
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("sched_update_client_o_tkn failed");
+ }
+}
+
+u32 vcd_handle_output_required(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status)
+{
+ struct vcd_transc *transc;
+ struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload;
+ u32 rc;
+
+ if (!cctxt->status.frame_submitted && !cctxt->status.frame_delayed) {
+ VCD_MSG_ERROR("\n Input done was not expected");
+ return VCD_ERR_BAD_STATE;
+ }
+
+ rc = vcd_validate_io_done_pyld(payload, status);
+ VCD_FAILED_RETURN(rc, "\n Bad input done payload");
+
+ transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag;
+
+ if (transc->ip_buf_entry->frame.virt_addr != frame->vcd_frm.virt_addr ||
+ !transc->ip_buf_entry->in_use) {
+ VCD_MSG_ERROR("\n Bad frm transaction state");
+ return VCD_ERR_BAD_STATE;
+ }
+
+ rc = vcd_map_sched_status(sched_re_queue_frame(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ (void *) transc->ip_buf_entry));
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame");
+
+ if (transc->ip_buf_entry->frame.flags & VCD_FRAME_FLAG_EOS) {
+ rc = vcd_map_sched_status(sched_mark_client_eof(
+ cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl));
+ }
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof");
+
+ transc->ip_buf_entry = NULL;
+ transc->in_use = false;
+ frame->frm_trans_end = true;
+
+ if (VCD_FAILED(status))
+ VCD_MSG_ERROR("\n OUTPUT_REQ returned err = 0x%x", status);
+
+ if (cctxt->status.frame_submitted > 0)
+ cctxt->status.frame_submitted--;
+ else
+ cctxt->status.frame_delayed--;
+
+ if (!VCD_FAILED(status) && cctxt->decoding &&
+ frame->vcd_frm.interlaced) {
+ if (cctxt->status.int_field_cnt > 0)
+ VCD_MSG_ERROR("\n Not expected: OUTPUT_REQ"
+ "for 2nd interlace field");
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt *cctxt,
+ void *payload)
+{
+ u32 rc;
+ struct vcd_transc *transc;
+
+ rc = vcd_validate_io_done_pyld(payload, VCD_S_SUCCESS);
+ VCD_FAILED_RETURN(rc, "Bad input done payload");
+
+ transc = (struct vcd_transc *) (((struct ddl_frame_data_tag *)payload)->
+ vcd_frm.ip_frm_tag);
+
+ ((struct ddl_frame_data_tag *)payload)->vcd_frm.interlaced = false;
+
+ rc = vcd_handle_input_done(cctxt, payload, VCD_EVT_RESP_INPUT_FLUSHED,
+ VCD_S_SUCCESS);
+
+ transc->in_use = false;
+ ((struct ddl_frame_data_tag *)payload)->frm_trans_end = true;
+
+ return rc;
+}
+
+u32 vcd_handle_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload, u32 event,
+ u32 status)
+{
+ struct vcd_buffer_entry *op_buf_entry;
+ struct ddl_frame_data_tag *op_frm = (struct ddl_frame_data_tag *)
+ payload;
+ struct vcd_transc *transc;
+ u32 rc;
+
+ rc = vcd_validate_io_done_pyld(payload, status);
+ VCD_FAILED_RETURN(rc, "Bad payload recvd");
+
+ transc = (struct vcd_transc *)op_frm->vcd_frm.ip_frm_tag;
+
+ if (op_frm->vcd_frm.virt_addr) {
+
+ if (!transc->op_buf_entry) {
+ op_buf_entry = vcd_find_buffer_pool_entry(
+ &cctxt->out_buf_pool, op_frm->vcd_frm.virt_addr);
+ } else {
+ op_buf_entry = transc->op_buf_entry;
+ }
+
+ if (!op_buf_entry) {
+ VCD_MSG_ERROR("Invalid output buffer returned"
+ "from DDL");
+ vcd_assert();
+ rc = VCD_ERR_BAD_POINTER;
+ } else if (!op_buf_entry->in_use) {
+ VCD_MSG_ERROR("Bad output buffer %p recv from DDL",
+ op_buf_entry->frame.virt_addr);
+ vcd_assert();
+ rc = VCD_ERR_BAD_POINTER;
+ } else {
+ op_buf_entry->in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(
+ cctxt->out_buf_pool.in_use);
+ VCD_MSG_LOW("outBufPool.InUse = %d",
+ cctxt->out_buf_pool.in_use);
+ }
+ }
+ VCD_FAILED_RETURN(rc, "Bad output buffer pointer");
+ op_frm->vcd_frm.time_stamp = transc->time_stamp;
+ op_frm->vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+ op_frm->vcd_frm.frame_type = transc->frame_type;
+
+ transc->frame_done = true;
+
+ if (transc->input_done && transc->frame_done)
+ transc->in_use = false;
+
+ if (status == VCD_ERR_INTRLCD_FIELD_DROP ||
+ (op_frm->intrlcd_ip_frm_tag != VCD_FRAMETAG_INVALID &&
+ op_frm->intrlcd_ip_frm_tag)) {
+ vcd_handle_frame_done_for_interlacing(cctxt, transc, op_frm,
+ status);
+ }
+
+ if (status != VCD_ERR_INTRLCD_FIELD_DROP) {
+ cctxt->callback(event, status, &op_frm->vcd_frm,
+ sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+ }
+ return VCD_S_SUCCESS;
+}
+
+void vcd_handle_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload,
+ u32 status)
+{
+ struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload;
+
+ VCD_MSG_LOW("vcd_handle_frame_done_in_eos:");
+
+ if (VCD_FAILED(vcd_validate_io_done_pyld(payload, status)))
+ return;
+
+ if (cctxt->status.eos_prev_valid) {
+ vcd_handle_frame_done(cctxt,
+ (void *)&cctxt->status.eos_prev_op_frm,
+ VCD_EVT_RESP_OUTPUT_DONE, status);
+ }
+
+ cctxt->status.eos_prev_op_frm = *frame;
+ cctxt->status.eos_prev_valid = true;
+}
+
+void vcd_handle_frame_done_for_interlacing(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc_ip1, struct ddl_frame_data_tag *op_frm,
+ u32 status)
+{
+ struct vcd_transc *transc_ip2 = (struct vcd_transc *)
+ op_frm->intrlcd_ip_frm_tag;
+
+ if (status == VCD_ERR_INTRLCD_FIELD_DROP) {
+ cctxt->status.int_field_cnt = 0;
+ return;
+ }
+
+ op_frm->intrlcd_ip_frm_tag = transc_ip2->ip_frm_tag;
+
+ transc_ip2->frame_done = true;
+
+ if (transc_ip2->input_done && transc_ip2->frame_done)
+ transc_ip2->in_use = false;
+
+ if (!transc_ip1->frame_type || !transc_ip2->frame_type) {
+ VCD_MSG_ERROR("DDL didn't provided frame type");
+ return;
+ }
+}
+
+u32 vcd_handle_first_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload)
+{
+ if (!cctxt->decoding)
+ return vcd_handle_first_encode_frame_done(cctxt, payload);
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_handle_first_encode_frame_done(struct vcd_clnt_ctxt *cctxt,
+ void *payload)
+{
+ struct vcd_buffer_entry *buf_entry;
+ struct vcd_frame_data *frm_entry;
+ u32 rc, seqhdr_present;
+ struct vcd_property_hdr prop_hdr;
+ struct vcd_sequence_hdr seq_hdr;
+ struct vcd_property_codec codec;
+ union sched_value_type sched_val;
+ struct vcd_transc *transc;
+ struct ddl_frame_data_tag *payload_frm = (struct ddl_frame_data_tag *)
+ payload;
+ VCD_MSG_LOW("vcd_handle_first_encode_frame_done:");
+
+ rc = vcd_validate_io_done_pyld(payload, VCD_S_SUCCESS);
+ VCD_FAILED_RETURN(rc, "Validate frame done payload failed");
+
+ transc = (struct vcd_transc *)payload_frm->vcd_frm.ip_frm_tag;
+
+ prop_hdr.id = DDL_I_SEQHDR_PRESENT;
+ prop_hdr.sz = sizeof(seqhdr_present);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &seqhdr_present);
+ VCD_FAILED_RETURN(rc, "Failed: DDL_I_SEQHDR_PRESENT");
+ if (!seqhdr_present)
+ return VCD_S_SUCCESS;
+
+ buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool);
+
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Sched provided frame when 2nd op buffer "
+ "was unavailable");
+
+ rc = VCD_ERR_FAIL;
+ vcd_assert();
+ return rc;
+ }
+
+ frm_entry = &buf_entry->frame;
+ prop_hdr.id = VCD_I_CODEC;
+ prop_hdr.sz = sizeof(struct vcd_property_codec);
+
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &codec);
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_get_property:VCD_I_CODEC",
+ rc);
+ goto out;
+ }
+
+ if (codec.codec != VCD_CODEC_H263) {
+ prop_hdr.id = VCD_I_SEQ_HEADER;
+ prop_hdr.sz = sizeof(struct vcd_sequence_hdr);
+
+ seq_hdr.addr = frm_entry->virt_addr;
+ seq_hdr.sz = buf_entry->size;
+
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &seq_hdr);
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: "
+ "ddl_get_property:VCD_I_SEQ_HEADER", rc);
+ goto out;
+ }
+ } else {
+ VCD_MSG_LOW("Codec Type is H.263\n");
+ }
+
+ sched_val.un_value = VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_OTKNPERIPFRM, &sched_val));
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x.Failed: sched_set_client_param", rc);
+ goto out;
+ }
+
+ frm_entry->data_len = seq_hdr.sz;
+ frm_entry->time_stamp = transc->time_stamp;
+ frm_entry->ip_frm_tag = transc->ip_frm_tag;
+ frm_entry->flags |= VCD_FRAME_FLAG_CODECCONFIG;
+
+ cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, frm_entry,
+ sizeof(struct vcd_frame_data), cctxt, cctxt->client_data);
+
+out:
+ if (VCD_FAILED(rc))
+ vcd_buffer_pool_entry_en_q(&cctxt->out_buf_pool, buf_entry);
+
+ return rc;
+}
+
+void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt)
+{
+ if (cctxt->status.eos_prev_valid) {
+ vcd_handle_frame_done(cctxt,
+ (void *)&cctxt->status.eos_prev_op_frm,
+ VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS);
+
+ cctxt->status.eos_prev_valid = false;
+ }
+
+ if (cctxt->status.flush_mode)
+ vcd_process_pending_flush_in_eos(cctxt);
+
+ if (cctxt->status.stop_pending)
+ vcd_process_pending_stop_in_eos(cctxt);
+ else {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ }
+}
+
+void vcd_handle_eos_done(struct vcd_clnt_ctxt *cctxt, struct vcd_transc *transc,
+ u32 status)
+{
+ struct vcd_frame_data vcd_frm;
+ VCD_MSG_LOW("vcd_handle_eos_done:");
+
+ if (VCD_FAILED(status))
+ VCD_MSG_ERROR("EOS DONE returned error = 0x%x", status);
+
+ if (cctxt->status.eos_prev_valid) {
+ cctxt->status.eos_prev_op_frm.vcd_frm.flags |=
+ VCD_FRAME_FLAG_EOS;
+
+ vcd_handle_frame_done(cctxt,
+ (void *)&cctxt->status.eos_prev_op_frm,
+ VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS);
+
+ cctxt->status.eos_prev_valid = false;
+ } else {
+ if (transc->ip_buf_entry) {
+ transc->ip_buf_entry->frame.ip_frm_tag =
+ transc->ip_frm_tag;
+
+ vcd_send_frame_done_in_eos(cctxt,
+ &transc->ip_buf_entry->frame, false);
+ } else {
+ memset(&vcd_frm, 0, sizeof(struct vcd_frame_data));
+ vcd_frm.ip_frm_tag = transc->ip_frm_tag;
+ vcd_frm.time_stamp = transc->time_stamp;
+ vcd_frm.flags = VCD_FRAME_FLAG_EOS;
+ vcd_send_frame_done_in_eos(cctxt, &vcd_frm, true);
+ }
+ }
+ if (transc->ip_buf_entry) {
+ if (transc->ip_buf_entry->frame.virt_addr) {
+ transc->ip_buf_entry->frame.ip_frm_tag =
+ transc->ip_frm_tag;
+
+ cctxt->callback(VCD_EVT_RESP_INPUT_DONE,
+ VCD_S_SUCCESS, &transc->ip_buf_entry->frame,
+ sizeof(struct vcd_frame_data), cctxt,
+ cctxt->client_data);
+ }
+ transc->ip_buf_entry->in_use = false;
+ VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use);
+ transc->ip_buf_entry = NULL;
+ cctxt->status.frame_submitted--;
+ }
+
+ transc->in_use = false;
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+ if (cctxt->status.flush_mode)
+ vcd_process_pending_flush_in_eos(cctxt);
+
+ if (cctxt->status.stop_pending) {
+ vcd_process_pending_stop_in_eos(cctxt);
+ } else if (!cctxt->status.eos_wait_for_op_buf) {
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ }
+}
+
+void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status)
+{
+ cctxt->status.cmd_submitted--;
+ vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+
+ if (!VCD_FAILED(status)) {
+ cctxt->callback(VCD_EVT_RESP_START, status, NULL, 0, cctxt,
+ cctxt->client_data);
+
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ } else {
+ VCD_MSG_ERROR("ddl callback returned failure.status = 0x%x",
+ status);
+ vcd_handle_err_in_starting(cctxt, status);
+ }
+}
+
+void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status)
+{
+ u32 rc = VCD_S_SUCCESS;
+ u32 seq_hdrpresent = 0;
+ union sched_value_type sched_val;
+ struct vcd_property_hdr prop_hdr;
+ VCD_MSG_LOW("vcd_handle_stop_done:");
+ cctxt->status.cmd_submitted--;
+ vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+
+ if (VCD_FAILED(status)) {
+ VCD_MSG_FATAL("STOP_DONE returned error = 0x%x", status);
+ status = VCD_ERR_HW_FATAL;
+ vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_INVALID,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ goto out;
+ }
+
+ if (!cctxt->decoding) {
+ prop_hdr.id = DDL_I_SEQHDR_PRESENT;
+ prop_hdr.sz = sizeof(seq_hdrpresent);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
+ &seq_hdrpresent);
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("Failed: DDL Get DDL_I_SEQHDR_PRESENT %d",
+ rc);
+ goto open_out;
+ }
+ if (seq_hdrpresent == 0x1) {
+ sched_val.un_value = VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM
+ + 1;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_OTKNPERIPFRM, &sched_val));
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("Failed: sched_set_client_param "
+ "%d", rc);
+ }
+ }
+open_out:
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_OPEN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+
+out:
+ cctxt->callback(VCD_EVT_RESP_STOP, status, NULL, 0, cctxt,
+ cctxt->client_data);
+
+ memset(&cctxt->status, 0, sizeof(struct vcd_clnt_status));
+}
+
+void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_transc *transc, u32 status)
+{
+ VCD_MSG_LOW("vcd_handle_stop_done_in_starting:");
+ cctxt->status.cmd_submitted--;
+ vcd_mark_command_channel(cctxt->dev_ctxt, transc);
+ if (!VCD_FAILED(status)) {
+ cctxt->callback(VCD_EVT_RESP_START, cctxt->status.last_err,
+ NULL, 0, cctxt, cctxt->client_data);
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_OPEN,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ } else {
+ VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error "
+ "= 0x%x", status);
+ vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START,
+ VCD_ERR_HW_FATAL);
+ }
+}
+
+void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 status)
+{
+ u32 rc;
+ VCD_MSG_LOW("vcd_handle_stop_done_in_invalid:");
+ if (!VCD_FAILED(status)) {
+ vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CLIENT_CLOSE);
+ if (cctxt->status.frame_submitted) {
+ vcd_release_multiple_frame_channels(cctxt->dev_ctxt,
+ cctxt->status.frame_submitted);
+
+ cctxt->status.frame_submitted = 0;
+ cctxt->status.frame_delayed = 0;
+ }
+ if (cctxt->status.cmd_submitted) {
+ vcd_release_multiple_command_channels(cctxt->dev_ctxt,
+ cctxt->status.cmd_submitted);
+ cctxt->status.cmd_submitted = 0;
+ }
+ } else {
+ VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error "
+ "= 0x%x", status);
+ vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+ cctxt->status.cleaning_up = false;
+ }
+ vcd_flush_buffers_in_err_fatal(cctxt);
+ VCD_MSG_HIGH("VCD cleanup: All buffers are returned");
+ if (cctxt->status.stop_pending) {
+ cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0,
+ cctxt, cctxt->client_data);
+ cctxt->status.stop_pending = false;
+ }
+ rc = vcd_power_event(cctxt->dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_ERRFATAL);
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_ERRFATAL failed");
+ if (!cctxt->status.cleaning_up &&
+ cctxt->status.close_pending) {
+ vcd_destroy_client_context(cctxt);
+ vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, false);
+ }
+}
+
+u32 vcd_handle_input_frame(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *input_frame)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ struct vcd_buffer_entry *buf_entry;
+ struct vcd_frame_data *frm_entry;
+ u32 rc = VCD_S_SUCCESS;
+ u32 eos_handled = false;
+
+ VCD_MSG_LOW("vcd_handle_input_frame:");
+
+ VCD_MSG_LOW("input buffer: addr=(0x%p), size=(%d), len=(%d)",
+ input_frame->virt_addr, input_frame->alloc_len,
+ input_frame->data_len);
+
+ if ((!input_frame->virt_addr || !input_frame->data_len) &&
+ !(input_frame->flags & VCD_FRAME_FLAG_EOS)) {
+ VCD_MSG_ERROR("Bad frame ptr/len/EOS combination");
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ if (!cctxt->status.b1st_frame_recvd) {
+ if (cctxt->decoding)
+ rc = vcd_handle_first_decode_frame(cctxt);
+
+ if (!VCD_FAILED(rc)) {
+ cctxt->status.first_ts = input_frame->time_stamp;
+ cctxt->status.prev_ts = cctxt->status.first_ts;
+
+ cctxt->status.b1st_frame_recvd = true;
+
+ vcd_power_event(cctxt->dev_ctxt, cctxt,
+ VCD_EVT_PWR_CLNT_FIRST_FRAME);
+ }
+ }
+ VCD_FAILED_RETURN(rc, "Failed: Frist frame handling");
+
+ buf_entry = vcd_find_buffer_pool_entry(&cctxt->in_buf_pool,
+ input_frame->virt_addr);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Bad buffer addr: %p", input_frame->virt_addr);
+ return VCD_ERR_FAIL;
+ }
+
+ if (buf_entry->in_use) {
+ VCD_MSG_ERROR("An inuse input frame is being re-queued to "
+ "scheduler");
+ return VCD_ERR_FAIL;
+ }
+
+ if (input_frame->alloc_len > buf_entry->size) {
+ VCD_MSG_ERROR("Bad buffer Alloc_len %d, Actual size=%d",
+ input_frame->alloc_len, buf_entry->size);
+
+ return VCD_ERR_ILLEGAL_PARM;
+ }
+
+ frm_entry = &buf_entry->frame;
+
+ *frm_entry = *input_frame;
+ frm_entry->phys_addr = buf_entry->phys_addr;
+
+ if (input_frame->flags & VCD_FRAME_FLAG_EOS)
+ rc = vcd_handle_recvd_eos(cctxt, input_frame, &eos_handled);
+
+ if (VCD_FAILED(rc) || eos_handled) {
+ VCD_MSG_HIGH("rc = 0x%x, eos_handled = %d", rc, eos_handled);
+
+ return rc;
+ }
+
+ rc = vcd_map_sched_status(sched_queue_frame(dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, (void *)buf_entry));
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame");
+
+ buf_entry->in_use = true;
+ cctxt->in_buf_pool.in_use++;
+ if (input_frame->flags & VCD_FRAME_FLAG_EOS) {
+ rc = vcd_map_sched_status(sched_mark_client_eof(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl));
+ }
+
+ VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof");
+
+ vcd_try_submit_frame(dev_ctxt);
+ return rc;
+}
+
+void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u8 i;
+
+ VCD_MSG_LOW("vcd_release_all_clnt_frm_transc:");
+
+ for (i = 0; i < dev_ctxt->trans_tbl_size; i++) {
+ if (dev_ctxt->trans_tbl[i].in_use &&
+ cctxt == dev_ctxt->trans_tbl[i].cctxt &&
+ dev_ctxt->trans_tbl[i].type ==
+ VCD_CMD_CODE_FRAME) {
+ vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]);
+ }
+ }
+}
+
+void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u8 i;
+
+ VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:");
+
+ for (i = 0; i < dev_ctxt->trans_tbl_size; i++) {
+ if (dev_ctxt->trans_tbl[i].in_use &&
+ cctxt == dev_ctxt->trans_tbl[i].cctxt &&
+ dev_ctxt->trans_tbl[i].type == VCD_CMD_NONE) {
+ vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]);
+ }
+ }
+}
+
+void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt)
+{
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ u8 i;
+
+ VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:");
+
+ for (i = 0; i < dev_ctxt->trans_tbl_size; i++) {
+ if (dev_ctxt->trans_tbl[i].in_use &&
+ cctxt == dev_ctxt->trans_tbl[i].cctxt) {
+ vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]);
+ }
+ }
+}
+
+void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status)
+{
+ VCD_MSG_LOW("vcd_send_flush_done:");
+
+ if (cctxt->status.flush_mode & VCD_FLUSH_INPUT) {
+ cctxt->callback(VCD_EVT_RESP_FLUSH_INPUT_DONE, status, NULL, 0,
+ cctxt, cctxt->client_data);
+ cctxt->status.flush_mode &= ~VCD_FLUSH_INPUT;
+ }
+
+ if (cctxt->status.flush_mode & VCD_FLUSH_OUTPUT) {
+ cctxt->callback(VCD_EVT_RESP_FLUSH_OUTPUT_DONE, status, NULL, 0,
+ cctxt, cctxt->client_data);
+ cctxt->status.flush_mode &= ~VCD_FLUSH_OUTPUT;
+ }
+}
+
+u32 vcd_store_seq_hdr(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_sequence_hdr *seq_hdr)
+{
+// u32 rc;
+// struct vcd_property_hdr prop_hdr;
+// u32 align;
+// u32 addr;
+// int ret = 0;
+
+ if (!seq_hdr->sz || !seq_hdr->addr) {
+ VCD_MSG_ERROR("Bad seq hdr");
+ return VCD_ERR_BAD_POINTER;
+ }
+
+ if (cctxt->seq_hdr.addr) {
+ VCD_MSG_HIGH("Old seq hdr detected");
+
+ dma_free_coherent(NULL, cctxt->seq_hdr.sz +
+ VCD_SEQ_HDR_PADDING_BYTES, cctxt->seq_hdr.addr,
+ cctxt->seq_hdr_phys_addr);
+ cctxt->seq_hdr.addr = NULL;
+ }
+
+ cctxt->seq_hdr.sz = seq_hdr->sz;
+
+ //TODO strip out all this alignment crap?
+#if 0
+ prop_hdr.id = DDL_I_SEQHDR_ALIGN_BYTES;
+ prop_hdr.size = sizeof(u32);
+
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &align);
+
+ VCD_FAILED_RETURN(rc,
+ "Failed: ddl_get_property DDL_I_SEQHDR_ALIGN_BYTES");
+
+ VCD_MSG_MED("Seq hdr alignment bytes = %d", align);
+#endif
+
+ cctxt->seq_hdr.addr = dma_alloc_coherent(NULL,
+ cctxt->seq_hdr.sz + VCD_SEQ_HDR_PADDING_BYTES,
+ &cctxt->seq_hdr_phys_addr, GFP_KERNEL);
+ if (!cctxt->seq_hdr.addr) {
+ VCD_MSG_ERROR("Seq hdr allocation failed");
+ return VCD_ERR_ALLOC_FAIL;
+ }
+
+ memset(cctxt->seq_hdr.addr, 0,
+ cctxt->seq_hdr.sz + VCD_SEQ_HDR_PADDING_BYTES);
+ memcpy(cctxt->seq_hdr.addr, seq_hdr->addr, seq_hdr->sz);
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_set_frame_rate(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_rate *fps)
+{
+ union sched_value_type sched_val;
+ u32 rc;
+
+ sched_val.frm_rate.numer = fps->fps_numerator;
+ sched_val.frm_rate.denom = fps->fps_denominator;
+ cctxt->frm_rate = *fps;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_FRAMERATE, &sched_val));
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_FRAMERATE",
+ rc);
+ }
+
+ rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate,
+ cctxt->frm_p_units);
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl",
+ rc);
+ }
+
+ sched_val.un_value = cctxt->reqd_perf_lvl;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_PTKNRATE, &sched_val));
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE",
+ rc);
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+u32 vcd_set_frame_size(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_property_frame_size *frm_size)
+{
+ struct vcd_property_hdr prop_hdr;
+ union sched_value_type sched_val;
+ u32 rc;
+ u32 frm_p_units;
+ frm_size = NULL;
+
+ prop_hdr.id = DDL_I_FRAME_PROC_UNITS;
+ prop_hdr.sz = sizeof(frm_p_units);
+ rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &frm_p_units);
+
+ VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS");
+
+ cctxt->frm_p_units = sched_val.un_value = frm_p_units;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_PTKNPERFRM, &sched_val));
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNPERFRM",
+ rc);
+ }
+
+ rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate, frm_p_units);
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl",
+ rc);
+ }
+
+ sched_val.un_value = cctxt->reqd_perf_lvl;
+
+ rc = vcd_map_sched_status(sched_set_client_param(
+ cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,
+ SCHED_I_CLNT_PTKNRATE, &sched_val));
+
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE",
+ rc);
+ }
+
+ return VCD_S_SUCCESS;
+}
+
+void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ VCD_MSG_HIGH("Buffer flush is pending");
+
+ rc = vcd_flush_buffers(cctxt, cctxt->status.flush_mode);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc);
+
+ cctxt->status.eos_wait_for_op_buf = false;
+
+ vcd_send_flush_done(cctxt, VCD_S_SUCCESS);
+}
+
+void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+
+ rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL);
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc);
+
+ VCD_MSG_HIGH("All buffers are returned. Enqueuing stop cmd");
+
+ vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+ cctxt->status.stop_pending = false;
+
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STOPPING,
+ CLIENT_STATE_EVENT_NUMBER(pf_stop));
+}
+
+u32 vcd_calculate_frame_delta(struct vcd_clnt_ctxt *cctxt,
+ struct vcd_frame_data *frame)
+{
+ u32 frm_delta;
+ u64 temp, temp1;
+
+ temp = frame->time_stamp - cctxt->status.prev_ts;
+
+ VCD_MSG_LOW("Curr_ts=%lld Prev_ts=%lld Diff=%llu", frame->time_stamp,
+ cctxt->status.prev_ts, temp);
+
+ temp = temp * cctxt->time_resoln;
+ temp = (temp + (VCD_TIMESTAMP_RESOLUTION >> 1));
+ temp1 = do_div(temp, VCD_TIMESTAMP_RESOLUTION);
+ frm_delta = temp;
+ VCD_MSG_LOW("temp1=%lld temp=%lld", temp1, temp);
+ cctxt->status.time_elapsed += frm_delta;
+
+ temp = ((u64)cctxt->status.time_elapsed * VCD_TIMESTAMP_RESOLUTION);
+ temp = (temp + (cctxt->time_resoln >> 1));
+ temp1 = do_div(temp, cctxt->time_resoln);
+
+ cctxt->status.prev_ts = cctxt->status.first_ts + temp;
+
+ VCD_MSG_LOW("Time_elapsed=%u, Drift=%llu, new Prev_ts=%lld",
+ cctxt->status.time_elapsed, temp1, cctxt->status.prev_ts);
+
+ return frm_delta;
+}
+
+struct vcd_buffer_entry *vcd_check_fill_output_buffer(struct vcd_clnt_ctxt
+ *cctxt, struct vcd_frame_data *buffer)
+{
+ struct vcd_buffer_pool *buf_pool = &cctxt->out_buf_pool;
+ struct vcd_buffer_entry *buf_entry;
+
+ if (!buf_pool->entries) {
+ VCD_MSG_ERROR("Buffers not set or allocated yet");
+ return NULL;
+ }
+
+ if (!buffer->virt_addr) {
+ VCD_MSG_ERROR("NULL buffer address provided");
+ return NULL;
+ }
+
+ buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer->virt_addr);
+ if (!buf_entry) {
+ VCD_MSG_ERROR("Unrecognized buffer address provided %p",
+ buffer->virt_addr);
+ return NULL;
+ }
+
+ if (buf_entry->in_use) {
+ VCD_MSG_ERROR("An inuse output frame is being provided for "
+ "reuse");
+ return NULL;
+ }
+
+ if (buffer->alloc_len < buf_pool->buf_req.size ||
+ buffer->alloc_len > buf_entry->size) {
+ VCD_MSG_ERROR("Bad buffer Alloc_len = %d, Actual size = %d, "
+ " Min size = %u", buffer->alloc_len, buf_entry->size,
+ buf_pool->buf_req.size);
+ return NULL;
+ }
+
+ return buf_entry;
+}
+
+void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event,
+ u32 status)
+{
+ if (cctxt->status.frame_submitted) {
+ cctxt->status.frame_submitted--;
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+ }
+ vcd_handle_err_fatal(cctxt, event, status);
+}
+
+void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, u32 status)
+{
+ u32 rc;
+ VCD_MSG_LOW("vcd_handle_err_fatal: event=%x, err=%x", event, status);
+ if (!VCD_FAILED_FATAL(status))
+ return;
+
+ if (VCD_FAILED_DEVICE_FATAL(status)) {
+ vcd_clnt_handle_device_err_fatal(cctxt, event);
+ vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt);
+ } else if (VCD_FAILED_CLIENT_FATAL(status)) {
+ cctxt->status.last_evt = event;
+
+ if (cctxt->sched_clnt_valid) {
+ rc = vcd_map_sched_status(sched_suspend_resume_client(
+ cctxt->dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, false));
+ if (VCD_FAILED(rc)) {
+ VCD_MSG_ERROR("Failed: sched_suspend_resume_"
+ "client rc=0x%x", rc);
+ }
+ }
+ cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, cctxt,
+ cctxt->client_data);
+ cctxt->status.cleaning_up = true;
+ vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+ vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_INVALID,
+ CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb));
+ }
+}
+
+void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt, u32 status)
+{
+ VCD_MSG_LOW("\n vcd_handle_err_in_starting:");
+ if (VCD_FAILED_FATAL(status)) {
+ vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, status);
+ } else {
+ cctxt->status.last_err = status;
+ VCD_MSG_HIGH("\n VCD cleanup: Enqueuing stop cmd");
+ vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP);
+ }
+}
+
+void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt)
+{
+ if (!cctxt->status.frame_submitted) {
+ VCD_MSG_ERROR("Transaction pending response was not expected");
+ vcd_assert();
+ return;
+ }
+ cctxt->status.frame_submitted--;
+ cctxt->status.frame_delayed++;
+ vcd_mark_frame_channel(cctxt->dev_ctxt);
+}
+
+u32 vcd_requeue_input_frame(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_clnt_ctxt *cctxt, struct vcd_buffer_entry *buf_entry)
+{
+ u32 rc;
+ rc = vcd_map_sched_status(sched_re_queue_frame(dev_ctxt->sched_hdl,
+ cctxt->sched_clnt_hdl, (void *) buf_entry));
+
+ VCD_FAILED_RETURN(rc, "Failed: Sched_ReQueueFrame");
+
+ if (buf_entry->frame.flags & VCD_FRAME_FLAG_EOS) {
+ rc = vcd_map_sched_status(sched_mark_client_eof(dev_ctxt->
+ sched_hdl, cctxt->sched_clnt_hdl));
+ }
+
+ if (VCD_FAILED(rc))
+ VCD_MSG_ERROR("rc = 0x%x: Failed: Sched_MarkClientEOF", rc);
+
+ return rc;
+}
+
+void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt *dev_ctxt,
+ struct vcd_transc *transc)
+{
+ struct vcd_clnt_ctxt *cctxt = transc->cctxt;
+ u32 rc;
+
+ vcd_mark_frame_channel(dev_ctxt);
+ transc->in_use = false;
+
+ vcd_handle_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL,
+ VCD_ERR_CLIENT_FATAL);
+
+ if (vcd_get_command_channel(dev_ctxt, &transc)) {
+ transc->type = VCD_CMD_CODEC_STOP;
+ transc->cctxt = cctxt;
+ rc = vcd_submit_cmd_sess_end(transc);
+ if (VCD_FAILED(rc)) {
+ vcd_release_command_channel(dev_ctxt, transc);
+ VCD_MSG_ERROR("rc = 0x%x. Failed: VCD_SubmitCmdSessEnd",
+ rc);
+ }
+ }
+}
diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.h b/drivers/misc/video_core/720p/vcd/vcd_util.h
new file mode 100644
index 0000000..2d90bf5
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/vcd_util.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _VCD_UTIL_H_
+#define _VCD_UTIL_H_
+
+#include "vcd_api.h"
+
+#if DEBUG
+
+//TODO what a load of crap in here
+#define VCD_MSG_LOW(xx_fmt...) printk(KERN_INFO "\t* " xx_fmt)
+#define VCD_MSG_MED(xx_fmt...) printk(KERN_INFO " * " xx_fmt)
+#define VCD_MSG_HIGH(xx_fmt...) printk(KERN_WARNING xx_fmt)
+
+#else
+
+#define VCD_MSG_LOW(xx_fmt...)
+#define VCD_MSG_MED(xx_fmt...)
+#define VCD_MSG_HIGH(xx_fmt...)
+
+#endif
+
+#define VCD_MSG_ERROR(xx_fmt...) printk(KERN_ERR "err: " xx_fmt)
+#define VCD_MSG_FATAL(xx_fmt...) printk(KERN_ERR "<FATAL> " xx_fmt)
+
+#define VCD_FAILED_RETURN(rc, xx_fmt...) \
+ do { \
+ if (VCD_FAILED(rc)) { \
+ printk(KERN_ERR xx_fmt); \
+ return rc; \
+ } \
+ } while (0)
+
+#define VCD_FAILED_DEVICE_FATAL(rc) \
+ (rc == VCD_ERR_HW_FATAL ? true : false)
+#define VCD_FAILED_CLIENT_FATAL(rc) \
+ (rc == VCD_ERR_CLIENT_FATAL ? true : false)
+
+#define VCD_FAILED_FATAL(rc) \
+ ((VCD_FAILED_DEVICE_FATAL(rc) || VCD_FAILED_CLIENT_FATAL(rc)) \
+ ? true : false)
+
+#define vcd_assert() VCD_MSG_FATAL("ASSERT")
+
+#endif
diff --git a/drivers/misc/video_core/720p/vcd/video_core_type.h b/drivers/misc/video_core/720p/vcd/video_core_type.h
new file mode 100644
index 0000000..febd4cf
--- /dev/null
+++ b/drivers/misc/video_core/720p/vcd/video_core_type.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef VIDEO_CORE_TYPE_H
+#define VIDEO_CORE_TYPE_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#define DEBUG 1
+
+#define USE_RES_TRACKER
+
+#undef CORE_TIMING_INFO
+
+#endif
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 549a341..d3d0e2e 100755
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -692,11 +692,13 @@
BUG_ON(!func);
+#if 0
if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) {
if (err_ret)
*err_ret = -EINVAL;
return;
}
+#endif
ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
if (err_ret)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f06d06e..e171e77 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -249,7 +249,7 @@
config MMC_MSM7X00A
tristate "Qualcomm MSM 7X00A SDCC Controller Support"
- depends on MMC && ARCH_MSM && !ARCH_MSM7X30
+ depends on MMC && ARCH_MSM
help
This provides support for the SD/MMC cell found in the
MSM 7X00A controllers from Qualcomm.
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 24e0945..8a149ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -52,7 +52,7 @@
#define BUSCLK_PWRSAVE 1
#define BUSCLK_TIMEOUT (HZ)
static unsigned int msmsdcc_fmin = 144000;
-static unsigned int msmsdcc_fmax = 50000000;
+static unsigned int msmsdcc_fmax = 49152000;
static unsigned int msmsdcc_4bit = 1;
static unsigned int msmsdcc_pwrsave = 1;
static unsigned int msmsdcc_piopoll = 1;
@@ -389,6 +389,7 @@
host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
+ host->dma.hdr.execute_func = NULL;
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
host->dma.num_ents, host->dma.dir);
@@ -988,7 +989,7 @@
if (status ^ host->oldstat) {
pr_info("%s: Slot status change detected (%d -> %d)\n",
mmc_hostname(host->mmc), host->oldstat, status);
- if (status)
+ if (status && !host->plat->built_in)
mmc_detect_change(host->mmc, (5 * HZ) / 2);
else
mmc_detect_change(host->mmc, 0);
@@ -1214,6 +1215,10 @@
msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
host->saved_irq0mask = MCI_IRQENABLE;
+ mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
+ if (plat->built_in)
+ mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
+
/*
* Setup card detect change
*/
@@ -1326,6 +1331,9 @@
if (host->stat_irq)
disable_irq(host->stat_irq);
+ if (host->plat->built_in)
+ mmc->pm_flags |= MMC_PM_KEEP_POWER;
+
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
rc = mmc_suspend_host(mmc);
if (!rc)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 35081ce..03fe3e3d 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -49,6 +49,14 @@
say M here and read <file:Documentation/kbuild/modules.txt>.
The module will be called ms02-nv.
+config MTD_MSM_NAND
+ tristate "MSM NAND Device Support"
+ depends on MTD && ARCH_MSM
+ select MTD_NAND_IDS
+ default y
+ help
+ Support for some NAND chips connected to the MSM NAND controller.
+
config MTD_DATAFLASH
tristate "Support for AT45xxx DataFlash"
depends on SPI_MASTER && EXPERIMENTAL
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f3226b1..fe959e8 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -11,6 +11,7 @@
obj-$(CONFIG_MTD_PHRAM) += phram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
+obj-$(CONFIG_MTD_MSM_NAND) += msm_nand.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c
new file mode 100644
index 0000000..a379a35
--- /dev/null
+++ b/drivers/mtd/devices/msm_nand.c
@@ -0,0 +1,1703 @@
+/* drivers/mtd/devices/msm_nand.c
+ *
+ * Copyright (C) 2007 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/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/stat.h>
+
+#include <asm/dma.h>
+#include <asm/mach/flash.h>
+
+#include <mach/dma.h>
+
+#include "msm_nand.h"
+
+#define MSM_NAND_DMA_BUFFER_SIZE SZ_4K
+#define MSM_NAND_DMA_BUFFER_SLOTS \
+ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8))
+
+#define SUPPORT_WRONG_ECC_CONFIG 1
+
+#define MSM_NAND_CFG0_RAW 0xA80420C0
+#define MSM_NAND_CFG1_RAW 0x5045D
+
+#define VERBOSE 0
+
+struct msm_nand_chip {
+ struct device *dev;
+ wait_queue_head_t wait_queue;
+ atomic_t dma_buffer_busy;
+ unsigned dma_channel;
+ uint8_t *dma_buffer;
+ dma_addr_t dma_addr;
+ unsigned CFG0, CFG1;
+ unsigned page_shift;
+ unsigned last_sector;
+ unsigned last_sectorsz;
+#if SUPPORT_WRONG_ECC_CONFIG
+ uint32_t ecc_buf_cfg;
+ uint32_t saved_ecc_buf_cfg;
+#endif
+};
+
+#define CFG1_WIDE_FLASH (1U << 1)
+
+#ifdef CONFIG_ARCH_MSM7X30
+#define BUF_STAT_UNCORRECTABLE (1U << 8)
+#define BUF_STAT_NUM_ERRS_MASK (0xf)
+#else
+#define BUF_STAT_UNCORRECTABLE (1U << 3)
+#define BUF_STAT_NUM_ERRS_MASK (0x7)
+#endif
+
+
+/* TODO: move datamover code out */
+
+#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
+#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
+#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
+#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
+
+#define msm_virt_to_dma(chip, vaddr) \
+ ((void)(*(vaddr)), (chip)->dma_addr + \
+ ((uint8_t *)(vaddr) - (chip)->dma_buffer))
+
+/**
+ * msm_nand_oob_64 - oob info for large (2KB) page
+ */
+static struct nand_ecclayout msm_nand_oob_64 = {
+ .oobavail = 16,
+ .oobfree = {
+ {30, 16},
+ }
+};
+
+/*
+ * msm_nand_oob_128 - oob info for 4KB page
+ */
+static struct nand_ecclayout msm_nand_oob_128 = {
+ .oobavail = 32,
+ .oobfree = {
+ {70, 32},
+ }
+};
+
+
+static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size)
+{
+ unsigned int bitmask, free_bitmask, old_bitmask;
+ unsigned int need_mask, current_need_mask;
+ int free_index;
+
+ need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
+ bitmask = atomic_read(&chip->dma_buffer_busy);
+ free_bitmask = ~bitmask;
+ do {
+ free_index = __ffs(free_bitmask);
+ current_need_mask = need_mask << free_index;
+ if ((bitmask & current_need_mask) == 0) {
+ old_bitmask =
+ atomic_cmpxchg(&chip->dma_buffer_busy,
+ bitmask,
+ bitmask | current_need_mask);
+ if (old_bitmask == bitmask)
+ return chip->dma_buffer +
+ free_index * MSM_NAND_DMA_BUFFER_SLOTS;
+ free_bitmask = 0; /* force return */
+ }
+ /* current free range was too small, clear all free bits */
+ /* below the top busy bit within current_need_mask */
+ free_bitmask &=
+ ~(~0U >> (32 - fls(bitmask & current_need_mask)));
+ } while (free_bitmask);
+
+ return NULL;
+}
+
+static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip,
+ void *buffer, size_t size)
+{
+ int index;
+ unsigned int used_mask;
+
+ used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1;
+ index = ((uint8_t *)buffer - chip->dma_buffer) /
+ MSM_NAND_DMA_BUFFER_SLOTS;
+ atomic_sub(used_mask << index, &chip->dma_buffer_busy);
+
+ wake_up(&chip->wait_queue);
+}
+
+uint32_t flash_read_id(struct msm_nand_chip *chip)
+{
+ struct {
+ dmov_s cmd[5];
+ unsigned cmdptr;
+ unsigned data[5];
+ } *dma_buffer;
+ uint32_t rv;
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ dma_buffer->data[0] = 0 | 4;
+ dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID;
+ dma_buffer->data[2] = 1;
+ dma_buffer->data[3] = 0xeeeeeeee;
+ dma_buffer->data[4] = 0xeeeeeeee;
+ BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data) - 1);
+
+ dma_buffer->cmd[0].cmd = 0 | CMD_OCB;
+ dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
+ dma_buffer->cmd[0].dst = MSM_NAND_FLASH_CHIP_SELECT;
+ dma_buffer->cmd[0].len = 4;
+
+ dma_buffer->cmd[1].cmd = DST_CRCI_NAND_CMD;
+ dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[1]);
+ dma_buffer->cmd[1].dst = MSM_NAND_FLASH_CMD;
+ dma_buffer->cmd[1].len = 4;
+
+ dma_buffer->cmd[2].cmd = 0;
+ dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[2]);
+ dma_buffer->cmd[2].dst = MSM_NAND_EXEC_CMD;
+ dma_buffer->cmd[2].len = 4;
+
+ dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA;
+ dma_buffer->cmd[3].src = MSM_NAND_FLASH_STATUS;
+ dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[3]);
+ dma_buffer->cmd[3].len = 4;
+
+ dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC;
+ dma_buffer->cmd[4].src = MSM_NAND_READ_ID;
+ dma_buffer->cmd[4].dst = msm_virt_to_dma(chip, &dma_buffer->data[4]);
+ dma_buffer->cmd[4].len = 4;
+ BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ rv = dma_buffer->data[4];
+ pr_info("msn_nand: nandid %x status %x\n", rv, dma_buffer->data[3]);
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ return rv;
+}
+
+int flash_read_config(struct msm_nand_chip *chip)
+{
+ struct {
+ dmov_s cmd[2];
+ unsigned cmdptr;
+ unsigned cfg0;
+ unsigned cfg1;
+ } *dma_buffer;
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+ dma_buffer->cfg0 = 0;
+ dma_buffer->cfg1 = 0;
+
+ dma_buffer->cmd[0].cmd = CMD_OCB;
+ dma_buffer->cmd[0].src = MSM_NAND_DEV0_CFG0;
+ dma_buffer->cmd[0].dst = msm_virt_to_dma(chip, &dma_buffer->cfg0);
+ dma_buffer->cmd[0].len = 4;
+
+ dma_buffer->cmd[1].cmd = CMD_OCU | CMD_LC;
+ dma_buffer->cmd[1].src = MSM_NAND_DEV0_CFG1;
+ dma_buffer->cmd[1].dst = msm_virt_to_dma(chip, &dma_buffer->cfg1);
+ dma_buffer->cmd[1].len = 4;
+ BUILD_BUG_ON(1 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ chip->CFG0 = dma_buffer->cfg0;
+ chip->CFG1 = dma_buffer->cfg1;
+ pr_info("msm_nand: read CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1);
+ pr_info("msm_nand: CFG0 cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n",
+ (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff,
+ (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15);
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+ if ((chip->CFG0 == 0) || (chip->CFG1 == 0))
+ return -1;
+
+ return 0;
+}
+
+unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr)
+{
+ struct {
+ dmov_s cmd;
+ unsigned cmdptr;
+ unsigned data;
+ } *dma_buffer;
+ unsigned rv;
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ dma_buffer->cmd.cmd = CMD_LC;
+ dma_buffer->cmd.src = addr;
+ dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data);
+ dma_buffer->cmd.len = 4;
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+ dma_buffer->data = 0xeeeeeeee;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+ rv = dma_buffer->data;
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+ return rv;
+}
+
+void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val)
+{
+ struct {
+ dmov_s cmd;
+ unsigned cmdptr;
+ unsigned data;
+ } *dma_buffer;
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ dma_buffer->cmd.cmd = CMD_LC;
+ dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data);
+ dma_buffer->cmd.dst = addr;
+ dma_buffer->cmd.len = 4;
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+ dma_buffer->data = val;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+}
+
+static dma_addr_t
+msm_nand_dma_map(struct device *dev, void *addr, size_t size,
+ enum dma_data_direction dir)
+{
+ struct page *page;
+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+ if (virt_addr_valid(addr))
+ page = virt_to_page(addr);
+ else {
+ if (WARN_ON(size + offset > PAGE_SIZE))
+ return ~0;
+ page = vmalloc_to_page(addr);
+ }
+ return dma_map_page(dev, page, offset, size, dir);
+}
+
+static int msm_nand_check_empty(struct mtd_info *mtd, struct mtd_oob_ops *ops,
+ unsigned long *uncorrected)
+{
+ unsigned int p, n, end;
+ uint8_t *datbuf = ops->datbuf;
+ uint8_t *oobbuf = ops->oobbuf;
+ size_t oobsize;
+ int page_count;
+
+ if (ops->mode == MTD_OOB_RAW)
+ return false;
+
+ page_count = ops->retlen / mtd->writesize;
+ oobsize = (ops->mode == MTD_OOB_AUTO) ? mtd->oobavail : mtd->oobsize;
+
+ for_each_set_bit(p, uncorrected, page_count) {
+ if (datbuf) {
+ datbuf = ops->datbuf + p * mtd->writesize;
+ for (n = 0; n < mtd->writesize; n++) {
+ /* empty blocks read 0x54 at these offsets */
+ if (datbuf[n] != ((n % 516 == 3) ? 0x54 : 0xff))
+ return false;
+ }
+ }
+ if (oobbuf) {
+ n = p * oobsize;
+ end = min(n + oobsize, ops->oobretlen);
+ for(; n < end; n++)
+ if (oobbuf[n] != 0xff)
+ return false;
+ }
+ if (ops->datbuf)
+ for (n = 3; n < mtd->writesize; n+= 516)
+ datbuf[n] = 0xff;
+ }
+ return true;
+}
+
+static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct msm_nand_chip *chip = mtd->priv;
+
+ struct {
+ dmov_s cmd[8 * 5 + 3];
+ unsigned cmdptr;
+ struct {
+ uint32_t cmd;
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t chipsel;
+ uint32_t cfg0;
+ uint32_t cfg1;
+ uint32_t exec;
+#if SUPPORT_WRONG_ECC_CONFIG
+ uint32_t ecccfg;
+ uint32_t ecccfg_restore;
+#endif
+ struct {
+ uint32_t flash_status;
+ uint32_t buffer_status;
+ } result[8];
+ } data;
+ } *dma_buffer;
+ dmov_s *cmd;
+ unsigned n;
+ unsigned page = from >> chip->page_shift;
+ uint32_t oob_len = ops->ooblen;
+ uint32_t sectordatasize;
+ uint32_t sectoroobsize;
+ int err, pageerr;
+ dma_addr_t data_dma_addr = 0;
+ dma_addr_t oob_dma_addr = 0;
+ dma_addr_t data_dma_addr_curr = 0;
+ dma_addr_t oob_dma_addr_curr = 0;
+ uint32_t oob_col = 0;
+ unsigned page_count;
+ unsigned pages_read = 0;
+ unsigned start_sector = 0;
+ uint32_t sector_corrected;
+ uint32_t page_corrected;
+ uint32_t total_corrected = 0;
+ uint32_t total_uncorrected = 0;
+ unsigned long uncorrected_noalloc = 0;
+ unsigned long *uncorrected = &uncorrected_noalloc;
+
+ if (from & (mtd->writesize - 1)) {
+ pr_err("%s: unsupported from, 0x%llx\n",
+ __func__, from);
+ return -EINVAL;
+ }
+ if (ops->mode != MTD_OOB_RAW) {
+ if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) {
+ /* when ops->datbuf is NULL, ops->len can be ooblen */
+ pr_err("%s: unsupported ops->len, %d\n",
+ __func__, ops->len);
+ return -EINVAL;
+ }
+ } else {
+ if (ops->datbuf != NULL &&
+ (ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+ pr_err("%s: unsupported ops->len,"
+ " %d for MTD_OOB_RAW\n", __func__, ops->len);
+ return -EINVAL;
+ }
+ }
+
+ if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+ pr_err("%s: unsupported ops->ooboffs, %d\n",
+ __func__, ops->ooboffs);
+ return -EINVAL;
+ }
+
+ if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO)
+ start_sector = chip->last_sector;
+
+ if (ops->oobbuf && !ops->datbuf) {
+ unsigned tmpoobsz = (ops->mode == MTD_OOB_AUTO) ?
+ mtd->oobavail : mtd->oobsize;
+ page_count = DIV_ROUND_UP(ops->ooblen, tmpoobsz);
+ } else if (ops->mode != MTD_OOB_RAW)
+ page_count = ops->len / mtd->writesize;
+ else
+ page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+#if 0 /* yaffs reads more oob data than it needs */
+ if (ops->ooblen >= sectoroobsize * 4) {
+ pr_err("%s: unsupported ops->ooblen, %d\n",
+ __func__, ops->ooblen);
+ return -EINVAL;
+ }
+#endif
+
+#if VERBOSE
+ pr_info("msm_nand_read_oob %llx %p %x %p %x\n",
+ from, ops->datbuf, ops->len, ops->oobbuf, ops->ooblen);
+#endif
+ if (ops->datbuf) {
+ /* memset(ops->datbuf, 0x55, ops->len); */
+ data_dma_addr_curr = data_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(chip->dev, data_dma_addr)) {
+ pr_err("msm_nand_read_oob: failed to get dma addr "
+ "for %p\n", ops->datbuf);
+ return -EIO;
+ }
+ }
+ if (ops->oobbuf) {
+ memset(ops->oobbuf, 0xff, ops->ooblen);
+ oob_dma_addr_curr = oob_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->oobbuf,
+ ops->ooblen, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+ pr_err("msm_nand_read_oob: failed to get dma addr "
+ "for %p\n", ops->oobbuf);
+ err = -EIO;
+ goto err_dma_map_oobbuf_failed;
+ }
+ }
+ if (BITS_TO_LONGS(page_count) > 1) {
+ uncorrected = kzalloc(BITS_TO_LONGS(page_count) * sizeof(long),
+ GFP_NOIO);
+ if (!uncorrected) {
+ err = -ENOMEM;
+ goto err_alloc_uncorrected_failed;
+ }
+ }
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ oob_col = start_sector * 0x210;
+ if (chip->CFG1 & CFG1_WIDE_FLASH)
+ oob_col >>= 1;
+
+ err = 0;
+ while (page_count-- > 0) {
+ cmd = dma_buffer->cmd;
+
+ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
+ if (ops->mode != MTD_OOB_RAW) {
+ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC;
+ dma_buffer->data.cfg0 =
+ (chip->CFG0 & ~(7U << 6)) |
+ ((chip->last_sector - start_sector) << 6);
+ dma_buffer->data.cfg1 = chip->CFG1;
+ } else {
+ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+ dma_buffer->data.cfg0 =
+ (MSM_NAND_CFG0_RAW & ~(7U << 6)) |
+ (chip->last_sector << 6);
+ dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW |
+ (chip->CFG1 & CFG1_WIDE_FLASH);
+ }
+
+ dma_buffer->data.addr0 = (page << 16) | oob_col;
+ /* qc example is (page >> 16) && 0xff !? */
+ dma_buffer->data.addr1 = (page >> 16) & 0xff;
+ /* flash0 + undoc bit */
+ dma_buffer->data.chipsel = 0 | 4;
+
+
+ /* GO bit for the EXEC register */
+ dma_buffer->data.exec = 1;
+
+
+ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result));
+
+ for (n = start_sector; n <= chip->last_sector; n++) {
+ /* flash + buffer status return words */
+ dma_buffer->data.result[n].flash_status = 0xeeeeeeee;
+ dma_buffer->data.result[n].buffer_status = 0xeeeeeeee;
+
+ /* block on cmd ready, then
+ * write CMD / ADDR0 / ADDR1 / CHIPSEL
+ * regs in a burst
+ */
+ cmd->cmd = DST_CRCI_NAND_CMD;
+ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+ cmd->dst = MSM_NAND_FLASH_CMD;
+ if (n == start_sector)
+ cmd->len = 16;
+ else
+ cmd->len = 4;
+ cmd++;
+
+ if (n == start_sector) {
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.cfg0);
+ cmd->dst = MSM_NAND_DEV0_CFG0;
+ cmd->len = 8;
+ cmd++;
+#if SUPPORT_WRONG_ECC_CONFIG
+ if (chip->saved_ecc_buf_cfg !=
+ chip->ecc_buf_cfg) {
+ dma_buffer->data.ecccfg =
+ chip->ecc_buf_cfg;
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.ecccfg);
+ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+ cmd->len = 4;
+ cmd++;
+ }
+#endif
+ }
+
+ /* kick the execute register */
+ cmd->cmd = 0;
+ cmd->src =
+ msm_virt_to_dma(chip, &dma_buffer->data.exec);
+ cmd->dst = MSM_NAND_EXEC_CMD;
+ cmd->len = 4;
+ cmd++;
+
+ /* block on data ready, then
+ * read the status register
+ */
+ cmd->cmd = SRC_CRCI_NAND_DATA;
+ cmd->src = MSM_NAND_FLASH_STATUS;
+ cmd->dst = msm_virt_to_dma(chip,
+ &dma_buffer->data.result[n]);
+ /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */
+ cmd->len = 8;
+ cmd++;
+
+ /* read data block
+ * (only valid if status says success)
+ */
+ if (ops->datbuf) {
+ if (ops->mode != MTD_OOB_RAW)
+ sectordatasize =
+ (n < chip->last_sector) ?
+ 516 : chip->last_sectorsz;
+ else
+ sectordatasize = 528;
+
+ cmd->cmd = 0;
+ cmd->src = MSM_NAND_FLASH_BUFFER;
+ cmd->dst = data_dma_addr_curr;
+ data_dma_addr_curr += sectordatasize;
+ cmd->len = sectordatasize;
+ cmd++;
+ }
+
+ if (ops->oobbuf && (n == chip->last_sector ||
+ ops->mode != MTD_OOB_AUTO)) {
+ cmd->cmd = 0;
+ if (n == chip->last_sector) {
+ cmd->src = MSM_NAND_FLASH_BUFFER +
+ chip->last_sectorsz;
+ sectoroobsize =
+ (chip->last_sector + 1) * 4;
+ if (ops->mode != MTD_OOB_AUTO)
+ sectoroobsize += 10;
+ } else {
+ cmd->src = MSM_NAND_FLASH_BUFFER + 516;
+ sectoroobsize = 10;
+ }
+
+ cmd->dst = oob_dma_addr_curr;
+ if (sectoroobsize < oob_len)
+ cmd->len = sectoroobsize;
+ else
+ cmd->len = oob_len;
+ oob_dma_addr_curr += cmd->len;
+ oob_len -= cmd->len;
+ if (cmd->len > 0)
+ cmd++;
+ }
+ }
+#if SUPPORT_WRONG_ECC_CONFIG
+ if (chip->saved_ecc_buf_cfg != chip->ecc_buf_cfg) {
+ dma_buffer->data.ecccfg_restore =
+ chip->saved_ecc_buf_cfg;
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.ecccfg_restore);
+ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+ cmd->len = 4;
+ cmd++;
+ }
+#endif
+
+ BUILD_BUG_ON(8 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd));
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->cmd[0].cmd |= CMD_OCB;
+ cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3)
+ | CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
+ msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ /* if any of the writes failed (0x10), or there
+ * was a protection violation (0x100), we lose
+ */
+ pageerr = 0;
+ page_corrected = 0;
+ for (n = start_sector; n <= chip->last_sector; n++) {
+ uint32_t buf_stat =
+ dma_buffer->data.result[n].buffer_status;
+ if (buf_stat & BUF_STAT_UNCORRECTABLE) {
+ total_uncorrected++;
+ uncorrected[BIT_WORD(pages_read)] |=
+ BIT_MASK(pages_read);
+ pageerr = -EBADMSG;
+ break;
+ }
+ if (dma_buffer->data.result[n].flash_status & 0x110) {
+ pageerr = -EIO;
+ break;
+ }
+ sector_corrected =buf_stat & BUF_STAT_NUM_ERRS_MASK;
+ page_corrected += sector_corrected;
+ if (sector_corrected > 1)
+ pageerr = -EUCLEAN;
+ }
+ if ((!pageerr && page_corrected) || pageerr == -EUCLEAN) {
+ total_corrected += page_corrected;
+ /* not thread safe */
+ mtd->ecc_stats.corrected += page_corrected;
+ }
+ if (pageerr && (pageerr != -EUCLEAN || err == 0))
+ err = pageerr;
+
+#if VERBOSE
+ pr_info("status: %x %x %x %x %x %x %x %x "
+ "%x %x %x %x %x %x %x %x\n",
+ dma_buffer->data.result[0].flash_status,
+ dma_buffer->data.result[0].buffer_status,
+ dma_buffer->data.result[1].flash_status,
+ dma_buffer->data.result[1].buffer_status,
+ dma_buffer->data.result[2].flash_status,
+ dma_buffer->data.result[2].buffer_status,
+ dma_buffer->data.result[3].flash_status,
+ dma_buffer->data.result[3].buffer_status,
+ dma_buffer->data.result[4].flash_status,
+ dma_buffer->data.result[4].buffer_status,
+ dma_buffer->data.result[5].flash_status,
+ dma_buffer->data.result[5].buffer_status,
+ dma_buffer->data.result[6].flash_status,
+ dma_buffer->data.result[6].buffer_status,
+ dma_buffer->data.result[7].flash_status,
+ dma_buffer->data.result[7].buffer_status);
+#endif
+ if (err && err != -EUCLEAN && err != -EBADMSG)
+ break;
+ pages_read++;
+ page++;
+ }
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+err_alloc_uncorrected_failed:
+ if (ops->oobbuf) {
+ dma_unmap_page(chip->dev, oob_dma_addr,
+ ops->ooblen, DMA_FROM_DEVICE);
+ }
+err_dma_map_oobbuf_failed:
+ if (ops->datbuf) {
+ dma_unmap_page(chip->dev, data_dma_addr,
+ ops->len, DMA_FROM_DEVICE);
+ }
+
+ if (ops->mode != MTD_OOB_RAW)
+ ops->retlen = mtd->writesize * pages_read;
+ else
+ ops->retlen = (mtd->writesize + mtd->oobsize) *
+ pages_read;
+ ops->oobretlen = ops->ooblen - oob_len;
+
+ if (err == -EBADMSG && msm_nand_check_empty(mtd, ops, uncorrected))
+ err = 0;
+ else if (total_uncorrected)
+ mtd->ecc_stats.failed += total_uncorrected; /* not threadsafe */
+ if (uncorrected != &uncorrected_noalloc)
+ kfree(uncorrected);
+
+ if (err)
+ pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n",
+ from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
+ total_corrected);
+ return err;
+}
+
+static int
+msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ /* printk("msm_nand_read %llx %x\n", from, len); */
+
+ ops.mode = MTD_OOB_PLACE;
+ ops.len = len;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_read_oob(mtd, from, &ops);
+ *retlen = ops.retlen;
+ return ret;
+}
+
+static int
+msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
+{
+ struct msm_nand_chip *chip = mtd->priv;
+ struct {
+ dmov_s cmd[8 * 6 + 3];
+ unsigned cmdptr;
+ struct {
+ uint32_t cmd;
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t chipsel;
+ uint32_t cfg0;
+ uint32_t cfg1;
+ uint32_t exec;
+#if SUPPORT_WRONG_ECC_CONFIG
+ uint32_t ecccfg;
+ uint32_t ecccfg_restore;
+#endif
+ uint32_t flash_status[8];
+ uint32_t zeroes;
+ } data;
+ } *dma_buffer;
+ dmov_s *cmd;
+ unsigned n;
+ unsigned page = to >> chip->page_shift;
+ uint32_t oob_len = ops->ooblen;
+ uint32_t sectordatawritesize;
+ int err;
+ dma_addr_t data_dma_addr = 0;
+ dma_addr_t oob_dma_addr = 0;
+ dma_addr_t data_dma_addr_curr = 0;
+ dma_addr_t oob_dma_addr_curr = 0;
+ unsigned page_count;
+ unsigned pages_written = 0;
+
+ if (to & (mtd->writesize - 1)) {
+ pr_err("%s: unsupported to, 0x%llx\n", __func__, to);
+ return -EINVAL;
+ }
+
+ if (ops->mode != MTD_OOB_RAW) {
+ if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) {
+ pr_err("%s: unsupported ops->mode,%d\n",
+ __func__, ops->mode);
+ return -EINVAL;
+ }
+ if ((ops->len % mtd->writesize) != 0) {
+ pr_err("%s: unsupported ops->len, %d\n",
+ __func__, ops->len);
+ return -EINVAL;
+ }
+ } else {
+ if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) {
+ pr_err("%s: unsupported ops->len, "
+ "%d for MTD_OOB_RAW mode\n",
+ __func__, ops->len);
+ return -EINVAL;
+ }
+ }
+
+ if (ops->datbuf == NULL) {
+ pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
+ return -EINVAL;
+ }
+#if 0 /* yaffs writes more oob data than it needs */
+ if (ops->ooblen >= sectoroobsize * 4) {
+ pr_err("%s: unsupported ops->ooblen, %d\n",
+ __func__, ops->ooblen);
+ return -EINVAL;
+ }
+#endif
+ if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
+ pr_err("%s: unsupported ops->ooboffs, %d\n",
+ __func__, ops->ooboffs);
+ return -EINVAL;
+ }
+
+ if (ops->datbuf) {
+ data_dma_addr_curr = data_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->datbuf,
+ ops->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(chip->dev, data_dma_addr)) {
+ pr_err("msm_nand_write_oob: failed to get dma addr "
+ "for %p\n", ops->datbuf);
+ return -EIO;
+ }
+ }
+ if (ops->oobbuf) {
+ oob_dma_addr_curr = oob_dma_addr =
+ msm_nand_dma_map(chip->dev, ops->oobbuf,
+ ops->ooblen, DMA_TO_DEVICE);
+ if (dma_mapping_error(chip->dev, oob_dma_addr)) {
+ pr_err("msm_nand_write_oob: failed to get dma addr "
+ "for %p\n", ops->oobbuf);
+ err = -EIO;
+ goto err_dma_map_oobbuf_failed;
+ }
+ }
+ if (ops->mode != MTD_OOB_RAW)
+ page_count = ops->len / mtd->writesize;
+ else
+ page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+ wait_event(chip->wait_queue, (dma_buffer =
+ msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
+
+ while (page_count-- > 0) {
+ cmd = dma_buffer->cmd;
+
+ /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
+ if (ops->mode != MTD_OOB_RAW) {
+ dma_buffer->data.cfg0 = chip->CFG0;
+ dma_buffer->data.cfg1 = chip->CFG1;
+ } else {
+ dma_buffer->data.cfg0 =
+ (MSM_NAND_CFG0_RAW & ~(7U << 6)) |
+ (chip->last_sector << 6);
+ dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW |
+ (chip->CFG1 & CFG1_WIDE_FLASH);
+ }
+
+ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
+ dma_buffer->data.addr0 = page << 16;
+ dma_buffer->data.addr1 = (page >> 16) & 0xff;
+ dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */
+ dma_buffer->data.zeroes = 0;
+
+
+ /* GO bit for the EXEC register */
+ dma_buffer->data.exec = 1;
+
+ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status));
+
+ for (n = 0; n <= chip->last_sector ; n++) {
+ /* status return words */
+ dma_buffer->data.flash_status[n] = 0xeeeeeeee;
+ /* block on cmd ready, then
+ * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst
+ */
+ cmd->cmd = DST_CRCI_NAND_CMD;
+ cmd->src =
+ msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+ cmd->dst = MSM_NAND_FLASH_CMD;
+ if (n == 0)
+ cmd->len = 16;
+ else
+ cmd->len = 4;
+ cmd++;
+
+ if (n == 0) {
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.cfg0);
+ cmd->dst = MSM_NAND_DEV0_CFG0;
+ cmd->len = 8;
+ cmd++;
+#if SUPPORT_WRONG_ECC_CONFIG
+ if (chip->saved_ecc_buf_cfg !=
+ chip->ecc_buf_cfg) {
+ dma_buffer->data.ecccfg =
+ chip->ecc_buf_cfg;
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.ecccfg);
+ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+ cmd->len = 4;
+ cmd++;
+ }
+#endif
+ }
+
+ /* write data block */
+ if (ops->mode != MTD_OOB_RAW)
+ sectordatawritesize = (n < chip->last_sector) ?
+ 516 : chip->last_sectorsz;
+ else
+ sectordatawritesize = 528;
+
+ cmd->cmd = 0;
+ cmd->src = data_dma_addr_curr;
+ data_dma_addr_curr += sectordatawritesize;
+ cmd->dst = MSM_NAND_FLASH_BUFFER;
+ cmd->len = sectordatawritesize;
+ cmd++;
+
+ if (ops->oobbuf) {
+ if (n == chip->last_sector) {
+ cmd->cmd = 0;
+ cmd->src = oob_dma_addr_curr;
+ cmd->dst = MSM_NAND_FLASH_BUFFER +
+ chip->last_sectorsz;
+ cmd->len = 516 - chip->last_sectorsz;
+ if (oob_len <= cmd->len)
+ cmd->len = oob_len;
+ oob_dma_addr_curr += cmd->len;
+ oob_len -= cmd->len;
+ if (cmd->len > 0)
+ cmd++;
+ }
+ if (ops->mode != MTD_OOB_AUTO) {
+ /* skip ecc bytes in oobbuf */
+ if (oob_len < 10) {
+ oob_dma_addr_curr += 10;
+ oob_len -= 10;
+ } else {
+ oob_dma_addr_curr += oob_len;
+ oob_len = 0;
+ }
+ }
+ }
+
+ /* kick the execute register */
+ cmd->cmd = 0;
+ cmd->src =
+ msm_virt_to_dma(chip, &dma_buffer->data.exec);
+ cmd->dst = MSM_NAND_EXEC_CMD;
+ cmd->len = 4;
+ cmd++;
+
+ /* block on data ready, then
+ * read the status register
+ */
+ cmd->cmd = SRC_CRCI_NAND_DATA;
+ cmd->src = MSM_NAND_FLASH_STATUS;
+ cmd->dst = msm_virt_to_dma(chip,
+ &dma_buffer->data.flash_status[n]);
+ cmd->len = 4;
+ cmd++;
+
+ /* clear the status register in case the OP_ERR is set
+ * due to the write, to work around a h/w bug */
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.zeroes);
+ cmd->dst = MSM_NAND_FLASH_STATUS;
+ cmd->len = 4;
+ cmd++;
+ }
+#if SUPPORT_WRONG_ECC_CONFIG
+ if (chip->saved_ecc_buf_cfg != chip->ecc_buf_cfg) {
+ dma_buffer->data.ecccfg_restore =
+ chip->saved_ecc_buf_cfg;
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip,
+ &dma_buffer->data.ecccfg_restore);
+ cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG;
+ cmd->len = 4;
+ cmd++;
+ }
+#endif
+ dma_buffer->cmd[0].cmd |= CMD_OCB;
+ cmd[-1].cmd |= CMD_OCU | CMD_LC;
+ BUILD_BUG_ON(8 * 6 + 3 != ARRAY_SIZE(dma_buffer->cmd));
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) |
+ CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(chip->dma_channel,
+ DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
+ msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ /* if any of the writes failed (0x10), or there was a
+ * protection violation (0x100), or the program success
+ * bit (0x80) is unset, we lose
+ */
+ err = 0;
+ for (n = 0; n <= chip->last_sector ; n++) {
+ if (dma_buffer->data.flash_status[n] & 0x110) {
+ if (dma_buffer->data.flash_status[n] & 0x10)
+ pr_err("msm_nand: critical write error,"
+ " 0x%x(%d)\n", page, n);
+ err = -EIO;
+ break;
+ }
+ if (!(dma_buffer->data.flash_status[n] & 0x80)) {
+ err = -EIO;
+ break;
+ }
+ }
+
+#if VERBOSE
+ pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n",
+ page, dma_buffer->data.flash_status[0],
+ dma_buffer->data.flash_status[1],
+ dma_buffer->data.flash_status[2],
+ dma_buffer->data.flash_status[3],
+ dma_buffer->data.flash_status[4],
+ dma_buffer->data.flash_status[5],
+ dma_buffer->data.flash_status[6],
+ dma_buffer->data.flash_status[7]);
+#endif
+ if (err)
+ break;
+ pages_written++;
+ page++;
+ }
+ if (ops->mode != MTD_OOB_RAW)
+ ops->retlen = mtd->writesize * pages_written;
+ else
+ ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
+
+ ops->oobretlen = ops->ooblen - oob_len;
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+
+ if (ops->oobbuf)
+ dma_unmap_page(chip->dev, oob_dma_addr,
+ ops->ooblen, DMA_TO_DEVICE);
+err_dma_map_oobbuf_failed:
+ if (ops->datbuf)
+ dma_unmap_page(chip->dev, data_dma_addr,
+ ops->len, DMA_TO_DEVICE);
+ if (err)
+ pr_err("msm_nand_write_oob %llx %x %x failed %d\n",
+ to, ops->len, ops->ooblen, err);
+ return err;
+}
+
+static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OOB_PLACE;
+ ops.len = len;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_write_oob(mtd, to, &ops);
+ *retlen = ops.retlen;
+ return ret;
+}
+
+static int
+msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ int err;
+ struct msm_nand_chip *chip = mtd->priv;
+ struct {
+ dmov_s cmd[5];
+ unsigned cmdptr;
+ unsigned data[9];
+ } *dma_buffer;
+ unsigned page = instr->addr >> chip->page_shift;
+
+ if (instr->addr & (mtd->erasesize - 1)) {
+ pr_err("%s: unsupported erase address, 0x%llx\n",
+ __func__, instr->addr);
+ return -EINVAL;
+ }
+ if (instr->len != mtd->erasesize) {
+ pr_err("%s: unsupported erase len, %lld\n",
+ __func__, instr->len);
+ return -EINVAL;
+ }
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(
+ chip, sizeof(*dma_buffer))));
+
+ dma_buffer->data[0] = MSM_NAND_CMD_BLOCK_ERASE;
+ dma_buffer->data[1] = page;
+ dma_buffer->data[2] = 0;
+ dma_buffer->data[3] = 0 | 4;
+ dma_buffer->data[4] = 1;
+ dma_buffer->data[5] = 0xeeeeeeee;
+ dma_buffer->data[6] = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
+ dma_buffer->data[7] = chip->CFG1;
+ dma_buffer->data[8] = 0;
+ BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data) - 1);
+
+ dma_buffer->cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
+ dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
+ dma_buffer->cmd[0].dst = MSM_NAND_FLASH_CMD;
+ dma_buffer->cmd[0].len = 16;
+
+ dma_buffer->cmd[1].cmd = 0;
+ dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
+ dma_buffer->cmd[1].dst = MSM_NAND_DEV0_CFG0;
+ dma_buffer->cmd[1].len = 8;
+
+ dma_buffer->cmd[2].cmd = 0;
+ dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[4]);
+ dma_buffer->cmd[2].dst = MSM_NAND_EXEC_CMD;
+ dma_buffer->cmd[2].len = 4;
+
+ dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA;
+ dma_buffer->cmd[3].src = MSM_NAND_FLASH_STATUS;
+ dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[5]);
+ dma_buffer->cmd[3].len = 4;
+
+ /* clear the status register in case the OP_ERR is set
+ * due to the write, to work around a h/w bug */
+ dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC;
+ dma_buffer->cmd[4].src = msm_virt_to_dma(chip, &dma_buffer->data[8]);
+ dma_buffer->cmd[4].dst = MSM_NAND_FLASH_STATUS;
+ dma_buffer->cmd[4].len = 4;
+
+ BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1);
+
+ dma_buffer->cmdptr =
+ (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(
+ chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ /* we fail if there was an operation error, a mpu error, or the
+ * erase success bit was not set.
+ */
+
+ if (dma_buffer->data[5] & 0x110 || !(dma_buffer->data[5] & 0x80)) {
+ if (dma_buffer->data[5] & 0x10)
+ pr_warning("msm_nand: critical erase error, 0x%llx\n",
+ instr->addr);
+ err = -EIO;
+ } else
+ err = 0;
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+ if (err) {
+ pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr);
+ instr->fail_addr = instr->addr;
+ instr->state = MTD_ERASE_FAILED;
+ } else {
+ instr->state = MTD_ERASE_DONE;
+ instr->fail_addr = 0xffffffff;
+ mtd_erase_callback(instr);
+ }
+ return err;
+}
+
+static int
+msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct msm_nand_chip *chip = mtd->priv;
+ int ret;
+ struct {
+ dmov_s cmd[5];
+ unsigned cmdptr;
+ struct {
+ uint32_t cmd;
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t chipsel;
+ uint32_t cfg0;
+ uint32_t cfg1;
+ uint32_t exec;
+ uint32_t ecccfg;
+ struct {
+ uint32_t flash_status;
+ uint32_t buffer_status;
+ } result;
+ } data;
+ } *dma_buffer;
+ dmov_s *cmd;
+ uint8_t *buf;
+ unsigned page = ofs >> chip->page_shift;
+
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+ if (ofs & (mtd->erasesize - 1)) {
+ pr_err("%s: unsupported block address, 0x%x\n",
+ __func__, (uint32_t)ofs);
+ return -EINVAL;
+ }
+
+ wait_event(chip->wait_queue,
+ (dma_buffer = msm_nand_get_dma_buffer(chip ,
+ sizeof(*dma_buffer) + 4)));
+ buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
+
+ /* Read 4 bytes starting from the bad block marker location
+ * in the last code word of the page
+ */
+
+ cmd = dma_buffer->cmd;
+
+ dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
+ dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW & ~(7U << 6);
+ dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH);
+
+ if (chip->CFG1 & CFG1_WIDE_FLASH)
+ dma_buffer->data.addr0 = (page << 16) |
+ ((528 * chip->last_sector) >> 1);
+ else
+ dma_buffer->data.addr0 = (page << 16) |
+ (528 * chip->last_sector);
+
+ dma_buffer->data.addr1 = (page >> 16) & 0xff;
+ dma_buffer->data.chipsel = 0 | 4;
+
+ dma_buffer->data.exec = 1;
+
+ dma_buffer->data.result.flash_status = 0xeeeeeeee;
+ dma_buffer->data.result.buffer_status = 0xeeeeeeee;
+
+ cmd->cmd = DST_CRCI_NAND_CMD;
+ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd);
+ cmd->dst = MSM_NAND_FLASH_CMD;
+ cmd->len = 16;
+ cmd++;
+
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0);
+ cmd->dst = MSM_NAND_DEV0_CFG0;
+ cmd->len = 8;
+ cmd++;
+
+ cmd->cmd = 0;
+ cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec);
+ cmd->dst = MSM_NAND_EXEC_CMD;
+ cmd->len = 4;
+ cmd++;
+
+ cmd->cmd = SRC_CRCI_NAND_DATA;
+ cmd->src = MSM_NAND_FLASH_STATUS;
+ cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result);
+ cmd->len = 8;
+ cmd++;
+
+ cmd->cmd = 0;
+ cmd->src = MSM_NAND_FLASH_BUFFER +
+ (mtd->writesize - 528 * chip->last_sector);
+ cmd->dst = msm_virt_to_dma(chip, buf);
+ cmd->len = 4;
+ cmd++;
+
+ BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd));
+ BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+ dma_buffer->cmd[0].cmd |= CMD_OCB;
+ cmd[-1].cmd |= CMD_OCU | CMD_LC;
+
+ dma_buffer->cmdptr = (msm_virt_to_dma(chip,
+ dma_buffer->cmd) >> 3) | CMD_PTR_LP;
+
+ msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
+
+ ret = 0;
+ if (dma_buffer->data.result.flash_status & 0x110)
+ ret = -EIO;
+
+ if (!ret) {
+ /* Check for bad block marker byte */
+ if (chip->CFG1 & CFG1_WIDE_FLASH) {
+ if (buf[0] != 0xFF || buf[1] != 0xFF)
+ ret = 1;
+ } else {
+ if (buf[0] != 0xFF)
+ ret = 1;
+ }
+ }
+
+ msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4);
+ return ret;
+}
+
+
+static int
+msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct mtd_oob_ops ops;
+ int ret;
+ uint8_t *buf;
+
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+ if (ofs & (mtd->erasesize - 1)) {
+ pr_err("%s: unsupported block address, 0x%x\n",
+ __func__, (uint32_t)ofs);
+ return -EINVAL;
+ }
+
+ /*
+ Write all 0s to the first page
+ This will set the BB marker to 0
+ */
+
+ /* Use the already existing zero page */
+ buf = page_address(ZERO_PAGE());
+
+ ops.mode = MTD_OOB_RAW;
+ ops.len = mtd->writesize + mtd->oobsize;
+ ops.retlen = 0;
+ ops.ooblen = 0;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ret = msm_nand_write_oob(mtd, ofs, &ops);
+
+ return ret;
+}
+
+/**
+ * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash
+ * @param mtd MTD device structure
+ */
+static int msm_nand_suspend(struct mtd_info *mtd)
+{
+ return 0;
+}
+
+/**
+ * msm_nand_resume - [MTD Interface] Resume the msm_nand flash
+ * @param mtd MTD device structure
+ */
+static void msm_nand_resume(struct mtd_info *mtd)
+{
+}
+
+
+/**
+ * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
+ * @param mtd MTD device structure
+ * @param maxchips Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ */
+int msm_nand_scan(struct mtd_info *mtd, int maxchips)
+{
+ unsigned n;
+ struct msm_nand_chip *chip = mtd->priv;
+ uint32_t flash_id;
+ uint32_t manid;
+ uint32_t devid;
+ uint32_t devcfg;
+ uint32_t busw16;
+ struct nand_flash_dev *flashdev = NULL;
+ struct nand_manufacturers *flashman = NULL;
+
+ if (flash_read_config(chip)) {
+ pr_err("ERRROR: could not save CFG0 & CFG1 state\n");
+ return -ENODEV;
+ }
+ pr_info("msm_nand: NAND_READ_ID = %x\n",
+ flash_rd_reg(chip, MSM_NAND_READ_ID));
+ flash_wr_reg(chip, MSM_NAND_READ_ID, 0x12345678);
+
+ flash_id = flash_read_id(chip);
+ manid = flash_id & 0xff;
+ devid = (flash_id >> 8) & 0xff;
+ devcfg = (flash_id >> 24) & 0xff;
+
+ for (n = 0; !flashman && nand_manuf_ids[n].id; ++n)
+ if (nand_manuf_ids[n].id == manid)
+ flashman = &nand_manuf_ids[n];
+ for (n = 0; !flashdev && nand_flash_ids[n].id; ++n)
+ if (nand_flash_ids[n].id == devid)
+ flashdev = &nand_flash_ids[n];
+ if (!flashdev || !flashman) {
+ pr_err("ERROR: unknown nand device manuf=%x devid=%x\n",
+ manid, devid);
+ return -ENOENT;
+ }
+
+ if (!flashdev->pagesize) {
+ mtd->erasesize = (64 * 1024) << ((devcfg >> 4) & 0x3);
+ mtd->writesize = 1024 << (devcfg & 0x3);
+ mtd->oobsize = (8 << ((devcfg >> 2) & 1)) *
+ (mtd->writesize / 512);
+ busw16 = devcfg & (1 << 6) ? CFG1_WIDE_FLASH : 0;
+ } else {
+ mtd->writesize = flashdev->pagesize;
+ mtd->erasesize = flashdev->erasesize;
+ mtd->oobsize = flashdev->pagesize / 32;
+ busw16 = flashdev->options & NAND_BUSWIDTH_16 ?
+ CFG1_WIDE_FLASH : 0;
+ }
+ mtd->size = flashdev->chipsize << 20;
+ pr_info("msm_nand: manuf %s (0x%x) device 0x%x blocksz %x pagesz %x "
+ "size %llx\n", flashman->name, flashman->id, flashdev->id,
+ mtd->erasesize, mtd->writesize, mtd->size);
+
+ if (mtd->writesize == 2048) {
+ chip->page_shift = 11;
+ } else if (mtd->writesize == 4096) {
+ chip->page_shift = 12;
+ } else {
+ pr_err("%s: Unsupported page size (%d)\n", __func__,
+ mtd->writesize);
+ return -EINVAL;
+ }
+
+ chip->last_sector = (mtd->writesize / 512) - 1;
+ chip->last_sectorsz = mtd->writesize - chip->last_sector * 516;
+
+ if (mtd->oobsize == 64) {
+ mtd->ecclayout = &msm_nand_oob_64;
+ } else if (mtd->oobsize == 128) {
+ mtd->ecclayout = &msm_nand_oob_128;
+ } else {
+ pr_err("%s: Unsupported oob size (%d)\n", __func__,
+ mtd->oobsize);
+ return -EINVAL;
+ }
+ mtd->oobavail = mtd->ecclayout->oobavail;
+
+ chip->CFG0 = (chip->last_sector << 6) /* codewords per page */
+ | (516 << 9) /* 516 user data bytes */
+ | (10 << 19) /* 10 parity bytes */
+ | (5 << 27) /* 5 address cycles */
+ | (1 << 30) /* Read status before data */
+ | (1 << 31) /* Send read cmd */
+ /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */
+ | ((busw16 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23));
+ chip->CFG1 = (0 << 0) /* Enable ecc */
+ | (7 << 2) /* 8 recovery cycles */
+ | (0 << 5) /* Allow CS deassertion */
+ | ((mtd->writesize - (528 * chip->last_sector) + 1) << 6)
+ /* Bad block marker location */
+ | (0 << 16) /* Bad block in user data area */
+ | (2 << 17) /* 6 cycle tWB/tRB */
+ | (busw16 & CFG1_WIDE_FLASH); /* preserve wide flag */
+
+ pr_info("msm_nand: save CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1);
+ pr_info("msm_nand: CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d "
+ "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7,
+ (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15,
+ (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7);
+
+ n = flash_rd_reg(chip, MSM_NAND_DEV_CMD1);
+ pr_info("msm_nand: DEV_CMD1: %x\n", n);
+
+ n = flash_rd_reg(chip, MSM_NAND_EBI2_ECC_BUF_CFG);
+ pr_info("msm_nand: NAND_EBI2_ECC_BUF_CFG: %x\n", n);
+
+#if SUPPORT_WRONG_ECC_CONFIG
+ chip->ecc_buf_cfg = 0x203;
+ chip->saved_ecc_buf_cfg = n;
+#endif
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ /* mtd->ecctype = MTD_ECC_SW; */
+ mtd->erase = msm_nand_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = msm_nand_read;
+ mtd->write = msm_nand_write;
+ mtd->read_oob = msm_nand_read_oob;
+ mtd->write_oob = msm_nand_write_oob;
+ /* mtd->sync = msm_nand_sync; */
+ mtd->lock = NULL;
+ /* mtd->unlock = msm_nand_unlock; */
+ mtd->suspend = msm_nand_suspend;
+ mtd->resume = msm_nand_resume;
+ mtd->block_isbad = msm_nand_block_isbad;
+ mtd->block_markbad = msm_nand_block_markbad;
+ mtd->owner = THIS_MODULE;
+
+ /* Unlock whole block */
+ /* msm_nand_unlock_all(mtd); */
+
+ /* return this->scan_bbt(mtd); */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(msm_nand_scan);
+
+/**
+ * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device
+ * @param mtd MTD device structure
+ */
+void msm_nand_release(struct mtd_info *mtd)
+{
+ /* struct msm_nand_chip *this = mtd->priv; */
+
+#ifdef CONFIG_MTD_PARTITIONS
+ /* Deregister partitions */
+ del_mtd_partitions(mtd);
+#endif
+ /* Deregister the device */
+ del_mtd_device(mtd);
+}
+EXPORT_SYMBOL_GPL(msm_nand_release);
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL, };
+#endif
+
+struct msm_nand_info {
+ struct mtd_info mtd;
+ struct mtd_partition *parts;
+ struct msm_nand_chip msm_nand;
+};
+
+static int __devinit msm_nand_probe(struct platform_device *pdev)
+{
+ struct msm_nand_info *info;
+ struct flash_platform_data *pdata = pdev->dev.platform_data;
+ int err;
+ int i;
+
+ if (pdev->num_resources != 1) {
+ pr_err("invalid num_resources");
+ return -ENODEV;
+ }
+ if (pdev->resource[0].flags != IORESOURCE_DMA) {
+ pr_err("invalid resource type");
+ return -ENODEV;
+ }
+
+ info = kzalloc(sizeof(struct msm_nand_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->msm_nand.dev = &pdev->dev;
+
+ init_waitqueue_head(&info->msm_nand.wait_queue);
+
+ info->msm_nand.dma_channel = pdev->resource[0].start;
+ /* this currently fails if dev is passed in */
+ info->msm_nand.dma_buffer =
+ dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE,
+ &info->msm_nand.dma_addr, GFP_KERNEL);
+ if (info->msm_nand.dma_buffer == NULL) {
+ err = -ENOMEM;
+ goto out_free_info;
+ }
+
+ pr_info("msm_nand: allocated dma buffer at %p, dma_addr %x\n",
+ info->msm_nand.dma_buffer, info->msm_nand.dma_addr);
+
+ info->mtd.name = dev_name(&pdev->dev);
+ info->mtd.priv = &info->msm_nand;
+ info->mtd.owner = THIS_MODULE;
+
+ if (msm_nand_scan(&info->mtd, 1)) {
+ err = -ENXIO;
+ goto out_free_dma_buffer;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
+ if (err > 0)
+ add_mtd_partitions(&info->mtd, info->parts, err);
+ else if (err <= 0 && pdata && pdata->parts) {
+ for (i = 0; i < pdata->nr_parts; ++i) {
+ pdata->parts[i].offset *= info->mtd.erasesize;
+ pdata->parts[i].size *= info->mtd.erasesize;
+ }
+ add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+ } else
+#endif
+ err = add_mtd_device(&info->mtd);
+
+ dev_set_drvdata(&pdev->dev, info);
+
+ return 0;
+
+out_free_dma_buffer:
+ dma_free_coherent(/*dev*/ NULL, SZ_4K, info->msm_nand.dma_buffer,
+ info->msm_nand.dma_addr);
+out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int __devexit msm_nand_remove(struct platform_device *pdev)
+{
+ struct msm_nand_info *info = dev_get_drvdata(&pdev->dev);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ if (info) {
+#ifdef CONFIG_MTD_PARTITIONS
+ if (info->parts)
+ del_mtd_partitions(&info->mtd);
+ else
+#endif
+ del_mtd_device(&info->mtd);
+
+ msm_nand_release(&info->mtd);
+ dma_free_coherent(/*dev*/ NULL, SZ_4K,
+ info->msm_nand.dma_buffer,
+ info->msm_nand.dma_addr);
+ kfree(info);
+ }
+
+ return 0;
+}
+
+#define DRIVER_NAME "msm_nand"
+
+static struct platform_driver msm_nand_driver = {
+ .probe = msm_nand_probe,
+ .remove = __devexit_p(msm_nand_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ }
+};
+
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init msm_nand_init(void)
+{
+ return platform_driver_register(&msm_nand_driver);
+}
+
+static void __exit msm_nand_exit(void)
+{
+ platform_driver_unregister(&msm_nand_driver);
+}
+
+module_init(msm_nand_init);
+module_exit(msm_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("msm_nand flash driver code");
diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h
new file mode 100644
index 0000000..18893f1
--- /dev/null
+++ b/drivers/mtd/devices/msm_nand.h
@@ -0,0 +1,71 @@
+/* drivers/mtd/devices/msm_nand.h
+ *
+ * Copyright (C) 2007 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.
+ *
+ */
+
+#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H
+#define __DRIVERS_MTD_DEVICES_MSM_NAND_H
+
+#include <mach/msm_iomap.h>
+
+#define MSM_NAND_REG(off) (MSM_NAND_PHYS + (off))
+
+#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000)
+#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004)
+#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008)
+#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C)
+#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010)
+#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014)
+#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018)
+#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020)
+#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024)
+#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030)
+#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034)
+#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040)
+#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044)
+#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050)
+#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054)
+#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058)
+#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060)
+#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064)
+#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070)
+#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074)
+#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078)
+#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C)
+#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080)
+#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084)
+#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088)
+#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0)
+#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4)
+#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8)
+#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC)
+#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0)
+#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0)
+#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100)
+
+/* device commands */
+
+#define MSM_NAND_CMD_SOFT_RESET 0x01
+#define MSM_NAND_CMD_PAGE_READ 0x32
+#define MSM_NAND_CMD_PAGE_READ_ECC 0x33
+#define MSM_NAND_CMD_PAGE_READ_ALL 0x34
+#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15
+#define MSM_NAND_CMD_PRG_PAGE 0x36
+#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37
+#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39
+#define MSM_NAND_CMD_BLOCK_ERASE 0x3A
+#define MSM_NAND_CMD_FETCH_ID 0x0B
+#define MSM_NAND_CMD_STATUS 0x0C
+#define MSM_NAND_CMD_RESET 0x0D
+
+#endif
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 416d7d2..67f927b 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -3292,6 +3292,21 @@
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
+config MSM_RMNET
+ tristate "MSM RMNET Virtual Network Device"
+ depends on ARCH_MSM
+ default y
+ help
+ Virtual ethernet interface for MSM RMNET transport.
+
+config MSM_RMNET_DEBUG
+ bool "MSM RMNET debug interface"
+ depends on MSM_RMNET
+ default n
+ help
+ Debug stats on wakeup counts.
+
+
config NETCONSOLE_DYNAMIC
bool "Dynamic reconfiguration of logging targets"
depends on NETCONSOLE && SYSFS
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 79bc9ca..a4fcb6a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -289,6 +289,8 @@
obj-$(CONFIG_FS_ENET) += fs_enet/
obj-$(CONFIG_NETXEN_NIC) += netxen/
+
+obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o
obj-$(CONFIG_NIU) += niu.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_SFC) += sfc/
diff --git a/drivers/net/msm_rmnet.c b/drivers/net/msm_rmnet.c
new file mode 100644
index 0000000..8a8173a
--- /dev/null
+++ b/drivers/net/msm_rmnet.c
@@ -0,0 +1,466 @@
+/* linux/drivers/net/msm_rmnet.c
+ *
+ * Virtual Ethernet Interface for MSM7K Networking
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <mach/msm_smd.h>
+
+/* XXX should come from smd headers */
+#define SMD_PORT_ETHER0 11
+#define POLL_DELAY 1000000 /* 1 second delay interval */
+
+struct rmnet_private
+{
+ smd_channel_t *ch;
+ struct net_device_stats stats;
+ const char *chname;
+ struct wake_lock wake_lock;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ ktime_t last_packet;
+ short active_countdown; /* Number of times left to check */
+ short restart_count; /* Number of polls seems so far */
+ unsigned long wakeups_xmit;
+ unsigned long wakeups_rcv;
+ unsigned long timeout_us;
+ unsigned long awake_time_ms;
+ struct delayed_work work;
+#endif
+};
+
+static int count_this_packet(void *_hdr, int len)
+{
+ struct ethhdr *hdr = _hdr;
+
+ if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+ return 0;
+
+ return 1;
+}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static int in_suspend;
+static unsigned long timeout_us;
+static struct workqueue_struct *rmnet_wq;
+
+static void do_check_active(struct work_struct *work)
+{
+ struct rmnet_private *p =
+ container_of(work, struct rmnet_private, work.work);
+
+ /*
+ * Soft timers do not wake the cpu from suspend.
+ * If we are in suspend, do_check_active is only called once at the
+ * timeout time instead of polling at POLL_DELAY interval. Otherwise the
+ * cpu will sleeps and the timer can fire much much later than POLL_DELAY
+ * casuing a skew in time calculations.
+ */
+ if (in_suspend) {
+ /*
+ * Assume for N packets sent durring this session, they are
+ * uniformly distributed durring the timeout window.
+ */
+ int tmp = p->timeout_us * 2 -
+ (p->timeout_us / (p->active_countdown + 1));
+ tmp /= 1000;
+ p->awake_time_ms += tmp;
+
+ p->active_countdown = p->restart_count = 0;
+ return;
+ }
+
+ /*
+ * Poll if not in suspend, since this gives more accurate tracking of
+ * rmnet sessions.
+ */
+ p->restart_count++;
+ if (--p->active_countdown == 0) {
+ p->awake_time_ms += p->restart_count * POLL_DELAY / 1000;
+ p->restart_count = 0;
+ } else {
+ queue_delayed_work(rmnet_wq, &p->work,
+ usecs_to_jiffies(POLL_DELAY));
+ }
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d, struct device_attribute *attr, const char *buf, size_t n)
+{
+ timeout_suspend_us = simple_strtoul(buf, NULL, 10);
+ return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show, timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler) {
+ if (rmnet0) {
+ struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+ p->timeout_us = timeout_suspend_us;
+ }
+ in_suspend = 1;
+}
+
+static void rmnet_late_resume(struct early_suspend *handler) {
+ if (rmnet0) {
+ struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+ p->timeout_us = timeout_us;
+ }
+ in_suspend = 0;
+}
+
+static struct early_suspend rmnet_power_suspend = {
+ .suspend = rmnet_early_suspend,
+ .resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+ register_early_suspend(&rmnet_power_suspend);
+ return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p) {
+ int ret = 0;
+ ktime_t now;
+ if (p->timeout_us == 0) /* Check if disabled */
+ return 0;
+
+ /* Start timer on a wakeup packet */
+ if (p->active_countdown == 0) {
+ ret = 1;
+ now = ktime_get_real();
+ p->last_packet = now;
+ if (in_suspend)
+ queue_delayed_work(rmnet_wq, &p->work,
+ usecs_to_jiffies(p->timeout_us));
+ else
+ queue_delayed_work(rmnet_wq, &p->work,
+ usecs_to_jiffies(POLL_DELAY));
+ }
+
+ if (in_suspend)
+ p->active_countdown++;
+ else
+ p->active_countdown = p->timeout_us / POLL_DELAY;
+
+ return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ return sprintf(buf, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ return sprintf(buf, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ p->timeout_us = timeout_us = simple_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+ timeout_us = simple_strtoul(buf, NULL, 10);
+#endif
+ return n;
+}
+
+static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ p = netdev_priv(to_net_dev(d));
+ return sprintf(buf, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+
+/* Show total radio awake time in ms */
+static ssize_t awake_time_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ return sprintf(buf, "%lu\n", p->awake_time_ms);
+}
+DEVICE_ATTR(awake_time_ms, 0444, awake_time_show, NULL);
+
+#endif
+
+/* Called in soft-irq context */
+static void smd_net_data_handler(unsigned long arg)
+{
+ struct net_device *dev = (struct net_device *) arg;
+ struct rmnet_private *p = netdev_priv(dev);
+ struct sk_buff *skb;
+ void *ptr = 0;
+ int sz;
+
+ for (;;) {
+ sz = smd_cur_packet_size(p->ch);
+ if (sz == 0) break;
+ if (smd_read_avail(p->ch) < sz) break;
+
+ if (sz > 1514) {
+ pr_err("rmnet_recv() discarding %d len\n", sz);
+ ptr = 0;
+ } else {
+ skb = dev_alloc_skb(sz + NET_IP_ALIGN);
+ if (skb == NULL) {
+ pr_err("rmnet_recv() cannot allocate skb\n");
+ } else {
+ skb->dev = dev;
+ skb_reserve(skb, NET_IP_ALIGN);
+ ptr = skb_put(skb, sz);
+ wake_lock_timeout(&p->wake_lock, HZ / 2);
+ if (smd_read(p->ch, ptr, sz) != sz) {
+ pr_err("rmnet_recv() smd lied about avail?!");
+ ptr = 0;
+ dev_kfree_skb_irq(skb);
+ } else {
+ skb->protocol = eth_type_trans(skb, dev);
+ if (count_this_packet(ptr, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->wakeups_rcv +=
+ rmnet_cause_wakeup(p);
+#endif
+ p->stats.rx_packets++;
+ p->stats.rx_bytes += skb->len;
+ }
+ netif_rx(skb);
+ }
+ continue;
+ }
+ }
+ if (smd_read(p->ch, ptr, sz) != sz)
+ pr_err("rmnet_recv() smd lied about avail?!");
+ }
+}
+
+static DECLARE_TASKLET(smd_net_data_tasklet, smd_net_data_handler, 0);
+
+static void smd_net_notify(void *_dev, unsigned event)
+{
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ smd_net_data_tasklet.data = (unsigned long) _dev;
+
+ tasklet_schedule(&smd_net_data_tasklet);
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+ int r;
+ struct rmnet_private *p = netdev_priv(dev);
+
+ pr_info("rmnet_open()\n");
+ if (!p->ch) {
+ r = smd_open(p->chname, &p->ch, dev, smd_net_notify);
+
+ if (r < 0)
+ return -ENODEV;
+ }
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int rmnet_stop(struct net_device *dev)
+{
+ pr_info("rmnet_stop()\n");
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ smd_channel_t *ch = p->ch;
+
+ if (smd_write_atomic(ch, skb->data, skb->len) != skb->len) {
+ pr_err("rmnet fifo full, dropping packet\n");
+ } else {
+ if (count_this_packet(skb->data, skb->len)) {
+ p->stats.tx_packets++;
+ p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+ }
+ }
+
+ dev_kfree_skb_irq(skb);
+ return 0;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ return &p->stats;
+}
+
+static void rmnet_set_multicast_list(struct net_device *dev)
+{
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+ pr_info("rmnet_tx_timeout()\n");
+}
+
+static struct net_device_ops rmnet_ops = {
+ .ndo_open = rmnet_open,
+ .ndo_stop = rmnet_stop,
+ .ndo_start_xmit = rmnet_xmit,
+ .ndo_get_stats = rmnet_get_stats,
+ .ndo_set_multicast_list = rmnet_set_multicast_list,
+ .ndo_tx_timeout = rmnet_tx_timeout,
+};
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+ dev->netdev_ops = &rmnet_ops;
+
+ dev->watchdog_timeo = 20; /* ??? */
+
+ ether_setup(dev);
+
+ //dev->change_mtu = 0; /* ??? */
+
+ random_ether_addr(dev->dev_addr);
+}
+
+
+static const char *ch_name[3] = {
+ "SMD_DATA5",
+ "SMD_DATA6",
+ "SMD_DATA7",
+};
+
+static int __init rmnet_init(void)
+{
+ int ret;
+ struct device *d;
+ struct net_device *dev;
+ struct rmnet_private *p;
+ unsigned n;
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ timeout_suspend_us = 0;
+#endif
+#endif
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ rmnet_wq = create_workqueue("rmnet");
+#endif
+
+ for (n = 0; n < 3; n++) {
+ dev = alloc_netdev(sizeof(struct rmnet_private),
+ "rmnet%d", rmnet_setup);
+
+ if (!dev)
+ return -ENOMEM;
+
+ d = &(dev->dev);
+ p = netdev_priv(dev);
+ p->chname = ch_name[n];
+ wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->timeout_us = timeout_us;
+ p->awake_time_ms = p->wakeups_xmit = p->wakeups_rcv = 0;
+ p->active_countdown = p->restart_count = 0;
+ INIT_DELAYED_WORK_DEFERRABLE(&p->work, do_check_active);
+#endif
+
+ ret = register_netdev(dev);
+ if (ret) {
+ free_netdev(dev);
+ return ret;
+ }
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ if (device_create_file(d, &dev_attr_timeout))
+ continue;
+ if (device_create_file(d, &dev_attr_wakeups_xmit))
+ continue;
+ if (device_create_file(d, &dev_attr_wakeups_rcv))
+ continue;
+ if (device_create_file(d, &dev_attr_awake_time_ms))
+ continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (device_create_file(d, &dev_attr_timeout_suspend))
+ continue;
+
+ /* Only care about rmnet0 for suspend/resume tiemout hooks. */
+ if (n == 0)
+ rmnet0 = d;
+#endif
+#endif
+ }
+ return 0;
+}
+
+module_init(rmnet_init);
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 8d2772c..e42d463 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -322,6 +322,20 @@
#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
#define SMC_IRQ_FLAGS (-1) /* from resource */
+#elif defined(CONFIG_ARCH_MSM)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH
+
#elif defined(CONFIG_MN10300)
/*
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8e9ba17..dff266b4 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -76,6 +76,12 @@
Say Y here to enable support for the DS2782/DS2786 standalone battery
gas-gauge.
+config BATTERY_DS2784
+ tristate "DS2784 battery driver "
+ select W1
+ help
+ Say Y here to enable support for batteries with ds2784 chip.
+
config BATTERY_PMU
tristate "Apple PMU battery"
depends on PPC32 && ADB_PMU
@@ -142,4 +148,10 @@
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
+config CHARGER_PM8058
+ bool "Qualcomm PM8058 pmic charger driver"
+ depends on PM8058
+ help
+ Say Y to include support for the pm8058 charge controller
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0005080..785ae2a 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -24,6 +24,7 @@
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
+obj-$(CONFIG_BATTERY_DS2784) += ds2784_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
@@ -34,3 +35,4 @@
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_CHARGER_PM8058) += pm8058-charger.o
diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c
new file mode 100755
index 0000000..bc1b4d7
--- /dev/null
+++ b/drivers/power/ds2784_battery.c
@@ -0,0 +1,757 @@
+/* drivers/power/ds2784_battery.c
+ *
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (C) 2009 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/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/android_alarm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/ds2784_battery.h>
+
+#include "../w1/w1.h"
+#include "w1_ds2784.h"
+
+extern int is_ac_power_supplied(void);
+
+struct battery_status {
+ int timestamp;
+
+ int voltage_uV; /* units of uV */
+ int current_uA; /* units of uA */
+ int current_avg_uA;
+ int charge_uAh;
+
+ u16 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;
+} __attribute__((packed));
+
+
+#define SOURCE_NONE 0
+#define SOURCE_USB 1
+#define SOURCE_AC 2
+
+#define CHARGE_OFF 0
+#define CHARGE_SLOW 1
+#define CHARGE_FAST 2
+#define CHARGE_BATT_DISABLE 3 /* disable charging at battery */
+
+#define TEMP_CRITICAL 600 /* no charging at all */
+#define TEMP_HOT 500 /* no fast charge, no charge > 4.1v */
+#define TEMP_WARM 450 /* no fast charge above this */
+
+#define TEMP_HOT_MAX_MV 4100 /* stop charging here when hot */
+#define TEMP_HOT_MIN_MV 3800 /* resume charging here when hot */
+#define CE_DISABLE_MIN_MV 4100
+
+#define BATTERY_LOG_MAX 1024
+#define BATTERY_LOG_MASK (BATTERY_LOG_MAX - 1)
+
+/* When we're awake or running on wall power, sample the battery
+ * gauge every FAST_POLL seconds. If we're asleep and on battery
+ * power, sample every SLOW_POLL seconds
+ */
+#define FAST_POLL (1 * 60)
+#define SLOW_POLL (10 * 60)
+
+static DEFINE_MUTEX(battery_log_lock);
+static struct battery_status battery_log[BATTERY_LOG_MAX];
+static unsigned battery_log_head;
+static unsigned battery_log_tail;
+
+void battery_log_status(struct battery_status *s)
+{
+ unsigned n;
+ mutex_lock(&battery_log_lock);
+ n = battery_log_head;
+ memcpy(battery_log + n, s, sizeof(struct battery_status));
+ n = (n + 1) & BATTERY_LOG_MASK;
+ if (n == battery_log_tail)
+ battery_log_tail = (battery_log_tail + 1) & BATTERY_LOG_MASK;
+ battery_log_head = n;
+ mutex_unlock(&battery_log_lock);
+}
+
+static const char *battery_source[3] = { "none", " usb", " ac" };
+static const char *battery_mode[4] = { " off", "slow", "fast", "full" };
+
+static int battery_log_print(struct seq_file *sf, void *private)
+{
+ unsigned n;
+ mutex_lock(&battery_log_lock);
+ seq_printf(sf, "timestamp mV mA avg mA uAh dC %% src mode reg full\n");
+ for (n = battery_log_tail; n != battery_log_head; n = (n + 1) & BATTERY_LOG_MASK) {
+ struct battery_status *s = battery_log + n;
+ seq_printf(sf, "%9d %5d %6d %6d %8d %4d %3d %s %s 0x%02x %d\n",
+ s->timestamp, s->voltage_uV / 1000,
+ s->current_uA / 1000, s->current_avg_uA / 1000,
+ s->charge_uAh, s->temp_C,
+ s->percentage,
+ battery_source[s->charge_source],
+ battery_mode[s->charge_mode],
+ s->status_reg, s->battery_full);
+ }
+ mutex_unlock(&battery_log_lock);
+ return 0;
+}
+
+
+struct ds2784_device_info {
+ struct device *dev;
+
+ /* DS2784 data, valid after calling ds2784_battery_read_status() */
+ char raw[DS2784_DATA_SIZE]; /* raw DS2784 data */
+
+ struct battery_status status;
+
+ struct power_supply bat;
+ struct workqueue_struct *monitor_wqueue;
+ struct work_struct monitor_work;
+ struct alarm alarm;
+ struct wake_lock work_wake_lock;
+
+ int (*charge)(int on, int fast);
+ struct w1_slave *w1_slave;
+
+ u8 dummy; /* dummy battery flag */
+ u8 last_charge_mode; /* previous charger state */
+ u8 slow_poll;
+
+ ktime_t last_poll;
+ ktime_t last_charge_seen;
+};
+
+#define psy_to_dev_info(x) container_of((x), struct ds2784_device_info, bat)
+
+static struct wake_lock vbus_wake_lock;
+
+#define BATT_RSNSP (67) /*Passion battery source 1*/
+
+static enum power_supply_property battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+};
+
+static int battery_initial;
+
+static int battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static void battery_ext_power_changed(struct power_supply *psy);
+
+#define to_ds2784_device_info(x) container_of((x), struct ds2784_device_info, \
+ bat);
+
+static void ds2784_parse_data(u8 *raw, struct battery_status *s)
+{
+ short n;
+
+ /* Get status reg */
+ s->status_reg = raw[DS2784_REG_STS];
+
+ /* Get Level */
+ s->percentage = raw[DS2784_REG_RARC];
+
+ /* Get Voltage: Unit=4.886mV, range is 0V to 4.99V */
+ n = (((raw[DS2784_REG_VOLT_MSB] << 8) |
+ (raw[DS2784_REG_VOLT_LSB])) >> 5);
+
+ s->voltage_uV = n * 4886;
+
+ /* Get Current: Unit= 1.5625uV x Rsnsp(67)=104.68 */
+ n = ((raw[DS2784_REG_CURR_MSB]) << 8) |
+ raw[DS2784_REG_CURR_LSB];
+ s->current_uA = ((n * 15625) / 10000) * 67;
+
+ n = ((raw[DS2784_REG_AVG_CURR_MSB]) << 8) |
+ raw[DS2784_REG_AVG_CURR_LSB];
+ s->current_avg_uA = ((n * 15625) / 10000) * 67;
+
+ /* Get Temperature:
+ * 11 bit signed result in Unit=0.125 degree C.
+ * Convert to integer tenths of degree C.
+ */
+ n = ((raw[DS2784_REG_TEMP_MSB] << 8) |
+ (raw[DS2784_REG_TEMP_LSB])) >> 5;
+
+ s->temp_C = (n * 10) / 8;
+
+ /* RAAC is in units of 1.6mAh */
+ s->charge_uAh = ((raw[DS2784_REG_RAAC_MSB] << 8) |
+ raw[DS2784_REG_RAAC_LSB]) * 1600;
+}
+
+static int w1_ds2784_io(struct w1_slave *sl, char *buf, int addr, size_t count, int io)
+{
+ if (!sl)
+ return 0;
+
+ 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);
+ /* XXX w1_write_block returns void, not n_written */
+ }
+ }
+
+out:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+static int w1_ds2784_read(struct w1_slave *sl, char *buf, int addr, size_t count)
+{
+ return w1_ds2784_io(sl, buf, addr, count, 0);
+}
+
+static int w1_ds2784_write(struct w1_slave *sl, char *buf, int addr, size_t count)
+{
+ return w1_ds2784_io(sl, buf, addr, count, 1);
+}
+
+static int ds2784_set_cc(struct ds2784_device_info *di, bool enable)
+{
+ int ret;
+
+ if (enable)
+ di->raw[DS2784_REG_PORT] |= 0x02;
+ else
+ di->raw[DS2784_REG_PORT] &= ~0x02;
+ ret = w1_ds2784_write(di->w1_slave, di->raw + DS2784_REG_PORT,
+ DS2784_REG_PORT, 1);
+ if (ret != 1) {
+ dev_warn(di->dev, "call to w1_ds2784_write failed (0x%p)\n",
+ di->w1_slave);
+ return 1;
+ }
+ return 0;
+}
+
+static int ds2784_battery_read_status(struct ds2784_device_info *di)
+{
+ int ret, start, count;
+
+ /* The first time we read the entire contents of SRAM/EEPROM,
+ * but after that we just read the interesting bits that change. */
+ if (di->raw[DS2784_REG_RSNSP] == 0x00) {
+ start = 0;
+ count = DS2784_DATA_SIZE;
+ } else {
+ start = DS2784_REG_PORT;
+ count = DS2784_REG_CURR_LSB - start + 1;
+ }
+
+ ret = w1_ds2784_read(di->w1_slave, di->raw + start, start, count);
+ if (ret != count) {
+ dev_warn(di->dev, "call to w1_ds2784_read failed (0x%p)\n",
+ di->w1_slave);
+ return 1;
+ }
+
+ if (battery_initial == 0) {
+ if (!memcmp(di->raw + 0x20, "DUMMY!", 6)) {
+ unsigned char acr[2];
+
+ di->dummy = 1;
+ pr_info("batt: dummy battery detected\n");
+
+ /* reset ACC register to ~500mAh, since it may have zeroed out */
+ acr[0] = 0x05;
+ acr[1] = 0x06;
+ w1_ds2784_write(di->w1_slave, acr, DS2784_REG_ACCUMULATE_CURR_MSB, 2);
+ }
+ battery_initial = 1;
+ }
+
+ ds2784_parse_data(di->raw, &di->status);
+
+ pr_info("batt: %3d%%, %d mV, %d mA (%d avg), %d.%d C, %d mAh\n",
+ di->status.percentage,
+ di->status.voltage_uV / 1000, di->status.current_uA / 1000,
+ di->status.current_avg_uA / 1000,
+ di->status.temp_C / 10, di->status.temp_C % 10,
+ di->status.charge_uAh / 1000);
+
+ return 0;
+}
+
+static int battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ds2784_device_info *di = psy_to_dev_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ switch (di->status.charge_source) {
+ case CHARGE_OFF:
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case CHARGE_FAST:
+ case CHARGE_SLOW:
+ if (di->status.battery_full)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else if (di->status.charge_mode == CHARGE_OFF ||
+ di->status.charge_mode == CHARGE_BATT_DISABLE)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->status.temp_C >= TEMP_HOT)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ /* XXX todo */
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ if (di->dummy)
+ val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ else
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ if (di->dummy)
+ val->intval = 75;
+ else
+ val->intval = di->status.percentage;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->status.voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = di->status.temp_C;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->status.current_uA;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = di->status.current_avg_uA;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = di->status.charge_uAh;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ds2784_battery_update_status(struct ds2784_device_info *di)
+{
+ u8 last_level;
+ last_level = di->status.percentage;
+
+ ds2784_battery_read_status(di);
+
+ if ((last_level != di->status.percentage) || (di->status.temp_C > 450))
+ power_supply_changed(&di->bat);
+}
+
+static DEFINE_MUTEX(charge_state_lock);
+
+static bool check_timeout(ktime_t now, ktime_t last, int seconds)
+{
+ ktime_t timeout = ktime_add(last, ktime_set(seconds, 0));
+ return ktime_sub(timeout, now).tv64 < 0;
+}
+
+static int battery_adjust_charge_state(struct ds2784_device_info *di)
+{
+ unsigned source;
+ int rc = 0;
+ int temp, volt;
+ u8 charge_mode;
+ bool charge_timeout = false;
+
+ mutex_lock(&charge_state_lock);
+
+ temp = di->status.temp_C;
+ volt = di->status.voltage_uV / 1000;
+
+ source = di->status.charge_source;
+
+ /* initially our charge mode matches our source:
+ * NONE:OFF, USB:SLOW, AC:FAST
+ */
+ charge_mode = source;
+
+ /* shut off charger when full:
+ * - CHGTF flag is set
+ */
+ if (di->status.status_reg & 0x80) {
+ di->status.battery_full = 1;
+ charge_mode = CHARGE_BATT_DISABLE;
+ } else
+ di->status.battery_full = 0;
+
+ if (temp >= TEMP_HOT) {
+ if (temp >= TEMP_CRITICAL)
+ charge_mode = CHARGE_BATT_DISABLE;
+
+ /* once we charge to max voltage when hot, disable
+ * charging until the temp drops or the voltage drops
+ */
+ if (volt >= TEMP_HOT_MAX_MV)
+ di->status.cooldown = 1;
+ }
+
+ /* when the battery is warm, only charge in slow charge mode */
+ if ((temp >= TEMP_WARM) && (charge_mode == CHARGE_FAST))
+ charge_mode = CHARGE_SLOW;
+
+ if (di->status.cooldown) {
+ if ((temp < TEMP_WARM) || (volt <= TEMP_HOT_MIN_MV))
+ di->status.cooldown = 0;
+ else
+ charge_mode = CHARGE_BATT_DISABLE;
+ }
+
+ if (di->status.current_uA > 1024)
+ di->last_charge_seen = di->last_poll;
+ else if (di->last_charge_mode != CHARGE_OFF &&
+ check_timeout(di->last_poll, di->last_charge_seen, 60 * 60)) {
+ if (di->last_charge_mode == CHARGE_BATT_DISABLE) {
+ /* The charger is only powering the phone. Toggle the
+ * enable line periodically to prevent auto shutdown.
+ */
+ di->last_charge_seen = di->last_poll;
+ pr_info("batt: charging POKE CHARGER\n");
+ di->charge(0, 0);
+ udelay(10);
+ di->charge(1, source == CHARGE_FAST);
+ } else {
+ /* The charger has probably stopped charging. Turn it
+ * off until the next sample period.
+ */
+ charge_timeout = true;
+ charge_mode = CHARGE_OFF;
+ }
+ }
+
+ if (source == CHARGE_OFF)
+ charge_mode = CHARGE_OFF;
+
+ /* Don't use CHARGE_BATT_DISABLE unless the voltage is high since the
+ * voltage drop over the discharge-path diode can cause a shutdown.
+ */
+ if (charge_mode == CHARGE_BATT_DISABLE && volt < CE_DISABLE_MIN_MV)
+ charge_mode = CHARGE_OFF;
+
+ if (di->last_charge_mode == charge_mode)
+ goto done;
+
+ di->last_charge_mode = charge_mode;
+ di->status.charge_mode = charge_mode;
+
+ switch (charge_mode) {
+ case CHARGE_OFF:
+ di->charge(0, 0);
+ ds2784_set_cc(di, true);
+ if (temp >= TEMP_CRITICAL)
+ pr_info("batt: charging OFF [OVERTEMP]\n");
+ else if (di->status.cooldown)
+ pr_info("batt: charging OFF [COOLDOWN]\n");
+ else if (di->status.battery_full)
+ pr_info("batt: charging OFF [FULL]\n");
+ else if (charge_timeout)
+ pr_info("batt: charging OFF [TIMEOUT]\n");
+ else
+ pr_info("batt: charging OFF\n");
+ break;
+ case CHARGE_BATT_DISABLE:
+ di->last_charge_seen = di->last_poll;
+ ds2784_set_cc(di, false);
+ di->charge(1, source == CHARGE_FAST);
+ if (temp >= TEMP_CRITICAL)
+ pr_info("batt: charging BATTOFF [OVERTEMP]\n");
+ else if (di->status.cooldown)
+ pr_info("batt: charging BATTOFF [COOLDOWN]\n");
+ else if (di->status.battery_full)
+ pr_info("batt: charging BATTOFF [FULL]\n");
+ else
+ pr_info("batt: charging BATTOFF [UNKNOWN]\n");
+ break;
+ case CHARGE_SLOW:
+ di->last_charge_seen = di->last_poll;
+ ds2784_set_cc(di, true);
+ di->charge(1, 0);
+ pr_info("batt: charging SLOW\n");
+ break;
+ case CHARGE_FAST:
+ di->last_charge_seen = di->last_poll;
+ ds2784_set_cc(di, true);
+ di->charge(1, 1);
+ pr_info("batt: charging FAST\n");
+ break;
+ }
+ rc = 1;
+done:
+ mutex_unlock(&charge_state_lock);
+ return rc;
+}
+
+static void ds2784_program_alarm(struct ds2784_device_info *di, int seconds)
+{
+ ktime_t low_interval = ktime_set(seconds - 10, 0);
+ ktime_t slack = ktime_set(20, 0);
+ ktime_t next;
+
+ next = ktime_add(di->last_poll, low_interval);
+
+ alarm_start_range(&di->alarm, next, ktime_add(next, slack));
+}
+
+static void ds2784_battery_work(struct work_struct *work)
+{
+ struct ds2784_device_info *di =
+ container_of(work, struct ds2784_device_info, monitor_work);
+ struct timespec ts;
+ unsigned long flags;
+
+ ds2784_battery_update_status(di);
+
+ di->last_poll = alarm_get_elapsed_realtime();
+
+ if (battery_adjust_charge_state(di))
+ power_supply_changed(&di->bat);
+
+ ts = ktime_to_timespec(di->last_poll);
+ di->status.timestamp = ts.tv_sec;
+ battery_log_status(&di->status);
+
+ /* prevent suspend before starting the alarm */
+ local_irq_save(flags);
+ wake_unlock(&di->work_wake_lock);
+ ds2784_program_alarm(di, FAST_POLL);
+ local_irq_restore(flags);
+}
+
+static void ds2784_battery_alarm(struct alarm *alarm)
+{
+ struct ds2784_device_info *di =
+ container_of(alarm, struct ds2784_device_info, alarm);
+ wake_lock(&di->work_wake_lock);
+ queue_work(di->monitor_wqueue, &di->monitor_work);
+}
+
+static void battery_ext_power_changed(struct power_supply *psy)
+{
+ struct ds2784_device_info *di;
+ int got_power;
+
+ di = psy_to_dev_info(psy);
+ got_power = power_supply_am_i_supplied(psy);
+
+ if (got_power) {
+ if (is_ac_power_supplied())
+ di->status.charge_source = SOURCE_AC;
+ else
+ di->status.charge_source = SOURCE_USB;
+ wake_lock(&vbus_wake_lock);
+ } else {
+ di->status.charge_source = SOURCE_NONE;
+ /* give userspace some time to see the uevent and update
+ * LED state or whatnot...
+ */
+ wake_lock_timeout(&vbus_wake_lock, HZ / 2);
+ }
+ battery_adjust_charge_state(di);
+ power_supply_changed(psy);
+}
+
+static int ds2784_battery_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct ds2784_device_info *di;
+ struct ds2784_platform_data *pdata;
+
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, di);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata || !pdata->charge || !pdata->w1_slave) {
+ pr_err("%s: pdata missing or invalid\n", __func__);
+ rc = -EINVAL;
+ goto fail_register;
+ }
+
+ di->charge = pdata->charge;
+ di->w1_slave = pdata->w1_slave;
+
+ di->dev = &pdev->dev;
+
+ di->bat.name = "battery";
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = battery_properties;
+ di->bat.num_properties = ARRAY_SIZE(battery_properties);
+ di->bat.external_power_changed = battery_ext_power_changed;
+ di->bat.get_property = battery_get_property;
+ di->last_charge_mode = 0xff;
+
+ rc = power_supply_register(&pdev->dev, &di->bat);
+ if (rc)
+ goto fail_register;
+
+ INIT_WORK(&di->monitor_work, ds2784_battery_work);
+ di->monitor_wqueue = create_freezeable_workqueue(dev_name(&pdev->dev));
+
+ /* init to something sane */
+ di->last_poll = alarm_get_elapsed_realtime();
+
+ if (!di->monitor_wqueue) {
+ rc = -ESRCH;
+ goto fail_workqueue;
+ }
+ wake_lock_init(&di->work_wake_lock, WAKE_LOCK_SUSPEND,
+ "ds2784-battery");
+ alarm_init(&di->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ ds2784_battery_alarm);
+ wake_lock(&di->work_wake_lock);
+ queue_work(di->monitor_wqueue, &di->monitor_work);
+ return 0;
+
+fail_workqueue:
+ power_supply_unregister(&di->bat);
+fail_register:
+ kfree(di);
+ return rc;
+}
+
+static int ds2784_suspend(struct device *dev)
+{
+ struct ds2784_device_info *di = dev_get_drvdata(dev);
+
+ /* If we are on battery, reduce our update rate until
+ * we next resume.
+ */
+ if (di->status.charge_source == SOURCE_NONE) {
+ ds2784_program_alarm(di, SLOW_POLL);
+ di->slow_poll = 1;
+ }
+ return 0;
+}
+
+static int ds2784_resume(struct device *dev)
+{
+ struct ds2784_device_info *di = dev_get_drvdata(dev);
+
+ /* We might be on a slow sample cycle. If we're
+ * resuming we should resample the battery state
+ * if it's been over a minute since we last did
+ * so, and move back to sampling every minute until
+ * we suspend again.
+ */
+ if (di->slow_poll) {
+ ds2784_program_alarm(di, FAST_POLL);
+ di->slow_poll = 0;
+ }
+ return 0;
+}
+
+static struct dev_pm_ops ds2784_pm_ops = {
+ .suspend = ds2784_suspend,
+ .resume = ds2784_resume,
+};
+
+static struct platform_driver ds2784_battery_driver = {
+ .driver = {
+ .name = "ds2784-battery",
+ .pm = &ds2784_pm_ops,
+ },
+ .probe = ds2784_battery_probe,
+};
+
+static int battery_log_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, battery_log_print, NULL);
+}
+
+static struct file_operations battery_log_fops = {
+ .open = battery_log_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init ds2784_battery_init(void)
+{
+ debugfs_create_file("battery_log", 0444, NULL, NULL, &battery_log_fops);
+ wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
+ return platform_driver_register(&ds2784_battery_driver);
+}
+
+module_init(ds2784_battery_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Justin Lin <Justin_lin@htc.com>");
+MODULE_DESCRIPTION("ds2784 battery driver");
diff --git a/drivers/power/pm8058-charger.c b/drivers/power/pm8058-charger.c
new file mode 100644
index 0000000..0642274
--- /dev/null
+++ b/drivers/power/pm8058-charger.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2010 Google, Inc.
+ *
+ * Author: Dima Zavin <dima@android.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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8058.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+
+struct pm8058_charger {
+ struct device *pmic_dev;
+ int chgval_irq;
+ int fastchg_irq;
+
+ struct power_supply ac_supply;
+ struct power_supply usb_supply;
+
+ struct pm8058_charger_platform_data *pdata;
+
+ spinlock_t lock;
+ bool can_charge;
+ bool is_ac;
+ bool is_online;
+ bool vbus_present;
+ int charge_type;
+ u32 max_current;
+};
+
+static struct pm8058_charger *the_pm8058_charger;
+
+/* TODO: the usb core driver should provide the maximum current draw value to us
+ * for charging */
+
+void pm8058_notify_charger_connected(int status)
+{
+ struct pm8058_charger *charger = the_pm8058_charger;
+ u32 max_current = 0;
+ bool is_ac;
+ bool is_online;
+ bool change = false;
+ unsigned long flags;
+
+ if (!charger)
+ return;
+
+ printk("### %s(%d) ###\n", __func__, status);
+ if (status && !charger->vbus_present)
+ pr_warning("%s: cable status mismatch %d %d\n", __func__,
+ status, charger->vbus_present);
+
+ switch (status) {
+ case 1:
+ /* usb (pc) charging */
+ max_current = 500;
+ is_ac = false;
+ is_online = true;
+ break;
+ case 2:
+ /* wall charger */
+ max_current = 1500;
+ is_ac = true;
+ is_online = true;
+ break;
+ case 0:
+ default:
+ /* disable charging */
+ max_current = 0;
+ is_ac = false;
+ is_online = false;
+ break;
+ }
+ spin_lock_irqsave(&charger->lock, flags);
+ if (max_current != charger->max_current ||
+ is_ac != charger->is_ac || is_online != charger->is_online) {
+ charger->max_current = max_current;
+ charger->is_ac = is_ac;
+ charger->is_online = is_online;
+ change = true;
+ }
+ spin_unlock_irqrestore(&charger->lock, flags);
+ /* for now, charge control is done on the modem side, so we have to
+ * delegate to the board file. Eventually, all charge control will
+ * be done in this driver */
+ if (change && charger->pdata->charge)
+ charger->pdata->charge(max_current, is_ac);
+
+ power_supply_changed(&charger->ac_supply);
+ power_supply_changed(&charger->usb_supply);
+}
+EXPORT_SYMBOL_GPL(pm8058_notify_charger_connected);
+
+static void check_chgval(struct pm8058_charger *charger)
+{
+ int ret;
+ unsigned long flags;
+
+ ret = pm8058_irq_get_status(charger->pmic_dev, PM8058_CHGVAL_IRQ);
+ if (ret >= 0) {
+ spin_lock_irqsave(&charger->lock, flags);
+ charger->vbus_present = !!ret;
+ spin_unlock_irqrestore(&charger->lock, flags);
+ charger->pdata->vbus_present(ret);
+ } else {
+ pr_err("%s: can't read status!! ignoring event?!\n", __func__);
+ }
+}
+
+static irqreturn_t chgval_irq_handler(int irq, void *dev_id)
+{
+ struct pm8058_charger *charger = dev_id;
+
+ check_chgval(charger);
+ return IRQ_HANDLED;
+}
+
+/* should only get this irq when we are plugged in */
+static irqreturn_t fastchg_irq_handler(int irq, void *dev_id)
+{
+ struct pm8058_charger *charger = dev_id;
+ int ret;
+ bool fast_charging;
+ unsigned long flags;
+
+ ret = pm8058_irq_get_status(charger->pmic_dev, PM8058_FASTCHG_IRQ);
+ if (ret < 0)
+ return IRQ_HANDLED;
+ fast_charging = !!ret;
+
+ spin_lock_irqsave(&charger->lock, flags);
+ if (fast_charging) {
+ if (!charger->vbus_present) {
+ pr_err("%s: charging without vbus?!\n", __func__);
+ goto done;
+ }
+ charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ } else {
+ /* charging is either stopped (done/overtemp/etc.), or we
+ * are trickle charging. */
+ /* TODO: detect trickle charging mode */
+ if (charger->is_online)
+ charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else
+ charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+done:
+ spin_unlock_irqrestore(&charger->lock, flags);
+
+ power_supply_changed(&charger->ac_supply);
+ power_supply_changed(&charger->usb_supply);
+ return IRQ_HANDLED;
+}
+
+static int power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pm8058_charger *charger;
+
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ charger = container_of(psy, struct pm8058_charger, ac_supply);
+ else
+ charger = container_of(psy, struct pm8058_charger, usb_supply);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = charger->is_online && charger->is_ac;
+ else
+ val->intval = charger->is_online && !charger->is_ac;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ /* for now, fake fast charge all the time if we're on */
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = charger->is_ac ? charger->charge_type :
+ POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ else
+ val->intval = charger->is_online && !charger->is_ac ?
+ charger->charge_type :
+ POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static int __init pm8058_charger_probe(struct platform_device *pdev)
+{
+ struct pm8058_charger_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8058_charger *charger;
+ int chgval_irq;
+ int fastchg_irq;
+ int ret;
+
+ chgval_irq = platform_get_irq_byname(pdev, "chgval_irq");
+ fastchg_irq = platform_get_irq_byname(pdev, "fastchg_irq");
+
+ if (!pdata || chgval_irq < 0 || fastchg_irq < 0) {
+ pr_err("%s: missing platform data/resources\n", __func__);
+ return -EINVAL;
+ }
+
+ charger = kzalloc(sizeof(struct pm8058_charger), GFP_KERNEL);
+ if (!charger) {
+ pr_err("%s: can't alloc mem for charger struct\n", __func__);
+ return -ENOMEM;
+ }
+
+ charger->pmic_dev = pdev->dev.parent;
+ charger->pdata = pdata;
+ platform_set_drvdata(pdev, charger);
+ spin_lock_init(&charger->lock);
+
+ the_pm8058_charger = charger;
+
+ charger->ac_supply.name = "ac";
+ charger->ac_supply.type = POWER_SUPPLY_TYPE_MAINS;
+ charger->ac_supply.supplied_to = pdata->supplied_to;
+ charger->ac_supply.num_supplicants = pdata->num_supplicants;
+ charger->ac_supply.properties = power_properties;
+ charger->ac_supply.num_properties = ARRAY_SIZE(power_properties);
+ charger->ac_supply.get_property = power_get_property;
+
+ charger->usb_supply.name = "usb";
+ charger->usb_supply.type = POWER_SUPPLY_TYPE_USB;
+ charger->usb_supply.supplied_to = pdata->supplied_to;
+ charger->usb_supply.num_supplicants = pdata->num_supplicants;
+ charger->usb_supply.properties = power_properties;
+ charger->usb_supply.num_properties = ARRAY_SIZE(power_properties);
+ charger->usb_supply.get_property = power_get_property;
+
+ ret = power_supply_register(&pdev->dev, &charger->ac_supply);
+ if (ret)
+ goto err_reg_ac_supply;
+ ret = power_supply_register(&pdev->dev, &charger->usb_supply);
+ if (ret)
+ goto err_reg_usb_supply;
+
+ ret = request_threaded_irq(chgval_irq, NULL, chgval_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "pm8058-charger-valid", charger);
+ if (ret) {
+ pr_err("%s: can't request chgval_irq\n", __func__);
+ goto err_req_chgval_irq;
+ }
+ charger->chgval_irq = chgval_irq;
+
+ ret = request_threaded_irq(fastchg_irq, NULL, fastchg_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "pm8058-charger-fastchg", charger);
+ if (ret) {
+ pr_err("%s: can't request stuck\n", __func__);
+ goto err_req_fastchg_irq;
+ }
+ charger->fastchg_irq = fastchg_irq;
+ enable_irq_wake(charger->chgval_irq);
+
+ pr_info("%s: driver initialized\n", __func__);
+ check_chgval(charger);
+
+ return 0;
+
+err_req_fastchg_irq:
+ free_irq(chgval_irq, charger);
+err_req_chgval_irq:
+ power_supply_unregister(&charger->usb_supply);
+err_reg_usb_supply:
+ power_supply_unregister(&charger->ac_supply);
+err_reg_ac_supply:
+ platform_set_drvdata(pdev, NULL);
+ the_pm8058_charger = NULL;
+ kfree(charger);
+ return ret;
+}
+
+static struct platform_driver pm8058_charger_driver = {
+ .probe = pm8058_charger_probe,
+ .driver = {
+ .name = "pm8058-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8058_charger_init(void)
+{
+ return platform_driver_register(&pm8058_charger_driver);
+}
+
+module_init(pm8058_charger_init);
+MODULE_DESCRIPTION("PM8058 Charger Driver");
+MODULE_AUTHOR("Dima Zavin <dima@android.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/power/w1_ds2784.h b/drivers/power/w1_ds2784.h
new file mode 100644
index 0000000..6822fc0
--- /dev/null
+++ b/drivers/power/w1_ds2784.h
@@ -0,0 +1,132 @@
+/* drivers/w1/slaves/w1_ds2784.h
+ *
+ * Copyright (C) 2009 HTC Corporation
+ * Author: Justin Lin <Justin_Lin@htc.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 __w1_ds2784_h__
+#define __w1_ds2784_h__
+
+
+/* Known commands to the DS2784 chip */
+#define W1_DS2784_SWAP 0xAA
+#define W1_DS2784_READ_DATA 0x69
+#define W1_DS2784_WRITE_DATA 0x6C
+#define W1_DS2784_COPY_DATA 0x48
+#define W1_DS2784_RECALL_DATA 0xB8
+#define W1_DS2784_LOCK 0x6A
+
+/* Number of valid register addresses */
+#define DS2784_DATA_SIZE 0x80
+
+#define DS2784_EEPROM_BLOCK0 0x20
+#define DS2784_ACTIVE_FULL 0x20
+#define DS2784_EEPROM_BLOCK1 0x30
+#define DS2784_RATED_CAPACITY 0x32
+#define DS2784_CURRENT_OFFSET_BIAS 0x33
+#define DS2784_ACTIVE_EMPTY 0x3b
+
+/**
+ * The DS2482 registers - there are 3 registers that are addressed by a read
+ * pointer. The read pointer is set by the last command executed.
+ *
+ * To read the data, issue a register read for any address
+ */
+#define DS2482_CMD_RESET 0xF0 /* No param */
+#define DS2482_CMD_SET_READ_PTR 0xE1 /* Param: DS2482_PTR_CODE_xxx */
+#define DS2482_CMD_CHANNEL_SELECT 0xC3
+#define DS2482_CMD_WRITE_CONFIG 0xD2 /* Param: Config byte */
+#define DS2482_CMD_1WIRE_RESET 0xB4 /* Param: None */
+#define DS2482_CMD_1WIRE_SINGLE_BIT 0x87 /* Param: Bit byte (bit7) */
+#define DS2482_CMD_1WIRE_WRITE_BYTE 0xA5 /* Param: Data byte */
+#define DS2482_CMD_1WIRE_READ_BYTE 0x96 /* Param: None */
+/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */
+#define DS2482_CMD_1WIRE_TRIPLET 0x78 /* Param: Dir byte (bit7) */
+
+/* Values for DS2482_CMD_SET_READ_PTR */
+#define DS2482_PTR_CODE_STATUS 0xF0
+#define DS2482_PTR_CODE_DATA 0xE1
+#define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */
+#define DS2482_PTR_CODE_CONFIG 0xC3
+
+/*
+DS2784 1-wire slave memory map definitions
+*/
+#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
+
+#endif /* !__w1_ds2784__ */
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index f50afc9f..7759790 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -62,6 +62,9 @@
#define TPS65023_REG_CTRL_LDO2_EN BIT(2)
#define TPS65023_REG_CTRL_LDO1_EN BIT(1)
+/* CON_CTRL2 bitfields */
+#define TPS65023_CON_CTRL2_GO BIT(7)
+
/* LDO_CTRL bitfields */
#define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4)
#define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4))
@@ -126,11 +129,38 @@
struct regulator_dev *rdev[TPS65023_NUM_REGULATOR];
const struct tps_info *info[TPS65023_NUM_REGULATOR];
struct mutex io_lock;
+ unsigned dcdc1_last_uV;
};
+static int tps_65023_read_3bytes(struct tps_pmic *tps, u8 reg)
+{
+ int rv;
+ u8 txbuf[1];
+ u8 rxbuf[3];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = tps->client->addr,
+ .flags = 0,
+ .len = sizeof(txbuf),
+ .buf = txbuf,
+ },
+ {
+ .addr = tps->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(rxbuf),
+ .buf = rxbuf,
+ },
+ };
+ txbuf[0] = reg;
+ rv = i2c_transfer(tps->client->adapter, msgs, 2);
+ if (rv < 0)
+ return rv;
+ return rxbuf[0];
+}
+
static inline int tps_65023_read(struct tps_pmic *tps, u8 reg)
{
- return i2c_smbus_read_byte_data(tps->client, reg);
+ return tps_65023_read_3bytes(tps, reg);
}
static inline int tps_65023_write(struct tps_pmic *tps, u8 reg, u8 val)
@@ -326,6 +356,9 @@
struct tps_pmic *tps = rdev_get_drvdata(dev);
int dcdc = rdev_get_id(dev);
int vsel;
+ int rv;
+ int uV = 0;
+ int delay;
if (dcdc != TPS65023_DCDC_1)
return -EINVAL;
@@ -339,7 +372,7 @@
for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) {
int mV = tps->info[dcdc]->table[vsel];
- int uV = mV * 1000;
+ uV = mV * 1000;
/* Break at the first in-range value */
if (min_uV <= uV && uV <= max_uV)
@@ -349,8 +382,22 @@
/* write to the register in case we found a match */
if (vsel == tps->info[dcdc]->table_len)
return -EINVAL;
+
+ rv = tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel);
+ if (!rv)
+ rv = tps_65023_reg_write(tps, TPS65023_REG_CON_CTRL2,
+ TPS65023_CON_CTRL2_GO);
+
+ /* Add delay to reach relected voltage (14.4 mV/us default slew rate) */
+ if (tps->dcdc1_last_uV)
+ delay = abs(tps->dcdc1_last_uV - uV);
else
- return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel);
+ delay = max(uV - 800000, 1600000 - uV);
+ delay = DIV_ROUND_UP(delay, 14400);
+ udelay(delay);
+ tps->dcdc1_last_uV = rv ? 0 /* Unknown voltage */ : uV;
+
+ return rv;
}
static int tps65023_ldo_get_voltage(struct regulator_dev *dev)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index d6d51e1..dec9e0d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -655,6 +655,13 @@
This driver can also be built as a module. If so, the module
will be called rtc-davinci.
+config RTC_DRV_MSM7X00A
+ tristate "MSM7X00A"
+ depends on ARCH_MSM
+ default y
+ help
+ RTC driver for Qualcomm MSM7K chipsets
+
config RTC_DRV_OMAP
tristate "TI OMAP1"
depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 825df62..26d46ee 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -60,6 +60,7 @@
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MSM7X00A) += rtc-msm7x00a.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c
new file mode 100644
index 0000000..f14608a
--- /dev/null
+++ b/drivers/rtc/rtc-msm7x00a.c
@@ -0,0 +1,306 @@
+/* drivers/rtc/rtc-msm7x00a.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: San Mehat <san@google.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/msm_rpcrouter.h>
+#include <linux/slab.h>
+
+#include <mach/msm_rpcrouter.h>
+
+#define RTC_DEBUG 0
+
+extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns);
+
+static const char *rpc_versions[] = {
+#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS)
+ "rs30000048:00040000",
+ "rs30000048:00010000",
+#else
+ "rs30000048:0da5b528",
+#endif
+};
+
+#define TIMEREMOTE_PROCEEDURE_SET_JULIAN 6
+#define TIMEREMOTE_PROCEEDURE_GET_JULIAN 7
+
+struct rpc_time_julian {
+ uint32_t year;
+ uint32_t month;
+ uint32_t day;
+ uint32_t hour;
+ uint32_t minute;
+ uint32_t second;
+ uint32_t day_of_week;
+};
+
+static struct msm_rpc_endpoint *ep;
+static struct rtc_device *rtc;
+static unsigned long rtcalarm_time;
+
+static int
+msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int rc;
+
+ struct timeremote_set_julian_req {
+ struct rpc_request_hdr hdr;
+ uint32_t opt_arg;
+
+ struct rpc_time_julian time;
+ } req;
+
+ struct timeremote_set_julian_rep {
+ struct rpc_reply_hdr hdr;
+ } rep;
+
+ if (tm->tm_year < 1900)
+ tm->tm_year += 1900;
+
+ if (tm->tm_year < 1970)
+ return -EINVAL;
+
+#if RTC_DEBUG
+ printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+ __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+#endif
+
+ req.opt_arg = cpu_to_be32(1);
+ req.time.year = cpu_to_be32(tm->tm_year);
+ req.time.month = cpu_to_be32(tm->tm_mon + 1);
+ req.time.day = cpu_to_be32(tm->tm_mday);
+ req.time.hour = cpu_to_be32(tm->tm_hour);
+ req.time.minute = cpu_to_be32(tm->tm_min);
+ req.time.second = cpu_to_be32(tm->tm_sec);
+ req.time.day_of_week = cpu_to_be32(tm->tm_wday);
+
+
+ rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_SET_JULIAN,
+ &req, sizeof(req),
+ &rep, sizeof(rep),
+ 5 * HZ);
+ return rc;
+}
+
+static int
+msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int rc;
+
+ struct timeremote_get_julian_req {
+ struct rpc_request_hdr hdr;
+ uint32_t julian_time_not_null;
+ } req;
+
+ struct timeremote_get_julian_rep {
+ struct rpc_reply_hdr hdr;
+ uint32_t opt_arg;
+ struct rpc_time_julian time;
+ } rep;
+
+ req.julian_time_not_null = cpu_to_be32(1);
+
+ rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_GET_JULIAN,
+ &req, sizeof(req),
+ &rep, sizeof(rep),
+ 5 * HZ);
+ if (rc < 0)
+ return rc;
+
+ if (!be32_to_cpu(rep.opt_arg)) {
+ printk(KERN_ERR "%s: No data from RTC\n", __func__);
+ return -ENODATA;
+ }
+
+ tm->tm_year = be32_to_cpu(rep.time.year);
+ tm->tm_mon = be32_to_cpu(rep.time.month);
+ tm->tm_mday = be32_to_cpu(rep.time.day);
+ tm->tm_hour = be32_to_cpu(rep.time.hour);
+ tm->tm_min = be32_to_cpu(rep.time.minute);
+ tm->tm_sec = be32_to_cpu(rep.time.second);
+ tm->tm_wday = be32_to_cpu(rep.time.day_of_week);
+
+#if RTC_DEBUG
+ printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
+ __func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+#endif
+
+ tm->tm_year -= 1900; /* RTC layer expects years to start at 1900 */
+ tm->tm_mon--; /* RTC layer expects mons to be 0 based */
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+
+ return 0;
+}
+
+
+static int
+msmrtc_virtual_alarm_set(struct device *dev, struct rtc_wkalrm *a)
+{
+ unsigned long now = get_seconds();
+
+ if (!a->enabled) {
+ rtcalarm_time = 0;
+ return 0;
+ } else
+ rtc_tm_to_time(&a->time, &rtcalarm_time);
+
+ if (now > rtcalarm_time) {
+ printk(KERN_ERR "%s: Attempt to set alarm in the past\n",
+ __func__);
+ rtcalarm_time = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct rtc_class_ops msm_rtc_ops = {
+ .read_time = msmrtc_timeremote_read_time,
+ .set_time = msmrtc_timeremote_set_time,
+ .set_alarm = msmrtc_virtual_alarm_set,
+};
+
+static void
+msmrtc_alarmtimer_expired(unsigned long _data)
+{
+#if RTC_DEBUG
+ printk(KERN_DEBUG "%s: Generating alarm event (src %lu)\n",
+ rtc->name, _data);
+#endif
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+ rtcalarm_time = 0;
+}
+
+static int
+msmrtc_probe(struct platform_device *pdev)
+{
+ struct rpcsvr_platform_device *rdev =
+ container_of(pdev, struct rpcsvr_platform_device, base);
+
+ if (rtc)
+ return -EBUSY;
+
+ ep = msm_rpc_connect(rdev->prog, rdev->vers, 0);
+ if (IS_ERR(ep)) {
+ printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+ __func__, PTR_ERR(ep));
+ return PTR_ERR(ep);
+ }
+
+ rtc = rtc_device_register("msm_rtc",
+ &pdev->dev,
+ &msm_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ printk(KERN_ERR "%s: Can't register RTC device (%ld)\n",
+ pdev->name, PTR_ERR(rtc));
+ return PTR_ERR(rtc);
+ }
+ return 0;
+}
+
+
+static unsigned long msmrtc_get_seconds(void)
+{
+ struct rtc_time tm;
+ unsigned long now;
+
+ msmrtc_timeremote_read_time(NULL, &tm);
+ rtc_tm_to_time(&tm, &now);
+ return now;
+}
+
+static int
+msmrtc_suspend(struct platform_device *dev, pm_message_t state)
+{
+ if (rtcalarm_time) {
+ unsigned long now = msmrtc_get_seconds();
+ int diff = rtcalarm_time - now;
+ if (diff <= 0) {
+ msmrtc_alarmtimer_expired(1);
+ msm_pm_set_max_sleep_time(0);
+ return 0;
+ }
+ msm_pm_set_max_sleep_time((int64_t) ((int64_t) diff * NSEC_PER_SEC));
+ } else
+ msm_pm_set_max_sleep_time(0);
+ return 0;
+}
+
+static int
+msmrtc_resume(struct platform_device *dev)
+{
+ if (rtcalarm_time) {
+ unsigned long now = msmrtc_get_seconds();
+ int diff = rtcalarm_time - now;
+ if (diff <= 0)
+ msmrtc_alarmtimer_expired(2);
+ }
+ return 0;
+}
+
+static int __init msmrtc_init(void)
+{
+ int i;
+ int ret;
+ struct platform_driver *pdrv[ARRAY_SIZE(rpc_versions)];
+
+ rtcalarm_time = 0;
+
+ /* register the devices for all the major versions we support, only
+ * one should match */
+ for (i = 0; i < ARRAY_SIZE(rpc_versions); i++) {
+ pdrv[i] = kzalloc(sizeof(struct platform_driver), GFP_KERNEL);
+ if (!pdrv[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdrv[i]->probe = msmrtc_probe;
+ pdrv[i]->suspend = msmrtc_suspend;
+ pdrv[i]->resume = msmrtc_resume;
+ pdrv[i]->driver.name = rpc_versions[i];
+ pdrv[i]->driver.owner = THIS_MODULE;
+ ret = platform_driver_register(pdrv[i]);
+ if (ret) {
+ kfree(pdrv[i]);
+ goto err;
+ }
+ }
+ return 0;
+
+err:
+ for (--i; i >= 0; i--)
+ platform_driver_unregister(pdrv[i]);
+ return ret;
+}
+
+module_init(msmrtc_init);
+
+MODULE_DESCRIPTION("RTC driver for Qualcomm MSM7x00a chipsets");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 8b23165..ae1684f 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1351,6 +1351,38 @@
depends on SERIAL_MSM=y
select SERIAL_CORE_CONSOLE
+config SERIAL_MSM_CLOCK_CONTROL
+ bool "Allow tty clients to make clock requests to msm uarts."
+ depends on SERIAL_MSM=y
+ default y
+ help
+ Provides an interface for tty clients to request the msm uart clock
+ to be turned on or off for power savings.
+
+config SERIAL_MSM_RX_WAKEUP
+ bool "Wakeup the msm uart clock on GPIO activity."
+ depends on SERIAL_MSM_CLOCK_CONTROL
+ default n
+ help
+ Requires SERIAL_MSM_CLOCK_CONTROL. Wake up the uart while the uart
+ clock is off, using a wakeup GPIO.
+
+config SERIAL_MSM_HS
+ tristate "MSM UART High Speed: Serial Driver"
+ depends on ARM && ARCH_MSM
+ select SERIAL_CORE
+ default n
+ help
+ Select this module to enable MSM high speed UART driver.
+
+config SERIAL_BCM_BT_LPM
+ tristate "Broadcom Bluetooth Low Power Mode"
+ depends on ARM && ARCH_MSM
+ select SERIAL_CORE
+ default n
+ help
+ Select this module for Broadcom Bluetooth low power management.
+
config SERIAL_NETX
tristate "NetX serial port support"
depends on ARM && ARCH_NETX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 208a855..262316e 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -35,6 +35,7 @@
obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
+obj-$(CONFIG_SERIAL_BCM_BT_LPM) += bcm_bt_lpm.o
obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
@@ -74,6 +75,8 @@
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_MSM_SERIAL_DEBUGGER) += msm_serial_debugger.o
+obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
diff --git a/drivers/serial/bcm_bt_lpm.c b/drivers/serial/bcm_bt_lpm.c
new file mode 100644
index 0000000..bf5ab33
--- /dev/null
+++ b/drivers/serial/bcm_bt_lpm.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/hrtimer.h>
+#include <linux/irq.h>
+#include <linux/serial_core.h>
+#include <mach/bcm_bt_lpm.h>
+#include <asm/gpio.h>
+
+/*
+ * Manage WAKE and HOST_WAKE low power mode signals for Broadcom
+ * Bluetooth chipsets.
+ *
+ * This driver needs to be tightly coupled with a uart driver that supports
+ * request_clock_off_locked() and request_clock_on_locked(), to clock off and
+ * on the uart indepdently of Linux suspend/resume.
+ *
+ * The uart driver needs to call bcm_bt_lpm_exit_lpm_locked() every time it
+ * begins TX, to ensure this driver keeps WAKE asserted during TX.
+ *
+ * The callbacks and hijacking of the uart_port struct are not a clean API,
+ * but the Linux tty and serial core layers do not have a better alternative
+ * right now: there is no good way to plumb uart clock control through these
+ * layers. See http://lkml.org/lkml/2008/12/19/213 for more background.
+ */
+
+struct bcm_bt_lpm {
+ unsigned int gpio_wake;
+ unsigned int gpio_host_wake;
+
+ int wake;
+ int host_wake;
+
+ struct hrtimer enter_lpm_timer;
+ ktime_t enter_lpm_delay;
+
+ struct uart_port *uport;
+
+ void (*request_clock_off_locked)(struct uart_port *uport);
+ void (*request_clock_on_locked)(struct uart_port *uport);
+} bt_lpm;
+
+static void set_wake_locked(int wake)
+{
+ if (wake == bt_lpm.wake)
+ return;
+ bt_lpm.wake = wake;
+
+ if (wake || bt_lpm.host_wake)
+ bt_lpm.request_clock_on_locked(bt_lpm.uport);
+ else
+ bt_lpm.request_clock_off_locked(bt_lpm.uport);
+
+ gpio_set_value(bt_lpm.gpio_wake, wake);
+}
+
+static enum hrtimer_restart enter_lpm(struct hrtimer *timer) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+ set_wake_locked(0);
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ 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);
+}
+EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked);
+
+static void update_host_wake_locked(int host_wake)
+{
+ if (host_wake == bt_lpm.host_wake)
+ return;
+ bt_lpm.host_wake = host_wake;
+
+ if (bt_lpm.wake || host_wake)
+ bt_lpm.request_clock_on_locked(bt_lpm.uport);
+ else
+ bt_lpm.request_clock_off_locked(bt_lpm.uport);
+}
+
+static irqreturn_t host_wake_isr(int irq, void *dev)
+{
+ int host_wake;
+ unsigned long flags;
+
+ host_wake = gpio_get_value(bt_lpm.gpio_host_wake);
+ 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;
+ }
+
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+
+ update_host_wake_locked(host_wake);
+
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm_bt_lpm_probe(struct platform_device *pdev)
+{
+ int irq;
+ int ret;
+ struct bcm_bt_lpm_platform_data *pdata = pdev->dev.platform_data;
+
+ if (bt_lpm.request_clock_off_locked != NULL) {
+ printk(KERN_ERR "Cannot register two bcm_bt_lpm drivers\n");
+ return -EINVAL;
+ }
+
+ bt_lpm.gpio_wake = pdata->gpio_wake;
+ bt_lpm.gpio_host_wake = pdata->gpio_host_wake;
+ bt_lpm.request_clock_off_locked = pdata->request_clock_off_locked;
+ bt_lpm.request_clock_on_locked = pdata->request_clock_on_locked;
+
+ hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ bt_lpm.enter_lpm_delay = ktime_set(1, 0); /* 1 sec */
+ bt_lpm.enter_lpm_timer.function = enter_lpm;
+
+ gpio_set_value(bt_lpm.gpio_wake, 0);
+ bt_lpm.host_wake = 0;
+
+ irq = gpio_to_irq(bt_lpm.gpio_host_wake);
+ ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH,
+ "bt host_wake", NULL);
+ if (ret)
+ return ret;
+ ret = set_irq_wake(irq, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct platform_driver bcm_bt_lpm_driver = {
+ .probe = bcm_bt_lpm_probe,
+ .driver = {
+ .name = "bcm_bt_lpm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bcm_bt_lpm_init(void)
+{
+ return platform_driver_register(&bcm_bt_lpm_driver);
+}
+
+module_init(bcm_bt_lpm_init);
+MODULE_DESCRIPTION("Broadcom Bluetooth low power mode driver");
+MODULE_AUTHOR("Nick Pelly <npelly@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c
index f8c816e..63f3f3e 100644
--- a/drivers/serial/msm_serial.c
+++ b/drivers/serial/msm_serial.c
@@ -34,45 +34,176 @@
#include "msm_serial.h"
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+enum msm_clk_states_e {
+ MSM_CLK_PORT_OFF, /* uart port not in use */
+ MSM_CLK_OFF, /* clock enabled */
+ MSM_CLK_REQUEST_OFF, /* disable after TX flushed */
+ MSM_CLK_ON, /* clock disabled */
+};
+#endif
+
struct msm_port {
struct uart_port uart;
char name[16];
struct clk *clk;
unsigned int imr;
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+ enum msm_clk_states_e clk_state;
+ struct hrtimer clk_off_timer;
+ ktime_t clk_off_delay;
+#endif
};
static void msm_stop_tx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_enable(msm_port->clk);
+
msm_port->imr &= ~UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
+
+ clk_disable(msm_port->clk);
}
static void msm_start_tx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_enable(msm_port->clk);
+
msm_port->imr |= UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
+
+ clk_disable(msm_port->clk);
}
static void msm_stop_rx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_enable(msm_port->clk);
+
msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
msm_write(port, msm_port->imr, UART_IMR);
+
+ clk_disable(msm_port->clk);
}
static void msm_enable_ms(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_enable(msm_port->clk);
+
msm_port->imr |= UART_IMR_DELTA_CTS;
msm_write(port, msm_port->imr, UART_IMR);
+
+ clk_disable(msm_port->clk);
}
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+/* turn clock off if TX buffer is empty, otherwise reschedule */
+static enum hrtimer_restart msm_serial_clock_off(struct hrtimer *timer) {
+ struct msm_port *msm_port = container_of(timer, struct msm_port,
+ clk_off_timer);
+ struct uart_port *port = &msm_port->uart;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+ int ret = HRTIMER_NORESTART;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (msm_port->clk_state == MSM_CLK_REQUEST_OFF) {
+ if (uart_circ_empty(xmit)) {
+ struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_disable(msm_port->clk);
+ msm_port->clk_state = MSM_CLK_OFF;
+ } else {
+ hrtimer_forward_now(timer, msm_port->clk_off_delay);
+ ret = HRTIMER_RESTART;
+ }
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+/* request to turn off uart clock once pending TX is flushed */
+void msm_serial_clock_request_off(struct uart_port *port) {
+ unsigned long flags;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (msm_port->clk_state == MSM_CLK_ON) {
+ msm_port->clk_state = MSM_CLK_REQUEST_OFF;
+ /* turn off TX later. unfortunately not all msm uart's have a
+ * TXDONE available, and TXLEV does not wait until completely
+ * flushed, so a timer is our only option
+ */
+ hrtimer_start(&msm_port->clk_off_timer,
+ msm_port->clk_off_delay, HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* request to immediately turn on uart clock.
+ * ignored if there is a pending off request, unless force = 1.
+ */
+void msm_serial_clock_on(struct uart_port *port, int force) {
+ unsigned long flags;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ switch (msm_port->clk_state) {
+ case MSM_CLK_OFF:
+ clk_enable(msm_port->clk);
+ force = 1;
+ case MSM_CLK_REQUEST_OFF:
+ if (force) {
+ hrtimer_try_to_cancel(&msm_port->clk_off_timer);
+ msm_port->clk_state = MSM_CLK_ON;
+ }
+ break;
+ case MSM_CLK_ON: break;
+ case MSM_CLK_PORT_OFF: break;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+#define WAKE_UP_IND 0x32
+static irqreturn_t msm_rx_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+ int inject_wakeup = 0;
+
+ spin_lock(&port->lock);
+
+ if (msm_port->clk_state == MSM_CLK_OFF)
+ inject_wakeup = 1;
+
+ msm_serial_clock_on(port, 0);
+
+ /* we missed an rx while asleep - it must be a wakeup indicator
+ */
+ if (inject_wakeup) {
+ struct tty_struct *tty = port->state->port.tty;
+ tty_insert_flip_char(tty, WAKE_UP_IND, TTY_NORMAL);
+ tty_flip_buffer_push(tty);
+ }
+
+ spin_unlock(&port->lock);
+ return IRQ_HANDLED;
+}
+#endif
+
static void handle_rx(struct uart_port *port)
{
struct tty_struct *tty = port->state->port.tty;
@@ -148,6 +279,14 @@
sent_tx = 1;
}
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+ if (sent_tx && msm_port->clk_state == MSM_CLK_REQUEST_OFF)
+ /* new TX - restart the timer */
+ if (hrtimer_try_to_cancel(&msm_port->clk_off_timer) == 1)
+ hrtimer_start(&msm_port->clk_off_timer,
+ msm_port->clk_off_delay, HRTIMER_MODE_REL);
+#endif
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
@@ -166,6 +305,7 @@
unsigned int misr;
spin_lock(&port->lock);
+ clk_enable(msm_port->clk);
misr = msm_read(port, UART_MISR);
msm_write(port, 0, UART_IMR); /* disable interrupt */
@@ -177,6 +317,7 @@
handle_delta_cts(port);
msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */
+ clk_disable(msm_port->clk);
spin_unlock(&port->lock);
return IRQ_HANDLED;
@@ -184,7 +325,14 @@
static unsigned int msm_tx_empty(struct uart_port *port)
{
- return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+ unsigned int ret;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ clk_enable(msm_port->clk);
+ ret = (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+ clk_disable(msm_port->clk);
+
+ return ret;
}
static unsigned int msm_get_mctrl(struct uart_port *port)
@@ -195,6 +343,9 @@
static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ clk_enable(msm_port->clk);
mr = msm_read(port, UART_MR1);
@@ -206,14 +357,22 @@
mr |= UART_MR1_RX_RDY_CTL;
msm_write(port, mr, UART_MR1);
}
+
+ clk_disable(msm_port->clk);
}
static void msm_break_ctl(struct uart_port *port, int break_ctl)
{
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ clk_enable(msm_port->clk);
+
if (break_ctl)
msm_write(port, UART_CR_CMD_START_BREAK, UART_CR);
else
msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
+
+ clk_disable(msm_port->clk);
}
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
@@ -307,6 +466,10 @@
struct msm_port *msm_port = UART_TO_MSM(port);
clk_enable(msm_port->clk);
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+ msm_port->clk_state = MSM_CLK_ON;
+#endif
+
msm_serial_set_mnd_regs(port);
}
@@ -356,6 +519,17 @@
UART_IMR_CURRENT_CTS;
msm_write(port, msm_port->imr, UART_IMR);
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+ /* Apply the RX GPIO wake irq workaround to the bluetooth uart */
+ if (port->line == 0) { /* BT is serial device 0 */
+ ret = request_irq(MSM_GPIO_TO_INT(45), msm_rx_irq,
+ IRQF_TRIGGER_FALLING, "msm_serial0_rx",
+ port);
+ if (unlikely(ret))
+ return ret;
+ }
+#endif
+
return 0;
}
@@ -363,12 +537,27 @@
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ clk_enable(msm_port->clk);
+
msm_port->imr = 0;
msm_write(port, 0, UART_IMR); /* disable interrupts */
clk_disable(msm_port->clk);
free_irq(port->irq, port);
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+ if (port->line == 0)
+ free_irq(MSM_GPIO_TO_INT(45), port);
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+ if (msm_port->clk_state != MSM_CLK_OFF)
+ clk_disable(msm_port->clk);
+ msm_port->clk_state = MSM_CLK_PORT_OFF;
+#else
+ clk_disable(msm_port->clk);
+#endif
}
static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -376,8 +565,10 @@
{
unsigned long flags;
unsigned int baud, mr;
+ struct msm_port *msm_port = UART_TO_MSM(port);
spin_lock_irqsave(&port->lock, flags);
+ clk_enable(msm_port->clk);
/* calculate and set baud rate */
baud = uart_get_baud_rate(port, termios, old, 300, 115200);
@@ -443,6 +634,7 @@
uart_update_timeout(port, termios->c_cflag, baud);
+ clk_disable(msm_port->clk);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -510,6 +702,7 @@
static void msm_power(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
+#ifndef CONFIG_SERIAL_MSM_CLOCK_CONTROL
struct msm_port *msm_port = UART_TO_MSM(port);
switch (state) {
@@ -522,6 +715,7 @@
default:
printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
}
+#endif
}
static struct uart_ops msm_uart_pops = {
@@ -602,7 +796,9 @@
msm_port = UART_TO_MSM(port);
spin_lock(&port->lock);
+ clk_enable(msm_port->clk);
uart_console_write(port, s, count, msm_console_putchar);
+ clk_disable(msm_port->clk);
spin_unlock(&port->lock);
}
@@ -704,6 +900,22 @@
platform_set_drvdata(pdev, port);
+ if (unlikely(set_irq_wake(port->irq, 1)))
+ return -ENXIO;
+
+#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
+ if (port->line == 0) /* BT is serial device 0 */
+ if (unlikely(set_irq_wake(MSM_GPIO_TO_INT(45), 1)))
+ return -ENXIO;
+#endif
+
+#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL
+ msm_port->clk_state = MSM_CLK_PORT_OFF;
+ hrtimer_init(&msm_port->clk_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ msm_port->clk_off_timer.function = msm_serial_clock_off;
+ msm_port->clk_off_delay = ktime_set(0, 1000000); /* 1 ms */
+#endif
+
return uart_add_one_port(&msm_uart_driver, port);
}
diff --git a/drivers/serial/msm_serial.h b/drivers/serial/msm_serial.h
index f6ca9ca..059a69e 100644
--- a/drivers/serial/msm_serial.h
+++ b/drivers/serial/msm_serial.h
@@ -114,6 +114,7 @@
#define UART_MISR 0x0010
#define UART_ISR 0x0014
+#ifndef BUILD_SERIAL_DEBUGGER
#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port)
static inline
@@ -169,5 +170,6 @@
#else
#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk
#endif
+#endif
#endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */
diff --git a/drivers/serial/msm_serial_debugger.c b/drivers/serial/msm_serial_debugger.c
new file mode 100644
index 0000000..f3214d2
--- /dev/null
+++ b/drivers/serial/msm_serial_debugger.c
@@ -0,0 +1,687 @@
+/*
+ * drivers/serial/msm_serial_debuger.c
+ *
+ * Serial Debugger Interface for MSM7K
+ *
+ * Copyright (C) 2008 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 <stdarg.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_debugger.h>
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+
+#include <asm/stacktrace.h>
+
+#include <mach/msm_serial_debugger.h>
+#include <mach/system.h>
+#include <mach/fiq.h>
+
+#define BUILD_SERIAL_DEBUGGER
+#include "msm_serial.h"
+
+#include <linux/uaccess.h>
+
+static void sleep_timer_expired(unsigned long);
+
+static unsigned int debug_port_base;
+static int debug_signal_irq;
+static struct clk *debug_clk;
+static bool debug_clk_enabled;
+static bool ignore_next_wakeup_irq;
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP
+static int no_sleep = true;
+#else
+static int no_sleep;
+#endif
+static DEFINE_TIMER(sleep_timer, sleep_timer_expired, 0, 0);
+static int debug_enable;
+static int debugger_enable;
+static struct wake_lock debugger_wake_lock;
+static struct {
+ unsigned int base;
+ int irq;
+ struct device *clk_device;
+ int signal_irq;
+ int wakeup_irq;
+} init_data;
+
+module_param(no_sleep, bool, 0644);
+
+#ifdef CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+static inline void enable_wakeup_irq(unsigned int irq) {}
+static inline void disable_wakeup_irq(unsigned int irq) {}
+#else
+static inline void enable_wakeup_irq(unsigned int irq) {enable_irq(irq);}
+static inline void disable_wakeup_irq(unsigned int irq)
+ {disable_irq_nosync(irq);}
+#endif
+
+
+static inline void msm_write(unsigned int val, unsigned int off)
+{
+ __raw_writel(val, debug_port_base + off);
+}
+
+static inline unsigned int msm_read(unsigned int off)
+{
+ return __raw_readl(debug_port_base + off);
+}
+
+static void debug_port_init(void)
+{
+ /* reset everything */
+ msm_write(UART_CR_CMD_RESET_RX, UART_CR);
+ msm_write(UART_CR_CMD_RESET_TX, UART_CR);
+ msm_write(UART_CR_CMD_RESET_ERR, UART_CR);
+ msm_write(UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+ msm_write(UART_CR_CMD_RESET_CTS, UART_CR);
+ msm_write(UART_CR_CMD_SET_RFR, UART_CR);
+
+ /* setup clock dividers */
+#ifdef CONFIG_ARCH_MSM_SCORPION
+ if (clk_get_rate(debug_clk) == 19200000) {
+ /* clock is TCXO (19.2MHz) */
+ msm_write(0x06, UART_MREG);
+ msm_write(0xF1, UART_NREG);
+ msm_write(0x0F, UART_DREG);
+ msm_write(0x1A, UART_MNDREG);
+ } else
+#endif
+ {
+ /* clock must be TCXO/4 */
+ msm_write(0xC0, UART_MREG);
+ msm_write(0xB2, UART_NREG);
+ msm_write(0x7D, UART_DREG);
+ msm_write(0x1C, UART_MNDREG);
+ }
+
+ msm_write(UART_CSR_115200, UART_CSR);
+
+ /* rx interrupt on every character -- keep it simple */
+ msm_write(0, UART_RFWR);
+
+ /* enable TX and RX */
+ msm_write(0x05, UART_CR);
+
+ /* enable RX interrupt */
+ msm_write(UART_IMR_RXLEV, UART_IMR);
+}
+
+static inline int debug_getc(void)
+{
+ if (msm_read(UART_SR) & UART_SR_RX_READY) {
+ return msm_read(UART_RF);
+ } else {
+ return -1;
+ }
+}
+
+static inline void debug_putc(unsigned int c)
+{
+ while (!(msm_read(UART_SR) & UART_SR_TX_READY)) ;
+ msm_write(c, UART_TF);
+}
+
+static inline void debug_flush(void)
+{
+ while (!(msm_read(UART_SR) & UART_SR_TX_EMPTY)) ;
+}
+
+static void debug_puts(char *s)
+{
+ unsigned c;
+ while ((c = *s++)) {
+ if (c == '\n')
+ debug_putc('\r');
+ debug_putc(c);
+ }
+}
+
+static void debug_prompt(void)
+{
+ debug_puts("debug> ");
+}
+
+int log_buf_copy(char *dest, int idx, int len);
+static void dump_kernel_log(void)
+{
+ char buf[1024];
+ int idx = 0;
+ int ret;
+ int saved_oip;
+
+ /* setting oops_in_progress prevents log_buf_copy()
+ * from trying to take a spinlock which will make it
+ * very unhappy in some cases...
+ */
+ saved_oip = oops_in_progress;
+ oops_in_progress = 1;
+ for (;;) {
+ ret = log_buf_copy(buf, idx, 1023);
+ if (ret <= 0)
+ break;
+ buf[ret] = 0;
+ debug_puts(buf);
+ idx += ret;
+ }
+ oops_in_progress = saved_oip;
+}
+
+static char *mode_name(unsigned cpsr)
+{
+ switch (cpsr & MODE_MASK) {
+ case USR_MODE: return "USR";
+ case FIQ_MODE: return "FIQ";
+ case IRQ_MODE: return "IRQ";
+ case SVC_MODE: return "SVC";
+ case ABT_MODE: return "ABT";
+ case UND_MODE: return "UND";
+ case SYSTEM_MODE: return "SYS";
+ default: return "???";
+ }
+}
+
+#define DEBUG_MAX 64
+static char debug_cmd[DEBUG_MAX];
+static int debug_busy;
+static int debug_abort;
+
+static int debug_printf(void *cookie, const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ debug_puts(buf);
+ return debug_abort;
+}
+
+/* Safe outside fiq context */
+static int debug_printf_nfiq(void *cookie, const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+ unsigned long irq_flags;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, 128, fmt, ap);
+ va_end(ap);
+
+ local_irq_save(irq_flags);
+ debug_puts(buf);
+ debug_flush();
+ local_irq_restore(irq_flags);
+ return debug_abort;
+}
+
+#define dprintf(fmt...) debug_printf(0, fmt)
+
+unsigned int last_irqs[NR_IRQS];
+
+static void dump_regs(unsigned *regs)
+{
+ dprintf(" r0 %08x r1 %08x r2 %08x r3 %08x\n",
+ regs[0], regs[1], regs[2], regs[3]);
+ dprintf(" r4 %08x r5 %08x r6 %08x r7 %08x\n",
+ regs[4], regs[5], regs[6], regs[7]);
+ dprintf(" r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n",
+ regs[8], regs[9], regs[10], regs[11],
+ mode_name(regs[16]));
+ if ((regs[16] & MODE_MASK) == USR_MODE)
+ dprintf(" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
+ regs[12], regs[13], regs[14], regs[15], regs[16]);
+ else
+ dprintf(" ip %08x sp %08x lr %08x pc %08x cpsr %08x "
+ "spsr %08x\n", regs[12], regs[13], regs[14], regs[15],
+ regs[16], regs[17]);
+}
+
+struct mode_regs {
+ unsigned long sp_svc;
+ unsigned long lr_svc;
+ unsigned long spsr_svc;
+
+ unsigned long sp_abt;
+ unsigned long lr_abt;
+ unsigned long spsr_abt;
+
+ unsigned long sp_und;
+ unsigned long lr_und;
+ unsigned long spsr_und;
+
+ unsigned long sp_irq;
+ unsigned long lr_irq;
+ unsigned long spsr_irq;
+
+ unsigned long r8_fiq;
+ unsigned long r9_fiq;
+ unsigned long r10_fiq;
+ unsigned long r11_fiq;
+ unsigned long r12_fiq;
+ unsigned long sp_fiq;
+ unsigned long lr_fiq;
+ unsigned long spsr_fiq;
+};
+
+void __naked get_mode_regs(struct mode_regs *regs)
+{
+ asm volatile (
+ "mrs r1, cpsr\n"
+ "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r8 - r14}\n"
+ "mrs r2, spsr\n"
+ "stmia r0!, {r2}\n"
+ "msr cpsr_c, r1\n"
+ "bx lr\n");
+}
+
+
+static void dump_allregs(unsigned *regs)
+{
+ struct mode_regs mode_regs;
+ dump_regs(regs);
+ get_mode_regs(&mode_regs);
+ dprintf(" svc: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc);
+ dprintf(" abt: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt);
+ dprintf(" und: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und);
+ dprintf(" irq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq);
+ dprintf(" fiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n",
+ mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq,
+ mode_regs.r11_fiq, mode_regs.r12_fiq);
+ dprintf(" fiq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq);
+}
+
+static void dump_irqs(void)
+{
+ int n;
+ dprintf("irqnr total since-last status name\n");
+ for (n = 1; n < NR_IRQS; n++) {
+ struct irqaction *act = irq_desc[n].action;
+ if (!act && !kstat_irqs(n))
+ continue;
+ dprintf("%5d: %10u %11u %8x %s\n", n,
+ kstat_irqs(n),
+ kstat_irqs(n) - last_irqs[n],
+ irq_desc[n].status,
+ (act && act->name) ? act->name : "???");
+ last_irqs[n] = kstat_irqs(n);
+ }
+}
+
+static int report_trace(struct stackframe *frame, void *d)
+{
+ unsigned int *depth = d;
+
+ if (*depth) {
+ dprintf(" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ frame->pc, frame->pc, frame->lr, frame->lr,
+ frame->sp, frame->fp);
+ (*depth)--;
+ return 0;
+ }
+ dprintf(" ...\n");
+
+ return *depth == 0;
+}
+
+struct frame_tail {
+ struct frame_tail *fp;
+ unsigned long sp;
+ unsigned long lr;
+} __attribute__((packed));
+
+static struct frame_tail *user_backtrace(struct frame_tail *tail)
+{
+ struct frame_tail buftail[2];
+
+ /* Also check accessibility of one struct frame_tail beyond */
+ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) {
+ dprintf(" invalid frame pointer %p\n", tail);
+ return NULL;
+ }
+ if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) {
+ dprintf(" failed to copy frame pointer %p\n", tail);
+ return NULL;
+ }
+
+ dprintf(" %p\n", buftail[0].lr);
+
+ /* frame pointers should strictly progress back up the stack
+ * (towards higher addresses) */
+ if (tail >= buftail[0].fp)
+ return NULL;
+
+ return buftail[0].fp-1;
+}
+
+void dump_stacktrace(struct pt_regs * const regs, unsigned int depth, void *ssp)
+{
+ struct frame_tail *tail;
+ struct thread_info *real_thread_info = (struct thread_info *)
+ ((unsigned long)ssp & ~(THREAD_SIZE - 1));
+
+ *current_thread_info() = *real_thread_info;
+
+ if (!current)
+ dprintf("current NULL\n");
+ else
+ dprintf("pid: %d comm: %s\n", current->pid, current->comm);
+ dump_regs((unsigned *)regs);
+
+ if (!user_mode(regs)) {
+ struct stackframe frame;
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+ dprintf(" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr,
+ regs->ARM_sp, regs->ARM_fp);
+ walk_stackframe(&frame, report_trace, &depth);
+ return;
+ }
+
+ tail = ((struct frame_tail *) regs->ARM_fp) - 1;
+ while (depth-- && tail && !((unsigned long) tail & 3))
+ tail = user_backtrace(tail);
+}
+
+static void debug_exec(const char *cmd, unsigned *regs, void *svc_sp)
+{
+ if (!strcmp(cmd, "pc")) {
+ dprintf(" pc %08x cpsr %08x mode %s\n",
+ regs[15], regs[16], mode_name(regs[16]));
+ } else if (!strcmp(cmd, "regs")) {
+ dump_regs(regs);
+ } else if (!strcmp(cmd, "allregs")) {
+ dump_allregs(regs);
+ } else if (!strcmp(cmd, "bt")) {
+ dump_stacktrace((struct pt_regs *)regs, 100, svc_sp);
+ } else if (!strcmp(cmd, "reboot")) {
+ if (msm_hw_reset_hook)
+ msm_hw_reset_hook();
+ } else if (!strcmp(cmd, "irqs")) {
+ dump_irqs();
+ } else if (!strcmp(cmd, "kmsg")) {
+ dump_kernel_log();
+ } else if (!strcmp(cmd, "version")) {
+ dprintf("%s\n", linux_banner);
+ } else if (!strcmp(cmd, "sleep")) {
+ no_sleep = false;
+ } else if (!strcmp(cmd, "nosleep")) {
+ no_sleep = true;
+ } else {
+ if (debug_busy) {
+ dprintf("command processor busy. trying to abort.\n");
+ debug_abort = -1;
+ } else {
+ strcpy(debug_cmd, cmd);
+ debug_busy = 1;
+ }
+ msm_trigger_irq(debug_signal_irq);
+ return;
+ }
+ debug_prompt();
+}
+
+static void sleep_timer_expired(unsigned long data)
+{
+ if (debug_clk_enabled && !no_sleep) {
+ if (debug_enable) {
+ debug_enable = 0;
+ debug_printf_nfiq(NULL,
+ "suspending fiq debugger\n");
+ }
+ ignore_next_wakeup_irq = true;
+ clk_disable(debug_clk);
+ debug_clk_enabled = false;
+ enable_wakeup_irq(init_data.wakeup_irq);
+ set_irq_wake(init_data.wakeup_irq, 1);
+ }
+ wake_unlock(&debugger_wake_lock);
+}
+
+static irqreturn_t wakeup_irq_handler(int irq, void *dev)
+{
+ if (ignore_next_wakeup_irq)
+ ignore_next_wakeup_irq = false;
+ else if (!debug_clk_enabled) {
+ wake_lock(&debugger_wake_lock);
+ clk_enable(debug_clk);
+ debug_clk_enabled = true;
+ set_irq_wake(irq, 0);
+ disable_wakeup_irq(irq);
+ mod_timer(&sleep_timer, jiffies + HZ / 2);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t debug_irq(int irq, void *dev)
+{
+ if (!no_sleep) {
+ wake_lock(&debugger_wake_lock);
+ mod_timer(&sleep_timer, jiffies + HZ * 5);
+ }
+ if (debug_busy) {
+ struct kdbg_ctxt ctxt;
+
+ ctxt.printf = debug_printf_nfiq;
+ kernel_debugger(&ctxt, debug_cmd);
+ debug_prompt();
+
+ debug_busy = 0;
+ }
+ return IRQ_HANDLED;
+}
+
+static char debug_buf[DEBUG_MAX];
+static int debug_count;
+
+static void debug_fiq(void *data, void *regs, void *svc_sp)
+{
+ int c;
+ static int last_c;
+
+ while ((c = debug_getc()) != -1) {
+ if (!debug_enable) {
+ if ((c == 13) || (c == 10)) {
+ debug_enable = true;
+ debug_count = 0;
+ debug_prompt();
+ }
+ } else if ((c >= ' ') && (c < 127)) {
+ if (debug_count < (DEBUG_MAX - 1)) {
+ debug_buf[debug_count++] = c;
+ debug_putc(c);
+ }
+ } else if ((c == 8) || (c == 127)) {
+ if (debug_count > 0) {
+ debug_count--;
+ debug_putc(8);
+ debug_putc(' ');
+ debug_putc(8);
+ }
+ } else if ((c == 13) || (c == 10)) {
+ if (c == '\r' || (c == '\n' && last_c != '\r')) {
+ debug_putc('\r');
+ debug_putc('\n');
+ }
+ if (debug_count) {
+ debug_buf[debug_count] = 0;
+ debug_count = 0;
+ debug_exec(debug_buf, regs, svc_sp);
+ } else {
+ debug_prompt();
+ }
+ }
+ last_c = c;
+ }
+ debug_flush();
+ if (debug_enable && !no_sleep)
+ msm_trigger_irq(debug_signal_irq); /* poke sleep timer */
+}
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+static void debug_console_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ unsigned long irq_flags;
+
+ /* disable irq's while TXing outside of FIQ context */
+ local_irq_save(irq_flags);
+ while (count--) {
+ if (*s == '\n')
+ debug_putc('\r');
+ debug_putc(*s++);
+ }
+ debug_flush();
+ local_irq_restore(irq_flags);
+}
+
+static struct console msm_serial_debug_console = {
+ .name = "debug_console",
+ .write = debug_console_write,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+};
+#endif
+
+void msm_serial_debug_enable(int enable) {
+ debug_enable = enable;
+}
+
+void msm_serial_debug_init(unsigned int base, int irq,
+ struct device *clk_device, int signal_irq, int wakeup_irq)
+{
+ int ret;
+ void *port;
+
+ debug_clk = clk_get(clk_device, "uart_clk");
+ if (!debug_clk)
+ return;
+
+ port = ioremap(base, 4096);
+ if (!port)
+ return;
+
+ wake_lock_init(&debugger_wake_lock, WAKE_LOCK_SUSPEND, "serial-debug");
+
+ init_data.base = base;
+ init_data.irq = irq;
+ init_data.clk_device = clk_device;
+ init_data.signal_irq = signal_irq;
+ init_data.wakeup_irq = wakeup_irq;
+ debug_port_base = (unsigned int) port;
+ debug_signal_irq = signal_irq;
+ clk_enable(debug_clk);
+ debug_port_init();
+
+ debug_printf_nfiq(NULL, "<hit enter %sto activate fiq debugger>\n",
+ no_sleep ? "" : "twice ");
+ ignore_next_wakeup_irq = !no_sleep;
+
+ msm_fiq_select(irq);
+ msm_fiq_set_handler(debug_fiq, 0);
+ msm_fiq_enable(irq);
+ clk_disable(debug_clk);
+
+ ret = request_irq(signal_irq, debug_irq,
+ IRQF_TRIGGER_RISING, "debug", 0);
+ if (ret)
+ printk(KERN_ERR
+ "serial_debugger: could not install signal_irq");
+
+ ret = set_irq_wake(wakeup_irq, 1);
+ if (ret)
+ pr_err("serial_debugger: could not enable wakeup\n");
+ ret = request_irq(wakeup_irq, wakeup_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ "debug-wakeup", 0);
+ if (ret)
+ pr_err("serial_debugger: could not install wakeup irq\n");
+ if (no_sleep)
+ wakeup_irq_handler(wakeup_irq, 0);
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+ register_console(&msm_serial_debug_console);
+ clk_enable(debug_clk);
+#endif
+ debugger_enable = 1;
+}
+static int msm_serial_debug_remove(const char *val, struct kernel_param *kp)
+{
+ int ret;
+ static int pre_stat = 1;
+ ret = param_set_bool(val, kp);
+ if (ret)
+ return ret;
+
+ if (pre_stat == *(int *)kp->arg)
+ return 0;
+
+ pre_stat = *(int *)kp->arg;
+
+ if (*(int *)kp->arg) {
+ msm_serial_debug_init(init_data.base, init_data.irq,
+ init_data.clk_device, init_data.signal_irq,
+ init_data.wakeup_irq);
+ printk(KERN_INFO "enable FIQ serial debugger\n");
+ return 0;
+ }
+
+#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
+ unregister_console(&msm_serial_debug_console);
+ clk_disable(debug_clk);
+#endif
+ free_irq(init_data.wakeup_irq, 0);
+ free_irq(init_data.signal_irq, 0);
+ msm_fiq_set_handler(NULL, 0);
+ msm_fiq_disable(init_data.irq);
+ msm_fiq_unselect(init_data.irq);
+ if (debug_clk_enabled)
+ clk_disable(debug_clk);
+ wake_lock_destroy(&debugger_wake_lock);
+ printk(KERN_INFO "disable FIQ serial debugger\n");
+ return 0;
+}
+module_param_call(enable, msm_serial_debug_remove, param_get_bool,
+ &debugger_enable, S_IWUSR | S_IRUGO);
diff --git a/drivers/serial/msm_serial_hs.c b/drivers/serial/msm_serial_hs.c
new file mode 100644
index 0000000..305b8b8
--- /dev/null
+++ b/drivers/serial/msm_serial_hs.c
@@ -0,0 +1,1555 @@
+/* drivers/serial/msm_serial_hs.c
+ *
+ * MSM 7k/8k High speed uart driver
+ *
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ * Copyright (c) 2008 Google Inc.
+ * Modified: Nick Pelly <npelly@google.com>
+ *
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+
+/*
+ * MSM 7k/8k High speed uart driver
+ *
+ * Has optional support for uart power management independent of linux
+ * suspend/resume:
+ *
+ * RX wakeup.
+ * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the
+ * UART RX pin). This should only be used if there is not a wakeup
+ * GPIO on the UART CTS, and the first RX byte is known (for example, with the
+ * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will
+ * always be lost. RTS will be asserted even while the UART is off in this mode
+ * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq.
+ */
+
+#include <linux/module.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/msm_serial_hs.h>
+
+#include "msm_serial_hs_hwreg.h"
+
+enum flush_reason {
+ FLUSH_NONE,
+ FLUSH_DATA_READY,
+ FLUSH_DATA_INVALID, /* values after this indicate invalid data */
+ FLUSH_IGNORE = FLUSH_DATA_INVALID,
+ FLUSH_STOP,
+ FLUSH_SHUTDOWN,
+};
+
+enum msm_hs_clk_states_e {
+ MSM_HS_CLK_PORT_OFF, /* port not in use */
+ MSM_HS_CLK_OFF, /* clock disabled */
+ MSM_HS_CLK_REQUEST_OFF, /* disable after TX and RX flushed */
+ MSM_HS_CLK_ON, /* clock enabled */
+};
+
+/* Track the forced RXSTALE flush during clock off sequence.
+ * These states are only valid during MSM_HS_CLK_REQUEST_OFF */
+enum msm_hs_clk_req_off_state_e {
+ CLK_REQ_OFF_START,
+ CLK_REQ_OFF_RXSTALE_ISSUED,
+ CLK_REQ_OFF_FLUSH_ISSUED,
+ CLK_REQ_OFF_RXSTALE_FLUSHED,
+};
+
+struct msm_hs_tx {
+ unsigned int tx_ready_int_en; /* ok to dma more tx */
+ unsigned int dma_in_flight; /* tx dma in progress */
+ struct msm_dmov_cmd xfer;
+ dmov_box *command_ptr;
+ u32 *command_ptr_ptr;
+ dma_addr_t mapped_cmd_ptr;
+ dma_addr_t mapped_cmd_ptr_ptr;
+ int tx_count;
+ dma_addr_t dma_base;
+};
+
+struct msm_hs_rx {
+ enum flush_reason flush;
+ struct msm_dmov_cmd xfer;
+ dma_addr_t cmdptr_dmaaddr;
+ dmov_box *command_ptr;
+ u32 *command_ptr_ptr;
+ dma_addr_t mapped_cmd_ptr;
+ wait_queue_head_t wait;
+ dma_addr_t rbuffer;
+ unsigned char *buffer;
+ struct dma_pool *pool;
+ struct wake_lock wake_lock;
+ struct work_struct tty_work;
+};
+
+/* optional RX GPIO IRQ low power wakeup */
+struct msm_hs_rx_wakeup {
+ int irq; /* < 0 indicates low power wakeup disabled */
+ unsigned char ignore; /* bool */
+
+ /* bool: inject char into rx tty on wakeup */
+ unsigned char inject_rx;
+ char rx_to_inject;
+};
+
+struct msm_hs_port {
+ struct uart_port uport;
+ unsigned long imr_reg; /* shadow value of UARTDM_IMR */
+ struct clk *clk;
+ struct msm_hs_tx tx;
+ struct msm_hs_rx rx;
+
+ int dma_tx_channel;
+ int dma_rx_channel;
+ int dma_tx_crci;
+ int dma_rx_crci;
+
+ struct hrtimer clk_off_timer; /* to poll TXEMT before clock off */
+ ktime_t clk_off_delay;
+ enum msm_hs_clk_states_e clk_state;
+ enum msm_hs_clk_req_off_state_e clk_req_off_state;
+
+ struct msm_hs_rx_wakeup rx_wakeup;
+ /* optional callback to exit low power mode */
+ void (*exit_lpm_cb)(struct uart_port *);
+
+ struct wake_lock dma_wake_lock; /* held while any DMA active */
+};
+
+#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
+#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE
+#define UARTDM_RX_BUF_SIZE 512
+
+#define UARTDM_NR 2
+
+static struct msm_hs_port q_uart_port[UARTDM_NR];
+static struct platform_driver msm_serial_hs_platform_driver;
+static struct uart_driver msm_hs_driver;
+static struct uart_ops msm_hs_ops;
+static struct workqueue_struct *msm_hs_workqueue;
+
+#define UARTDM_TO_MSM(uart_port) \
+ container_of((uart_port), struct msm_hs_port, uport)
+
+static inline unsigned int use_low_power_rx_wakeup(struct msm_hs_port *msm_uport)
+{
+ return (msm_uport->rx_wakeup.irq >= 0);
+}
+
+static inline unsigned int msm_hs_read(struct uart_port *uport,
+ unsigned int offset)
+{
+ return ioread32(uport->membase + offset);
+}
+
+static inline void msm_hs_write(struct uart_port *uport, unsigned int offset,
+ unsigned int value)
+{
+ iowrite32(value, uport->membase + offset);
+}
+
+static void msm_hs_release_port(struct uart_port *port)
+{
+}
+
+static int msm_hs_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static int __devexit msm_hs_remove(struct platform_device *pdev)
+{
+
+ struct msm_hs_port *msm_uport;
+ struct device *dev;
+
+ if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+ printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ return -EINVAL;
+ }
+
+ msm_uport = &q_uart_port[pdev->id];
+ dev = msm_uport->uport.dev;
+
+ dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box),
+ DMA_TO_DEVICE);
+ dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
+ msm_uport->rx.rbuffer);
+ dma_pool_destroy(msm_uport->rx.pool);
+
+ dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *),
+ DMA_TO_DEVICE);
+ dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *),
+ DMA_TO_DEVICE);
+ dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box),
+ DMA_TO_DEVICE);
+
+ wake_lock_destroy(&msm_uport->rx.wake_lock);
+ wake_lock_destroy(&msm_uport->dma_wake_lock);
+
+ uart_remove_one_port(&msm_hs_driver, &msm_uport->uport);
+ clk_put(msm_uport->clk);
+
+ /* Free the tx resources */
+ kfree(msm_uport->tx.command_ptr);
+ kfree(msm_uport->tx.command_ptr_ptr);
+
+ /* Free the rx resources */
+ kfree(msm_uport->rx.command_ptr);
+ kfree(msm_uport->rx.command_ptr_ptr);
+
+ iounmap(msm_uport->uport.membase);
+
+ return 0;
+}
+
+static int msm_hs_init_clk_locked(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ wake_lock(&msm_uport->dma_wake_lock);
+ ret = clk_enable(msm_uport->clk);
+ if (ret) {
+ printk(KERN_ERR "Error could not turn on UART clk\n");
+ return ret;
+ }
+
+ /* Set up the MREG/NREG/DREG/MNDREG */
+ ret = clk_set_rate(msm_uport->clk, uport->uartclk);
+ if (ret) {
+ printk(KERN_WARNING "Error setting clock rate on UART\n");
+ return ret;
+ }
+
+ msm_uport->clk_state = MSM_HS_CLK_ON;
+ return 0;
+}
+
+/* Enable and Disable clocks (Used for power management) */
+static void msm_hs_pm(struct uart_port *uport, unsigned int state,
+ unsigned int oldstate)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ if (use_low_power_rx_wakeup(msm_uport) || msm_uport->exit_lpm_cb)
+ return; /* ignore linux PM states, use msm_hs_request_clock API */
+
+ switch (state) {
+ case 0:
+ clk_enable(msm_uport->clk);
+ break;
+ case 3:
+ clk_disable(msm_uport->clk);
+ break;
+ default:
+ printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
+ }
+}
+
+/*
+ * programs the UARTDM_CSR register with correct bit rates
+ *
+ * Interrupts should be disabled before we are called, as
+ * we modify Set Baud rate
+ * Set receive stale interrupt level, dependant on Bit Rate
+ * Goal is to have around 8 ms before indicate stale.
+ * roundup (((Bit Rate * .008) / 10) + 1
+ */
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+ unsigned int bps)
+{
+ unsigned long rxstale;
+ unsigned long data;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ switch (bps) {
+ case 300:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x00);
+ rxstale = 1;
+ break;
+ case 600:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x11);
+ rxstale = 1;
+ break;
+ case 1200:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x22);
+ rxstale = 1;
+ break;
+ case 2400:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x33);
+ rxstale = 1;
+ break;
+ case 4800:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x44);
+ rxstale = 1;
+ break;
+ case 9600:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x55);
+ rxstale = 2;
+ break;
+ case 14400:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x66);
+ rxstale = 3;
+ break;
+ case 19200:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x77);
+ rxstale = 4;
+ break;
+ case 28800:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x88);
+ rxstale = 6;
+ break;
+ case 38400:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99);
+ rxstale = 8;
+ break;
+ case 57600:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa);
+ rxstale = 16;
+ break;
+ case 76800:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb);
+ rxstale = 16;
+ break;
+ case 115200:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc);
+ rxstale = 31;
+ break;
+ case 230400:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee);
+ rxstale = 31;
+ break;
+ case 460800:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
+ rxstale = 31;
+ break;
+ case 4000000:
+ case 3686400:
+ case 3200000:
+ case 3500000:
+ case 3000000:
+ case 2500000:
+ case 1500000:
+ case 1152000:
+ case 1000000:
+ case 921600:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
+ rxstale = 31;
+ break;
+ default:
+ msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff);
+ /* default to 9600 */
+ bps = 9600;
+ rxstale = 2;
+ break;
+ }
+ if (bps > 460800) {
+ uport->uartclk = bps * 16;
+ } else {
+ uport->uartclk = 7372800;
+ }
+ if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
+ printk(KERN_WARNING "Error setting clock rate on UART\n");
+ return;
+ }
+
+ data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
+ data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+
+ msm_hs_write(uport, UARTDM_IPR_ADDR, data);
+}
+
+/*
+ * termios : new ktermios
+ * oldtermios: old ktermios previous setting
+ *
+ * Configure the serial port
+ */
+static void msm_hs_set_termios(struct uart_port *uport,
+ struct ktermios *termios,
+ struct ktermios *oldtermios)
+{
+ unsigned int bps;
+ unsigned long data;
+ unsigned long flags;
+ unsigned int c_cflag = termios->c_cflag;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ spin_lock_irqsave(&uport->lock, flags);
+ clk_enable(msm_uport->clk);
+
+ /* 300 is the minimum baud support by the driver */
+ bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000);
+
+ /* Temporary remapping 200 BAUD to 3.2 mbps */
+ if (bps == 200)
+ bps = 3200000;
+
+ msm_hs_set_bps_locked(uport, bps);
+
+ data = msm_hs_read(uport, UARTDM_MR2_ADDR);
+ data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
+ /* set parity */
+ if (PARENB == (c_cflag & PARENB)) {
+ if (PARODD == (c_cflag & PARODD)) {
+ data |= ODD_PARITY;
+ } else if (CMSPAR == (c_cflag & CMSPAR)) {
+ data |= SPACE_PARITY;
+ } else {
+ data |= EVEN_PARITY;
+ }
+ }
+
+ /* Set bits per char */
+ data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK;
+
+ switch (c_cflag & CSIZE) {
+ case CS5:
+ data |= FIVE_BPC;
+ break;
+ case CS6:
+ data |= SIX_BPC;
+ break;
+ case CS7:
+ data |= SEVEN_BPC;
+ break;
+ default:
+ data |= EIGHT_BPC;
+ break;
+ }
+ /* stop bits */
+ if (c_cflag & CSTOPB) {
+ data |= STOP_BIT_TWO;
+ } else {
+ /* otherwise 1 stop bit */
+ data |= STOP_BIT_ONE;
+ }
+ data |= UARTDM_MR2_ERROR_MODE_BMSK;
+ /* write parity/bits per char/stop bit configuration */
+ msm_hs_write(uport, UARTDM_MR2_ADDR, data);
+
+ /* Configure HW flow control */
+ data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+
+ data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK);
+
+ if (c_cflag & CRTSCTS) {
+ data |= UARTDM_MR1_CTS_CTL_BMSK;
+ data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+ }
+
+ msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+
+ uport->ignore_status_mask = termios->c_iflag & INPCK;
+ uport->ignore_status_mask |= termios->c_iflag & IGNPAR;
+ uport->read_status_mask = (termios->c_cflag & CREAD);
+
+ msm_hs_write(uport, UARTDM_IMR_ADDR, 0);
+
+ /* Set Transmit software time out */
+ uart_update_timeout(uport, c_cflag, bps);
+
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+
+ if (msm_uport->rx.flush == FLUSH_NONE) {
+ wake_lock(&msm_uport->rx.wake_lock);
+ msm_uport->rx.flush = FLUSH_IGNORE;
+ msm_dmov_flush(msm_uport->dma_rx_channel);
+ }
+
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+ clk_disable(msm_uport->clk);
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+/*
+ * Standard API, Transmitter
+ * Any character in the transmit shift register is sent
+ */
+static unsigned int msm_hs_tx_empty(struct uart_port *uport)
+{
+ unsigned int data;
+ unsigned int ret = 0;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ clk_enable(msm_uport->clk);
+
+ data = msm_hs_read(uport, UARTDM_SR_ADDR);
+ if (data & UARTDM_SR_TXEMT_BMSK)
+ ret = TIOCSER_TEMT;
+
+ clk_disable(msm_uport->clk);
+
+ return ret;
+}
+
+/*
+ * Standard API, Stop transmitter.
+ * Any character in the transmit shift register is sent as
+ * well as the current data mover transfer .
+ */
+static void msm_hs_stop_tx_locked(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ msm_uport->tx.tx_ready_int_en = 0;
+}
+
+/*
+ * Standard API, Stop receiver as soon as possible.
+ *
+ * Function immediately terminates the operation of the
+ * channel receiver and any incoming characters are lost. None
+ * of the receiver status bits are affected by this command and
+ * characters that are already in the receive FIFO there.
+ */
+static void msm_hs_stop_rx_locked(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ unsigned int data;
+
+ clk_enable(msm_uport->clk);
+
+ /* disable dlink */
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data &= ~UARTDM_RX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+
+ /* Disable the receiver */
+ if (msm_uport->rx.flush == FLUSH_NONE) {
+ wake_lock(&msm_uport->rx.wake_lock);
+ msm_dmov_flush(msm_uport->dma_rx_channel);
+ }
+ if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
+ msm_uport->rx.flush = FLUSH_STOP;
+
+ clk_disable(msm_uport->clk);
+}
+
+/* Transmit the next chunk of data */
+static void msm_hs_submit_tx_locked(struct uart_port *uport)
+{
+ int left;
+ int tx_count;
+ dma_addr_t src_addr;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+
+ if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
+ msm_hs_stop_tx_locked(uport);
+ return;
+ }
+
+ tx->dma_in_flight = 1;
+
+ tx_count = uart_circ_chars_pending(tx_buf);
+
+ if (UARTDM_TX_BUF_SIZE < tx_count)
+ tx_count = UARTDM_TX_BUF_SIZE;
+
+ left = UART_XMIT_SIZE - tx_buf->tail;
+
+ if (tx_count > left)
+ tx_count = left;
+
+ src_addr = tx->dma_base + tx_buf->tail;
+ dma_sync_single_for_device(uport->dev, src_addr, tx_count,
+ DMA_TO_DEVICE);
+
+ tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
+ ((tx_count + 15) >> 4);
+ tx->command_ptr->src_row_addr = src_addr;
+
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+ *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+ sizeof(u32 *), DMA_TO_DEVICE);
+
+ /* Save tx_count to use in Callback */
+ tx->tx_count = tx_count;
+ msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count);
+
+ /* Disable the tx_ready interrupt */
+ msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+ msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+}
+
+/* Start to receive the next chunk of data */
+static void msm_hs_start_rx_locked(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+ msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
+ msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
+ msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+ msm_uport->rx.flush = FLUSH_NONE;
+ msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
+
+ /* might have finished RX and be ready to clock off */
+ hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay,
+ HRTIMER_MODE_REL);
+}
+
+/* Enable the transmitter Interrupt */
+static void msm_hs_start_tx_locked(struct uart_port *uport )
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ clk_enable(msm_uport->clk);
+
+ if (msm_uport->exit_lpm_cb)
+ msm_uport->exit_lpm_cb(uport);
+
+ if (msm_uport->tx.tx_ready_int_en == 0) {
+ msm_uport->tx.tx_ready_int_en = 1;
+ msm_hs_submit_tx_locked(uport);
+ }
+
+ clk_disable(msm_uport->clk);
+}
+
+/*
+ * This routine is called when we are done with a DMA transfer
+ *
+ * This routine is registered with Data mover when we set
+ * up a Data Mover transfer. It is called from Data mover ISR
+ * when the DMA transfer is done.
+ */
+static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr,
+ unsigned int result,
+ struct msm_dmov_errdata *err)
+{
+ unsigned long flags;
+ struct msm_hs_port *msm_uport;
+
+ WARN_ON(result != 0x80000002); /* DMA did not finish properly */
+ msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer);
+
+ spin_lock_irqsave(&msm_uport->uport.lock, flags);
+ clk_enable(msm_uport->clk);
+
+ msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK;
+ msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+ clk_disable(msm_uport->clk);
+ spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+}
+
+/*
+ * This routine is called when we are done with a DMA transfer or the
+ * a flush has been sent to the data mover driver.
+ *
+ * This routine is registered with Data mover when we set up a Data Mover
+ * transfer. It is called from Data mover ISR when the DMA transfer is done.
+ */
+static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
+ unsigned int result,
+ struct msm_dmov_errdata *err)
+{
+ int retval;
+ int rx_count;
+ unsigned long status;
+ unsigned int error_f = 0;
+ unsigned long flags;
+ unsigned int flush;
+ struct tty_struct *tty;
+ struct uart_port *uport;
+ struct msm_hs_port *msm_uport;
+
+ msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer);
+ uport = &msm_uport->uport;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ clk_enable(msm_uport->clk);
+
+ tty = uport->state->port.tty;
+
+ msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
+
+ status = msm_hs_read(uport, UARTDM_SR_ADDR);
+
+ /* overflow is not connect to data in a FIFO */
+ if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
+ (uport->read_status_mask & CREAD))) {
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ uport->icount.buf_overrun++;
+ error_f = 1;
+ }
+
+ if (!(uport->ignore_status_mask & INPCK))
+ status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
+
+ if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
+ /* Can not tell difference between parity & frame error */
+ uport->icount.parity++;
+ error_f = 1;
+ if (uport->ignore_status_mask & IGNPAR)
+ tty_insert_flip_char(tty, 0, TTY_PARITY);
+ }
+
+ if (error_f)
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
+
+ if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED)
+ msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED;
+
+ flush = msm_uport->rx.flush;
+ if (flush == FLUSH_IGNORE)
+ msm_hs_start_rx_locked(uport);
+ if (flush == FLUSH_STOP)
+ msm_uport->rx.flush = FLUSH_SHUTDOWN;
+ if (flush >= FLUSH_DATA_INVALID)
+ goto out;
+
+ rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+
+ if (0 != (uport->read_status_mask & CREAD)) {
+ retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
+ rx_count);
+ BUG_ON(retval != rx_count);
+ }
+
+ msm_hs_start_rx_locked(uport);
+
+out:
+ clk_disable(msm_uport->clk);
+ /* release wakelock in 500ms, not immediately, because higher layers
+ * don't always take wakelocks when they should */
+ wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ if (flush < FLUSH_DATA_INVALID)
+ queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
+}
+
+static void msm_hs_tty_flip_buffer_work(struct work_struct *work)
+{
+ struct msm_hs_port *msm_uport =
+ container_of(work, struct msm_hs_port, rx.tty_work);
+ struct tty_struct *tty = msm_uport->uport.state->port.tty;
+
+ tty_flip_buffer_push(tty);
+}
+
+/*
+ * Standard API, Current states of modem control inputs
+ *
+ * Since CTS can be handled entirely by HARDWARE we always
+ * indicate clear to send and count on the TX FIFO to block when
+ * it fills up.
+ *
+ * - TIOCM_DCD
+ * - TIOCM_CTS
+ * - TIOCM_DSR
+ * - TIOCM_RI
+ * (Unsupported) DCD and DSR will return them high. RI will return low.
+ */
+static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+/*
+ * True enables UART auto RFR, which indicates we are ready for data if the RX
+ * buffer is not full. False disables auto RFR, and deasserts RFR to indicate
+ * we are not ready for data. Must be called with UART clock on.
+ */
+static void set_rfr_locked(struct uart_port *uport, int auto_rfr) {
+ unsigned int data;
+
+ data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+
+ if (auto_rfr) {
+ /* enable auto ready-for-receiving */
+ data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+ msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+ } else {
+ /* disable auto ready-for-receiving */
+ data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
+ msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+ /* RFR is active low, set high */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH);
+ }
+}
+
+/*
+ * Standard API, used to set or clear RFR
+ */
+static void msm_hs_set_mctrl_locked(struct uart_port *uport,
+ unsigned int mctrl)
+{
+ unsigned int auto_rfr;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ clk_enable(msm_uport->clk);
+
+ auto_rfr = TIOCM_RTS & mctrl ? 1 : 0;
+ set_rfr_locked(uport, auto_rfr);
+
+ clk_disable(msm_uport->clk);
+}
+
+/* Standard API, Enable modem status (CTS) interrupt */
+static void msm_hs_enable_ms_locked(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ clk_enable(msm_uport->clk);
+
+ /* Enable DELTA_CTS Interrupt */
+ msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+ clk_disable(msm_uport->clk);
+
+}
+
+/*
+ * Standard API, Break Signal
+ *
+ * Control the transmission of a break signal. ctl eq 0 => break
+ * signal terminate ctl ne 0 => start break signal
+ */
+static void msm_hs_break_ctl(struct uart_port *uport, int ctl)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ clk_enable(msm_uport->clk);
+ msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK);
+ clk_disable(msm_uport->clk);
+}
+
+static void msm_hs_config_port(struct uart_port *uport, int cfg_flags)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ if (cfg_flags & UART_CONFIG_TYPE) {
+ uport->type = PORT_MSM;
+ msm_hs_request_port(uport);
+ }
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+/* Handle CTS changes (Called from interrupt handler) */
+static void msm_hs_handle_delta_cts(struct uart_port *uport)
+{
+ unsigned long flags;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ spin_lock_irqsave(&uport->lock, flags);
+ clk_enable(msm_uport->clk);
+
+ /* clear interrupt */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS);
+ uport->icount.cts++;
+
+ clk_disable(msm_uport->clk);
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ /* clear the IOCTL TIOCMIWAIT if called */
+ wake_up_interruptible(&uport->state->port.delta_msr_wait);
+}
+
+/* check if the TX path is flushed, and if so clock off
+ * returns 0 did not clock off, need to retry (still sending final byte)
+ * -1 did not clock off, do not retry
+ * 1 if we clocked off
+ */
+static int msm_hs_check_clock_off_locked(struct uart_port *uport)
+{
+ unsigned long sr_status;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct circ_buf *tx_buf = &uport->state->xmit;
+
+ /* Cancel if tx tty buffer is not empty, dma is in flight,
+ * or tx fifo is not empty, or rx fifo is not empty */
+ if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF ||
+ !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight ||
+ (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) ||
+ !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK)) {
+ return -1;
+ }
+
+ /* Make sure the uart is finished with the last byte */
+ sr_status = msm_hs_read(uport, UARTDM_SR_ADDR);
+ if (!(sr_status & UARTDM_SR_TXEMT_BMSK))
+ return 0; /* retry */
+
+ /* Make sure forced RXSTALE flush complete */
+ switch (msm_uport->clk_req_off_state) {
+ case CLK_REQ_OFF_START:
+ msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
+ msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
+ return 0; /* RXSTALE flush not complete - retry */
+ case CLK_REQ_OFF_RXSTALE_ISSUED:
+ case CLK_REQ_OFF_FLUSH_ISSUED:
+ return 0; /* RXSTALE flush not complete - retry */
+ case CLK_REQ_OFF_RXSTALE_FLUSHED:
+ break; /* continue */
+ }
+
+ if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
+ if (msm_uport->rx.flush == FLUSH_NONE)
+ msm_hs_stop_rx_locked(uport);
+ return 0; /* come back later to really clock off */
+ }
+
+ /* we really want to clock off */
+ clk_disable(msm_uport->clk);
+ msm_uport->clk_state = MSM_HS_CLK_OFF;
+ wake_unlock(&msm_uport->dma_wake_lock);
+ if (use_low_power_rx_wakeup(msm_uport)) {
+ msm_uport->rx_wakeup.ignore = 1;
+ enable_irq(msm_uport->rx_wakeup.irq);
+ }
+ return 1;
+}
+
+static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) {
+ unsigned long flags;
+ int ret = HRTIMER_NORESTART;
+ struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port,
+ clk_off_timer);
+ struct uart_port *uport = &msm_uport->uport;
+
+ spin_lock_irqsave(&uport->lock, flags);
+
+ if (!msm_hs_check_clock_off_locked(uport)) {
+ hrtimer_forward_now(timer, msm_uport->clk_off_delay);
+ ret = HRTIMER_RESTART;
+ }
+
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ return ret;
+}
+
+static irqreturn_t msm_hs_isr(int irq, void *dev)
+{
+ unsigned long flags;
+ unsigned long isr_status;
+ struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
+ struct uart_port *uport = &msm_uport->uport;
+ struct circ_buf *tx_buf = &uport->state->xmit;
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+
+ spin_lock_irqsave(&uport->lock, flags);
+
+ isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR);
+
+ /* Uart RX starting */
+ if (isr_status & UARTDM_ISR_RXLEV_BMSK) {
+ wake_lock(&rx->wake_lock); /* hold wakelock while rx dma */
+ msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+ }
+ /* Stale rx interrupt */
+ if (isr_status & UARTDM_ISR_RXSTALE_BMSK) {
+ msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+
+ if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
+ msm_uport->clk_req_off_state =
+ CLK_REQ_OFF_FLUSH_ISSUED;
+ if (rx->flush == FLUSH_NONE) {
+ rx->flush = FLUSH_DATA_READY;
+ msm_dmov_flush(msm_uport->dma_rx_channel);
+ }
+ }
+ /* tx ready interrupt */
+ if (isr_status & UARTDM_ISR_TX_READY_BMSK) {
+ /* Clear TX Ready */
+ msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY);
+
+ if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF) {
+ msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR,
+ msm_uport->imr_reg);
+ }
+
+ /* Complete DMA TX transactions and submit new transactions */
+ tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE;
+
+ tx->dma_in_flight = 0;
+
+ uport->icount.tx += tx->tx_count;
+ if (tx->tx_ready_int_en)
+ msm_hs_submit_tx_locked(uport);
+
+ if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
+ uart_write_wakeup(uport);
+ }
+ if (isr_status & UARTDM_ISR_TXLEV_BMSK) {
+ /* TX FIFO is empty */
+ msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+ if (!msm_hs_check_clock_off_locked(uport))
+ hrtimer_start(&msm_uport->clk_off_timer,
+ msm_uport->clk_off_delay,
+ HRTIMER_MODE_REL);
+ }
+
+ /* Change in CTS interrupt */
+ if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK)
+ msm_hs_handle_delta_cts(uport);
+
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void msm_hs_request_clock_off_locked(struct uart_port *uport) {
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ if (msm_uport->clk_state == MSM_HS_CLK_ON) {
+ msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF;
+ msm_uport->clk_req_off_state = CLK_REQ_OFF_START;
+ if (!use_low_power_rx_wakeup(msm_uport))
+ set_rfr_locked(uport, 0);
+ msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+ }
+}
+EXPORT_SYMBOL(msm_hs_request_clock_off_locked);
+
+/* request to turn off uart clock once pending TX is flushed */
+void msm_hs_request_clock_off(struct uart_port *uport) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ msm_hs_request_clock_off_locked(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+EXPORT_SYMBOL(msm_hs_request_clock_off);
+
+void msm_hs_request_clock_on_locked(struct uart_port *uport) {
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ unsigned int data;
+
+ switch (msm_uport->clk_state) {
+ case MSM_HS_CLK_OFF:
+ wake_lock(&msm_uport->dma_wake_lock);
+ clk_enable(msm_uport->clk);
+ disable_irq_nosync(msm_uport->rx_wakeup.irq);
+ /* fall-through */
+ case MSM_HS_CLK_REQUEST_OFF:
+ if (msm_uport->rx.flush == FLUSH_STOP ||
+ msm_uport->rx.flush == FLUSH_SHUTDOWN) {
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data |= UARTDM_RX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+ }
+ hrtimer_try_to_cancel(&msm_uport->clk_off_timer);
+ if (msm_uport->rx.flush == FLUSH_SHUTDOWN)
+ msm_hs_start_rx_locked(uport);
+ if (!use_low_power_rx_wakeup(msm_uport))
+ set_rfr_locked(uport, 1);
+ if (msm_uport->rx.flush == FLUSH_STOP)
+ msm_uport->rx.flush = FLUSH_IGNORE;
+ msm_uport->clk_state = MSM_HS_CLK_ON;
+ break;
+ case MSM_HS_CLK_ON: break;
+ case MSM_HS_CLK_PORT_OFF: break;
+ }
+}
+EXPORT_SYMBOL(msm_hs_request_clock_on_locked);
+
+void msm_hs_request_clock_on(struct uart_port *uport) {
+ unsigned long flags;
+ spin_lock_irqsave(&uport->lock, flags);
+ msm_hs_request_clock_on_locked(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+EXPORT_SYMBOL(msm_hs_request_clock_on);
+
+static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev)
+{
+ unsigned int wakeup = 0;
+ unsigned long flags;
+ struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
+ struct uart_port *uport = &msm_uport->uport;
+ struct tty_struct *tty = NULL;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ if (msm_uport->clk_state == MSM_HS_CLK_OFF) {
+ /* ignore the first irq - it is a pending irq that occured
+ * before enable_irq() */
+ if (msm_uport->rx_wakeup.ignore)
+ msm_uport->rx_wakeup.ignore = 0;
+ else
+ wakeup = 1;
+ }
+
+ if (wakeup) {
+ /* the uart was clocked off during an rx, wake up and
+ * optionally inject char into tty rx */
+ msm_hs_request_clock_on_locked(uport);
+ if (msm_uport->rx_wakeup.inject_rx) {
+ tty = uport->state->port.tty;
+ tty_insert_flip_char(tty,
+ msm_uport->rx_wakeup.rx_to_inject,
+ TTY_NORMAL);
+ queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
+ }
+ }
+
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static const char *msm_hs_type(struct uart_port *port)
+{
+ return ("MSM HS UART");
+}
+
+/* Called when port is opened */
+static int msm_hs_startup(struct uart_port *uport)
+{
+ int ret;
+ int rfr_level;
+ unsigned long flags;
+ unsigned int data;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct circ_buf *tx_buf = &uport->state->xmit;
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+
+ rfr_level = uport->fifosize;
+ if (rfr_level > 16)
+ rfr_level -= 16;
+
+ tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+
+ /* do not let tty layer execute RX in global workqueue, use a
+ * dedicated workqueue managed by this driver */
+ uport->state->port.tty->low_latency = 1;
+
+ /* turn on uart clk */
+ ret = msm_hs_init_clk_locked(uport);
+ if (unlikely(ret))
+ return ret;
+
+ /* Set auto RFR Level */
+ data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+ data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
+ data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK;
+ data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2));
+ data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level);
+ msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+
+ /* Make sure RXSTALE count is non-zero */
+ data = msm_hs_read(uport, UARTDM_IPR_ADDR);
+ if (!data) {
+ data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK;
+ msm_hs_write(uport, UARTDM_IPR_ADDR, data);
+ }
+
+ /* Enable Data Mover Mode */
+ data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+
+ /* Reset TX */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS);
+ msm_hs_write(uport, UARTDM_CR_ADDR, RFR_LOW);
+ /* Turn on Uart Receiver */
+ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK);
+
+ /* Turn on Uart Transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK);
+
+ /* Initialize the tx */
+ tx->tx_ready_int_en = 0;
+ tx->dma_in_flight = 0;
+
+ tx->xfer.complete_func = msm_hs_dmov_tx_callback;
+ tx->xfer.execute_func = NULL;
+
+ tx->command_ptr->cmd = CMD_LC |
+ CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
+
+ tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+ | (MSM_UARTDM_BURST_SIZE);
+
+ tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
+
+ tx->command_ptr->dst_row_addr =
+ msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+
+
+ /* Turn on Uart Receive */
+ rx->xfer.complete_func = msm_hs_dmov_rx_callback;
+ rx->xfer.execute_func = NULL;
+
+ rx->command_ptr->cmd = CMD_LC |
+ CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX;
+
+ rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+ | (MSM_UARTDM_BURST_SIZE);
+ rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE;
+ rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR;
+
+
+ msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
+ /* Enable reading the current CTS, no harm even if CTS is ignored */
+ msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK;
+
+ msm_hs_write(uport, UARTDM_TFWR_ADDR, 0); /* TXLEV on empty TX fifo */
+
+
+ ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH,
+ "msm_hs_uart", msm_uport);
+ if (unlikely(ret))
+ return ret;
+ if (use_low_power_rx_wakeup(msm_uport)) {
+ ret = request_irq(msm_uport->rx_wakeup.irq,
+ msm_hs_rx_wakeup_isr,
+ IRQF_TRIGGER_FALLING,
+ "msm_hs_rx_wakeup", msm_uport);
+ if (unlikely(ret))
+ return ret;
+ disable_irq(msm_uport->rx_wakeup.irq);
+ }
+
+ spin_lock_irqsave(&uport->lock, flags);
+
+ msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+ msm_hs_start_rx_locked(uport);
+
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ return 0;
+}
+
+/* Initialize tx and rx data structures */
+static int uartdm_init_port(struct uart_port *uport)
+{
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+
+ tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+
+ if (!tx->command_ptr || !tx->command_ptr_ptr)
+ return -ENOMEM;
+
+ tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+ tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+ tx->command_ptr_ptr,
+ sizeof(u32 *), DMA_TO_DEVICE);
+ tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
+ init_waitqueue_head(&rx->wait);
+ wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
+ wake_lock_init(&msm_uport->dma_wake_lock, WAKE_LOCK_SUSPEND,
+ "msm_serial_hs_dma");
+
+ rx->pool = dma_pool_create("rx_buffer_pool", uport->dev,
+ UARTDM_RX_BUF_SIZE, 16, 0);
+
+ rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer);
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+
+ rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+
+ if (!rx->command_ptr || !rx->command_ptr_ptr || !rx->pool ||
+ !rx->buffer)
+ return -ENOMEM;
+
+ rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) |
+ (UARTDM_RX_BUF_SIZE >> 4);
+
+ rx->command_ptr->dst_row_addr = rx->rbuffer;
+
+ rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+ *rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr);
+
+ rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr,
+ sizeof(u32 *), DMA_TO_DEVICE);
+ rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
+
+ INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work);
+
+ return 0;
+}
+
+static int msm_hs_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct uart_port *uport;
+ struct msm_hs_port *msm_uport;
+ struct resource *resource;
+ struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+ printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ return -EINVAL;
+ }
+
+ msm_uport = &q_uart_port[pdev->id];
+ uport = &msm_uport->uport;
+
+ uport->dev = &pdev->dev;
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource))
+ return -ENXIO;
+ uport->mapbase = resource->start; /* virtual address */
+
+ uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
+ if (unlikely(!uport->membase))
+ return -ENOMEM;
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (unlikely(uport->irq < 0))
+ return -ENXIO;
+ if (unlikely(set_irq_wake(uport->irq, 1)))
+ return -ENXIO;
+
+ if (pdata == NULL || pdata->rx_wakeup_irq < 0)
+ msm_uport->rx_wakeup.irq = -1;
+ else {
+ msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq;
+ msm_uport->rx_wakeup.ignore = 1;
+ msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup;
+ msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject;
+
+ if (unlikely(msm_uport->rx_wakeup.irq < 0))
+ return -ENXIO;
+ if (unlikely(set_irq_wake(msm_uport->rx_wakeup.irq, 1)))
+ return -ENXIO;
+ }
+
+ if (pdata == NULL)
+ msm_uport->exit_lpm_cb = NULL;
+ else
+ msm_uport->exit_lpm_cb = pdata->exit_lpm_cb;
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+ "uartdm_channels");
+ if (unlikely(!resource))
+ return -ENXIO;
+ msm_uport->dma_tx_channel = resource->start;
+ msm_uport->dma_rx_channel = resource->end;
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+ "uartdm_crci");
+ if (unlikely(!resource))
+ return -ENXIO;
+ msm_uport->dma_tx_crci = resource->start;
+ msm_uport->dma_rx_crci = resource->end;
+
+ uport->iotype = UPIO_MEM;
+ uport->fifosize = 64;
+ uport->ops = &msm_hs_ops;
+ uport->flags = UPF_BOOT_AUTOCONF;
+ uport->uartclk = 7372800;
+ msm_uport->imr_reg = 0x0;
+ msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk");
+ if (IS_ERR(msm_uport->clk))
+ return PTR_ERR(msm_uport->clk);
+
+ ret = uartdm_init_port(uport);
+ if (unlikely(ret))
+ return ret;
+
+ /* configure the CR Protection to Enable */
+ msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN);
+
+ msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
+ hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ msm_uport->clk_off_timer.function = msm_hs_clk_off_retry;
+ msm_uport->clk_off_delay = ktime_set(0, 1000000); /* 1ms */
+
+ uport->line = pdev->id;
+ return uart_add_one_port(&msm_hs_driver, uport);
+}
+
+static int __init msm_serial_hs_init(void)
+{
+ int ret;
+ int i;
+
+ /* Init all UARTS as non-configured */
+ for (i = 0; i < UARTDM_NR; i++)
+ q_uart_port[i].uport.type = PORT_UNKNOWN;
+
+ msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs");
+
+ ret = uart_register_driver(&msm_hs_driver);
+ if (unlikely(ret)) {
+ printk(KERN_ERR "%s failed to load\n", __FUNCTION__);
+ return ret;
+ }
+ ret = platform_driver_register(&msm_serial_hs_platform_driver);
+ if (ret) {
+ printk(KERN_ERR "%s failed to load\n", __FUNCTION__);
+ uart_unregister_driver(&msm_hs_driver);
+ return ret;
+ }
+
+ printk(KERN_INFO "msm_serial_hs module loaded\n");
+ return ret;
+}
+
+/*
+ * Called by the upper layer when port is closed.
+ * - Disables the port
+ * - Unhook the ISR
+ */
+static void msm_hs_shutdown(struct uart_port *uport)
+{
+ unsigned long flags;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+ BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
+
+ spin_lock_irqsave(&uport->lock, flags);
+ clk_enable(msm_uport->clk);
+
+ /* Disable the transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
+ /* Disable the receiver */
+ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK);
+
+ /* Free the interrupt */
+ free_irq(uport->irq, msm_uport);
+ if (use_low_power_rx_wakeup(msm_uport))
+ free_irq(msm_uport->rx_wakeup.irq, msm_uport);
+
+ msm_uport->imr_reg = 0;
+ msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+ wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
+
+ clk_disable(msm_uport->clk); /* to balance local clk_enable() */
+ if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
+ wake_unlock(&msm_uport->dma_wake_lock);
+ clk_disable(msm_uport->clk); /* to balance clk_state */
+ }
+ msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
+
+ dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ if (cancel_work_sync(&msm_uport->rx.tty_work))
+ msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work);
+}
+
+static void __exit msm_serial_hs_exit(void)
+{
+ printk(KERN_INFO "msm_serial_hs module removed\n");
+ platform_driver_unregister(&msm_serial_hs_platform_driver);
+ uart_unregister_driver(&msm_hs_driver);
+ destroy_workqueue(msm_hs_workqueue);
+}
+
+static struct platform_driver msm_serial_hs_platform_driver = {
+ .probe = msm_hs_probe,
+ .remove = msm_hs_remove,
+ .driver = {
+ .name = "msm_serial_hs",
+ },
+};
+
+static struct uart_driver msm_hs_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "msm_serial_hs",
+ .dev_name = "ttyHS",
+ .nr = UARTDM_NR,
+ .cons = 0,
+};
+
+static struct uart_ops msm_hs_ops = {
+ .tx_empty = msm_hs_tx_empty,
+ .set_mctrl = msm_hs_set_mctrl_locked,
+ .get_mctrl = msm_hs_get_mctrl_locked,
+ .stop_tx = msm_hs_stop_tx_locked,
+ .start_tx = msm_hs_start_tx_locked,
+ .stop_rx = msm_hs_stop_rx_locked,
+ .enable_ms = msm_hs_enable_ms_locked,
+ .break_ctl = msm_hs_break_ctl,
+ .startup = msm_hs_startup,
+ .shutdown = msm_hs_shutdown,
+ .set_termios = msm_hs_set_termios,
+ .pm = msm_hs_pm,
+ .type = msm_hs_type,
+ .config_port = msm_hs_config_port,
+ .release_port = msm_hs_release_port,
+ .request_port = msm_hs_request_port,
+};
+
+module_init(msm_serial_hs_init);
+module_exit(msm_serial_hs_exit);
+MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset");
+MODULE_VERSION("1.2");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/serial/msm_serial_hs_hwreg.h b/drivers/serial/msm_serial_hs_hwreg.h
new file mode 100644
index 0000000..df59678
--- /dev/null
+++ b/drivers/serial/msm_serial_hs_hwreg.h
@@ -0,0 +1,149 @@
+/* drivers/serial/msm_serial_hs_hwreg.h
+ *
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+
+#ifndef MSM_SERIAL_HS_HWREG_H
+#define MSM_SERIAL_HS_HWREG_H
+
+#define UARTDM_MR1_ADDR 0x0
+#define UARTDM_MR2_ADDR 0x4
+
+/* write only register */
+#define UARTDM_CSR_ADDR 0x8
+
+/* write only register */
+#define UARTDM_TF_ADDR 0x70
+#define UARTDM_TF2_ADDR 0x74
+#define UARTDM_TF3_ADDR 0x78
+#define UARTDM_TF4_ADDR 0x7C
+
+/* write only register */
+#define UARTDM_CR_ADDR 0x10
+/* write only register */
+#define UARTDM_IMR_ADDR 0x14
+
+#define UARTDM_IPR_ADDR 0x18
+#define UARTDM_TFWR_ADDR 0x1c
+#define UARTDM_RFWR_ADDR 0x20
+#define UARTDM_HCR_ADDR 0x24
+#define UARTDM_DMRX_ADDR 0x34
+#define UARTDM_IRDA_ADDR 0x38
+#define UARTDM_DMEN_ADDR 0x3c
+
+/* UART_DM_NO_CHARS_FOR_TX */
+#define UARTDM_NCF_TX_ADDR 0x40
+
+#define UARTDM_BADR_ADDR 0x44
+
+#define UARTDM_SIM_CFG_ADDR 0x80
+
+/* Read Only register */
+#define UARTDM_SR_ADDR 0x8
+
+/* Read Only register */
+#define UARTDM_RF_ADDR 0x70
+#define UARTDM_RF2_ADDR 0x74
+#define UARTDM_RF3_ADDR 0x78
+#define UARTDM_RF4_ADDR 0x7C
+
+/* Read Only register */
+#define UARTDM_MISR_ADDR 0x10
+
+/* Read Only register */
+#define UARTDM_ISR_ADDR 0x14
+#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38
+
+#define UARTDM_RXFS_ADDR 0x50
+
+/* Register field Mask Mapping */
+#define UARTDM_SR_PAR_FRAME_BMSK BIT(5)
+#define UARTDM_SR_OVERRUN_BMSK BIT(4)
+#define UARTDM_SR_TXEMT_BMSK BIT(3)
+#define UARTDM_SR_TXRDY_BMSK BIT(2)
+#define UARTDM_SR_RXRDY_BMSK BIT(0)
+
+#define UARTDM_CR_TX_DISABLE_BMSK BIT(3)
+#define UARTDM_CR_RX_DISABLE_BMSK BIT(1)
+#define UARTDM_CR_TX_EN_BMSK BIT(2)
+#define UARTDM_CR_RX_EN_BMSK BIT(0)
+
+/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */
+#define RESET_RX 0x10
+#define RESET_TX 0x20
+#define RESET_ERROR_STATUS 0x30
+#define RESET_BREAK_INT 0x40
+#define START_BREAK 0x50
+#define STOP_BREAK 0x60
+#define RESET_CTS 0x70
+#define RESET_STALE_INT 0x80
+#define RFR_LOW 0xD0
+#define RFR_HIGH 0xE0
+#define CR_PROTECTION_EN 0x100
+#define STALE_EVENT_ENABLE 0x500
+#define STALE_EVENT_DISABLE 0x600
+#define FORCE_STALE_EVENT 0x400
+#define CLEAR_TX_READY 0x300
+#define RESET_TX_ERROR 0x800
+#define RESET_TX_DONE 0x810
+
+#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00
+#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f
+#define UARTDM_MR1_CTS_CTL_BMSK 0x40
+#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80
+
+#define UARTDM_MR2_ERROR_MODE_BMSK 0x40
+#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30
+
+/* bits per character configuration */
+#define FIVE_BPC (0 << 4)
+#define SIX_BPC (1 << 4)
+#define SEVEN_BPC (2 << 4)
+#define EIGHT_BPC (3 << 4)
+
+#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc
+#define STOP_BIT_ONE (1 << 2)
+#define STOP_BIT_TWO (3 << 2)
+
+#define UARTDM_MR2_PARITY_MODE_BMSK 0x3
+
+/* Parity configuration */
+#define NO_PARITY 0x0
+#define EVEN_PARITY 0x1
+#define ODD_PARITY 0x2
+#define SPACE_PARITY 0x3
+
+#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
+#define UARTDM_IPR_STALE_LSB_BMSK 0x1f
+
+/* These can be used for both ISR and IMR register */
+#define UARTDM_ISR_TX_READY_BMSK BIT(7)
+#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6)
+#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5)
+#define UARTDM_ISR_RXLEV_BMSK BIT(4)
+#define UARTDM_ISR_RXSTALE_BMSK BIT(3)
+#define UARTDM_ISR_RXBREAK_BMSK BIT(2)
+#define UARTDM_ISR_RXHUNT_BMSK BIT(1)
+#define UARTDM_ISR_TXLEV_BMSK BIT(0)
+
+/* Field definitions for UART_DM_DMEN*/
+#define UARTDM_TX_DM_EN_BMSK 0x1
+#define UARTDM_RX_DM_EN_BMSK 0x2
+
+#endif /* MSM_SERIAL_HS_HWREG_H */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f..9048096 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -243,6 +243,11 @@
The driver can be configured to use any SSP port and additional
documentation can be found a Documentation/spi/pxa2xx.
+config SPI_QSD
+ tristate "Qualcomm MSM SPI support"
+ default n
+ depends on ARCH_MSM_SCORPION
+
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410 && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18..50b4ee3 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -36,6 +36,7 @@
obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
+obj-$(CONFIG_SPI_QSD) += spi_qsd.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
new file mode 100644
index 0000000..1392d29
--- /dev/null
+++ b/drivers/spi/spi_qsd.c
@@ -0,0 +1,1605 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora Forum nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this software
+ * may be relicensed by the recipient under the terms of the GNU General Public
+ * License version 2 ("GPL") and only version 2, in which case the provisions of
+ * the GPL apply INSTEAD OF those given above. If the recipient relicenses the
+ * software under the GPL, then the identification text in the MODULE_LICENSE
+ * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a
+ * recipient changes the license terms to the GPL, subsequent recipients shall
+ * not relicense under alternate licensing terms, including the BSD or dual
+ * BSD/GPL terms. In addition, the following license statement immediately
+ * below and between the words START and END shall also then apply when this
+ * software is relicensed under the GPL:
+ *
+ * START
+ *
+ * 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * END
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * SPI driver for Qualcomm QSD platforms
+ *
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <mach/msm_spi.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <mach/dma.h>
+#include <asm/atomic.h>
+
+#define SPI_CONFIG 0x0000
+#define SPI_IO_CONTROL 0x0004
+#define SPI_IO_MODES 0x0008
+#define SPI_SW_RESET 0x000C
+#define SPI_TIME_OUT 0x0010
+#define SPI_TIME_OUT_CURRENT 0x0014
+#define SPI_MX_OUTPUT_COUNT 0x0018
+#define SPI_MX_OUTPUT_CNT_CURRENT 0x001C
+#define SPI_MX_INPUT_COUNT 0x0020
+#define SPI_MX_INPUT_CNT_CURRENT 0x0024
+#define SPI_MX_READ_COUNT 0x0028
+#define SPI_MX_READ_CNT_CURRENT 0x002C
+#define SPI_OPERATIONAL 0x0030
+#define SPI_ERROR_FLAGS 0x0034
+#define SPI_ERROR_FLAGS_EN 0x0038
+#define SPI_DEASSERT_WAIT 0x003C
+#define SPI_OUTPUT_DEBUG 0x0040
+#define SPI_INPUT_DEBUG 0x0044
+#define SPI_FIFO_WORD_CNT 0x0048
+#define SPI_TEST_CTRL 0x004C
+#define SPI_OUTPUT_FIFO 0x0100
+#define SPI_INPUT_FIFO 0x0200
+
+/* SPI_CONFIG fields */
+#define SPI_CFG_INPUT_FIRST 0x00000200
+#define SPI_NO_INPUT 0x00000080
+#define SPI_NO_OUTPUT 0x00000040
+#define SPI_CFG_LOOPBACK 0x00000100
+#define SPI_CFG_N 0x0000001F
+
+/* SPI_IO_CONTROL fields */
+#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400
+#define SPI_IO_C_MX_CS_MODE 0x00000100
+#define SPI_IO_C_CS_N_POLARITY 0x000000F0
+#define SPI_IO_C_CS_N_POLARITY_0 0x00000010
+#define SPI_IO_C_CS_SELECT 0x0000000C
+#define SPI_IO_C_TRISTATE_CS 0x00000002
+#define SPI_IO_C_NO_TRI_STATE 0x00000001
+
+/* SPI_IO_MODES fields */
+#define SPI_IO_M_OUTPUT_BIT_SHIFT_EN 0x00004000
+#define SPI_IO_M_PACK_EN 0x00002000
+#define SPI_IO_M_UNPACK_EN 0x00001000
+#define SPI_IO_M_INPUT_MODE 0x00000C00
+#define SPI_IO_M_OUTPUT_MODE 0x00000300
+#define SPI_IO_M_INPUT_FIFO_SIZE 0x000000C0
+#define SPI_IO_M_INPUT_BLOCK_SIZE 0x00000030
+#define SPI_IO_M_OUTPUT_FIFO_SIZE 0x0000000C
+#define SPI_IO_M_OUTPUT_BLOCK_SIZE 0x00000003
+
+/* SPI_OPERATIONAL fields */
+#define SPI_OP_MAX_INPUT_DONE_FLAG 0x00000800
+#define SPI_OP_MAX_OUTPUT_DONE_FLAG 0x00000400
+#define SPI_OP_INPUT_SERVICE_FLAG 0x00000200
+#define SPI_OP_OUTPUT_SERVICE_FLAG 0x00000100
+#define SPI_OP_INPUT_FIFO_FULL 0x00000080
+#define SPI_OP_OUTPUT_FIFO_FULL 0x00000040
+#define SPI_OP_IP_FIFO_NOT_EMPTY 0x00000020
+#define SPI_OP_OP_FIFO_NOT_EMPTY 0x00000010
+#define SPI_OP_STATE_VALID 0x00000004
+#define SPI_OP_STATE 0x00000003
+#define SPI_OP_STATE_RESET 0x00000000
+#define SPI_OP_STATE_RUN 0x00000001
+#define SPI_OP_STATE_PAUSE 0x00000003
+
+/* SPI_ERROR_FLAGS fields */
+#define SPI_ERR_TIME_OUT_ERR 0x00000040
+#define SPI_ERR_OUTPUT_OVER_RUN_ERR 0x00000020
+#define SPI_ERR_INPUT_UNDER_RUN_ERR 0x00000010
+#define SPI_ERR_OUTPUT_UNDER_RUN_ERR 0x00000008
+#define SPI_ERR_INPUT_OVER_RUN_ERR 0x00000004
+#define SPI_ERR_CLK_OVER_RUN_ERR 0x00000002
+#define SPI_ERR_CLK_UNDER_RUN_ERR 0x00000001
+#define SPI_ERR_MASK 0x0000007F
+
+/* We don't allow transactions larger than 4K-64 */
+#define SPI_MAX_TRANSFERS 0x000FC0
+#define SPI_MAX_LEN (SPI_MAX_TRANSFERS * dd->bytes_per_word)
+#define SPI_MAX_TIMEOUT 0x00010000
+#define SPI_MIN_TRANS_TIME 50
+
+#define SPI_NUM_CHIPSELECTS 4
+#define SPI_QSD_NAME "spi_qsd"
+
+/* Data Mover burst size */
+#define DM_BURST_SIZE 16
+/* Data Mover commands should be aligned to 64 bit(8 bytes) */
+#define DM_BYTE_ALIGN 8
+
+enum msm_spi_mode {
+ SPI_FIFO_MODE = 0x0, /* 00 */
+ SPI_BLOCK_MODE = 0x1, /* 01 */
+ SPI_DMOV_MODE = 0x2, /* 10 */
+ SPI_MODE_NONE = 0xFF, /* invalid value */
+};
+
+/* Structures for Data Mover */
+struct spi_dmov_cmd {
+ dmov_box box; /* data aligned to max(dm_burst_size, block_size)
+ (<= fifo_size) */
+ dmov_s single_pad; /* data unaligned to max(dm_burst_size, block_size)
+ padded to fit */
+ dma_addr_t cmd_ptr;
+};
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2");
+MODULE_ALIAS("platform:spi_qsd");
+
+#ifdef CONFIG_DEBUG_FS
+/* Used to create debugfs entries */
+static const struct {
+ const char *name;
+ mode_t mode;
+ int offset;
+} debugfs_spi_regs[] = {
+ {"config", S_IRUGO | S_IWUSR, SPI_CONFIG},
+ {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL},
+ {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES},
+ {"sw_reset", S_IWUSR, SPI_SW_RESET},
+ {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT},
+ {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT},
+ {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT},
+ {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT},
+ {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT},
+ {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT},
+ {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT},
+ {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT},
+ {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL},
+ {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS},
+ {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN},
+ {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT},
+ {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG},
+ {"input_debug", S_IRUGO, SPI_INPUT_DEBUG},
+ {"fifo_word_cnt", S_IRUGO, SPI_FIFO_WORD_CNT},
+ {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL},
+ {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO},
+ {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO},
+};
+#endif
+
+struct msm_spi {
+ u8 *read_buf;
+ const u8 *write_buf;
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t queue_lock;
+ struct list_head queue;
+ struct workqueue_struct *workqueue;
+ struct work_struct work_data;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct completion transfer_complete;
+ struct clk *clk;
+ struct clk *pclk;
+ int max_clock_speed;
+ unsigned long mem_phys_addr;
+ size_t mem_size;
+ u32 rx_bytes_remaining;
+ u32 tx_bytes_remaining;
+ u32 clock_speed;
+ u32 irq_in;
+ u32 irq_out;
+ u32 irq_err;
+ int bytes_per_word;
+ bool suspended;
+ bool transfer_in_progress;
+ /* DMA data */
+ enum msm_spi_mode mode;
+ bool use_dma;
+ int tx_dma_chan;
+ int tx_dma_crci;
+ int rx_dma_chan;
+ int rx_dma_crci;
+ /* Data Mover Commands */
+ struct spi_dmov_cmd *tx_dmov_cmd;
+ struct spi_dmov_cmd *rx_dmov_cmd;
+ /* Physical address of the tx dmov box command */
+ dma_addr_t tx_dmov_cmd_dma;
+ dma_addr_t rx_dmov_cmd_dma;
+ struct msm_dmov_cmd tx_hdr;
+ struct msm_dmov_cmd rx_hdr;
+ int block_size;
+ int burst_size;
+ atomic_t rx_irq_called;
+ /* Used to pad messages unaligned to block size */
+ u8 *tx_padding;
+ dma_addr_t tx_padding_dma;
+ u8 *rx_padding;
+ dma_addr_t rx_padding_dma;
+ u32 unaligned_len;
+ /* DMA statistics */
+ u32 stat_dmov_err;
+ u32 stat_rx;
+ u32 stat_tx;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent_spi;
+ struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)];
+#endif
+};
+
+static int input_fifo_size;
+
+static void msm_spi_clock_set(struct msm_spi *dd, int speed)
+{
+ int rc;
+
+ rc = clk_set_rate(dd->clk, speed);
+ if (!rc)
+ dd->clock_speed = speed;
+}
+
+/* Assumption: input_fifo=output_fifo */
+static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd)
+{
+ u32 spi_iom;
+ int block;
+ int mult;
+ int words;
+
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> 4;
+ mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> 6;
+ switch (block) {
+ case 0:
+ words = 1;
+ break;
+ case 1:
+ words = 4;
+ break;
+ case 2:
+ words = 8;
+ break;
+ default:
+ goto fifo_size_err;
+ }
+ switch (mult) {
+ case 0:
+ input_fifo_size = words * 2;
+ break;
+ case 1:
+ input_fifo_size = words * 4;
+ break;
+ case 2:
+ input_fifo_size = words * 8;
+ break;
+ default:
+ goto fifo_size_err;
+ }
+
+ /* we can't use dma with 8 bytes of fifo since dm burst size is 16 */
+ if (input_fifo_size*sizeof(u32) < DM_BURST_SIZE)
+ dd->use_dma = 0;
+ if (dd->use_dma) {
+ dd->block_size = words*sizeof(u32); /* in bytes */
+ dd->burst_size = max(dd->block_size, DM_BURST_SIZE);
+ }
+
+ return;
+
+fifo_size_err:
+ printk(KERN_WARNING "%s: invalid FIFO size, SPI_IO_MODES=0x%x\n",
+ __func__, spi_iom);
+ return;
+}
+
+static void msm_spi_read_word_from_fifo(struct msm_spi *dd)
+{
+ u32 data_in;
+ int i;
+ int shift;
+
+ data_in = readl(dd->base + SPI_INPUT_FIFO);
+ if (dd->read_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->rx_bytes_remaining; i++) {
+ /* The data format depends on bytes_per_word:
+ 4 bytes: 0x12345678
+ 3 bytes: 0x00123456
+ 2 bytes: 0x00001234
+ 1 byte : 0x00000012
+ */
+ shift = 8 * (dd->bytes_per_word - i - 1);
+ *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift;
+ dd->rx_bytes_remaining--;
+ }
+ } else {
+ if (dd->rx_bytes_remaining >= dd->bytes_per_word)
+ dd->rx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->rx_bytes_remaining = 0;
+ }
+}
+
+static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
+{
+ dmov_box *box;
+ int bytes_to_send, num_rows, bytes_sent;
+ u32 num_transfers, timeout;
+
+ atomic_set(&dd->rx_irq_called, 0);
+ bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
+ /* We'll send in chunks of SPI_MAX_LEN if larger */
+ bytes_to_send = dd->tx_bytes_remaining / SPI_MAX_LEN ?
+ SPI_MAX_LEN : dd->tx_bytes_remaining;
+ num_transfers = DIV_ROUND_UP(bytes_to_send, dd->bytes_per_word);
+ dd->unaligned_len = bytes_to_send % dd->burst_size;
+ /* We multiply by 8bitsinbyte and multiply by 1.5 for safety */
+ timeout = bytes_to_send * 12 > SPI_MAX_TIMEOUT ?
+ 0 : roundup(bytes_to_send * 12, SPI_MIN_TRANS_TIME);
+ num_rows = bytes_to_send / dd->burst_size;
+
+ dd->mode = SPI_DMOV_MODE;
+
+ if (num_rows) {
+ /* src in 16 MSB, dst in 16 LSB */
+ box = &dd->tx_dmov_cmd->box;
+ box->src_row_addr = dd->cur_transfer->tx_dma + bytes_sent;
+ box->src_dst_len = (dd->burst_size << 16) | dd->burst_size;
+ box->num_rows = (num_rows << 16) | num_rows;
+ box->row_offset = (dd->burst_size << 16) | 0;
+
+ box = &dd->rx_dmov_cmd->box;
+ box->dst_row_addr = dd->cur_transfer->rx_dma + bytes_sent;
+ box->src_dst_len = (dd->burst_size << 16) | dd->burst_size;
+ box->num_rows = (num_rows << 16) | num_rows;
+ box->row_offset = (0 << 16) | dd->burst_size;
+
+ dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, box));
+ dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, box));
+ } else {
+ dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, single_pad));
+ dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, single_pad));
+ }
+
+ if (!dd->unaligned_len) {
+ dd->tx_dmov_cmd->box.cmd |= CMD_LC;
+ dd->rx_dmov_cmd->box.cmd |= CMD_LC;
+ } else {
+ dmov_s *tx_cmd = &(dd->tx_dmov_cmd->single_pad);
+ dmov_s *rx_cmd = &(dd->rx_dmov_cmd->single_pad);
+ u32 offset = dd->cur_transfer->len - dd->unaligned_len;
+
+ dd->tx_dmov_cmd->box.cmd &= ~CMD_LC;
+ dd->rx_dmov_cmd->box.cmd &= ~CMD_LC;
+
+ memset(dd->tx_padding, 0, dd->burst_size);
+ memset(dd->rx_padding, 0, dd->burst_size);
+ if (dd->write_buf)
+ memcpy(dd->tx_padding, dd->write_buf + offset,
+ dd->unaligned_len);
+
+ tx_cmd->src = dd->tx_padding_dma;
+ rx_cmd->dst = dd->rx_padding_dma;
+ tx_cmd->len = rx_cmd->len = dd->burst_size;
+ }
+ /* This also takes care of the padding dummy buf
+ Since this is set to the correct length, the
+ dummy bytes won't be actually sent */
+ if (dd->write_buf)
+ writel(num_transfers, dd->base + SPI_MX_OUTPUT_COUNT);
+ if (dd->read_buf)
+ writel(num_transfers, dd->base + SPI_MX_INPUT_COUNT);
+ /* Write timeout */
+ writel(timeout, dd->base + SPI_TIME_OUT);
+}
+
+static void msm_spi_enqueue_dm_commands(struct msm_spi *dd)
+{
+ if (dd->write_buf)
+ msm_dmov_enqueue_cmd(dd->tx_dma_chan, &dd->tx_hdr);
+ if (dd->read_buf)
+ msm_dmov_enqueue_cmd(dd->rx_dma_chan, &dd->rx_hdr);
+}
+
+/* SPI core can send maximum of 4K transfers, because there is HW problem
+ with infinite mode.
+ Therefore, we are sending several chunks of 3K or less (depending on how
+ much is left).
+ Upon completion we send the next chunk, or complete the transfer if
+ everything is finished.
+*/
+static int msm_spi_dm_send_next(struct msm_spi *dd)
+{
+ /* By now we should have sent all the bytes in FIFO mode,
+ * However to make things right, we'll check anyway.
+ */
+ if (dd->mode != SPI_DMOV_MODE)
+ return 0;
+
+ /* We need to send more chunks, if we sent max last time */
+ if (dd->tx_bytes_remaining > SPI_MAX_LEN) {
+ dd->tx_bytes_remaining -= SPI_MAX_LEN;
+ writel((readl(dd->base + SPI_OPERATIONAL)
+ & ~SPI_OP_STATE) | SPI_OP_STATE_PAUSE,
+ dd->base + SPI_OPERATIONAL);
+ msm_spi_setup_dm_transfer(dd);
+ msm_spi_enqueue_dm_commands(dd);
+
+ writel((readl(dd->base + SPI_OPERATIONAL)
+ & ~SPI_OP_STATE) | SPI_OP_STATE_RUN,
+ dd->base + SPI_OPERATIONAL);
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void msm_spi_ack_transfer(struct msm_spi *dd)
+{
+ writel(SPI_OP_MAX_INPUT_DONE_FLAG | SPI_OP_MAX_OUTPUT_DONE_FLAG,
+ dd->base + SPI_OPERATIONAL);
+ writel(0, dd->base + SPI_TIME_OUT);
+}
+
+static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+
+ dd->stat_rx++;
+
+ if (dd->mode == SPI_MODE_NONE)
+ return IRQ_HANDLED;
+
+ if (dd->mode == SPI_DMOV_MODE) {
+ u32 op = readl(dd->base + SPI_OPERATIONAL);
+ if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) &&
+ (!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) {
+ msm_spi_ack_transfer(dd);
+ if (dd->unaligned_len == 0) {
+ if (atomic_inc_return(&dd->rx_irq_called) == 1)
+ return IRQ_HANDLED;
+ }
+ complete(&dd->transfer_complete);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+ }
+
+ /* fifo mode */
+ while ((readl(dd->base + SPI_OPERATIONAL) & SPI_OP_IP_FIFO_NOT_EMPTY) &&
+ (dd->rx_bytes_remaining > 0)) {
+ msm_spi_read_word_from_fifo(dd);
+ }
+ if (dd->rx_bytes_remaining == 0)
+ complete(&dd->transfer_complete);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_write_word_to_fifo(struct msm_spi *dd)
+{
+ u32 word;
+ u8 byte;
+ int i;
+
+ word = 0;
+ if (dd->write_buf) {
+ for (i = 0; (i < dd->bytes_per_word) &&
+ dd->tx_bytes_remaining; i++) {
+ dd->tx_bytes_remaining--;
+ byte = *dd->write_buf++;
+ word |= (byte << (BITS_PER_BYTE * (3 - i)));
+ }
+ } else
+ if (dd->tx_bytes_remaining > dd->bytes_per_word)
+ dd->tx_bytes_remaining -= dd->bytes_per_word;
+ else
+ dd->tx_bytes_remaining = 0;
+ writel(word, dd->base + SPI_OUTPUT_FIFO);
+}
+
+static irqreturn_t msm_spi_output_irq(int irq, void *dev_id)
+{
+ struct msm_spi *dd = dev_id;
+ int count = 0;
+
+ dd->stat_tx++;
+
+ if (dd->mode == SPI_MODE_NONE)
+ return IRQ_HANDLED;
+
+ if (dd->mode == SPI_DMOV_MODE) {
+ /* TX_ONLY transaction is handled here
+ This is the only place we send complete at tx and not rx */
+ if (dd->read_buf == NULL && readl(dd->base + SPI_OPERATIONAL) &
+ SPI_OP_MAX_OUTPUT_DONE_FLAG) {
+ msm_spi_ack_transfer(dd);
+ complete(&dd->transfer_complete);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+ }
+
+ /* Output FIFO is empty. Transmit any outstanding write data. */
+ /* There could be one word in input FIFO, so don't send more */
+ /* than input_fifo_size - 1 more words. */
+ while ((dd->tx_bytes_remaining > 0) &&
+ (count < input_fifo_size - 1) &&
+ !(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_OUTPUT_FIFO_FULL)) {
+ msm_spi_write_word_to_fifo(dd);
+ count++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct msm_spi *dd = spi_master_get_devdata(master);
+ u32 spi_err;
+
+ spi_err = readl(dd->base + SPI_ERROR_FLAGS);
+ if (spi_err & SPI_ERR_TIME_OUT_ERR) {
+ dev_warn(master->dev.parent, "SPI timeout error\n");
+ msm_dmov_flush(dd->tx_dma_chan);
+ msm_dmov_flush(dd->rx_dma_chan);
+ }
+ if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output overrun error\n");
+ if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI input underrun error\n");
+ if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI output underrun error\n");
+ if (spi_err & SPI_ERR_INPUT_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI input overrun error\n");
+ if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock overrun error\n");
+ if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR)
+ dev_warn(master->dev.parent, "SPI clock underrun error\n");
+ writel(SPI_ERR_MASK, dd->base + SPI_ERROR_FLAGS);
+ return IRQ_HANDLED;
+}
+
+static void msm_spi_unmap_dma_buffers(struct msm_spi *dd)
+{
+ struct device *dev;
+
+ dev = &dd->cur_msg->spi->dev;
+ if (!dd->cur_msg->is_dma_mapped) {
+ if (dd->cur_transfer->rx_buf)
+ dma_unmap_single(dev, dd->cur_transfer->rx_dma,
+ dd->cur_transfer->len,
+ DMA_FROM_DEVICE);
+ if (dd->cur_transfer->tx_buf)
+ dma_unmap_single(dev, dd->cur_transfer->tx_dma,
+ dd->cur_transfer->len,
+ DMA_TO_DEVICE);
+ }
+ /* If we padded the transfer, we copy it from the padding buf */
+ if (dd->unaligned_len && dd->read_buf) {
+ u32 offset = dd->cur_transfer->len - dd->unaligned_len;
+ memcpy(dd->read_buf + offset, dd->rx_padding,
+ dd->unaligned_len);
+ }
+}
+
+/**
+ * msm_use_dm - decides whether to use data mover for this
+ * transfer
+ * @dd: device
+ * @tr: transfer
+ *
+ * Start using DM if:
+ * 1. Transfer is longer than 3*block size.
+ * 2. Buffers should be aligned to cache line.
+ * 3. If Transfer is bigger than max_output_count, we accept only aligned to
+ * block size transfers.
+ */
+static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr)
+{
+ u32 cache_line = dma_get_cache_alignment();
+
+ if (!dd->use_dma)
+ return 0;
+
+ if (tr->len < 3*dd->block_size)
+ return 0;
+
+ if (tr->tx_buf) {
+ if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
+ return 0;
+ }
+ if (tr->rx_buf) {
+ if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
+ return 0;
+ }
+ return 1;
+}
+
+static void msm_spi_process_transfer(struct msm_spi *dd)
+{
+ u8 bpw;
+ u32 spi_config;
+ u32 spi_ioc;
+ u32 spi_iom;
+ u32 spi_ioc_orig;
+ u32 max_speed;
+ u32 chip_select;
+ u32 read_count;
+ u32 timeout;
+
+ if (!dd->cur_transfer->len)
+ return;
+ dd->tx_bytes_remaining = dd->cur_transfer->len;
+ dd->rx_bytes_remaining = dd->cur_transfer->len;
+ dd->read_buf = dd->cur_transfer->rx_buf;
+ dd->write_buf = dd->cur_transfer->tx_buf;
+ init_completion(&dd->transfer_complete);
+ if (dd->cur_transfer->bits_per_word)
+ bpw = dd->cur_transfer->bits_per_word;
+ else
+ if (dd->cur_msg->spi->bits_per_word)
+ bpw = dd->cur_msg->spi->bits_per_word;
+ else
+ bpw = 8;
+ dd->bytes_per_word = (bpw + 7) / 8;
+
+ if (dd->cur_transfer->speed_hz)
+ max_speed = dd->cur_transfer->speed_hz;
+ else
+ max_speed = dd->cur_msg->spi->max_speed_hz;
+ if (!dd->clock_speed || max_speed < dd->clock_speed)
+ msm_spi_clock_set(dd, max_speed);
+
+ read_count = DIV_ROUND_UP(dd->cur_transfer->len, dd->bytes_per_word);
+ if (!msm_use_dm(dd, dd->cur_transfer)) {
+ dd->mode = SPI_FIFO_MODE;
+ /* read_count cannot exceed fifo_size, and only one READ COUNT
+ interrupt is generated per transaction, so for transactions
+ larger than fifo size READ COUNT must be disabled.
+ For those transactions we usually move to Data Mover mode.
+ */
+ if (read_count <= input_fifo_size)
+ writel(read_count, dd->base + SPI_MX_READ_COUNT);
+ else
+ writel(0, dd->base + SPI_MX_READ_COUNT);
+ } else
+ dd->mode = SPI_DMOV_MODE;
+
+ /* Write mode - fifo or data mover*/
+ spi_iom = readl(dd->base + SPI_IO_MODES);
+ spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+ spi_iom = (spi_iom | (dd->mode << 10));
+ spi_iom = (spi_iom | (dd->mode << 8));
+ /* Turn on packing for data mover */
+ if (dd->mode == SPI_DMOV_MODE)
+ spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
+ else
+ spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+ writel(spi_iom, dd->base + SPI_IO_MODES);
+
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if ((bpw - 1) != (spi_config & SPI_CFG_N))
+ spi_config = (spi_config & ~SPI_CFG_N) | (bpw - 1);
+ if (dd->cur_msg->spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+ if (dd->cur_msg->spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+ spi_config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
+ if (dd->mode == SPI_DMOV_MODE) {
+ if (dd->read_buf == NULL)
+ spi_config |= SPI_NO_INPUT;
+ if (dd->write_buf == NULL)
+ spi_config |= SPI_NO_OUTPUT;
+ }
+ writel(spi_config, dd->base + SPI_CONFIG);
+
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ if (dd->cur_msg->spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+ chip_select = dd->cur_msg->spi->chip_select << 2;
+ if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+ spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+ if (!dd->cur_transfer->cs_change)
+ spi_ioc |= SPI_IO_C_MX_CS_MODE;
+ if (spi_ioc != spi_ioc_orig)
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+ if (dd->mode == SPI_DMOV_MODE) {
+ msm_spi_setup_dm_transfer(dd);
+ msm_spi_enqueue_dm_commands(dd);
+ }
+ /* The output fifo interrupt handler will handle all writes after
+ the first. Restricting this to one write avoids contention
+ issues and race conditions between this thread and the int handler
+ */
+ else if (dd->mode == SPI_FIFO_MODE)
+ msm_spi_write_word_to_fifo(dd);
+
+ /* Only enter the RUN state after the first word is written into
+ the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+ might fire before the first word is written resulting in a
+ possible race condition.
+ */
+ writel((readl(dd->base + SPI_OPERATIONAL)
+ & ~SPI_OP_STATE) | SPI_OP_STATE_RUN,
+ dd->base + SPI_OPERATIONAL);
+
+ timeout = 100 * msecs_to_jiffies(
+ DIV_ROUND_UP(dd->cur_transfer->len * 8,
+ max_speed / MSEC_PER_SEC));
+ do {
+ if (!wait_for_completion_timeout(&dd->transfer_complete,
+ timeout)) {
+ dev_err(dd->dev, "%s: SPI transaction "
+ "timeout\n", __func__);
+ dd->cur_msg->status = -EIO;
+ if (dd->mode == SPI_DMOV_MODE) {
+ writel(0, dd->base + SPI_TIME_OUT);
+ msm_dmov_flush(dd->tx_dma_chan);
+ msm_dmov_flush(dd->rx_dma_chan);
+ }
+ break;
+ }
+ } while (msm_spi_dm_send_next(dd));
+
+ if (dd->mode == SPI_DMOV_MODE)
+ msm_spi_unmap_dma_buffers(dd);
+ dd->mode = SPI_MODE_NONE;
+
+ writel(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL);
+ writel((readl(dd->base + SPI_OPERATIONAL)
+ & ~SPI_OP_STATE) | SPI_OP_STATE_RESET,
+ dd->base + SPI_OPERATIONAL);
+}
+
+/* workqueue - pull messages from queue & process */
+static void msm_spi_workq(struct work_struct *work)
+{
+ struct msm_spi *dd =
+ container_of(work, struct msm_spi, work_data);
+ unsigned long flags;
+ u32 spi_op;
+ bool status_error = 0;
+
+ spi_op = readl(dd->base + SPI_OPERATIONAL);
+ if (spi_op & SPI_OP_STATE_VALID) {
+ spi_op &= ~SPI_OP_STATE;
+ spi_op |= SPI_OP_STATE_RUN;
+ } else {
+ dev_err(dd->dev, "%s: SPI operational state not valid\n",
+ __func__);
+ status_error = 1;
+ }
+
+ dd->transfer_in_progress = 1;
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ while (!list_empty(&dd->queue)) {
+ dd->cur_msg = list_entry(dd->queue.next,
+ struct spi_message, queue);
+ list_del_init(&dd->cur_msg->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ if (status_error)
+ dd->cur_msg->status = -EIO;
+ else {
+ list_for_each_entry(dd->cur_transfer,
+ &dd->cur_msg->transfers,
+ transfer_list) {
+ msm_spi_process_transfer(dd);
+ if (dd->cur_msg->status == -EINPROGRESS)
+ dd->cur_msg->status = 0;
+ }
+ }
+ if (dd->cur_msg->complete)
+ dd->cur_msg->complete(dd->cur_msg->context);
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ }
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ dd->transfer_in_progress = 0;
+}
+
+static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct msm_spi *dd;
+ unsigned long flags;
+ struct spi_transfer *tr;
+
+ dd = spi_master_get_devdata(spi->master);
+ if (dd->suspended)
+ return -EBUSY;
+
+ if (list_empty(&msg->transfers) || !msg->complete)
+ return -EINVAL;
+
+ list_for_each_entry(tr, &msg->transfers, transfer_list) {
+ void *tx_buf = (void *)tr->tx_buf;
+ void *rx_buf = tr->rx_buf;
+ unsigned len = tr->len;
+
+ /* Check message parameters */
+ if (tr->speed_hz > dd->max_clock_speed || (tr->bits_per_word &&
+ (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
+ (tx_buf == NULL && rx_buf == NULL)) {
+ dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw"
+ "tx=%p, rx=%p\n",
+ tr->speed_hz, tr->bits_per_word,
+ tx_buf, rx_buf);
+ goto error;
+ }
+
+ if (!msm_use_dm(dd, tr) || msg->is_dma_mapped)
+ continue;
+
+ /* Do DMA mapping "early" for better error reporting */
+ if (tx_buf != NULL) {
+ tr->tx_dma = dma_map_single(&spi->dev, tx_buf, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(NULL, tr->tx_dma)) {
+ dev_err(&spi->dev, "dma %cX %d bytes error\n",
+ 'T', len);
+ goto error;
+ }
+ }
+
+ if (rx_buf != NULL) {
+ tr->rx_dma = dma_map_single(&spi->dev, rx_buf, len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(NULL, tr->rx_dma)) {
+ dev_err(&spi->dev, "dma %cX %d bytes error\n",
+ 'R', len);
+ if (tx_buf != NULL)
+ dma_unmap_single(NULL, tr->tx_dma,
+ len, DMA_TO_DEVICE);
+ goto error;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ list_add_tail(&msg->queue, &dd->queue);
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+ queue_work(dd->workqueue, &dd->work_data);
+ return 0;
+
+error:
+ list_for_each_entry_continue_reverse(tr, &msg->transfers, transfer_list)
+ {
+ if (msm_use_dm(dd, tr) && !msg->is_dma_mapped) {
+ if (tr->rx_buf != NULL)
+ dma_unmap_single(&spi->dev, tr->rx_dma, tr->len,
+ DMA_TO_DEVICE);
+ if (tr->tx_buf != NULL)
+ dma_unmap_single(&spi->dev, tr->tx_dma, tr->len,
+ DMA_FROM_DEVICE);
+ }
+ }
+ return -EINVAL;
+}
+
+static int msm_spi_setup(struct spi_device *spi)
+{
+ struct msm_spi *dd;
+ int rc = 0;
+ u32 spi_ioc;
+ u32 spi_config;
+ u32 mask;
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+ if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+ dev_err(&spi->dev, "%s: invalid bits_per_word %d\n",
+ __func__, spi->bits_per_word);
+ rc = -EINVAL;
+ }
+ if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)) {
+ dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+ __func__,
+ spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+ | SPI_LOOP));
+ rc = -EINVAL;
+ }
+ if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) {
+ dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n",
+ __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1);
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ goto err_setup_exit;
+
+ dd = spi_master_get_devdata(spi->master);
+ spi_ioc = readl(dd->base + SPI_IO_CONTROL);
+ mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
+ if (spi->mode & SPI_CS_HIGH)
+ spi_ioc |= mask;
+ else
+ spi_ioc &= ~mask;
+ if (spi->mode & SPI_CPOL)
+ spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+ else
+ spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+ writel(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+ spi_config = readl(dd->base + SPI_CONFIG);
+ if (spi->mode & SPI_LOOP)
+ spi_config |= SPI_CFG_LOOPBACK;
+ else
+ spi_config &= ~SPI_CFG_LOOPBACK;
+ if (spi->mode & SPI_CPHA)
+ spi_config &= ~SPI_CFG_INPUT_FIRST;
+ else
+ spi_config |= SPI_CFG_INPUT_FIRST;
+ writel(spi_config, dd->base + SPI_CONFIG);
+
+err_setup_exit:
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ iowrite32(val, data);
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = ioread32(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx\n");
+
+static void spi_debugfs_init(struct msm_spi *dd)
+{
+ dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL);
+ if (dd->dent_spi) {
+ int i;
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
+ dd->debugfs_spi_regs[i] =
+ debugfs_create_file(
+ debugfs_spi_regs[i].name,
+ debugfs_spi_regs[i].mode,
+ dd->dent_spi,
+ dd->base + debugfs_spi_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void spi_debugfs_exit(struct msm_spi *dd)
+{
+ if (dd->dent_spi) {
+ int i;
+ debugfs_remove_recursive(dd->dent_spi);
+ dd->dent_spi = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
+ dd->debugfs_spi_regs[i] = NULL;
+ }
+}
+#else
+static void spi_debugfs_init(struct msm_spi *dd) {}
+static void spi_debugfs_exit(struct msm_spi *dd) {}
+#endif
+
+/* ===Device attributes begin=== */
+static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+
+ return snprintf(buf, PAGE_SIZE,
+ "Device %s\n"
+ "use_dma ? %s\n"
+ "DMA configuration:\n"
+ "tx_ch=%d, rx_ch=%d, tx_crci= %d, rx_crci=%d\n"
+ "--statistics--\n"
+ "Rx isrs = %d\n"
+ "Tx isrs = %d\n"
+ "DMA error = %d\n"
+ "--debug--\n"
+ "NA yet\n",
+ dev_name(dev),
+ dd->use_dma ? "yes" : "no",
+ dd->tx_dma_chan,
+ dd->rx_dma_chan,
+ dd->tx_dma_crci,
+ dd->rx_dma_crci,
+ dd->stat_rx,
+ dd->stat_tx,
+ dd->stat_dmov_err
+ );
+}
+
+/* Reset statistics on write */
+static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_spi *dd = dev_get_drvdata(dev);
+ dd->stat_rx = 0;
+ dd->stat_tx = 0;
+ dd->stat_dmov_err = 0;
+ return count;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+/* ===Device attributes end=== */
+
+/**
+ * spi_dmov_tx_complete_func - DataMover tx completion callback
+ *
+ * Executed in IRQ context (Data Mover's IRQ) DataMover's
+ * spinlock @msm_dmov_lock held.
+ */
+static void spi_dmov_tx_complete_func(struct msm_dmov_cmd *cmd,
+ unsigned int result,
+ struct msm_dmov_errdata *err)
+{
+ struct msm_spi *dd;
+
+ if (!(result & DMOV_RSLT_VALID)) {
+ pr_err("Invalid DMOV result: rc=0x%08x, cmd = %p", result, cmd);
+ return;
+ }
+ /* restore original context */
+ dd = container_of(cmd, struct msm_spi, tx_hdr);
+ if (result & DMOV_RSLT_DONE)
+ dd->stat_tx++;
+ else {
+ /* Error or flush */
+ if (result & DMOV_RSLT_ERROR) {
+ dev_err(dd->dev, "DMA error (0x%08x)\n", result);
+ dd->stat_dmov_err++;
+ }
+ if (result & DMOV_RSLT_FLUSH) {
+ /*
+ * Flushing normally happens in process of
+ * removing, when we are waiting for outstanding
+ * DMA commands to be flushed.
+ */
+ dev_info(dd->dev,
+ "DMA channel flushed (0x%08x)\n", result);
+ }
+ if (err)
+ dev_err(dd->dev,
+ "Flush data(%08x %08x %08x %08x %08x %08x)\n",
+ err->flush[0], err->flush[1], err->flush[2],
+ err->flush[3], err->flush[4], err->flush[5]);
+ dd->cur_msg->status = -EIO;
+ writel(0, dd->base + SPI_TIME_OUT);
+ complete(&dd->transfer_complete);
+ }
+}
+
+/**
+ * spi_dmov_rx_complete_func - DataMover rx completion callback
+ *
+ * Executed in IRQ context (Data Mover's IRQ)
+ * DataMover's spinlock @msm_dmov_lock held.
+ */
+static void spi_dmov_rx_complete_func(struct msm_dmov_cmd *cmd,
+ unsigned int result,
+ struct msm_dmov_errdata *err)
+{
+ struct msm_spi *dd;
+
+ if (!(result & DMOV_RSLT_VALID)) {
+ pr_err("Invalid DMOV result(rc = 0x%08x, cmd = %p)",
+ result, cmd);
+ return;
+ }
+ /* restore original context */
+ dd = container_of(cmd, struct msm_spi, rx_hdr);
+ if (result & DMOV_RSLT_DONE) {
+ dd->stat_rx++;
+ if (atomic_inc_return(&dd->rx_irq_called) == 1)
+ return;
+ complete(&dd->transfer_complete);
+ } else {
+ /** Error or flush */
+ if (result & DMOV_RSLT_ERROR) {
+ dev_err(dd->dev, "DMA error(0x%08x)\n", result);
+ dd->stat_dmov_err++;
+ }
+ if (result & DMOV_RSLT_FLUSH) {
+ dev_info(dd->dev,
+ "DMA channel flushed(0x%08x)\n", result);
+ }
+ if (err)
+ dev_err(dd->dev,
+ "Flush data(%08x %08x %08x %08x %08x %08x)\n",
+ err->flush[0], err->flush[1], err->flush[2],
+ err->flush[3], err->flush[4], err->flush[5]);
+ dd->cur_msg->status = -EIO;
+ writel(0, dd->base + SPI_TIME_OUT);
+ complete(&dd->transfer_complete);
+ }
+}
+
+static inline u32 get_chunk_size(struct msm_spi *dd)
+{
+ u32 cache_line = dma_get_cache_alignment();
+
+ return (roundup(sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN) +
+ roundup(dd->burst_size, cache_line))*2;
+}
+
+static void msm_spi_teardown_dma(struct msm_spi *dd)
+{
+ int limit = 0;
+
+ if (!dd->use_dma)
+ return;
+
+ while (dd->mode == SPI_DMOV_MODE && limit++ < 50) {
+ msm_dmov_flush(dd->tx_dma_chan);
+ msm_dmov_flush(dd->rx_dma_chan);
+ msleep(10);
+ }
+
+ dma_free_coherent(NULL, get_chunk_size(dd), dd->tx_dmov_cmd,
+ dd->tx_dmov_cmd_dma);
+ dd->tx_dmov_cmd = dd->rx_dmov_cmd = NULL;
+ dd->tx_padding = dd->rx_padding = NULL;
+}
+
+static __init int msm_spi_init_dma(struct msm_spi *dd)
+{
+ dmov_box *box;
+ u32 cache_line = dma_get_cache_alignment();
+
+ /* Allocate all as one chunk, since all is smaller than page size */
+
+ /* We send NULL device, since it requires coherent_dma_mask id
+ device definition, we're okay with using system pool */
+ dd->tx_dmov_cmd = dma_alloc_coherent(NULL, get_chunk_size(dd),
+ &dd->tx_dmov_cmd_dma, GFP_KERNEL);
+ if (dd->tx_dmov_cmd == NULL)
+ return -ENOMEM;
+
+ /* DMA addresses should be 64 bit aligned aligned */
+ dd->rx_dmov_cmd = (struct spi_dmov_cmd *)
+ ALIGN((size_t)&dd->tx_dmov_cmd[1], DM_BYTE_ALIGN);
+ dd->rx_dmov_cmd_dma = ALIGN(dd->tx_dmov_cmd_dma +
+ sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN);
+
+ /* Buffers should be aligned to cache line */
+ dd->tx_padding = (u8 *)ALIGN((size_t)&dd->rx_dmov_cmd[1], cache_line);
+ dd->tx_padding_dma = ALIGN(dd->rx_dmov_cmd_dma +
+ sizeof(struct spi_dmov_cmd), cache_line);
+ dd->rx_padding = (u8 *)ALIGN((size_t)(dd->tx_padding + dd->burst_size),
+ cache_line);
+ dd->rx_padding_dma = ALIGN(dd->tx_padding_dma + dd->burst_size,
+ cache_line);
+
+ /* Setup DM commands */
+ box = &(dd->rx_dmov_cmd->box);
+ box->cmd = CMD_MODE_BOX | CMD_SRC_CRCI(dd->rx_dma_crci);
+ box->src_row_addr = (uint32_t)dd->mem_phys_addr + SPI_INPUT_FIFO;
+ dd->rx_hdr.cmdptr = DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, cmd_ptr));
+ dd->rx_hdr.complete_func = spi_dmov_rx_complete_func;
+
+ box = &(dd->tx_dmov_cmd->box);
+ box->cmd = CMD_MODE_BOX | CMD_DST_CRCI(dd->tx_dma_crci);
+ box->dst_row_addr = (uint32_t)dd->mem_phys_addr + SPI_OUTPUT_FIFO;
+ dd->tx_hdr.cmdptr = DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma +
+ offsetof(struct spi_dmov_cmd, cmd_ptr));
+ dd->tx_hdr.complete_func = spi_dmov_tx_complete_func;
+
+ dd->tx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC |
+ CMD_DST_CRCI(dd->tx_dma_crci);
+ dd->tx_dmov_cmd->single_pad.dst = (uint32_t)dd->mem_phys_addr +
+ SPI_OUTPUT_FIFO;
+ dd->rx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC |
+ CMD_SRC_CRCI(dd->rx_dma_crci);
+ dd->rx_dmov_cmd->single_pad.src = (uint32_t)dd->mem_phys_addr +
+ SPI_INPUT_FIFO;
+
+ /* Clear remaining activities on channel */
+ msm_dmov_flush(dd->tx_dma_chan);
+ msm_dmov_flush(dd->rx_dma_chan);
+
+ return 0;
+}
+
+static int __init msm_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct msm_spi *dd;
+ struct resource *resource;
+ int rc = 0;
+ struct clk *pclk;
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
+ if (!master) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev, "master allocation failed\n");
+ goto err_probe_exit;
+ }
+
+ master->bus_num = pdev->id;
+ master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ master->setup = msm_spi_setup;
+ master->transfer = msm_spi_transfer;
+ platform_set_drvdata(pdev, master);
+ dd = spi_master_get_devdata(master);
+
+ dd->irq_in = platform_get_irq_byname(pdev, "irq_in");
+ dd->irq_out = platform_get_irq_byname(pdev, "irq_out");
+ dd->irq_err = platform_get_irq_byname(pdev, "irq_err");
+ if ((dd->irq_in < 0) || (dd->irq_out < 0) || (dd->irq_err < 0))
+ goto err_probe_res;
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+ dd->mem_phys_addr = resource->start;
+ dd->mem_size = (resource->end - resource->start) + 1;
+
+ if (pdata && pdata->dma_config) {
+ rc = pdata->dma_config();
+ if (!rc) {
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "spidm_channels");
+ if (resource) {
+ dd->rx_dma_chan = resource->start;
+ dd->tx_dma_chan = resource->end;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "spidm_crci");
+ if (!resource) {
+ rc = -ENXIO;
+ goto err_probe_res;
+ }
+ dd->rx_dma_crci = resource->start;
+ dd->tx_dma_crci = resource->end;
+ dd->use_dma = 1;
+ }
+ }
+ }
+
+ if (pdata && pdata->gpio_config) {
+ rc = pdata->gpio_config();
+ if (rc) {
+ dev_err(&pdev->dev, "%s: error configuring GPIOs\n",
+ __func__);
+ goto err_probe_gpio;
+ }
+ }
+
+ spin_lock_init(&dd->queue_lock);
+ INIT_LIST_HEAD(&dd->queue);
+ INIT_WORK(&dd->work_data, msm_spi_workq);
+ dd->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (!dd->workqueue)
+ goto err_probe_workq;
+
+ if (!request_mem_region(dd->mem_phys_addr, dd->mem_size,
+ SPI_QSD_NAME)) {
+ rc = -ENXIO;
+ goto err_probe_reqmem;
+ }
+
+ dd->base = ioremap(dd->mem_phys_addr, dd->mem_size);
+ if (!dd->base)
+ goto err_probe_ioremap;
+
+ dd->dev = &pdev->dev;
+ dd->clk = clk_get(&pdev->dev, "spi_clk");
+ if (IS_ERR(dd->clk)) {
+ dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__);
+ rc = PTR_ERR(dd->clk);
+ goto err_probe_clk_get;
+ }
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto err_probe_clk_enable;
+ }
+ pclk = clk_get(&pdev->dev, "spi_pclk");
+ if (!IS_ERR(pclk)) {
+ dd->pclk = pclk;
+ rc = clk_enable(dd->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n",
+ __func__);
+ goto err_probe_pclk_enable;
+ }
+ }
+ if (pdata && pdata->max_clock_speed) {
+ msm_spi_clock_set(dd, pdata->max_clock_speed);
+ dd->max_clock_speed = pdata->max_clock_speed;
+ }
+ msm_spi_calculate_fifo_size(dd);
+ writel(0x1, dd->base + SPI_SW_RESET);
+ if (dd->use_dma) {
+ rc = msm_spi_init_dma(dd);
+ if (rc)
+ goto err_probe_dma;
+ }
+ writel(0x00000000, dd->base + SPI_OPERATIONAL);
+ writel(0x00000000, dd->base + SPI_CONFIG);
+ writel(0x00000000, dd->base + SPI_IO_MODES);
+ writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
+ if (!(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_STATE_VALID)) {
+ dev_err(&pdev->dev, "%s: SPI operational state not valid\n",
+ __func__);
+ rc = -1;
+ goto err_probe_state;
+ }
+ writel(SPI_OP_STATE_RUN, dd->base + SPI_OPERATIONAL);
+
+ dd->suspended = 0;
+ dd->transfer_in_progress = 0;
+ dd->mode = SPI_MODE_NONE;
+
+ rc = request_irq(dd->irq_in, msm_spi_input_irq, IRQF_TRIGGER_RISING,
+ pdev->name, dd);
+ if (rc)
+ goto err_probe_irq1;
+ rc = request_irq(dd->irq_out, msm_spi_output_irq, IRQF_TRIGGER_RISING,
+ pdev->name, dd);
+ if (rc)
+ goto err_probe_irq2;
+ rc = request_irq(dd->irq_err, msm_spi_error_irq, IRQF_TRIGGER_RISING,
+ pdev->name, master);
+ if (rc)
+ goto err_probe_irq3;
+
+ rc = spi_register_master(master);
+ if (rc)
+ goto err_probe_reg_master;
+
+ rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
+ goto err_attrs;
+ }
+
+ spi_debugfs_init(dd);
+
+ return 0;
+
+err_attrs:
+err_probe_reg_master:
+ free_irq(dd->irq_err, master);
+err_probe_irq3:
+ free_irq(dd->irq_out, dd);
+err_probe_irq2:
+ free_irq(dd->irq_in, dd);
+err_probe_irq1:
+err_probe_state:
+ msm_spi_teardown_dma(dd);
+err_probe_dma:
+ if (dd->pclk)
+ clk_disable(dd->pclk);
+err_probe_pclk_enable:
+ if (dd->pclk)
+ clk_put(dd->pclk);
+ clk_disable(dd->clk);
+err_probe_clk_enable:
+ clk_put(dd->clk);
+err_probe_clk_get:
+ iounmap(dd->base);
+err_probe_ioremap:
+ release_mem_region(dd->mem_phys_addr, dd->mem_size);
+err_probe_reqmem:
+ destroy_workqueue(dd->workqueue);
+err_probe_workq:
+err_probe_gpio:
+ if (pdata && pdata->gpio_release)
+ pdata->gpio_release();
+err_probe_res:
+ spi_master_put(master);
+err_probe_exit:
+ return rc;
+}
+
+#ifdef CONFIG_PM
+static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+ int limit = 0;
+
+ if (!master)
+ goto suspend_exit;
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto suspend_exit;
+ dd->suspended = 1;
+ while ((!list_empty(&dd->queue) || dd->transfer_in_progress) &&
+ limit < 50) {
+ if (dd->mode == SPI_DMOV_MODE) {
+ msm_dmov_flush(dd->tx_dma_chan);
+ msm_dmov_flush(dd->rx_dma_chan);
+ }
+ limit++;
+ msleep(1);
+ }
+
+ disable_irq(dd->irq_in);
+ disable_irq(dd->irq_out);
+ disable_irq(dd->irq_err);
+ clk_disable(dd->clk);
+
+suspend_exit:
+ return 0;
+}
+
+static int msm_spi_resume(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd;
+ int rc;
+
+ if (!master)
+ goto resume_exit;
+ dd = spi_master_get_devdata(master);
+ if (!dd)
+ goto resume_exit;
+
+ rc = clk_enable(dd->clk);
+ if (rc) {
+ dev_err(dd->dev, "%s: unable to enable spi_clk\n",
+ __func__);
+ goto resume_exit;
+ }
+
+ enable_irq(dd->irq_in);
+ enable_irq(dd->irq_out);
+ enable_irq(dd->irq_err);
+ dd->suspended = 0;
+resume_exit:
+ return 0;
+}
+#else
+#define msm_spi_suspend NULL
+#define msm_spi_resume NULL
+#endif /* CONFIG_PM */
+
+static int __devexit msm_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct msm_spi *dd = spi_master_get_devdata(master);
+ struct msm_spi_platform_data *pdata = pdev->dev.platform_data;
+
+ spi_debugfs_exit(dd);
+ sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
+
+ free_irq(dd->irq_in, dd);
+ free_irq(dd->irq_out, dd);
+ free_irq(dd->irq_err, master);
+
+ msm_spi_teardown_dma(dd);
+
+ if (pdata && pdata->gpio_release)
+ pdata->gpio_release();
+
+ iounmap(dd->base);
+ release_mem_region(dd->mem_phys_addr, dd->mem_size);
+ clk_disable(dd->clk);
+ clk_put(dd->clk);
+ if (dd->pclk) {
+ clk_disable(dd->pclk);
+ clk_put(dd->pclk);
+ }
+ destroy_workqueue(dd->workqueue);
+ platform_set_drvdata(pdev, 0);
+ spi_unregister_master(master);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver msm_spi_driver = {
+ .probe = msm_spi_probe,
+ .driver = {
+ .name = "msm_spi",
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_spi_suspend,
+ .resume = msm_spi_resume,
+ .remove = __exit_p(msm_spi_remove),
+};
+
+static int __init msm_spi_init(void)
+{
+ return platform_driver_register(&msm_spi_driver);
+}
+module_init(msm_spi_init);
+
+static void __exit msm_spi_exit(void)
+{
+ platform_driver_unregister(&msm_spi_driver);
+}
+module_exit(msm_spi_exit);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 78bb222..7459c30 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -510,6 +510,24 @@
# LAST -- dummy/emulated controller
#
+config USB_GADGET_MSM_72K
+ boolean "MSM 72K Device Controller"
+ depends on ARCH_MSM
+ select USB_GADGET_SELECTED
+ select USB_GADGET_DUALSPEED
+ help
+ USB gadget driver for Qualcomm MSM 72K architecture.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "msm72k" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_MSM_72K
+ tristate
+ depends on USB_GADGET_MSM_72K
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
config USB_GADGET_DUMMY_HCD
boolean "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
@@ -856,6 +874,12 @@
help
Provides adb function for android gadget driver.
+config USB_ANDROID_DIAG
+ boolean "USB MSM7K Diag Function"
+ depends on USB_ANDROID
+ help
+ Qualcomm diagnostics interface support.
+
config USB_ANDROID_MASS_STORAGE
boolean "Android gadget mass storage function"
depends on USB_ANDROID && SWITCH
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index d385002..83122fd 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,7 @@
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
+obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o
#
# USB gadget drivers
@@ -71,3 +72,6 @@
obj-$(CONFIG_USB_ANDROID_RNDIS) += f_rndis.o u_ether.o
obj-$(CONFIG_USB_ANDROID_ACCESSORY) += f_accessory.o
+# MSM specific
+obj-$(CONFIG_USB_ANDROID_DIAG) += diag.o
+
diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c
new file mode 100644
index 0000000..c100ed4
--- /dev/null
+++ b/drivers/usb/gadget/diag.c
@@ -0,0 +1,935 @@
+/*
+ * Diag Function Device - Route DIAG frames between SMD and USB
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+
+#include <mach/msm_smd.h>
+
+#include <linux/usb/android_composite.h>
+
+#define NO_HDLC 1
+#define ROUTE_TO_USERSPACE 1
+
+#if 1
+#define TRACE(tag,data,len,decode) do {} while(0)
+#else
+static void TRACE(const char *tag, const void *_data, int len, int decode)
+{
+ const unsigned char *data = _data;
+ int escape = 0;
+
+ printk(KERN_INFO "%s", tag);
+ if (decode) {
+ while (len-- > 0) {
+ unsigned x = *data++;
+ if (x == 0x7e) {
+ printk(" $$");
+ escape = 0;
+ continue;
+ }
+ if (x == 0x7d) {
+ escape = 1;
+ continue;
+ }
+ if (escape) {
+ escape = 0;
+ printk(" %02x", x ^ 0x20);
+ } else {
+ printk(" %02x", x);
+ }
+ }
+ } else {
+ while (len-- > 0) {
+ printk(" %02x", *data++);
+ }
+ printk(" $$");
+ }
+ printk("\n");
+}
+#endif
+
+#define HDLC_MAX 4096
+
+#define TX_REQ_BUF_SZ 8192
+#define RX_REQ_BUF_SZ 8192
+
+/* number of tx/rx requests to allocate */
+#define TX_REQ_NUM 4
+#define RX_REQ_NUM 4
+
+struct diag_context
+{
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ struct usb_ep *out;
+ struct usb_ep *in;
+ struct list_head tx_req_idle;
+ struct list_head rx_req_idle;
+ spinlock_t req_lock;
+#if ROUTE_TO_USERSPACE
+ struct mutex user_lock;
+#define ID_TABLE_SZ 10 /* keep this small */
+ struct list_head rx_req_user;
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ char *user_read_buf;
+ uint32_t user_read_len;
+ char *user_readp;
+ bool opened;
+
+ /* list of registered command ids to be routed to userspace */
+ unsigned char id_table[ID_TABLE_SZ];
+#endif
+ smd_channel_t *ch;
+ int in_busy;
+#ifdef CONFIG_ARCH_QSD8X50
+ smd_channel_t *ch_dsp;
+ int in_busy_dsp;
+#endif
+ int online;
+
+ /* assembly buffer for USB->A9 HDLC frames */
+ unsigned char hdlc_buf[HDLC_MAX];
+ unsigned hdlc_count;
+ unsigned hdlc_escape;
+
+ u64 tx_count; /* to smd */
+ u64 rx_count; /* from smd */
+
+ int function_enable;
+};
+
+static struct usb_interface_descriptor diag_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xFF,
+ .bInterfaceSubClass = 0xFF,
+ .bInterfaceProtocol = 0xFF,
+};
+
+static struct usb_endpoint_descriptor diag_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor diag_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor diag_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor diag_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_diag_descs[] = {
+ (struct usb_descriptor_header *) &diag_interface_desc,
+ (struct usb_descriptor_header *) &diag_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &diag_fullspeed_out_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_diag_descs[] = {
+ (struct usb_descriptor_header *) &diag_interface_desc,
+ (struct usb_descriptor_header *) &diag_highspeed_in_desc,
+ (struct usb_descriptor_header *) &diag_highspeed_out_desc,
+ NULL,
+};
+
+static struct diag_context _context;
+
+static inline struct diag_context *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct diag_context, function);
+}
+
+static void smd_try_to_send(struct diag_context *ctxt);
+#ifdef CONFIG_ARCH_QSD8X50
+static void dsp_try_to_send(struct diag_context *ctxt);
+#endif
+
+static void diag_queue_out(struct diag_context *ctxt);
+
+/* add a request to the tail of a list */
+static void req_put(struct diag_context *ctxt, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctxt->req_lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&ctxt->req_lock, flags);
+}
+
+/* remove a request from the head of a list */
+static struct usb_request *req_get(struct diag_context *ctxt,
+ struct list_head *head)
+{
+ struct usb_request *req = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctxt->req_lock, flags);
+ if (!list_empty(head)) {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&ctxt->req_lock, flags);
+
+ return req;
+}
+
+static void reqs_free(struct diag_context *ctxt, struct usb_ep *ep,
+ struct list_head *head)
+{
+ struct usb_request *req;
+ while ((req = req_get(ctxt, head))) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+#if ROUTE_TO_USERSPACE
+#define USB_DIAG_IOC_MAGIC 0xFF
+#define USB_DIAG_FUNC_IOC_REGISTER_SET _IOW(USB_DIAG_IOC_MAGIC, 3, char *)
+
+static long diag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct diag_context *ctxt = &_context;
+ void __user *argp = (void __user *)arg;
+ unsigned long flags;
+ unsigned char temp_id_table[ID_TABLE_SZ];
+
+ if (cmd != USB_DIAG_FUNC_IOC_REGISTER_SET) {
+ pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ if (copy_from_user(temp_id_table, (unsigned char *)argp, ID_TABLE_SZ))
+ return -EFAULT;
+
+ spin_lock_irqsave(&ctxt->req_lock, flags);
+ memcpy(ctxt->id_table, temp_id_table, ID_TABLE_SZ);
+ spin_unlock_irqrestore(&ctxt->req_lock, flags);
+
+ return 0;
+}
+
+static ssize_t diag_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct diag_context *ctxt = &_context;
+ struct usb_request *req = 0;
+ int ret = 0;
+
+ mutex_lock(&ctxt->user_lock);
+
+ if (ctxt->user_read_len && ctxt->user_readp) {
+ if (count > ctxt->user_read_len)
+ count = ctxt->user_read_len;
+ if (copy_to_user(buf, ctxt->user_readp, count))
+ ret = -EFAULT;
+ else {
+ ctxt->user_readp += count;
+ ctxt->user_read_len -= count;
+ ret = count;
+ }
+ goto end;
+ }
+
+ mutex_unlock(&ctxt->user_lock);
+ ret = wait_event_interruptible(ctxt->read_wq,
+ (req = req_get(ctxt, &ctxt->rx_req_user)) || !ctxt->online);
+ mutex_lock(&ctxt->user_lock);
+ if (ret < 0) {
+ pr_err("%s: wait_event_interruptible error %d\n",
+ __func__, ret);
+ goto end;
+ }
+ if (!ctxt->online) {
+ pr_err("%s: offline\n", __func__);
+ ret = -EIO;
+ goto end;
+ }
+ if (req) {
+ if (req->actual == 0) {
+ pr_info("%s: no data\n", __func__);
+ goto end;
+ }
+ if (count > req->actual)
+ count = req->actual;
+ if (copy_to_user(buf, req->buf, count)) {
+ ret = -EFAULT;
+ goto end;
+ }
+ req->actual -= count;
+ if (req->actual) {
+ memcpy(ctxt->user_read_buf, req->buf + count, req->actual);
+ ctxt->user_read_len = req->actual;
+ ctxt->user_readp = ctxt->user_read_buf;
+ }
+ ret = count;
+ }
+
+end:
+ if (req)
+ req_put(ctxt, &ctxt->rx_req_idle, req);
+
+ mutex_unlock(&ctxt->user_lock);
+ return ret;
+}
+
+static ssize_t diag_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct diag_context *ctxt = &_context;
+ struct usb_request *req = 0;
+ int ret = 0;
+
+ ret = wait_event_interruptible(ctxt->write_wq,
+ ((req = req_get(ctxt, &ctxt->tx_req_idle)) || !ctxt->online));
+
+ mutex_lock(&ctxt->user_lock);
+ if (ret < 0) {
+ pr_err("%s: wait_event_interruptible error %d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ if (!ctxt->online) {
+ pr_err("%s: offline\n", __func__);
+ ret = -EIO;
+ goto end;
+ }
+
+ if (count > TX_REQ_BUF_SZ)
+ count = TX_REQ_BUF_SZ;
+
+ if (req) {
+ if (copy_from_user(req->buf, buf, count)) {
+ ret = -EFAULT;
+ goto end;
+ }
+
+ req->length = count;
+ ret = usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
+ if (ret < 0) {
+ pr_err("%s: usb_ep_queue error %d\n", __func__, ret);
+ goto end;
+ }
+
+ ret = req->length;
+ }
+
+end:
+ if (req)
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+
+ mutex_unlock(&ctxt->user_lock);
+ return ret;
+}
+
+static int diag_open(struct inode *ip, struct file *fp)
+{
+ struct diag_context *ctxt = &_context;
+ int rc = 0;
+
+ mutex_lock(&ctxt->user_lock);
+ if (ctxt->opened) {
+ pr_err("%s: already opened\n", __func__);
+ rc = -EBUSY;
+ goto done;
+ }
+ ctxt->user_read_len = 0;
+ ctxt->user_readp = 0;
+ if (!ctxt->user_read_buf) {
+ ctxt->user_read_buf = kmalloc(RX_REQ_BUF_SZ, GFP_KERNEL);
+ if (!ctxt->user_read_buf) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+ ctxt->opened = true;
+
+done:
+ mutex_unlock(&ctxt->user_lock);
+ return rc;
+}
+
+static int diag_release(struct inode *ip, struct file *fp)
+{
+ struct diag_context *ctxt = &_context;
+
+ mutex_lock(&ctxt->user_lock);
+ ctxt->opened = false;
+ ctxt->user_read_len = 0;
+ ctxt->user_readp = 0;
+ if (ctxt->user_read_buf) {
+ kfree(ctxt->user_read_buf);
+ ctxt->user_read_buf = 0;
+ }
+ mutex_unlock(&ctxt->user_lock);
+
+ return 0;
+}
+
+static struct file_operations diag_fops = {
+ .owner = THIS_MODULE,
+ .read = diag_read,
+ .write = diag_write,
+ .open = diag_open,
+ .release = diag_release,
+ .unlocked_ioctl = diag_ioctl,
+};
+
+static struct miscdevice diag_device_fops = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "diag",
+ .fops = &diag_fops,
+};
+#endif
+
+static void diag_in_complete(struct usb_ep *ept, struct usb_request *req)
+{
+ struct diag_context *ctxt = req->context;
+#if ROUTE_TO_USERSPACE
+ char c;
+#endif
+
+ ctxt->in_busy = 0;
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+
+#if ROUTE_TO_USERSPACE
+ c = *((char *)req->buf + req->actual - 1);
+ if (c == 0x7e)
+ wake_up(&ctxt->write_wq);
+#endif
+
+ smd_try_to_send(ctxt);
+}
+
+#ifdef CONFIG_ARCH_QSD8X50
+static void diag_dsp_in_complete(struct usb_ep *ept, struct usb_request *req)
+{
+ struct diag_context *ctxt = req->context;
+
+ ctxt->in_busy_dsp = 0;
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ dsp_try_to_send(ctxt);
+ wake_up(&ctxt->write_wq);
+}
+#endif
+
+static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned len)
+{
+ unsigned char *data = _data;
+ unsigned count = ctxt->hdlc_count;
+ unsigned escape = ctxt->hdlc_escape;
+ unsigned char *hdlc = ctxt->hdlc_buf;
+
+ while (len-- > 0) {
+ unsigned char x = *data++;
+ if (x == 0x7E) {
+ if (count > 2) {
+ /* we're just ignoring the crc here */
+ TRACE("PC>", hdlc, count - 2, 0);
+ if (ctxt->ch)
+ smd_write(ctxt->ch, hdlc, count - 2);
+ }
+ count = 0;
+ escape = 0;
+ } else if (x == 0x7D) {
+ escape = 1;
+ } else {
+ if (escape) {
+ x = x ^ 0x20;
+ escape = 0;
+ }
+ hdlc[count++] = x;
+
+ /* discard frame if we overflow */
+ if (count == HDLC_MAX)
+ count = 0;
+ }
+ }
+
+ ctxt->hdlc_count = count;
+ ctxt->hdlc_escape = escape;
+}
+
+#if ROUTE_TO_USERSPACE
+static int if_route_to_userspace(struct diag_context *ctxt, unsigned int cmd_id)
+{
+ unsigned long flags;
+ int i;
+
+ if (!ctxt->opened || cmd_id == 0)
+ return 0;
+
+ /* command ids 0xfb..0xff are not used by msm diag; we steal these ids
+ * for communication between userspace tool and host test tool.
+ */
+ if (cmd_id >= 0xfb && cmd_id <= 0xff)
+ return 1;
+
+ spin_lock_irqsave(&ctxt->req_lock, flags);
+ for (i = 0; i < ARRAY_SIZE(ctxt->id_table); i++)
+ if (ctxt->id_table[i] == cmd_id) {
+ /* if the command id equals to any of registered ids,
+ * route to userspace to handle.
+ */
+ spin_unlock_irqrestore(&ctxt->req_lock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&ctxt->req_lock, flags);
+
+ return 0;
+}
+#endif
+
+static void diag_out_complete(struct usb_ep *ept, struct usb_request *req)
+{
+ struct diag_context *ctxt = req->context;
+
+ if (req->status == 0) {
+#if ROUTE_TO_USERSPACE
+ unsigned int cmd_id = *((unsigned char *)req->buf);
+ if (if_route_to_userspace(ctxt, cmd_id)) {
+ req_put(ctxt, &ctxt->rx_req_user, req);
+ wake_up(&ctxt->read_wq);
+ diag_queue_out(ctxt);
+ return;
+ }
+#endif
+
+#if NO_HDLC
+ TRACE("PC>", req->buf, req->actual, 0);
+ if (ctxt->ch)
+ smd_write(ctxt->ch, req->buf, req->actual);
+#else
+ diag_process_hdlc(ctxt, req->buf, req->actual);
+#endif
+ ctxt->tx_count += req->actual;
+ }
+
+ req_put(ctxt, &ctxt->rx_req_idle, req);
+ diag_queue_out(ctxt);
+}
+
+static void diag_queue_out(struct diag_context *ctxt)
+{
+ struct usb_request *req;
+ int rc;
+
+ req = req_get(ctxt, &ctxt->rx_req_idle);
+ if (!req) {
+ pr_err("%s: rx req queue - out of buffer\n", __func__);
+ return;
+ }
+
+ req->complete = diag_out_complete;
+ req->context = ctxt;
+ req->length = RX_REQ_BUF_SZ;
+
+ rc = usb_ep_queue(ctxt->out, req, GFP_ATOMIC);
+ if (rc < 0) {
+ pr_err("%s: usb_ep_queue failed: %d\n", __func__, rc);
+ req_put(ctxt, &ctxt->rx_req_idle, req);
+ }
+}
+
+static void smd_try_to_send(struct diag_context *ctxt)
+{
+again:
+ if (ctxt->ch && (!ctxt->in_busy)) {
+ int r = smd_read_avail(ctxt->ch);
+
+ if (r > TX_REQ_BUF_SZ) {
+ return;
+ }
+ if (r > 0) {
+ struct usb_request *req;
+ req = req_get(ctxt, &ctxt->tx_req_idle);
+ if (!req) {
+ pr_err("%s: tx req queue is out of buffers\n",
+ __func__);
+ return;
+ }
+ smd_read(ctxt->ch, req->buf, r);
+ ctxt->rx_count += r;
+
+ if (!ctxt->online) {
+// printk("$$$ discard %d\n", r);
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ goto again;
+ }
+ req->complete = diag_in_complete;
+ req->context = ctxt;
+ req->length = r;
+
+ TRACE("A9>", req->buf, r, 1);
+ ctxt->in_busy = 1;
+ r = usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
+ if (r < 0) {
+ pr_err("%s: usb_ep_queue failed: %d\n",
+ __func__, r);
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ ctxt->in_busy = 0;
+ }
+ }
+ }
+}
+
+static void smd_diag_notify(void *priv, unsigned event)
+{
+ struct diag_context *ctxt = priv;
+ smd_try_to_send(ctxt);
+}
+
+#ifdef CONFIG_ARCH_QSD8X50
+static void dsp_try_to_send(struct diag_context *ctxt)
+{
+again:
+ if (ctxt->ch_dsp && (!ctxt->in_busy_dsp)) {
+ int r = smd_read_avail(ctxt->ch_dsp);
+
+ if (r > TX_REQ_BUF_SZ) {
+ return;
+ }
+ if (r > 0) {
+ struct usb_request *req;
+ req = req_get(ctxt, &ctxt->tx_req_idle);
+ if (!req) {
+ pr_err("%s: tx req queue is out of buffers\n",
+ __func__);
+ return;
+ }
+ smd_read(ctxt->ch_dsp, req->buf, r);
+
+ if (!ctxt->online) {
+// printk("$$$ discard %d\n", r);
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ goto again;
+ }
+ req->complete = diag_dsp_in_complete;
+ req->context = ctxt;
+ req->length = r;
+
+ TRACE("Q6>", req->buf, r, 1);
+ ctxt->in_busy_dsp = 1;
+ r = usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
+ if (r < 0) {
+ pr_err("%s: usb_ep_queue failed: %d\n",
+ __func__, r);
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ ctxt->in_busy_dsp = 0;
+ }
+ }
+ }
+}
+
+static void dsp_diag_notify(void *priv, unsigned event)
+{
+ struct diag_context *ctxt = priv;
+ dsp_try_to_send(ctxt);
+}
+#endif
+
+static int __init create_bulk_endpoints(struct diag_context *ctxt,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc)
+{
+ struct usb_composite_dev *cdev = ctxt->cdev;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int n;
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ ctxt->in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ return -ENODEV;
+ }
+ ctxt->out = ep;
+
+ ctxt->tx_count = ctxt->rx_count = 0;
+
+ for (n = 0; n < RX_REQ_NUM; n++) {
+ req = usb_ep_alloc_request(ctxt->out, GFP_KERNEL);
+ if (!req) {
+ DBG(cdev, "%s: usb_ep_alloc_request out of memory\n",
+ __func__);
+ goto rx_fail;
+ }
+ req->buf = kmalloc(RX_REQ_BUF_SZ, GFP_KERNEL);
+ if (!req->buf) {
+ DBG(cdev, "%s: kmalloc out of memory\n", __func__);
+ goto rx_fail;
+ }
+ req->context = ctxt;
+ req->complete = diag_out_complete;
+ req_put(ctxt, &ctxt->rx_req_idle, req);
+ }
+
+ for (n = 0; n < TX_REQ_NUM; n++) {
+ req = usb_ep_alloc_request(ctxt->in, GFP_KERNEL);
+ if (!req) {
+ DBG(cdev, "%s: usb_ep_alloc_request out of memory\n",
+ __func__);
+ goto tx_fail;
+ }
+ req->buf = kmalloc(TX_REQ_BUF_SZ, GFP_KERNEL);
+ if (!req->buf) {
+ DBG(cdev, "%s: kmalloc out of memory\n", __func__);
+ goto tx_fail;
+ }
+ req->context = ctxt;
+ req->complete = diag_in_complete;
+ req_put(ctxt, &ctxt->tx_req_idle, req);
+ }
+
+ return 0;
+
+tx_fail:
+ reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle);
+rx_fail:
+ reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle);
+ return -ENOMEM;
+}
+
+static int
+diag_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct diag_context *ctxt = func_to_dev(f);
+ int id;
+ int ret;
+
+ ctxt->cdev = cdev;
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ diag_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(ctxt, &diag_fullspeed_in_desc,
+ &diag_fullspeed_out_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ diag_highspeed_in_desc.bEndpointAddress =
+ diag_fullspeed_in_desc.bEndpointAddress;
+ diag_highspeed_out_desc.bEndpointAddress =
+ diag_fullspeed_out_desc.bEndpointAddress;
+ }
+
+#if ROUTE_TO_USERSPACE
+ misc_register(&diag_device_fops);
+#endif
+
+ return 0;
+}
+
+static void
+diag_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct diag_context *ctxt = func_to_dev(f);
+ reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle);
+ reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle);
+
+#if ROUTE_TO_USERSPACE
+ misc_deregister(&diag_device_fops);
+#endif
+
+ ctxt->tx_count = ctxt->rx_count = 0;
+}
+
+static int diag_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct diag_context *ctxt = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+#if ROUTE_TO_USERSPACE
+ struct usb_request *req;
+#endif
+ int ret;
+
+ ret = usb_ep_enable(ctxt->in,
+ ep_choose(cdev->gadget,
+ &diag_highspeed_in_desc,
+ &diag_fullspeed_in_desc));
+ if (ret)
+ return ret;
+ ret = usb_ep_enable(ctxt->out,
+ ep_choose(cdev->gadget,
+ &diag_highspeed_out_desc,
+ &diag_fullspeed_out_desc));
+ if (ret) {
+ usb_ep_disable(ctxt->in);
+ return ret;
+ }
+ ctxt->online = 1;
+
+#if ROUTE_TO_USERSPACE
+ /* recycle unhandled rx reqs to user if any */
+ while ((req = req_get(ctxt, &ctxt->rx_req_user)))
+ req_put(ctxt, &ctxt->rx_req_idle, req);
+#endif
+
+ diag_queue_out(ctxt);
+ smd_try_to_send(ctxt);
+#ifdef CONFIG_ARCH_QSD8X50
+ dsp_try_to_send(ctxt);
+#endif
+
+#if ROUTE_TO_USERSPACE
+ wake_up(&ctxt->read_wq);
+ wake_up(&ctxt->write_wq);
+#endif
+
+ return 0;
+}
+
+static void diag_function_disable(struct usb_function *f)
+{
+ struct diag_context *ctxt = func_to_dev(f);
+
+ ctxt->online = 0;
+#if ROUTE_TO_USERSPACE
+ wake_up(&ctxt->read_wq);
+ wake_up(&ctxt->write_wq);
+#endif
+ usb_ep_disable(ctxt->in);
+ usb_ep_disable(ctxt->out);
+}
+
+static int diag_set_enabled(const char *val, struct kernel_param *kp)
+{
+ int enabled = simple_strtol(val, NULL, 0);
+ if (_context.cdev)
+ android_enable_function(&_context.function, enabled);
+ _context.function_enable = !!enabled;
+ return 0;
+}
+
+static int diag_get_tx_rx_count(char *buffer, struct kernel_param *kp)
+{
+ struct diag_context *ctxt = &_context;
+
+ return sprintf(buffer, "tx: %llu bytes, rx: %llu bytes",
+ ctxt->tx_count, ctxt->rx_count);
+}
+module_param_call(tx_rx_count, NULL, diag_get_tx_rx_count, NULL, 0444);
+
+static int diag_get_enabled(char *buffer, struct kernel_param *kp)
+{
+ buffer[0] = '0' + !_context.function.disabled;
+ return 1;
+}
+module_param_call(enabled, diag_set_enabled, diag_get_enabled, NULL, 0664);
+
+
+int diag_bind_config(struct usb_configuration *c)
+{
+ struct diag_context *ctxt = &_context;
+ int ret;
+
+ printk(KERN_INFO "diag_bind_config\n");
+
+ ret = smd_open("SMD_DIAG", &ctxt->ch, ctxt, smd_diag_notify);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_ARCH_QSD8X50
+ ret = smd_open("DSP_DIAG", &ctxt->ch_dsp, ctxt, dsp_diag_notify);
+ if (ret) {
+ pr_err("%s: smd_open failed (DSP_DIAG)\n", __func__);
+ return ret;
+ }
+#endif
+
+ ctxt->cdev = c->cdev;
+ ctxt->function.name = "diag";
+ ctxt->function.descriptors = fs_diag_descs;
+ ctxt->function.hs_descriptors = hs_diag_descs;
+ ctxt->function.bind = diag_function_bind;
+ ctxt->function.unbind = diag_function_unbind;
+ ctxt->function.set_alt = diag_function_set_alt;
+ ctxt->function.disable = diag_function_disable;
+
+ ctxt->function.disabled = !_context.function_enable;
+
+ return usb_add_function(c, &ctxt->function);
+}
+
+static struct android_usb_function diag_function = {
+ .name = "diag",
+ .bind_config = diag_bind_config,
+};
+
+static int __init init(void)
+{
+ struct diag_context *ctxt = &_context;
+
+ printk(KERN_INFO "diag init\n");
+ spin_lock_init(&ctxt->req_lock);
+ INIT_LIST_HEAD(&ctxt->rx_req_idle);
+ INIT_LIST_HEAD(&ctxt->tx_req_idle);
+#if ROUTE_TO_USERSPACE
+ mutex_init(&ctxt->user_lock);
+ INIT_LIST_HEAD(&ctxt->rx_req_user);
+ init_waitqueue_head(&ctxt->read_wq);
+ init_waitqueue_head(&ctxt->write_wq);
+#endif
+
+ android_register_function(&diag_function);
+ return 0;
+}
+module_init(init);
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e511fec..2c7a8e6c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -126,6 +126,12 @@
#define gadget_is_ci13xxx(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_MSM_72K
+#define gadget_is_msm72k(g) !strcmp("msm72k_udc", (g)->name)
+#else
+#define gadget_is_msm72k(g) 0
+#endif
+
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
// ...
@@ -200,6 +206,8 @@
return 0x25;
else if (gadget_is_s3c_hsotg(gadget))
return 0x26;
+ else if (gadget_is_msm72k(gadget))
+ return 0x27;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
new file mode 100644
index 0000000..ac1b9ec
--- /dev/null
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -0,0 +1,1937 @@
+/*
+ * Driver for HighSpeed USB Client Controller in MSM7K
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ * Brian Swetland <swetland@google.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board.h>
+#include <mach/msm_hsusb.h>
+#include <linux/device.h>
+#include <mach/msm_hsusb_hw.h>
+
+static const char driver_name[] = "msm72k_udc";
+
+/* #define DEBUG */
+/* #define VERBOSE */
+
+#define MSM_USB_BASE ((unsigned) ui->addr)
+
+#define DRIVER_DESC "MSM 72K USB Peripheral Controller"
+
+#define EPT_FLAG_IN 0x0001
+
+#define SETUP_BUF_SIZE 4096
+
+typedef void (*completion_func)(struct usb_ep *ep, struct usb_request *req);
+
+static const char *const ep_name[] = {
+ "ep0out", "ep1out", "ep2out", "ep3out",
+ "ep4out", "ep5out", "ep6out", "ep7out",
+ "ep8out", "ep9out", "ep10out", "ep11out",
+ "ep12out", "ep13out", "ep14out", "ep15out",
+ "ep0in", "ep1in", "ep2in", "ep3in",
+ "ep4in", "ep5in", "ep6in", "ep7in",
+ "ep8in", "ep9in", "ep10in", "ep11in",
+ "ep12in", "ep13in", "ep14in", "ep15in"
+};
+
+/* current state of VBUS */
+static int vbus;
+
+struct msm_request {
+ struct usb_request req;
+
+ /* saved copy of req.complete */
+ completion_func gadget_complete;
+
+ struct usb_info *ui;
+ struct msm_request *next;
+
+ unsigned busy:1;
+ unsigned live:1;
+ unsigned alloced:1;
+
+ dma_addr_t dma;
+ dma_addr_t item_dma;
+
+ struct ept_queue_item *item;
+};
+
+#define to_msm_request(r) container_of(r, struct msm_request, req)
+#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep)
+
+struct msm_endpoint {
+ struct usb_ep ep;
+ struct usb_info *ui;
+ struct msm_request *req; /* head of pending requests */
+ struct msm_request *last;
+ unsigned flags;
+
+ /* bit number (0-31) in various status registers
+ ** as well as the index into the usb_info's array
+ ** of all endpoints
+ */
+ unsigned char bit;
+ unsigned char num;
+
+ /* pointers to DMA transfer list area */
+ /* these are allocated from the usb_info dma space */
+ struct ept_queue_head *head;
+};
+
+static void usb_do_work(struct work_struct *w);
+
+
+#define USB_STATE_IDLE 0
+#define USB_STATE_ONLINE 1
+#define USB_STATE_OFFLINE 2
+
+#define USB_FLAG_START 0x0001
+#define USB_FLAG_VBUS_ONLINE 0x0002
+#define USB_FLAG_VBUS_OFFLINE 0x0004
+#define USB_FLAG_RESET 0x0008
+
+struct usb_info {
+ /* lock for register/queue/device state changes */
+ spinlock_t lock;
+
+ /* single request used for handling setup transactions */
+ struct usb_request *setup_req;
+
+ struct platform_device *pdev;
+ int irq;
+ void *addr;
+
+ unsigned state;
+ unsigned flags;
+
+ unsigned online:1;
+ unsigned running:1;
+
+ struct dma_pool *pool;
+
+ /* dma page to back the queue heads and items */
+ unsigned char *buf;
+ dma_addr_t dma;
+
+ struct ept_queue_head *head;
+
+ /* used for allocation */
+ unsigned next_item;
+ unsigned next_ifc_num;
+
+ /* endpoints are ordered based on their status bits,
+ ** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15
+ */
+ struct msm_endpoint ept[32];
+
+ int *phy_init_seq;
+ void (*phy_reset)(void);
+ void (*hw_reset)(bool en);
+
+ /* for notification when USB is connected or disconnected */
+ void (*usb_connected)(int);
+
+ struct work_struct work;
+ unsigned phy_status;
+ unsigned phy_fail_count;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+#define ep0out ept[0]
+#define ep0in ept[16]
+
+ struct clk *clk;
+ struct clk *coreclk;
+ struct clk *pclk;
+ struct clk *otgclk;
+ struct clk *ebi1clk;
+
+ unsigned int ep0_dir;
+ u16 test_mode;
+
+ u8 remote_wakeup;
+};
+
+static const struct usb_ep_ops msm72k_ep_ops;
+
+
+static int msm72k_pullup(struct usb_gadget *_gadget, int is_active);
+static int msm72k_set_halt(struct usb_ep *_ep, int value);
+static void flush_endpoint(struct msm_endpoint *ept);
+
+static int usb_ep_get_stall(struct msm_endpoint *ept)
+{
+ unsigned int n;
+ struct usb_info *ui = ept->ui;
+
+ n = readl(USB_ENDPTCTRL(ept->num));
+ if (ept->flags & EPT_FLAG_IN)
+ return (CTRL_TXS & n) ? 1 : 0;
+ else
+ return (CTRL_RXS & n) ? 1 : 0;
+}
+
+static unsigned ulpi_read(struct usb_info *ui, unsigned reg)
+{
+ unsigned timeout = 100000;
+
+ /* initiate read operation */
+ writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+ USB_ULPI_VIEWPORT);
+
+ /* wait for completion */
+ while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ;
+
+ if (timeout == 0) {
+ ERROR("ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT));
+ return 0xffffffff;
+ }
+ return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+}
+
+static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg)
+{
+ unsigned timeout = 10000;
+
+ /* initiate write operation */
+ writel(ULPI_RUN | ULPI_WRITE |
+ ULPI_ADDR(reg) | ULPI_DATA(val),
+ USB_ULPI_VIEWPORT);
+
+ /* wait for completion */
+ while((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ;
+
+ if (timeout == 0) {
+ printk(KERN_ERR "ulpi_write: timeout\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ulpi_init(struct usb_info *ui)
+{
+ int *seq = ui->phy_init_seq;
+
+ if (!seq)
+ return;
+
+ while (seq[0] >= 0) {
+// INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]);
+ ulpi_write(ui, seq[0], seq[1]);
+ seq += 2;
+ }
+}
+
+static void init_endpoints(struct usb_info *ui)
+{
+ unsigned n;
+
+ for (n = 0; n < 32; n++) {
+ struct msm_endpoint *ept = ui->ept + n;
+
+ ept->ui = ui;
+ ept->bit = n;
+ ept->num = n & 15;
+ ept->ep.name = ep_name[n];
+ ept->ep.ops = &msm72k_ep_ops;
+
+ if (ept->bit > 15) {
+ /* IN endpoint */
+ ept->head = ui->head + (ept->num << 1) + 1;
+ ept->flags = EPT_FLAG_IN;
+ } else {
+ /* OUT endpoint */
+ ept->head = ui->head + (ept->num << 1);
+ ept->flags = 0;
+ }
+
+ }
+}
+
+static void config_ept(struct msm_endpoint *ept)
+{
+ unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+
+ if (ept->bit == 0)
+ /* ep0 out needs interrupt-on-setup */
+ cfg |= CONFIG_IOS;
+
+ ept->head->config = cfg;
+ ept->head->next = TERMINATE;
+
+#if 0
+ if (ept->ep.maxpacket)
+ INFO("ept #%d %s max:%d head:%p bit:%d\n",
+ ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+ ept->ep.maxpacket, ept->head, ept->bit);
+#endif
+}
+
+static void configure_endpoints(struct usb_info *ui)
+{
+ unsigned n;
+
+ for (n = 0; n < 32; n++)
+ config_ept(ui->ept + n);
+}
+
+struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept,
+ unsigned bufsize, gfp_t gfp_flags)
+{
+ struct usb_info *ui = ept->ui;
+ struct msm_request *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ goto fail1;
+
+ req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma);
+ if (!req->item)
+ goto fail2;
+
+ if (bufsize) {
+ req->req.buf = kmalloc(bufsize, gfp_flags);
+ if (!req->req.buf)
+ goto fail3;
+ req->alloced = 1;
+ }
+
+ return &req->req;
+
+fail3:
+ dma_pool_free(ui->pool, req->item, req->item_dma);
+fail2:
+ kfree(req);
+fail1:
+ return 0;
+}
+
+static void usb_ept_enable(struct msm_endpoint *ept, int yes,
+ unsigned char ep_type)
+{
+ struct usb_info *ui = ept->ui;
+ int in = ept->flags & EPT_FLAG_IN;
+ unsigned n;
+
+ n = readl(USB_ENDPTCTRL(ept->num));
+
+ if (in) {
+ n = (n & (~CTRL_TXT_MASK));
+ if (yes) {
+ n |= CTRL_TXE | CTRL_TXR;
+ } else {
+ n &= (~CTRL_TXE);
+ }
+ if (yes) {
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_BULK:
+ n |= CTRL_TXT_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ n |= CTRL_TXT_INT;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ n |= CTRL_TXT_ISOCH;
+ break;
+ default:
+ pr_err("%s: unsupported ep_type %d for %s\n",
+ __func__, ep_type, ept->ep.name);
+ break;
+ }
+ }
+ } else {
+ n = (n & (~CTRL_RXT_MASK));
+ if (yes) {
+ n |= CTRL_RXE | CTRL_RXR;
+ } else {
+ n &= ~(CTRL_RXE);
+ }
+ if (yes) {
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_BULK:
+ n |= CTRL_RXT_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ n |= CTRL_RXT_INT;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ n |= CTRL_RXT_ISOCH;
+ break;
+ default:
+ pr_err("%s: unsupported ep_type %d for %s\n",
+ __func__, ep_type, ept->ep.name);
+ break;
+ }
+ }
+ }
+ writel(n, USB_ENDPTCTRL(ept->num));
+
+#if 0
+ INFO("ept %d %s %s\n",
+ ept->num, in ? "in" : "out", yes ? "enabled" : "disabled");
+#endif
+}
+
+static void usb_ept_start(struct msm_endpoint *ept)
+{
+ struct usb_info *ui = ept->ui;
+ struct msm_request *req = ept->req;
+
+ BUG_ON(req->live);
+
+ /* link the hw queue head to the request's transaction item */
+ ept->head->next = req->item_dma;
+ ept->head->info = 0;
+
+ /* start the endpoint */
+ writel(1 << ept->bit, USB_ENDPTPRIME);
+
+ /* mark this chain of requests as live */
+ while (req) {
+ req->live = 1;
+ req = req->next;
+ }
+}
+
+int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req)
+{
+ unsigned long flags;
+ struct msm_request *req = to_msm_request(_req);
+ struct msm_request *last;
+ struct usb_info *ui = ept->ui;
+ struct ept_queue_item *item = req->item;
+ unsigned length = req->req.length;
+
+ if (length > 0x4000)
+ return -EMSGSIZE;
+
+ spin_lock_irqsave(&ui->lock, flags);
+
+ if (req->busy) {
+ req->req.status = -EBUSY;
+ spin_unlock_irqrestore(&ui->lock, flags);
+ INFO("usb_ept_queue_xfer() tried to queue busy request\n");
+ return -EBUSY;
+ }
+
+ if (!ui->online && (ept->num != 0)) {
+ req->req.status = -ESHUTDOWN;
+ spin_unlock_irqrestore(&ui->lock, flags);
+ INFO("usb_ept_queue_xfer() called while offline\n");
+ return -ESHUTDOWN;
+ }
+
+ req->busy = 1;
+ req->live = 0;
+ req->next = 0;
+ req->req.status = -EBUSY;
+
+ req->dma = dma_map_single(NULL, req->req.buf, length,
+ (ept->flags & EPT_FLAG_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ /* prepare the transaction descriptor item for the hardware */
+ item->next = TERMINATE;
+ item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE;
+ item->page0 = req->dma;
+ item->page1 = (req->dma + 0x1000) & 0xfffff000;
+ item->page2 = (req->dma + 0x2000) & 0xfffff000;
+ item->page3 = (req->dma + 0x3000) & 0xfffff000;
+
+ /* Add the new request to the end of the queue */
+ last = ept->last;
+ if (last) {
+ /* Already requests in the queue. add us to the
+ * end, but let the completion interrupt actually
+ * start things going, to avoid hw issues
+ */
+ last->next = req;
+
+ /* only modify the hw transaction next pointer if
+ * that request is not live
+ */
+ if (!last->live)
+ last->item->next = req->item_dma;
+ } else {
+ /* queue was empty -- kick the hardware */
+ ept->req = req;
+ usb_ept_start(ept);
+ }
+ ept->last = req;
+
+ spin_unlock_irqrestore(&ui->lock, flags);
+ return 0;
+}
+
+/* --- endpoint 0 handling --- */
+
+static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct msm_request *r = to_msm_request(req);
+ struct msm_endpoint *ept = to_msm_endpoint(ep);
+ struct usb_info *ui = ept->ui;
+
+ req->complete = r->gadget_complete;
+ r->gadget_complete = NULL;
+ if (req->complete)
+ req->complete(&ui->ep0in.ep, req);
+}
+
+static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(ep);
+ struct msm_request *r = to_msm_request(req);
+ completion_func gadget_complete = r->gadget_complete;
+
+ if (gadget_complete) {
+ r->gadget_complete = NULL;
+ gadget_complete(ep, req);
+ }
+
+ /* queue up the receive of the ACK response from the host */
+ if (req->status == 0) {
+ struct usb_info *ui = ept->ui;
+ req->length = 0;
+ req->complete = ep0_complete;
+ if (ui->ep0_dir == USB_DIR_IN)
+ usb_ept_queue_xfer(&ui->ep0out, req);
+ else
+ usb_ept_queue_xfer(&ui->ep0in, req);
+ } else
+ ep0_complete(ep, req);
+}
+
+static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(ep);
+ struct usb_info *ui = ept->ui;
+ unsigned int temp;
+
+ if (!ui->test_mode)
+ return;
+
+ switch (ui->test_mode) {
+ case J_TEST:
+ pr_info("usb electrical test mode: (J)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC);
+ break;
+
+ case K_TEST:
+ pr_info("usb electrical test mode: (K)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC);
+ break;
+
+ case SE0_NAK_TEST:
+ pr_info("usb electrical test mode: (SE0-NAK)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC);
+ break;
+
+ case TST_PKT_TEST:
+ pr_info("usb electrical test mode: (TEST_PKT)\n");
+ temp = readl(USB_PORTSC) & (~PORTSC_PTC);
+ writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC);
+ break;
+ }
+}
+
+static void ep0_setup_ack(struct usb_info *ui)
+{
+ struct usb_request *req = ui->setup_req;
+ req->length = 0;
+ req->complete = ep0_setup_ack_complete;
+ usb_ept_queue_xfer(&ui->ep0in, req);
+}
+
+static void ep0_setup_stall(struct usb_info *ui)
+{
+ writel((1<<16) | (1<<0), USB_ENDPTCTRL(0));
+}
+
+static void ep0_setup_send(struct usb_info *ui, unsigned length)
+{
+ struct usb_request *req = ui->setup_req;
+ struct msm_request *r = to_msm_request(req);
+ struct msm_endpoint *ept = &ui->ep0in;
+
+ req->length = length;
+ req->complete = ep0_queue_ack_complete;
+ r->gadget_complete = NULL;
+ usb_ept_queue_xfer(ept, req);
+}
+
+static void handle_setup(struct usb_info *ui)
+{
+ struct usb_ctrlrequest ctl;
+ struct usb_request *req = ui->setup_req;
+ int ret;
+
+ memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl));
+ writel(EPT_RX(0), USB_ENDPTSETUPSTAT);
+
+ if (ctl.bRequestType & USB_DIR_IN)
+ ui->ep0_dir = USB_DIR_IN;
+ else
+ ui->ep0_dir = USB_DIR_OUT;
+
+ /* any pending ep0 transactions must be canceled */
+ flush_endpoint(&ui->ep0out);
+ flush_endpoint(&ui->ep0in);
+
+#if 0
+ INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n",
+ ctl.bRequestType, ctl.bRequest, ctl.wValue,
+ ctl.wIndex, ctl.wLength);
+#endif
+
+ if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) ==
+ (USB_DIR_IN | USB_TYPE_STANDARD)) {
+ if (ctl.bRequest == USB_REQ_GET_STATUS) {
+ if (ctl.wLength != 2)
+ goto stall;
+ switch (ctl.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_ENDPOINT:
+ {
+ struct msm_endpoint *ept;
+ unsigned num =
+ ctl.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ u16 temp = 0;
+
+ if (num == 0) {
+ memset(req->buf, 0, 2);
+ break;
+ }
+ if (ctl.wIndex & USB_ENDPOINT_DIR_MASK)
+ num += 16;
+ ept = &ui->ep0out + num;
+ temp = usb_ep_get_stall(ept);
+ temp = temp << USB_ENDPOINT_HALT;
+ memcpy(req->buf, &temp, 2);
+ break;
+ }
+ case USB_RECIP_DEVICE:
+ {
+ u16 temp = 0;
+
+ temp = 1 << USB_DEVICE_SELF_POWERED;
+ temp |= (ui->remote_wakeup <<
+ USB_DEVICE_REMOTE_WAKEUP);
+ memcpy(req->buf, &temp, 2);
+ break;
+ }
+ case USB_RECIP_INTERFACE:
+ memset(req->buf, 0, 2);
+ break;
+ default:
+ goto stall;
+ }
+ ep0_setup_send(ui, 2);
+ return;
+ }
+ }
+ if (ctl.bRequestType ==
+ (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) {
+ if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) ||
+ (ctl.bRequest == USB_REQ_SET_FEATURE)) {
+ if ((ctl.wValue == 0) && (ctl.wLength == 0)) {
+ unsigned num = ctl.wIndex & 0x0f;
+
+ if (num != 0) {
+ struct msm_endpoint *ept;
+
+ if (ctl.wIndex & 0x80)
+ num += 16;
+ ept = &ui->ep0out + num;
+
+ if (ctl.bRequest == USB_REQ_SET_FEATURE)
+ msm72k_set_halt(&ept->ep, 1);
+ else
+ msm72k_set_halt(&ept->ep, 0);
+ }
+ goto ack;
+ }
+ }
+ }
+ if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) {
+ if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) {
+ ui->online = !!ctl.wValue;
+ if (ui->online && ui->usb_connected)
+ ui->usb_connected(1);
+ } else if (ctl.bRequest == USB_REQ_SET_ADDRESS) {
+ /* write address delayed (will take effect
+ ** after the next IN txn)
+ */
+ writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR);
+ goto ack;
+ } else if (ctl.bRequest == USB_REQ_SET_FEATURE) {
+ switch (ctl.wValue) {
+ case USB_DEVICE_TEST_MODE:
+ switch (ctl.wIndex) {
+ case J_TEST:
+ case K_TEST:
+ case SE0_NAK_TEST:
+ case TST_PKT_TEST:
+ ui->test_mode = ctl.wIndex;
+ goto ack;
+ }
+ goto stall;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ ui->remote_wakeup = 1;
+ goto ack;
+ }
+ } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) &&
+ (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) {
+ ui->remote_wakeup = 0;
+ goto ack;
+ }
+ }
+
+ /* delegate if we get here */
+ if (ui->driver) {
+ ret = ui->driver->setup(&ui->gadget, &ctl);
+ if (ret >= 0)
+ return;
+ }
+
+stall:
+ /* stall ep0 on error */
+ ep0_setup_stall(ui);
+ return;
+
+ack:
+ ep0_setup_ack(ui);
+}
+
+static void handle_endpoint(struct usb_info *ui, unsigned bit)
+{
+ struct msm_endpoint *ept = ui->ept + bit;
+ struct msm_request *req;
+ unsigned long flags;
+ unsigned info;
+
+#if 0
+ INFO("handle_endpoint() %d %s req=%p(%08x)\n",
+ ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+ ept->req, ept->req ? ept->req->item_dma : 0);
+#endif
+
+ /* expire all requests that are no longer active */
+ spin_lock_irqsave(&ui->lock, flags);
+ while ((req = ept->req)) {
+ info = req->item->info;
+
+ /* if we've processed all live requests, time to
+ * restart the hardware on the next non-live request
+ */
+ if (!req->live) {
+ usb_ept_start(ept);
+ break;
+ }
+
+ /* if the transaction is still in-flight, stop here */
+ if (info & INFO_ACTIVE)
+ break;
+
+ /* advance ept queue to the next request */
+ ept->req = req->next;
+ if (ept->req == 0)
+ ept->last = 0;
+
+ dma_unmap_single(NULL, req->dma, req->req.length,
+ (ept->flags & EPT_FLAG_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) {
+ /* XXX pass on more specific error code */
+ req->req.status = -EIO;
+ req->req.actual = 0;
+ INFO("msm72k_udc: ept %d %s error. info=%08x\n",
+ ept->num,
+ (ept->flags & EPT_FLAG_IN) ? "in" : "out",
+ info);
+ } else {
+ req->req.status = 0;
+ req->req.actual =
+ req->req.length - ((info >> 16) & 0x7FFF);
+ }
+ req->busy = 0;
+ req->live = 0;
+
+ if (req->req.complete) {
+ spin_unlock_irqrestore(&ui->lock, flags);
+ req->req.complete(&ept->ep, &req->req);
+ spin_lock_irqsave(&ui->lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+#define FLUSH_WAIT_US 5
+#define FLUSH_TIMEOUT (2 * (USEC_PER_SEC / FLUSH_WAIT_US))
+static void flush_endpoint_hw(struct usb_info *ui, unsigned bits)
+{
+ uint32_t unflushed = 0;
+ uint32_t stat = 0;
+ int cnt = 0;
+
+ /* flush endpoint, canceling transactions
+ ** - this can take a "large amount of time" (per databook)
+ ** - the flush can fail in some cases, thus we check STAT
+ ** and repeat if we're still operating
+ ** (does the fact that this doesn't use the tripwire matter?!)
+ */
+ while (cnt < FLUSH_TIMEOUT) {
+ writel(bits, USB_ENDPTFLUSH);
+ while (((unflushed = readl(USB_ENDPTFLUSH)) & bits) &&
+ cnt < FLUSH_TIMEOUT) {
+ cnt++;
+ udelay(FLUSH_WAIT_US);
+ }
+
+ stat = readl(USB_ENDPTSTAT);
+ if (cnt >= FLUSH_TIMEOUT)
+ goto err;
+ if (!(stat & bits))
+ goto done;
+ cnt++;
+ udelay(FLUSH_WAIT_US);
+ }
+
+err:
+ pr_warning("%s: Could not complete flush! NOT GOOD! "
+ "stat: %x unflushed: %x bits: %x\n", __func__,
+ stat, unflushed, bits);
+done:
+ return;
+}
+
+static void flush_endpoint_sw(struct msm_endpoint *ept)
+{
+ struct usb_info *ui = ept->ui;
+ struct msm_request *req;
+ unsigned long flags;
+
+ /* inactive endpoints have nothing to do here */
+ if (ept->ep.maxpacket == 0)
+ return;
+
+ /* put the queue head in a sane state */
+ ept->head->info = 0;
+ ept->head->next = TERMINATE;
+
+ /* cancel any pending requests */
+ spin_lock_irqsave(&ui->lock, flags);
+ req = ept->req;
+ ept->req = 0;
+ ept->last = 0;
+ while (req != 0) {
+ req->busy = 0;
+ req->live = 0;
+ req->req.status = -ECONNRESET;
+ req->req.actual = 0;
+ if (req->req.complete) {
+ spin_unlock_irqrestore(&ui->lock, flags);
+ req->req.complete(&ept->ep, &req->req);
+ spin_lock_irqsave(&ui->lock, flags);
+ }
+ req = req->next;
+ }
+ spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void flush_endpoint(struct msm_endpoint *ept)
+{
+ flush_endpoint_hw(ept->ui, (1 << ept->bit));
+ flush_endpoint_sw(ept);
+}
+
+static void flush_all_endpoints(struct usb_info *ui)
+{
+ unsigned n;
+
+ flush_endpoint_hw(ui, 0xffffffff);
+
+ for (n = 0; n < 32; n++)
+ flush_endpoint_sw(ui->ept + n);
+}
+
+
+static irqreturn_t usb_interrupt(int irq, void *data)
+{
+ struct usb_info *ui = data;
+ unsigned n;
+
+ n = readl(USB_USBSTS);
+ writel(n, USB_USBSTS);
+
+ /* somehow we got an IRQ while in the reset sequence: ignore it */
+ if (ui->running == 0)
+ return IRQ_HANDLED;
+
+ if (n & STS_PCI) {
+ switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) {
+ case PORTSC_PSPD_FS:
+ INFO("msm72k_udc: portchange USB_SPEED_FULL\n");
+ ui->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSC_PSPD_LS:
+ INFO("msm72k_udc: portchange USB_SPEED_LOW\n");
+ ui->gadget.speed = USB_SPEED_LOW;
+ break;
+ case PORTSC_PSPD_HS:
+ INFO("msm72k_udc: portchange USB_SPEED_HIGH\n");
+ ui->gadget.speed = USB_SPEED_HIGH;
+ break;
+ }
+ }
+
+ if (n & STS_URI) {
+ INFO("msm72k_udc: reset\n");
+
+ writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+ writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+ writel(0xffffffff, USB_ENDPTFLUSH);
+ writel(0, USB_ENDPTCTRL(1));
+
+ if (ui->online != 0) {
+ /* marking us offline will cause ept queue attempts
+ ** to fail
+ */
+ ui->online = 0;
+
+ flush_all_endpoints(ui);
+
+ /* XXX: we can't seem to detect going offline,
+ * XXX: so deconfigure on reset for the time being
+ */
+ if (ui->driver) {
+ printk(KERN_INFO "usb: notify offline\n");
+ ui->driver->disconnect(&ui->gadget);
+ }
+ }
+ }
+
+ if (n & STS_SLI)
+ INFO("msm72k_udc: suspend\n");
+
+ if (n & STS_UI) {
+ n = readl(USB_ENDPTSETUPSTAT);
+ if (n & EPT_RX(0))
+ handle_setup(ui);
+
+ n = readl(USB_ENDPTCOMPLETE);
+ writel(n, USB_ENDPTCOMPLETE);
+ while (n) {
+ unsigned bit = __ffs(n);
+ handle_endpoint(ui, bit);
+ n = n & (~(1 << bit));
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static void usb_prepare(struct usb_info *ui)
+{
+ spin_lock_init(&ui->lock);
+
+ memset(ui->buf, 0, 4096);
+ ui->head = (void *) (ui->buf + 0);
+
+ /* only important for reset/reinit */
+ memset(ui->ept, 0, sizeof(ui->ept));
+ ui->next_item = 0;
+ ui->next_ifc_num = 0;
+
+ init_endpoints(ui);
+
+ ui->ep0in.ep.maxpacket = 64;
+ ui->ep0out.ep.maxpacket = 64;
+
+ ui->setup_req =
+ usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL);
+
+ INIT_WORK(&ui->work, usb_do_work);
+}
+
+static void usb_suspend_phy(struct usb_info *ui)
+{
+#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30)
+ /* clear VBusValid and SessionEnd rising interrupts */
+ ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f);
+ /* clear VBusValid and SessionEnd falling interrupts */
+ ulpi_write(ui, (1 << 1) | (1 << 3), 0x12);
+
+ /* Disable 60MHz CLKOUT in serial or carkit mode */
+ ulpi_write(ui, 0x08, 0x09);
+
+ /* Enable PHY Low Power Suspend - Clock Disable (PLPSCD) */
+ writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+ mdelay(1);
+#else
+ /* clear VBusValid and SessionEnd rising interrupts */
+ ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f);
+ /* clear VBusValid and SessionEnd falling interrupts */
+ ulpi_write(ui, (1 << 1) | (1 << 3), 0x12);
+ /* disable interface protect circuit to drop current consumption */
+ ulpi_write(ui, (1 << 7), 0x08);
+ /* clear the SuspendM bit -> suspend the PHY */
+ ulpi_write(ui, 1 << 6, 0x06);
+#endif
+}
+
+/* If this function returns < 0, the phy reset failed and we cannot
+ * continue at this point. The only solution is to wait until the next
+ * cable disconnect/reconnect to bring the phy back */
+static int usb_phy_reset(struct usb_info *ui)
+{
+ u32 val;
+ int ret;
+ int retries;
+
+ if (!ui->phy_reset)
+ return 0;
+
+ if (ui->hw_reset)
+ ui->hw_reset(1);
+ ui->phy_reset();
+ if (ui->hw_reset)
+ ui->hw_reset(0);
+
+#if defined(CONFIG_ARCH_QSD8X50)
+ val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
+ writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+ /* XXX: only necessary for pre-45nm internal PHYs. */
+ for (retries = 3; retries > 0; retries--) {
+ ret = ulpi_write(ui, ULPI_FUNC_SUSPENDM, ULPI_FUNC_CTRL_CLR);
+ if (!ret)
+ break;
+ ui->phy_reset();
+ }
+ if (!retries)
+ return -1;
+
+ /* this reset calibrates the phy, if the above write succeeded */
+ ui->phy_reset();
+
+ /* XXX: pre-45nm internal phys have a known issue which can cause them
+ * to lockup on reset. If ULPI accesses fail, try resetting the phy
+ * again */
+ for (retries = 3; retries > 0; retries--) {
+ ret = ulpi_read(ui, ULPI_DEBUG_REG);
+ if (ret != 0xffffffff)
+ break;
+ ui->phy_reset();
+ }
+ if (!retries)
+ return -1;
+#endif
+ pr_info("msm_hsusb_phy_reset: success\n");
+ return 0;
+}
+
+static void usb_reset(struct usb_info *ui)
+{
+ unsigned long flags;
+ printk(KERN_INFO "hsusb: reset controller\n");
+
+ spin_lock_irqsave(&ui->lock, flags);
+ ui->running = 0;
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ /* To prevent phantom packets being received by the usb core on
+ * some devices, put the controller into reset prior to
+ * resetting the phy. */
+ writel(2, USB_USBCMD);
+ msleep(10);
+
+#if 0
+ /* we should flush and shutdown cleanly if already running */
+ writel(0xffffffff, USB_ENDPTFLUSH);
+ msleep(2);
+#endif
+
+ if (usb_phy_reset(ui) < 0)
+ pr_err("%s: Phy reset failed!\n", __func__);
+
+ msleep(100);
+
+ /* toggle non-driving mode after phy reset to ensure that
+ * we cause a disconnect event to the host */
+ ulpi_write(ui, 0x18, 0x6);
+ msleep(1);
+ ulpi_write(ui, 0x8, 0x5);
+ msleep(1);
+
+ /* RESET */
+ writel(2, USB_USBCMD);
+ msleep(10);
+
+#ifdef CONFIG_ARCH_MSM7X00A
+ /* INCR4 BURST mode */
+ writel(0x01, USB_SBUSCFG);
+#else
+ /* bursts of unspecified length. */
+ writel(0, USB_AHBBURST);
+ /* Use the AHB transactor */
+ writel(0, USB_AHBMODE);
+#endif
+
+ /* select DEVICE mode */
+ writel(0x12, USB_USBMODE);
+ msleep(1);
+
+ /* select ULPI phy */
+ writel(0x80000000, USB_PORTSC);
+
+ ulpi_init(ui);
+
+ writel(ui->dma, USB_ENDPOINTLISTADDR);
+
+ configure_endpoints(ui);
+
+ /* marking us offline will cause ept queue attempts to fail */
+ ui->online = 0;
+
+ /* terminate any pending transactions */
+ flush_all_endpoints(ui);
+
+ if (ui->driver) {
+ printk(KERN_INFO "usb: notify offline\n");
+ ui->driver->disconnect(&ui->gadget);
+ }
+
+ /* enable interrupts */
+ writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+
+ /* go to RUN mode (D+ pullup enable) */
+ msm72k_pullup(&ui->gadget, 1);
+
+ spin_lock_irqsave(&ui->lock, flags);
+ ui->running = 1;
+ spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static void usb_start(struct usb_info *ui)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ ui->flags |= USB_FLAG_START;
+ schedule_work(&ui->work);
+ spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+static struct usb_info *the_usb_info;
+
+static int usb_free(struct usb_info *ui, int ret)
+{
+ INFO("usb_free(%d)\n", ret);
+
+ if (ui->irq)
+ free_irq(ui->irq, 0);
+ if (ui->pool)
+ dma_pool_destroy(ui->pool);
+ if (ui->dma)
+ dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma);
+ if (ui->addr)
+ iounmap(ui->addr);
+ if (ui->clk)
+ clk_put(ui->clk);
+ if (ui->pclk)
+ clk_put(ui->pclk);
+ if (ui->otgclk)
+ clk_put(ui->otgclk);
+ if (ui->coreclk)
+ clk_put(ui->coreclk);
+ if (ui->ebi1clk)
+ clk_put(ui->ebi1clk);
+ kfree(ui);
+ return ret;
+}
+
+static void usb_do_work_check_vbus(struct usb_info *ui)
+{
+ unsigned long iflags;
+
+ spin_lock_irqsave(&ui->lock, iflags);
+ if (vbus) {
+ ui->flags |= USB_FLAG_VBUS_ONLINE;
+ } else {
+ ui->flags |= USB_FLAG_VBUS_OFFLINE;
+ }
+ spin_unlock_irqrestore(&ui->lock, iflags);
+}
+
+static void usb_do_work(struct work_struct *w)
+{
+ struct usb_info *ui = container_of(w, struct usb_info, work);
+ unsigned long iflags;
+ unsigned flags, _vbus;
+
+ for (;;) {
+ spin_lock_irqsave(&ui->lock, iflags);
+ flags = ui->flags;
+ ui->flags = 0;
+ _vbus = vbus;
+ spin_unlock_irqrestore(&ui->lock, iflags);
+
+ /* give up if we have nothing to do */
+ if (flags == 0)
+ break;
+
+ switch (ui->state) {
+ case USB_STATE_IDLE:
+ if (flags & USB_FLAG_START) {
+ pr_info("msm72k_udc: IDLE -> ONLINE\n");
+ clk_set_rate(ui->ebi1clk, 128000000);
+ udelay(10);
+ if (ui->coreclk)
+ clk_enable(ui->coreclk);
+ clk_enable(ui->clk);
+ clk_enable(ui->pclk);
+ if (ui->otgclk)
+ clk_enable(ui->otgclk);
+ usb_reset(ui);
+
+ ui->state = USB_STATE_ONLINE;
+ usb_do_work_check_vbus(ui);
+ }
+ break;
+ case USB_STATE_ONLINE:
+ /* If at any point when we were online, we received
+ * the signal to go offline, we must honor it
+ */
+ if (flags & USB_FLAG_VBUS_OFFLINE) {
+ pr_info("msm72k_udc: ONLINE -> OFFLINE\n");
+
+ /* synchronize with irq context */
+ spin_lock_irqsave(&ui->lock, iflags);
+ ui->running = 0;
+ ui->online = 0;
+ msm72k_pullup(&ui->gadget, 0);
+ spin_unlock_irqrestore(&ui->lock, iflags);
+
+ if (ui->usb_connected)
+ ui->usb_connected(0);
+
+ /* terminate any transactions, etc */
+ flush_all_endpoints(ui);
+
+ if (ui->driver) {
+ printk(KERN_INFO "usb: notify offline\n");
+ ui->driver->disconnect(&ui->gadget);
+ }
+
+ usb_phy_reset(ui);
+
+ /* power down phy, clock down usb */
+ spin_lock_irqsave(&ui->lock, iflags);
+ usb_suspend_phy(ui);
+ clk_disable(ui->pclk);
+ clk_disable(ui->clk);
+ if (ui->otgclk)
+ clk_disable(ui->otgclk);
+ if (ui->coreclk)
+ clk_disable(ui->coreclk);
+ clk_set_rate(ui->ebi1clk, 0);
+ spin_unlock_irqrestore(&ui->lock, iflags);
+
+ ui->state = USB_STATE_OFFLINE;
+ usb_do_work_check_vbus(ui);
+ break;
+ }
+ if (flags & USB_FLAG_RESET) {
+ pr_info("msm72k_udc: ONLINE -> RESET\n");
+ usb_reset(ui);
+ pr_info("msm72k_udc: RESET -> ONLINE\n");
+ break;
+ }
+ break;
+ case USB_STATE_OFFLINE:
+ /* If we were signaled to go online and vbus is still
+ * present when we received the signal, go online.
+ */
+ if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) {
+ pr_info("msm72k_udc: OFFLINE -> ONLINE\n");
+ clk_set_rate(ui->ebi1clk, 128000000);
+ udelay(10);
+ if (ui->coreclk)
+ clk_enable(ui->coreclk);
+ clk_enable(ui->clk);
+ clk_enable(ui->pclk);
+ if (ui->otgclk)
+ clk_enable(ui->otgclk);
+ usb_reset(ui);
+
+ /* detect shorted D+/D-, indicating AC power */
+ msleep(10);
+ if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
+ if (ui->usb_connected)
+ ui->usb_connected(2);
+
+ ui->state = USB_STATE_ONLINE;
+ usb_do_work_check_vbus(ui);
+ }
+ break;
+ }
+ }
+}
+
+/* FIXME - the callers of this function should use a gadget API instead.
+ * This is called from htc_battery.c and board-halibut.c
+ * WARNING - this can get called before this driver is initialized.
+ */
+void msm_hsusb_set_vbus_state(int online)
+{
+ unsigned long flags = 0;
+ struct usb_info *ui = the_usb_info;
+
+ if (ui)
+ spin_lock_irqsave(&ui->lock, flags);
+ if (vbus != online) {
+ vbus = online;
+ if (ui) {
+ if (online) {
+ ui->flags |= USB_FLAG_VBUS_ONLINE;
+ } else {
+ ui->flags |= USB_FLAG_VBUS_OFFLINE;
+ }
+ schedule_work(&ui->work);
+ }
+ }
+ if (ui)
+ spin_unlock_irqrestore(&ui->lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS) && 0
+
+void usb_function_reenumerate(void)
+{
+ struct usb_info *ui = the_usb_info;
+
+ /* disable and re-enable the D+ pullup */
+ msm72k_pullup(&ui->gadget, false);
+ msleep(10);
+ msm72k_pullup(&ui->gadget, true);
+}
+
+static char debug_buffer[PAGE_SIZE];
+
+static ssize_t debug_read_status(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_info *ui = file->private_data;
+ char *buf = debug_buffer;
+ unsigned long flags;
+ struct msm_endpoint *ept;
+ struct msm_request *req;
+ int n;
+ int i = 0;
+
+ spin_lock_irqsave(&ui->lock, flags);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "regs: setup=%08x prime=%08x stat=%08x done=%08x\n",
+ readl(USB_ENDPTSETUPSTAT),
+ readl(USB_ENDPTPRIME),
+ readl(USB_ENDPTSTAT),
+ readl(USB_ENDPTCOMPLETE));
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n",
+ readl(USB_USBCMD),
+ readl(USB_USBSTS),
+ readl(USB_USBINTR),
+ readl(USB_PORTSC));
+
+
+ for (n = 0; n < 32; n++) {
+ ept = ui->ept + n;
+ if (ept->ep.maxpacket == 0)
+ continue;
+
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n",
+ ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+ ept->head->config, ept->head->active,
+ ept->head->next, ept->head->info);
+
+ for (req = ept->req; req; req = req->next)
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ " req @%08x next=%08x info=%08x page0=%08x %c %c\n",
+ req->item_dma, req->item->next,
+ req->item->info, req->item->page0,
+ req->busy ? 'B' : ' ',
+ req->live ? 'L' : ' '
+ );
+ }
+
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "phy failure count: %d\n", ui->phy_fail_count);
+
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+}
+
+static ssize_t debug_write_reset(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_info *ui = file->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ ui->flags |= USB_FLAG_RESET;
+ schedule_work(&ui->work);
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return count;
+}
+
+static ssize_t debug_write_cycle(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ usb_function_reenumerate();
+ return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+const struct file_operations debug_stat_ops = {
+ .open = debug_open,
+ .read = debug_read_status,
+};
+
+const struct file_operations debug_reset_ops = {
+ .open = debug_open,
+ .write = debug_write_reset,
+};
+
+const struct file_operations debug_cycle_ops = {
+ .open = debug_open,
+ .write = debug_write_cycle,
+};
+
+static void usb_debugfs_init(struct usb_info *ui)
+{
+ struct dentry *dent;
+ dent = debugfs_create_dir("usb", 0);
+ if (IS_ERR(dent))
+ return;
+
+ debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops);
+ debugfs_create_file("reset", 0220, dent, ui, &debug_reset_ops);
+ debugfs_create_file("cycle", 0220, dent, ui, &debug_cycle_ops);
+}
+#else
+static void usb_debugfs_init(struct usb_info *ui) {}
+#endif
+
+static int
+msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+ unsigned char ep_type =
+ desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (ep_type == USB_ENDPOINT_XFER_BULK)
+ _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ else
+ _ep->maxpacket = le16_to_cpu(64);
+ config_ept(ept);
+ usb_ept_enable(ept, 1, ep_type);
+ return 0;
+}
+
+static int msm72k_disable(struct usb_ep *_ep)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+
+ usb_ept_enable(ept, 0, 0);
+ return 0;
+}
+
+static struct usb_request *
+msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags);
+}
+
+static void
+msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct msm_request *req = to_msm_request(_req);
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+ struct usb_info *ui = ept->ui;
+
+ /* request should not be busy */
+ BUG_ON(req->busy);
+
+ if (req->alloced)
+ kfree(req->req.buf);
+ dma_pool_free(ui->pool, req->item, req->item_dma);
+ kfree(req);
+}
+
+static int
+msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags)
+{
+ struct msm_endpoint *ep = to_msm_endpoint(_ep);
+ struct usb_info *ui = ep->ui;
+
+ if (ep == &ui->ep0in) {
+ struct msm_request *r = to_msm_request(req);
+ if (!req->length)
+ goto ep_queue_done;
+ else {
+ if (ui->ep0_dir == USB_DIR_OUT) {
+ ep = &ui->ep0out;
+ ep->ep.driver_data = ui->ep0in.ep.driver_data;
+ }
+ /* ep0_queue_ack_complete queue a receive for ACK before
+ ** calling req->complete
+ */
+ r->gadget_complete = req->complete;
+ req->complete = ep0_queue_ack_complete;
+ }
+ }
+ep_queue_done:
+ return usb_ept_queue_xfer(ep, req);
+}
+
+static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct msm_endpoint *ep = to_msm_endpoint(_ep);
+ struct msm_request *req = to_msm_request(_req);
+ struct usb_info *ui = ep->ui;
+
+ struct msm_request *cur, *prev;
+ unsigned long flags;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ cur = ep->req;
+ prev = NULL;
+
+ while (cur != 0) {
+ if (cur == req) {
+ req->busy = 0;
+ req->live = 0;
+ req->req.status = -ECONNRESET;
+ req->req.actual = 0;
+ if (req->req.complete) {
+ spin_unlock_irqrestore(&ui->lock, flags);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock_irqsave(&ui->lock, flags);
+ }
+ /* remove from linked list */
+ if (prev)
+ prev->next = cur->next;
+ else
+ ep->req = cur->next;
+ if (ep->last == cur)
+ ep->last = prev;
+ /* break from loop */
+ cur = NULL;
+ } else {
+ prev = cur;
+ cur = cur->next;
+ }
+ }
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return 0;
+}
+
+static int
+msm72k_set_halt(struct usb_ep *_ep, int value)
+{
+ struct msm_endpoint *ept = to_msm_endpoint(_ep);
+ struct usb_info *ui = ept->ui;
+ unsigned int in = ept->flags & EPT_FLAG_IN;
+ unsigned int n;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ n = readl(USB_ENDPTCTRL(ept->num));
+
+ if (in) {
+ if (value)
+ n |= CTRL_TXS;
+ else {
+ n &= ~CTRL_TXS;
+ n |= CTRL_TXR;
+ }
+ } else {
+ if (value)
+ n |= CTRL_RXS;
+ else {
+ n &= ~CTRL_RXS;
+ n |= CTRL_RXR;
+ }
+ }
+ writel(n, USB_ENDPTCTRL(ept->num));
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return 0;
+}
+
+static int
+msm72k_fifo_status(struct usb_ep *_ep)
+{
+ return -EOPNOTSUPP;
+}
+
+static void
+msm72k_fifo_flush(struct usb_ep *_ep)
+{
+ flush_endpoint(to_msm_endpoint(_ep));
+}
+
+static const struct usb_ep_ops msm72k_ep_ops = {
+ .enable = msm72k_enable,
+ .disable = msm72k_disable,
+
+ .alloc_request = msm72k_alloc_request,
+ .free_request = msm72k_free_request,
+
+ .queue = msm72k_queue,
+ .dequeue = msm72k_dequeue,
+
+ .set_halt = msm72k_set_halt,
+ .fifo_status = msm72k_fifo_status,
+ .fifo_flush = msm72k_fifo_flush,
+};
+
+static int msm72k_get_frame(struct usb_gadget *_gadget)
+{
+ struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+
+ /* frame number is in bits 13:3 */
+ return (readl(USB_FRINDEX) >> 3) & 0x000007FF;
+}
+
+/* VBUS reporting logically comes from a transceiver */
+static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ msm_hsusb_set_vbus_state(is_active);
+ return 0;
+}
+
+/* drivers may have software control over D+ pullup */
+static int msm72k_pullup(struct usb_gadget *_gadget, int is_active)
+{
+ struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+
+ u32 cmd = (8 << 16);
+
+ /* disable/enable D+ pullup */
+ if (is_active) {
+ pr_info("msm_hsusb: enable pullup\n");
+ writel(cmd | 1, USB_USBCMD);
+ } else {
+ pr_info("msm_hsusb: disable pullup\n");
+ writel(cmd, USB_USBCMD);
+
+#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30)
+ ulpi_write(ui, 0x48, 0x04);
+#endif
+ }
+
+ return 0;
+}
+
+static int msm72k_wakeup(struct usb_gadget *_gadget)
+{
+ struct usb_info *ui = container_of(_gadget, struct usb_info, gadget);
+ unsigned long flags;
+
+ if (!ui->remote_wakeup) {
+ pr_err("%s: remote wakeup not supported\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ if (!ui->online) {
+ pr_err("%s: device is not configured\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&ui->lock, flags);
+ if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) {
+ pr_info("%s: enabling force resume\n", __func__);
+ writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC);
+ }
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops msm72k_ops = {
+ .get_frame = msm72k_get_frame,
+ .vbus_session = msm72k_udc_vbus_session,
+ .pullup = msm72k_pullup,
+ .wakeup = msm72k_wakeup,
+};
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_info *ui = the_usb_info;
+
+ msm72k_wakeup(&ui->gadget);
+
+ return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
+static int msm72k_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct usb_info *ui;
+ int irq;
+ int ret;
+
+ INFO("msm72k_probe\n");
+ ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL);
+ if (!ui)
+ return -ENOMEM;
+
+ spin_lock_init(&ui->lock);
+ ui->pdev = pdev;
+
+ if (pdev->dev.platform_data) {
+ struct msm_hsusb_platform_data *pdata = pdev->dev.platform_data;
+ ui->phy_reset = pdata->phy_reset;
+ ui->phy_init_seq = pdata->phy_init_seq;
+ ui->usb_connected = pdata->usb_connected;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res || (irq < 0))
+ return usb_free(ui, -ENODEV);
+
+ ui->addr = ioremap(res->start, 4096);
+ if (!ui->addr)
+ return usb_free(ui, -ENOMEM);
+
+ ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL);
+ if (!ui->buf)
+ return usb_free(ui, -ENOMEM);
+
+ ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0);
+ if (!ui->pool)
+ return usb_free(ui, -ENOMEM);
+
+ INFO("msm72k_probe() io=%p, irq=%d, dma=%p(%x)\n",
+ ui->addr, irq, ui->buf, ui->dma);
+
+ ui->clk = clk_get(&pdev->dev, "usb_hs_clk");
+ if (IS_ERR(ui->clk))
+ return usb_free(ui, PTR_ERR(ui->clk));
+
+ ui->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
+ if (IS_ERR(ui->pclk))
+ return usb_free(ui, PTR_ERR(ui->pclk));
+
+ ui->otgclk = clk_get(&pdev->dev, "usb_otg_clk");
+ if (IS_ERR(ui->otgclk))
+ ui->otgclk = NULL;
+
+ ui->coreclk = clk_get(&pdev->dev, "usb_hs_core_clk");
+ if (IS_ERR(ui->coreclk))
+ ui->coreclk = NULL;
+
+ ui->ebi1clk = clk_get(NULL, "ebi1_clk");
+ if (IS_ERR(ui->ebi1clk))
+ return usb_free(ui, PTR_ERR(ui->ebi1clk));
+
+ /* clear interrupts before requesting irq */
+ if (ui->coreclk)
+ clk_enable(ui->coreclk);
+ clk_enable(ui->clk);
+ clk_enable(ui->pclk);
+ if (ui->otgclk)
+ clk_enable(ui->otgclk);
+ writel(0, USB_USBINTR);
+ writel(0, USB_OTGSC);
+ if (ui->coreclk)
+ clk_disable(ui->coreclk);
+ if (ui->otgclk)
+ clk_disable(ui->otgclk);
+ clk_disable(ui->pclk);
+ clk_disable(ui->clk);
+
+ ret = request_irq(irq, usb_interrupt, 0, pdev->name, ui);
+ if (ret)
+ return usb_free(ui, ret);
+ enable_irq_wake(irq);
+ ui->irq = irq;
+
+ ui->gadget.ops = &msm72k_ops;
+ ui->gadget.is_dualspeed = 1;
+ device_initialize(&ui->gadget.dev);
+ dev_set_name(&ui->gadget.dev, "gadget");
+ ui->gadget.dev.parent = &pdev->dev;
+ ui->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+ the_usb_info = ui;
+
+ usb_debugfs_init(ui);
+
+ usb_prepare(ui);
+
+ return 0;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct usb_info *ui = the_usb_info;
+ int retval, n;
+
+ if (!driver
+ || driver->speed < USB_SPEED_FULL
+ || !driver->bind
+ || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+ if (!ui)
+ return -ENODEV;
+ if (ui->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ ui->driver = driver;
+ ui->gadget.dev.driver = &driver->driver;
+ ui->gadget.name = driver_name;
+ INIT_LIST_HEAD(&ui->gadget.ep_list);
+ ui->gadget.ep0 = &ui->ep0in.ep;
+ INIT_LIST_HEAD(&ui->gadget.ep0->ep_list);
+ ui->gadget.speed = USB_SPEED_UNKNOWN;
+
+ for (n = 1; n < 16; n++) {
+ struct msm_endpoint *ept = ui->ept + n;
+ list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+ ept->ep.maxpacket = 512;
+ }
+ for (n = 17; n < 32; n++) {
+ struct msm_endpoint *ept = ui->ept + n;
+ list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list);
+ ept->ep.maxpacket = 512;
+ }
+
+ retval = device_add(&ui->gadget.dev);
+ if (retval)
+ goto fail;
+
+ retval = driver->bind(&ui->gadget);
+ if (retval) {
+ INFO("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ device_del(&ui->gadget.dev);
+ goto fail;
+ }
+
+ /* create sysfs node for remote wakeup */
+ retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup);
+ if (retval != 0)
+ INFO("failed to create sysfs entry: (wakeup) error: (%d)\n",
+ retval);
+ INFO("msm72k_udc: registered gadget driver '%s'\n",
+ driver->driver.name);
+ usb_start(ui);
+
+ return 0;
+
+fail:
+ ui->driver = NULL;
+ ui->gadget.dev.driver = NULL;
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usb_info *dev = the_usb_info;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver || !driver->unbind)
+ return -EINVAL;
+
+ device_remove_file(&dev->gadget.dev, &dev_attr_wakeup);
+ driver->unbind(&dev->gadget);
+ dev->gadget.dev.driver = NULL;
+ dev->driver = NULL;
+
+ device_del(&dev->gadget.dev);
+
+ VDEBUG("unregistered gadget driver '%s'\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+static struct platform_driver usb_driver = {
+ .probe = msm72k_probe,
+ .driver = { .name = "msm_hsusb", },
+};
+
+static int __init init(void)
+{
+ return platform_driver_register(&usb_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ platform_driver_unregister(&usb_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Mike Lockwood, Brian Swetland");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a14..20fbb49 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2196,13 +2196,6 @@
Select this option if display contents should be inherited as set by
the bootloader.
-config FB_MSM
- tristate "MSM Framebuffer support"
- depends on FB && ARCH_MSM
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
-
config FB_MX3
tristate "MX3 Framebuffer support"
depends on FB && MX3_IPU
@@ -2229,6 +2222,8 @@
and could also have been called by other names when coupled with
a bridge adapter.
+source "drivers/video/msm/Kconfig"
+
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
new file mode 100644
index 0000000..fe5ee26
--- /dev/null
+++ b/drivers/video/msm/Kconfig
@@ -0,0 +1,46 @@
+config FB_MSM
+ tristate
+ depends on FB && ARCH_MSM
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ default y
+
+config FB_MSM_LEGACY_MDP
+ bool
+ depends on FB_MSM && (MSM_MDP31 || MSM_MDP22)
+ default y
+
+config FB_MSM_MDP_PPP
+ bool
+ depends on FB_MSM_LEGACY_MDP
+ default y
+
+config FB_MSM_LCDC
+ bool "Support for integrated LCD controller in qsd8x50"
+ depends on FB_MSM && MSM_MDP31
+ default y
+
+config FB_MSM_MDDI
+ bool "Support for MSM MDDI controllers"
+ depends on FB_MSM
+ default y
+
+config GPU_MSM_KGSL
+ tristate "MSM 3D Graphics driver for Adreno class GPUs"
+ default n
+ depends on FB_MSM && (ARCH_QSD8X50 || ARCH_MSM7X30)
+ select GENERIC_ALLOCATOR
+ select CONFIG_FW_LOADER
+ help
+ 3D graphics driver for QSD8x50 and MSM7x27. Required to
+ use hardware accelerated OpenGL ES 2.0 and 1.1 on these
+ chips.
+
+config MSM_KGSL_MMU
+ bool "Turn on MMU for graphics driver "
+ depends on GPU_MSM_KGSL && MMU
+ default n
+ help
+ If enabled, the GPU driver will allocate memory from vmalloc
+ and enable the use of GPU MMU, instead of using pmem.
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index 802d6ae..3fcfbd8 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -2,18 +2,33 @@
# core framebuffer
#
obj-y := msm_fb.o
+ifeq ($(CONFIG_FB_MSM_LOGO),y)
+obj-y += logo.o
+endif
# MDP DMA/PPP engine
#
-obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o
+obj-y += mdp.o
+
+obj-$(CONFIG_MSM_MDP40) += mdp_hw40.o
+
+obj-$(CONFIG_FB_MSM_LEGACY_MDP) += mdp_hw_legacy.o
+
+obj-$(CONFIG_FB_MSM_MDP_PPP) += mdp_ppp.o
+obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o
+obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o
# MDDI interface
#
-obj-y += mddi.o
+obj-$(CONFIG_FB_MSM_MDDI) += mddi.o
# MDDI client/panel drivers
#
-obj-y += mddi_client_dummy.o
-obj-y += mddi_client_toshiba.o
-obj-y += mddi_client_nt35399.o
+obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_simple.o
+obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_toshiba.o
+# MDP LCD controller driver
+obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o
+
+# Yamato GL driver
+obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/
diff --git a/drivers/video/msm/gpu/kgsl/Makefile b/drivers/video/msm/gpu/kgsl/Makefile
new file mode 100644
index 0000000..0290b50
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/Makefile
@@ -0,0 +1,11 @@
+msm_kgsl-objs = \
+ kgsl_drawctxt.o \
+ kgsl_cmdstream.o \
+ kgsl.o \
+ kgsl_log.o \
+ kgsl_mmu.o \
+ kgsl_ringbuffer.o \
+ kgsl_sharedmem.o \
+ kgsl_yamato.o
+
+obj-$(CONFIG_GPU_MSM_KGSL) += msm_kgsl.o
diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c
new file mode 100644
index 0000000..4dde98e
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl.c
@@ -0,0 +1,1230 @@
+/*
+* Copyright (c) 2008-2009 QUALCOMM USA, INC.
+*
+* All source code in this file is licensed under the following license
+*
+* 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, you can find it at http://www.fsf.org
+*/
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/android_pmem.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
+
+#include <asm/atomic.h>
+
+#include "kgsl.h"
+#include "kgsl_drawctxt.h"
+#include "kgsl_ringbuffer.h"
+#include "kgsl_cmdstream.h"
+#include "kgsl_log.h"
+
+struct kgsl_file_private {
+ struct list_head list;
+ struct list_head mem_list;
+ uint32_t ctxt_id_mask;
+ struct kgsl_pagetable *pagetable;
+ unsigned long vmalloc_size;
+};
+
+static void kgsl_put_phys_file(struct file *file);
+
+#ifdef CONFIG_MSM_KGSL_MMU
+static long flush_l1_cache_range(unsigned long addr, int size)
+{
+ struct page *page;
+ pte_t *pte_ptr;
+ unsigned long end;
+
+ for (end = addr; end < (addr + size); end += KGSL_PAGESIZE) {
+ pte_ptr = kgsl_get_pte_from_vaddr(end);
+ if (!pte_ptr)
+ return -EINVAL;
+
+ page = pte_page(pte_val(*pte_ptr));
+ if (!page) {
+ KGSL_DRV_ERR("could not find page for pte\n");
+ pte_unmap(pte_ptr);
+ return -EINVAL;
+ }
+
+ pte_unmap(pte_ptr);
+ flush_dcache_page(page);
+ }
+
+ return 0;
+}
+
+static long flush_l1_cache_all(struct kgsl_file_private *private)
+{
+ int result = 0;
+ struct kgsl_mem_entry *entry = NULL;
+
+ kgsl_yamato_runpending(&kgsl_driver.yamato_device);
+ list_for_each_entry(entry, &private->mem_list, list) {
+ if (KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH & entry->memdesc.priv) {
+ result =
+ flush_l1_cache_range((unsigned long)entry->
+ memdesc.hostptr,
+ entry->memdesc.size);
+ if (result)
+ goto done;
+ }
+ }
+done:
+ return result;
+}
+#else
+static inline long flush_l1_cache_range(unsigned long addr, int size)
+{ return 0; }
+
+static inline long flush_l1_cache_all(struct kgsl_file_private *private)
+{ return 0; }
+#endif
+
+/*this is used for logging, so that we can call the dev_printk
+ functions without export struct kgsl_driver everywhere*/
+struct device *kgsl_driver_getdevnode(void)
+{
+ BUG_ON(kgsl_driver.pdev == NULL);
+ return &kgsl_driver.pdev->dev;
+}
+
+/* the hw and clk enable/disable funcs must be either called from softirq or
+ * with mutex held */
+static void kgsl_clk_enable(void)
+{
+ clk_set_rate(kgsl_driver.ebi1_clk, 128000000);
+ clk_enable(kgsl_driver.imem_clk);
+ if (kgsl_driver.grp_pclk)
+ clk_enable(kgsl_driver.grp_pclk);
+ clk_enable(kgsl_driver.grp_clk);
+}
+
+static void kgsl_clk_disable(void)
+{
+ clk_disable(kgsl_driver.grp_clk);
+ if (kgsl_driver.grp_pclk)
+ clk_disable(kgsl_driver.grp_pclk);
+ clk_disable(kgsl_driver.imem_clk);
+ clk_set_rate(kgsl_driver.ebi1_clk, 0);
+}
+
+static void kgsl_hw_disable(void)
+{
+ kgsl_driver.active = false;
+ disable_irq(kgsl_driver.interrupt_num);
+ kgsl_clk_disable();
+ pr_debug("kgsl: hw disabled\n");
+ wake_unlock(&kgsl_driver.wake_lock);
+}
+
+static void kgsl_hw_enable(void)
+{
+ wake_lock(&kgsl_driver.wake_lock);
+ kgsl_clk_enable();
+ enable_irq(kgsl_driver.interrupt_num);
+ kgsl_driver.active = true;
+ pr_debug("kgsl: hw enabled\n");
+}
+
+static void kgsl_hw_get_locked(void)
+{
+ /* active_cnt is protected by driver mutex */
+ if (kgsl_driver.active_cnt++ == 0) {
+ if (kgsl_driver.active) {
+ del_timer_sync(&kgsl_driver.standby_timer);
+ barrier();
+ }
+ if (!kgsl_driver.active)
+ kgsl_hw_enable();
+ }
+}
+
+static void kgsl_hw_put_locked(bool start_timer)
+{
+ if ((--kgsl_driver.active_cnt == 0) && start_timer) {
+ mod_timer(&kgsl_driver.standby_timer,
+ jiffies + msecs_to_jiffies(20));
+ }
+}
+
+static void kgsl_do_standby_timer(unsigned long data)
+{
+ if (kgsl_yamato_is_idle(&kgsl_driver.yamato_device))
+ kgsl_hw_disable();
+ else
+ mod_timer(&kgsl_driver.standby_timer,
+ jiffies + msecs_to_jiffies(10));
+}
+
+/* file operations */
+static int kgsl_first_open_locked(void)
+{
+ int result = 0;
+
+ BUG_ON(kgsl_driver.active);
+ BUG_ON(kgsl_driver.active_cnt);
+
+ kgsl_clk_enable();
+
+ /* init memory apertures */
+ result = kgsl_sharedmem_init(&kgsl_driver.shmem);
+ if (result != 0)
+ goto done;
+
+ /* init devices */
+ result = kgsl_yamato_init(&kgsl_driver.yamato_device,
+ &kgsl_driver.yamato_config);
+ if (result != 0)
+ goto done;
+
+ result = kgsl_yamato_start(&kgsl_driver.yamato_device, 0);
+ if (result != 0)
+ goto done;
+
+done:
+ kgsl_clk_disable();
+ return result;
+}
+
+static int kgsl_last_release_locked(void)
+{
+ BUG_ON(kgsl_driver.active_cnt);
+
+ disable_irq(kgsl_driver.interrupt_num);
+
+ kgsl_yamato_stop(&kgsl_driver.yamato_device);
+
+ /* close devices */
+ kgsl_yamato_close(&kgsl_driver.yamato_device);
+
+ /* shutdown memory apertures */
+ kgsl_sharedmem_close(&kgsl_driver.shmem);
+
+ kgsl_clk_disable();
+ kgsl_driver.active = false;
+ wake_unlock(&kgsl_driver.wake_lock);
+
+ return 0;
+}
+
+static int kgsl_release(struct inode *inodep, struct file *filep)
+{
+ int result = 0;
+ unsigned int i;
+ struct kgsl_mem_entry *entry, *entry_tmp;
+ struct kgsl_file_private *private = NULL;
+
+ mutex_lock(&kgsl_driver.mutex);
+
+ private = filep->private_data;
+ BUG_ON(private == NULL);
+ filep->private_data = NULL;
+ list_del(&private->list);
+
+ kgsl_hw_get_locked();
+
+ for (i = 0; i < KGSL_CONTEXT_MAX; i++)
+ if (private->ctxt_id_mask & (1 << i))
+ kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, i);
+
+ list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list)
+ kgsl_remove_mem_entry(entry);
+
+ if (private->pagetable != NULL) {
+ kgsl_yamato_cleanup_pt(&kgsl_driver.yamato_device,
+ private->pagetable);
+ kgsl_mmu_destroypagetableobject(private->pagetable);
+ private->pagetable = NULL;
+ }
+
+ kfree(private);
+
+ if (atomic_dec_return(&kgsl_driver.open_count) == 0) {
+ KGSL_DRV_VDBG("last_release\n");
+ kgsl_hw_put_locked(false);
+ result = kgsl_last_release_locked();
+ } else
+ kgsl_hw_put_locked(true);
+
+ mutex_unlock(&kgsl_driver.mutex);
+
+ return result;
+}
+
+static int kgsl_open(struct inode *inodep, struct file *filep)
+{
+ int result = 0;
+ struct kgsl_file_private *private = NULL;
+
+ KGSL_DRV_DBG("file %p pid %d\n", filep, task_pid_nr(current));
+
+
+ if (filep->f_flags & O_EXCL) {
+ KGSL_DRV_ERR("O_EXCL not allowed\n");
+ return -EBUSY;
+ }
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (private == NULL) {
+ KGSL_DRV_ERR("cannot allocate file private data\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&kgsl_driver.mutex);
+
+ private->ctxt_id_mask = 0;
+ INIT_LIST_HEAD(&private->mem_list);
+
+ filep->private_data = private;
+
+ list_add(&private->list, &kgsl_driver.client_list);
+
+ if (atomic_inc_return(&kgsl_driver.open_count) == 1) {
+ result = kgsl_first_open_locked();
+ if (result != 0)
+ goto done;
+ }
+
+ kgsl_hw_get_locked();
+
+ /*NOTE: this must happen after first_open */
+ private->pagetable =
+ kgsl_mmu_createpagetableobject(&kgsl_driver.yamato_device.mmu);
+ if (private->pagetable == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+ result = kgsl_yamato_setup_pt(&kgsl_driver.yamato_device,
+ private->pagetable);
+ if (result) {
+ kgsl_mmu_destroypagetableobject(private->pagetable);
+ private->pagetable = NULL;
+ goto done;
+ }
+ private->vmalloc_size = 0;
+done:
+ kgsl_hw_put_locked(true);
+ mutex_unlock(&kgsl_driver.mutex);
+ if (result != 0)
+ kgsl_release(inodep, filep);
+ return result;
+}
+
+
+/*call with driver locked */
+static struct kgsl_mem_entry *
+kgsl_sharedmem_find(struct kgsl_file_private *private, unsigned int gpuaddr)
+{
+ struct kgsl_mem_entry *entry = NULL, *result = NULL;
+
+ BUG_ON(private == NULL);
+
+ list_for_each_entry(entry, &private->mem_list, list) {
+ if (entry->memdesc.gpuaddr == gpuaddr) {
+ result = entry;
+ break;
+ }
+ }
+ return result;
+}
+
+/*call with driver locked */
+struct kgsl_mem_entry *
+kgsl_sharedmem_find_region(struct kgsl_file_private *private,
+ unsigned int gpuaddr,
+ size_t size)
+{
+ struct kgsl_mem_entry *entry = NULL, *result = NULL;
+
+ BUG_ON(private == NULL);
+
+ list_for_each_entry(entry, &private->mem_list, list) {
+ if (gpuaddr >= entry->memdesc.gpuaddr &&
+ ((gpuaddr + size) <=
+ (entry->memdesc.gpuaddr + entry->memdesc.size))) {
+ result = entry;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*call all ioctl sub functions with driver locked*/
+
+static long kgsl_ioctl_device_getproperty(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_device_getproperty param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+ result = kgsl_yamato_getproperty(&kgsl_driver.yamato_device,
+ param.type,
+ param.value, param.sizebytes);
+done:
+ return result;
+}
+
+static long kgsl_ioctl_device_regread(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_device_regread param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+ result = kgsl_yamato_regread(&kgsl_driver.yamato_device,
+ param.offsetwords, ¶m.value);
+ if (result != 0)
+ goto done;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+done:
+ return result;
+}
+
+
+static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_device_waittimestamp param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ /* Don't wait forever, set a max value for now */
+ if (param.timeout == -1)
+ param.timeout = 10 * MSEC_PER_SEC;
+ result = kgsl_yamato_waittimestamp(&kgsl_driver.yamato_device,
+ param.timestamp,
+ param.timeout);
+
+ kgsl_yamato_runpending(&kgsl_driver.yamato_device);
+done:
+ return result;
+}
+
+static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_ringbuffer_issueibcmds param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (param.drawctxt_id >= KGSL_CONTEXT_MAX
+ || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) {
+ result = -EINVAL;
+ KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n",
+ param.drawctxt_id);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (kgsl_sharedmem_find_region(private, param.ibaddr,
+ param.sizedwords*sizeof(uint32_t)) == NULL) {
+ KGSL_DRV_ERR("invalid cmd buffer ibaddr %08x sizedwords %d\n",
+ param.ibaddr, param.sizedwords);
+ result = -EINVAL;
+ goto done;
+
+ }
+
+ result = kgsl_ringbuffer_issueibcmds(&kgsl_driver.yamato_device,
+ param.drawctxt_id,
+ param.ibaddr,
+ param.sizedwords,
+ ¶m.timestamp,
+ param.flags);
+ if (result != 0)
+ goto done;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_file_private
+ *private, void __user *arg)
+{
+ int result = 0;
+ struct kgsl_cmdstream_readtimestamp param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ param.timestamp =
+ kgsl_cmdstream_readtimestamp(&kgsl_driver.yamato_device,
+ param.type);
+ if (result != 0)
+ goto done;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+done:
+ return result;
+}
+
+static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_file_private
+ *private, void __user *arg)
+{
+ int result = 0;
+ struct kgsl_cmdstream_freememontimestamp param;
+ struct kgsl_mem_entry *entry = NULL;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ entry = kgsl_sharedmem_find(private, param.gpuaddr);
+ if (entry == NULL) {
+ KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (entry->memdesc.priv & KGSL_MEMFLAGS_VMALLOC_MEM)
+ entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH;
+
+ result = kgsl_cmdstream_freememontimestamp(&kgsl_driver.yamato_device,
+ entry,
+ param.timestamp,
+ param.type);
+
+ kgsl_yamato_runpending(&kgsl_driver.yamato_device);
+
+done:
+ return result;
+}
+
+static long kgsl_ioctl_drawctxt_create(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_drawctxt_create param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ result = kgsl_drawctxt_create(&kgsl_driver.yamato_device,
+ private->pagetable,
+ param.flags,
+ ¶m.drawctxt_id);
+ if (result != 0)
+ goto done;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ private->ctxt_id_mask |= 1 << param.drawctxt_id;
+
+done:
+ return result;
+}
+
+static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_drawctxt_destroy param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (param.drawctxt_id >= KGSL_CONTEXT_MAX
+ || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) {
+ result = -EINVAL;
+ goto done;
+ }
+
+ result = kgsl_drawctxt_destroy(&kgsl_driver.yamato_device,
+ param.drawctxt_id);
+ if (result == 0)
+ private->ctxt_id_mask &= ~(1 << param.drawctxt_id);
+
+done:
+ return result;
+}
+
+void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry)
+{
+ kgsl_mmu_unmap(entry->memdesc.pagetable,
+ entry->memdesc.gpuaddr & KGSL_PAGEMASK,
+ entry->memdesc.size);
+ if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) {
+ vfree((void *)entry->memdesc.physaddr);
+ entry->priv->vmalloc_size -= entry->memdesc.size;
+ } else
+ kgsl_put_phys_file(entry->pmem_file);
+ list_del(&entry->list);
+
+ if (entry->free_list.prev)
+ list_del(&entry->free_list);
+
+ kfree(entry);
+
+}
+
+static long kgsl_ioctl_sharedmem_free(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_sharedmem_free param;
+ struct kgsl_mem_entry *entry = NULL;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ entry = kgsl_sharedmem_find(private, param.gpuaddr);
+ if (entry == NULL) {
+ KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr);
+ result = -EINVAL;
+ goto done;
+ }
+
+ kgsl_remove_mem_entry(entry);
+done:
+ return result;
+}
+
+#ifdef CONFIG_MSM_KGSL_MMU
+static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0, len;
+ struct kgsl_sharedmem_from_vmalloc param;
+ struct kgsl_mem_entry *entry = NULL;
+ void *vmalloc_area;
+ struct vm_area_struct *vma;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto error;
+ }
+
+ if (!param.hostptr) {
+ KGSL_DRV_ERR
+ ("Invalid host pointer of malloc passed: param.hostptr "
+ "%08x\n", param.hostptr);
+ result = -EINVAL;
+ goto error;
+ }
+
+ vma = find_vma(current->mm, param.hostptr);
+ if (!vma) {
+ KGSL_MEM_ERR("Could not find vma for address %x\n",
+ param.hostptr);
+ result = -EINVAL;
+ goto error;
+ }
+ len = vma->vm_end - vma->vm_start;
+ if (vma->vm_pgoff || !IS_ALIGNED(len, PAGE_SIZE)
+ || !IS_ALIGNED(vma->vm_start, PAGE_SIZE)) {
+ KGSL_MEM_ERR
+ ("kgsl vmalloc mapping must be at offset 0 and page aligned\n");
+ result = -EINVAL;
+ goto error;
+ }
+ if (vma->vm_start != param.hostptr) {
+ KGSL_MEM_ERR
+ ("vma start address is not equal to mmap address\n");
+ result = -EINVAL;
+ goto error;
+ }
+
+ if ((private->vmalloc_size + len) > KGSL_GRAPHICS_MEMORY_LOW_WATERMARK
+ && !param.force_no_low_watermark) {
+ result = -ENOMEM;
+ goto error;
+ }
+
+ entry = kzalloc(sizeof(struct kgsl_mem_entry), GFP_KERNEL);
+ if (entry == NULL) {
+ result = -ENOMEM;
+ goto error;
+ }
+
+ /* allocate memory and map it to user space */
+ vmalloc_area = vmalloc_user(len);
+ if (!vmalloc_area) {
+ KGSL_MEM_ERR("vmalloc failed\n");
+ result = -ENOMEM;
+ goto error_free_entry;
+ }
+ if (!kgsl_cache_enable) {
+ /* If we are going to map non-cached, make sure to flush the
+ * cache to ensure that previously cached data does not
+ * overwrite this memory */
+ dmac_flush_range(vmalloc_area, vmalloc_area + len);
+ KGSL_MEM_INFO("Caching for memory allocation turned off\n");
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ } else {
+ KGSL_MEM_INFO("Caching for memory allocation turned on\n");
+ }
+
+ result = remap_vmalloc_range(vma, vmalloc_area, 0);
+ if (result) {
+ KGSL_MEM_ERR("remap_vmalloc_range returned %d\n", result);
+ goto error_free_vmalloc;
+ }
+
+ result =
+ kgsl_mmu_map(private->pagetable, (unsigned long)vmalloc_area, len,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV,
+ &entry->memdesc.gpuaddr, KGSL_MEMFLAGS_ALIGN4K);
+
+ if (result != 0)
+ goto error_free_vmalloc;
+
+ entry->memdesc.pagetable = private->pagetable;
+ entry->memdesc.size = len;
+ entry->memdesc.hostptr = (void *)param.hostptr;
+ entry->memdesc.priv = KGSL_MEMFLAGS_VMALLOC_MEM |
+ KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH;
+ entry->memdesc.physaddr = (unsigned long)vmalloc_area;
+ entry->priv = private;
+
+ param.gpuaddr = entry->memdesc.gpuaddr;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto error_unmap_entry;
+ }
+ private->vmalloc_size += len;
+ list_add(&entry->list, &private->mem_list);
+
+ return 0;
+
+error_unmap_entry:
+ kgsl_mmu_unmap(private->pagetable, entry->memdesc.gpuaddr,
+ entry->memdesc.size);
+
+error_free_vmalloc:
+ vfree(vmalloc_area);
+
+error_free_entry:
+ kfree(entry);
+
+error:
+ return result;
+}
+#else
+static inline int kgsl_ioctl_sharedmem_from_vmalloc(
+ struct kgsl_file_private *private, void __user *arg)
+{
+ return -ENOSYS;
+}
+#endif
+
+static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len,
+ struct file **filep)
+{
+ struct file *fbfile;
+ int put_needed;
+ unsigned long vstart = 0;
+ int ret = 0;
+ dev_t rdev;
+ struct fb_info *info;
+
+ *filep = NULL;
+ if (!get_pmem_file(fd, start, &vstart, len, filep))
+ return 0;
+
+ fbfile = fget_light(fd, &put_needed);
+ if (fbfile == NULL)
+ return -1;
+
+ rdev = fbfile->f_dentry->d_inode->i_rdev;
+ info = MAJOR(rdev) == FB_MAJOR ? registered_fb[MINOR(rdev)] : NULL;
+ if (info) {
+ *start = info->fix.smem_start;
+ *len = info->fix.smem_len;
+ ret = 0;
+ } else
+ ret = -1;
+ fput_light(fbfile, put_needed);
+
+ return ret;
+}
+
+static void kgsl_put_phys_file(struct file *file)
+{
+ KGSL_DRV_DBG("put phys file %p\n", file);
+ if (file)
+ put_pmem_file(file);
+}
+
+static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_sharedmem_from_pmem param;
+ struct kgsl_mem_entry *entry = NULL;
+ unsigned long start = 0, len = 0;
+ struct file *pmem_file = NULL;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto error;
+ }
+
+ if (kgsl_get_phys_file(param.pmem_fd, &start, &len, &pmem_file)) {
+ result = -EINVAL;
+ goto error;
+ } else if (param.offset + param.len > len) {
+ KGSL_DRV_ERR("%s: region too large 0x%x + 0x%x >= 0x%lx\n",
+ __func__, param.offset, param.len, len);
+ result = -EINVAL;
+ goto error_put_pmem;
+ }
+
+ KGSL_MEM_INFO("get phys file %p start 0x%lx len 0x%lx\n",
+ pmem_file, start, len);
+ KGSL_DRV_DBG("locked phys file %p\n", pmem_file);
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry == NULL) {
+ result = -ENOMEM;
+ goto error_put_pmem;
+ }
+
+ entry->pmem_file = pmem_file;
+
+ entry->memdesc.pagetable = private->pagetable;
+
+ /* Any MMU mapped memory must have a length in multiple of PAGESIZE */
+ entry->memdesc.size = ALIGN(param.len, PAGE_SIZE);
+
+ /*we shouldn't need to write here from kernel mode */
+ entry->memdesc.hostptr = NULL;
+
+ /* ensure that MMU mappings are at page boundary */
+ entry->memdesc.physaddr = start + (param.offset & KGSL_PAGEMASK);
+ result = kgsl_mmu_map(private->pagetable, entry->memdesc.physaddr,
+ entry->memdesc.size, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV,
+ &entry->memdesc.gpuaddr,
+ KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS);
+ if (result)
+ goto error_free_entry;
+
+ /* If the offset is not at 4K boundary then add the correct offset
+ * value to gpuaddr */
+ entry->memdesc.gpuaddr += (param.offset & ~KGSL_PAGEMASK);
+ param.gpuaddr = entry->memdesc.gpuaddr;
+
+ if (copy_to_user(arg, ¶m, sizeof(param))) {
+ result = -EFAULT;
+ goto error_unmap_entry;
+ }
+ list_add(&entry->list, &private->mem_list);
+ return result;
+
+error_unmap_entry:
+ kgsl_mmu_unmap(entry->memdesc.pagetable,
+ entry->memdesc.gpuaddr & KGSL_PAGEMASK,
+ entry->memdesc.size);
+error_free_entry:
+ kfree(entry);
+
+error_put_pmem:
+ kgsl_put_phys_file(pmem_file);
+
+error:
+ return result;
+}
+
+#ifdef CONFIG_MSM_KGSL_MMU
+/*This function flushes a graphics memory allocation from CPU cache
+ *when caching is enabled with MMU*/
+static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ int result = 0;
+ struct kgsl_mem_entry *entry;
+ struct kgsl_sharedmem_free param;
+
+ if (copy_from_user(¶m, arg, sizeof(param))) {
+ result = -EFAULT;
+ goto done;
+ }
+
+ entry = kgsl_sharedmem_find(private, param.gpuaddr);
+ if (!entry) {
+ KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr);
+ result = -EINVAL;
+ goto done;
+ }
+ result = flush_l1_cache_range((unsigned long)entry->memdesc.hostptr,
+ entry->memdesc.size);
+ /* Mark memory as being flushed so we don't flush it again */
+ entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH;
+done:
+ return result;
+}
+#else
+static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private,
+ void __user *arg)
+{
+ return -ENOSYS;
+}
+#endif
+
+
+static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result = 0;
+ struct kgsl_file_private *private = filep->private_data;
+ struct kgsl_drawctxt_set_bin_base_offset binbase;
+
+ BUG_ON(private == NULL);
+
+ KGSL_DRV_VDBG("filep %p cmd 0x%08x arg 0x%08lx\n", filep, cmd, arg);
+
+ mutex_lock(&kgsl_driver.mutex);
+
+ kgsl_hw_get_locked();
+
+ switch (cmd) {
+
+ case IOCTL_KGSL_DEVICE_GETPROPERTY:
+ result =
+ kgsl_ioctl_device_getproperty(private, (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_DEVICE_REGREAD:
+ result = kgsl_ioctl_device_regread(private, (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_DEVICE_WAITTIMESTAMP:
+ result = kgsl_ioctl_device_waittimestamp(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS:
+ if (kgsl_cache_enable)
+ flush_l1_cache_all(private);
+ result = kgsl_ioctl_rb_issueibcmds(private, (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_CMDSTREAM_READTIMESTAMP:
+ result =
+ kgsl_ioctl_cmdstream_readtimestamp(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP:
+ result =
+ kgsl_ioctl_cmdstream_freememontimestamp(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_DRAWCTXT_CREATE:
+ result = kgsl_ioctl_drawctxt_create(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_DRAWCTXT_DESTROY:
+ result =
+ kgsl_ioctl_drawctxt_destroy(private, (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_SHAREDMEM_FREE:
+ result = kgsl_ioctl_sharedmem_free(private, (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC:
+ kgsl_yamato_runpending(&kgsl_driver.yamato_device);
+ result = kgsl_ioctl_sharedmem_from_vmalloc(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE:
+ if (kgsl_cache_enable)
+ result = kgsl_ioctl_sharedmem_flush_cache(private,
+ (void __user *)arg);
+ break;
+ case IOCTL_KGSL_SHAREDMEM_FROM_PMEM:
+ kgsl_yamato_runpending(&kgsl_driver.yamato_device);
+ result = kgsl_ioctl_sharedmem_from_pmem(private,
+ (void __user *)arg);
+ break;
+
+ case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET:
+ if (copy_from_user(&binbase, (void __user *)arg,
+ sizeof(binbase))) {
+ result = -EFAULT;
+ break;
+ }
+
+ if (private->ctxt_id_mask & (1 << binbase.drawctxt_id)) {
+ result = kgsl_drawctxt_set_bin_base_offset(
+ &kgsl_driver.yamato_device,
+ binbase.drawctxt_id,
+ binbase.offset);
+ } else {
+ result = -EINVAL;
+ KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n",
+ binbase.drawctxt_id);
+ }
+ break;
+
+ default:
+ KGSL_DRV_ERR("invalid ioctl code %08x\n", cmd);
+ result = -EINVAL;
+ break;
+ }
+
+ kgsl_hw_put_locked(true);
+ mutex_unlock(&kgsl_driver.mutex);
+ KGSL_DRV_VDBG("result %d\n", result);
+ return result;
+}
+
+static struct file_operations kgsl_fops = {
+ .owner = THIS_MODULE,
+ .release = kgsl_release,
+ .open = kgsl_open,
+ .unlocked_ioctl = kgsl_ioctl,
+};
+
+
+struct kgsl_driver kgsl_driver = {
+ .misc = {
+ .name = DRIVER_NAME,
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &kgsl_fops,
+ },
+ .open_count = ATOMIC_INIT(0),
+ .mutex = __MUTEX_INITIALIZER(kgsl_driver.mutex),
+};
+
+static void kgsl_driver_cleanup(void)
+{
+
+ wake_lock_destroy(&kgsl_driver.wake_lock);
+
+ if (kgsl_driver.interrupt_num > 0) {
+ if (kgsl_driver.have_irq) {
+ free_irq(kgsl_driver.interrupt_num, NULL);
+ kgsl_driver.have_irq = 0;
+ }
+ kgsl_driver.interrupt_num = 0;
+ }
+
+ if (kgsl_driver.grp_clk) {
+ clk_put(kgsl_driver.grp_clk);
+ kgsl_driver.grp_clk = NULL;
+ }
+
+ if (kgsl_driver.imem_clk != NULL) {
+ clk_put(kgsl_driver.imem_clk);
+ kgsl_driver.imem_clk = NULL;
+ }
+
+ if (kgsl_driver.ebi1_clk != NULL) {
+ clk_put(kgsl_driver.ebi1_clk);
+ kgsl_driver.ebi1_clk = NULL;
+ }
+
+ kgsl_driver.pdev = NULL;
+
+}
+
+
+static int __devinit kgsl_platform_probe(struct platform_device *pdev)
+{
+ int result = 0;
+ struct clk *clk;
+ struct resource *res = NULL;
+
+ kgsl_debug_init();
+
+ INIT_LIST_HEAD(&kgsl_driver.client_list);
+
+ /*acquire clocks */
+ BUG_ON(kgsl_driver.grp_clk != NULL);
+ BUG_ON(kgsl_driver.imem_clk != NULL);
+ BUG_ON(kgsl_driver.ebi1_clk != NULL);
+
+ kgsl_driver.pdev = pdev;
+
+ setup_timer(&kgsl_driver.standby_timer, kgsl_do_standby_timer, 0);
+ wake_lock_init(&kgsl_driver.wake_lock, WAKE_LOCK_SUSPEND, "kgsl");
+
+ clk = clk_get(&pdev->dev, "grp_clk");
+ if (IS_ERR(clk)) {
+ result = PTR_ERR(clk);
+ KGSL_DRV_ERR("clk_get(grp_clk) returned %d\n", result);
+ goto done;
+ }
+ kgsl_driver.grp_clk = clk;
+
+ clk = clk_get(&pdev->dev, "grp_pclk");
+ if (IS_ERR(clk)) {
+ KGSL_DRV_ERR("no grp_pclk, continuing\n");
+ clk = NULL;
+ }
+ kgsl_driver.grp_pclk = clk;
+
+ clk = clk_get(&pdev->dev, "imem_clk");
+ if (IS_ERR(clk)) {
+ result = PTR_ERR(clk);
+ KGSL_DRV_ERR("clk_get(imem_clk) returned %d\n", result);
+ goto done;
+ }
+ kgsl_driver.imem_clk = clk;
+
+ clk = clk_get(&pdev->dev, "ebi1_clk");
+ if (IS_ERR(clk)) {
+ result = PTR_ERR(clk);
+ KGSL_DRV_ERR("clk_get(ebi1_clk) returned %d\n", result);
+ goto done;
+ }
+ kgsl_driver.ebi1_clk = clk;
+
+ /*acquire interrupt */
+ kgsl_driver.interrupt_num = platform_get_irq(pdev, 0);
+ if (kgsl_driver.interrupt_num <= 0) {
+ KGSL_DRV_ERR("platform_get_irq() returned %d\n",
+ kgsl_driver.interrupt_num);
+ result = -EINVAL;
+ goto done;
+ }
+
+ result = request_irq(kgsl_driver.interrupt_num, kgsl_yamato_isr,
+ IRQF_TRIGGER_HIGH, DRIVER_NAME, NULL);
+ if (result) {
+ KGSL_DRV_ERR("request_irq(%d) returned %d\n",
+ kgsl_driver.interrupt_num, result);
+ goto done;
+ }
+ kgsl_driver.have_irq = 1;
+ disable_irq(kgsl_driver.interrupt_num);
+
+ result = kgsl_yamato_config(&kgsl_driver.yamato_config, pdev);
+ if (result != 0)
+ goto done;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "kgsl_phys_memory");
+ if (res == NULL) {
+ result = -EINVAL;
+ goto done;
+ }
+
+ kgsl_driver.shmem.physbase = res->start;
+ kgsl_driver.shmem.size = resource_size(res);
+
+done:
+ if (result)
+ kgsl_driver_cleanup();
+ else
+ result = misc_register(&kgsl_driver.misc);
+
+ return result;
+}
+
+static int kgsl_platform_remove(struct platform_device *pdev)
+{
+
+ kgsl_driver_cleanup();
+ misc_deregister(&kgsl_driver.misc);
+
+ return 0;
+}
+
+static int kgsl_platform_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ mutex_lock(&kgsl_driver.mutex);
+ if (atomic_read(&kgsl_driver.open_count) > 0) {
+ if (kgsl_driver.active)
+ pr_err("%s: Suspending while active???\n", __func__);
+ }
+ mutex_unlock(&kgsl_driver.mutex);
+ return 0;
+}
+
+static struct platform_driver kgsl_platform_driver = {
+ .probe = kgsl_platform_probe,
+ .remove = __devexit_p(kgsl_platform_remove),
+ .suspend = kgsl_platform_suspend,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME
+ }
+};
+
+static int __init kgsl_mod_init(void)
+{
+ return platform_driver_register(&kgsl_platform_driver);
+}
+
+static void __exit kgsl_mod_exit(void)
+{
+ platform_driver_unregister(&kgsl_platform_driver);
+}
+
+module_init(kgsl_mod_init);
+module_exit(kgsl_mod_exit);
+
+MODULE_AUTHOR("QUALCOMM");
+MODULE_DESCRIPTION("3D graphics driver for QSD8x50 and MSM7x27");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:kgsl");
diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h
new file mode 100644
index 0000000..e9f0d7c
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl.h
@@ -0,0 +1,84 @@
+/*
+* Copyright (c) 2008-2009 QUALCOMM USA, INC.
+*
+* All source code in this file is licensed under the following license
+*
+* 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, you can find it at http://www.fsf.org
+*/
+#ifndef _GSL_DRIVER_H
+#define _GSL_DRIVER_H
+
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+
+#include <asm/atomic.h>
+
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+
+#define DRIVER_NAME "kgsl"
+
+struct kgsl_driver {
+ struct miscdevice misc;
+ struct platform_device *pdev;
+ atomic_t open_count;
+ struct mutex mutex;
+
+ int interrupt_num;
+ int have_irq;
+
+ struct clk *grp_clk;
+ struct clk *grp_pclk;
+ struct clk *imem_clk;
+ struct clk *ebi1_clk;
+
+ struct kgsl_devconfig yamato_config;
+
+ uint32_t flags_debug;
+
+ struct kgsl_sharedmem shmem;
+ struct kgsl_device yamato_device;
+
+ struct list_head client_list;
+
+ bool active;
+ int active_cnt;
+ struct timer_list standby_timer;
+
+ struct wake_lock wake_lock;
+};
+
+extern struct kgsl_driver kgsl_driver;
+
+struct kgsl_mem_entry {
+ struct kgsl_memdesc memdesc;
+ struct file *pmem_file;
+ struct list_head list;
+ struct list_head free_list;
+ uint32_t free_timestamp;
+
+ /* back pointer to private structure under whose context this
+ * allocation is made */
+ struct kgsl_file_private *priv;
+};
+
+void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry);
+
+#endif /* _GSL_DRIVER_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c
new file mode 100644
index 0000000..9b7183c
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c
@@ -0,0 +1,103 @@
+/*
+* Copyright (c) 2008-2009 QUALCOMM USA, INC.
+*
+* All source code in this file is licensed under the following license
+*
+* 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, you can find it at http://www.fsf.org
+*/
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+#include "kgsl_cmdstream.h"
+#include "kgsl_sharedmem.h"
+
+int kgsl_cmdstream_init(struct kgsl_device *device)
+{
+ return 0;
+}
+
+int kgsl_cmdstream_close(struct kgsl_device *device)
+{
+ return 0;
+}
+
+uint32_t
+kgsl_cmdstream_readtimestamp(struct kgsl_device *device,
+ enum kgsl_timestamp_type type)
+{
+ uint32_t timestamp = 0;
+
+ KGSL_CMD_VDBG("enter (device_id=%d, type=%d)\n", device->id, type);
+
+ if (type == KGSL_TIMESTAMP_CONSUMED)
+ KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device,
+ (unsigned int *)×tamp);
+ else if (type == KGSL_TIMESTAMP_RETIRED)
+ KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device,
+ (unsigned int *)×tamp);
+
+ KGSL_CMD_VDBG("return %d\n", timestamp);
+
+ return timestamp;
+}
+
+int kgsl_cmdstream_check_timestamp(struct kgsl_device *device,
+ unsigned int timestamp)
+{
+ unsigned int ts_processed;
+
+ ts_processed = kgsl_cmdstream_readtimestamp(device,
+ KGSL_TIMESTAMP_RETIRED);
+ return timestamp_cmp(ts_processed, timestamp);
+}
+
+void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device)
+{
+ struct kgsl_mem_entry *entry, *entry_tmp;
+ uint32_t ts_processed;
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+
+ /* get current EOP timestamp */
+ ts_processed =
+ kgsl_cmdstream_readtimestamp(device, KGSL_TIMESTAMP_RETIRED);
+
+ list_for_each_entry_safe(entry, entry_tmp, &rb->memqueue, free_list) {
+ /*NOTE: this assumes that the free list is sorted by
+ * timestamp, but I'm not yet sure that it is a valid
+ * assumption
+ */
+ if (!timestamp_cmp(ts_processed, entry->free_timestamp))
+ break;
+ KGSL_MEM_DBG("ts_processed %d ts_free %d gpuaddr %x)\n",
+ ts_processed, entry->free_timestamp,
+ entry->memdesc.gpuaddr);
+ kgsl_remove_mem_entry(entry);
+ }
+}
+
+int
+kgsl_cmdstream_freememontimestamp(struct kgsl_device *device,
+ struct kgsl_mem_entry *entry,
+ uint32_t timestamp,
+ enum kgsl_timestamp_type type)
+{
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+ KGSL_MEM_DBG("enter (dev %p gpuaddr %x ts %d)\n",
+ device, entry->memdesc.gpuaddr, timestamp);
+ (void)type; /* unref. For now just use EOP timestamp */
+
+ list_add_tail(&entry->free_list, &rb->memqueue);
+ entry->free_timestamp = timestamp;
+
+ return 0;
+}
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h
new file mode 100644
index 0000000..f81ef54
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h
@@ -0,0 +1,54 @@
+#ifndef __KGSL_CMDSTREAM_H
+#define __KGSL_CMDSTREAM_H
+
+#include <linux/msm_kgsl.h>
+#include "kgsl_device.h"
+#include "kgsl_log.h"
+
+#ifdef KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER
+#define KGSL_CMDSTREAM_USE_MEM_TIMESTAMP
+#endif /* KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER */
+
+#ifdef KGSL_CMDSTREAM_USE_MEM_TIMESTAMP
+#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \
+ kgsl_sharedmem_read(&device->memstore, (data), \
+ KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), 4)
+#else
+#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \
+ kgsl_yamato_regread(device, REG_CP_TIMESTAMP, (data))
+#endif /* KGSL_CMDSTREAM_USE_MEM_TIMESTAMP */
+
+#define KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, data) \
+ kgsl_sharedmem_read(&device->memstore, (data), \
+ KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), 4)
+
+/* Flags to control command packet settings */
+#define KGSL_CMD_FLAGS_PMODE 0x00000001
+#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002
+
+int kgsl_cmdstream_init(struct kgsl_device *device);
+
+int kgsl_cmdstream_close(struct kgsl_device *device);
+
+void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device);
+
+uint32_t
+kgsl_cmdstream_readtimestamp(struct kgsl_device *device,
+ enum kgsl_timestamp_type type);
+
+int kgsl_cmdstream_check_timestamp(struct kgsl_device *device,
+ unsigned int timestamp);
+
+int
+kgsl_cmdstream_freememontimestamp(struct kgsl_device *device,
+ struct kgsl_mem_entry *entry,
+ uint32_t timestamp,
+ enum kgsl_timestamp_type type);
+
+static inline bool timestamp_cmp(unsigned int new, unsigned int old)
+{
+ int ts_diff = new - old;
+ return (ts_diff >= 0) || (ts_diff < -20000);
+}
+
+#endif /* __KGSL_CMDSTREAM_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h
new file mode 100644
index 0000000..dcbf4db
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_device.h
@@ -0,0 +1,141 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef _KGSL_DEVICE_H
+#define _KGSL_DEVICE_H
+
+#include <asm/atomic.h>
+
+#include <linux/types.h>
+#include <linux/irqreturn.h>
+#include <linux/wait.h>
+#include <linux/msm_kgsl.h>
+
+#include "kgsl_drawctxt.h"
+#include "kgsl_mmu.h"
+#include "kgsl_ringbuffer.h"
+
+#define KGSL_CONTEXT_MAX 8
+
+#define KGSL_TIMEOUT_NONE 0
+#define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF
+
+#define KGSL_DEV_FLAGS_INITIALIZED0 0x00000001
+#define KGSL_DEV_FLAGS_INITIALIZED 0x00000002
+#define KGSL_DEV_FLAGS_STARTED 0x00000004
+#define KGSL_DEV_FLAGS_ACTIVE 0x00000008
+
+#define KGSL_CHIPID_YAMATODX_REV21 0x20100
+#define KGSL_CHIPID_YAMATODX_REV211 0x20101
+
+/* Private memory flags for use with memdesc->priv feild */
+#define KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH 0x00000001
+#define KGSL_MEMFLAGS_VMALLOC_MEM 0x00000002
+
+#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000
+#define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
+
+struct kgsl_device;
+struct platform_device;
+
+
+struct kgsl_memregion {
+ unsigned char *mmio_virt_base;
+ unsigned int mmio_phys_base;
+ uint32_t gpu_base;
+ unsigned int sizebytes;
+};
+
+struct kgsl_device {
+
+ unsigned int refcnt;
+ uint32_t flags;
+ enum kgsl_deviceid id;
+ unsigned int chip_id;
+ struct kgsl_memregion regspace;
+ struct kgsl_memdesc memstore;
+
+ struct kgsl_mmu mmu;
+ struct kgsl_memregion gmemspace;
+ struct kgsl_ringbuffer ringbuffer;
+ unsigned int drawctxt_count;
+ struct kgsl_drawctxt *drawctxt_active;
+ struct kgsl_drawctxt drawctxt[KGSL_CONTEXT_MAX];
+
+ wait_queue_head_t ib1_wq;
+};
+
+struct kgsl_devconfig {
+ struct kgsl_memregion regspace;
+
+ unsigned int mmu_config;
+ uint32_t mpu_base;
+ int mpu_range;
+ uint32_t va_base;
+ unsigned int va_range;
+
+ struct kgsl_memregion gmemspace;
+};
+
+int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags);
+
+int kgsl_yamato_stop(struct kgsl_device *device);
+
+bool kgsl_yamato_is_idle(struct kgsl_device *device);
+
+int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout);
+
+int kgsl_yamato_getproperty(struct kgsl_device *device,
+ enum kgsl_property_type type, void *value,
+ unsigned int sizebytes);
+
+int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords,
+ unsigned int *value);
+
+int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords,
+ unsigned int value);
+
+int kgsl_yamato_waittimestamp(struct kgsl_device *device,
+ unsigned int timestamp, unsigned int timeout);
+
+
+int kgsl_yamato_init(struct kgsl_device *, struct kgsl_devconfig *);
+
+int kgsl_yamato_close(struct kgsl_device *device);
+
+int kgsl_yamato_runpending(struct kgsl_device *device);
+
+int __init kgsl_yamato_config(struct kgsl_devconfig *,
+ struct platform_device *pdev);
+
+void kgsl_register_dump(struct kgsl_device *device);
+
+int kgsl_yamato_setup_pt(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable);
+int kgsl_yamato_cleanup_pt(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable);
+#ifdef CONFIG_MSM_KGSL_MMU
+int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags);
+#else
+static inline int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags)
+{ return 0; }
+#endif
+
+irqreturn_t kgsl_yamato_isr(int irq, void *data);
+
+#endif /* _KGSL_DEVICE_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c
new file mode 100644
index 0000000..3e5262e
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c
@@ -0,0 +1,1823 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+
+#include "kgsl_drawctxt.h"
+
+#include "yamato_reg.h"
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "kgsl_pm4types.h"
+#include "kgsl_cmdstream.h"
+
+/*
+*
+* Memory Map for Register, Constant & Instruction Shadow, and Command Buffers
+* (34.5KB)
+*
+* +---------------------+------------+-------------+---+---------------------+
+* | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow |
+* +---------------------+------------+-------------+---+---------------------+
+* ________________________________/ \____________________
+* / |
+* +--------------+-----------+------+-----------+------------------------+
+* | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused |
+* +--------------+-----------+------+-----------+------------------------+
+*
+* 8K - ALU Constant Shadow (8K aligned)
+* 4K - H/W Register Shadow (8K aligned)
+* 4K - Command and Vertex Buffers
+* - Indirect command buffer : Const/Reg restore
+* - includes Loop & Bool const shadows
+* - Indirect command buffer : Const/Reg save
+* - Quad vertices & texture coordinates
+* - Indirect command buffer : Gmem save
+* - Indirect command buffer : Gmem restore
+* - Unused (padding to 8KB boundary)
+* <1K - Texture Constant Shadow (768 bytes) (8K aligned)
+* 18K - Shader Instruction Shadow
+* - 6K vertex (32 byte aligned)
+* - 6K pixel (32 byte aligned)
+* - 6K shared (32 byte aligned)
+*
+* Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes
+* 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of
+* 16 bytes per constant. If the texture constants were transfered this way,
+* the Command & Vertex Buffers section would extend past the 16K boundary.
+* By moving the texture constant shadow area to start at 16KB boundary, we
+* only require approximately 40 bytes more memory, but are able to use the
+* LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up
+* context switching.
+*
+* [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool
+* constants would require an additional 8KB each, for alignment.]
+*
+*/
+
+/* Constants */
+
+#define ALU_CONSTANTS 2048 /* DWORDS */
+#define NUM_REGISTERS 1024 /* DWORDS */
+#ifdef DISABLE_SHADOW_WRITES
+#define CMD_BUFFER_LEN 9216 /* DWORDS */
+#else
+#define CMD_BUFFER_LEN 3072 /* DWORDS */
+#endif
+#define TEX_CONSTANTS (32*6) /* DWORDS */
+#define BOOL_CONSTANTS 8 /* DWORDS */
+#define LOOP_CONSTANTS 56 /* DWORDS */
+#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */
+
+#if defined(PM4_IM_STORE)
+/* 96-bit instructions */
+#define SHADER_INSTRUCT (1<<SHADER_INSTRUCT_LOG2)
+#else
+#define SHADER_INSTRUCT 0
+#endif
+
+/* LOAD_CONSTANT_CONTEXT shadow size */
+#define LCC_SHADOW_SIZE 0x2000 /* 8KB */
+
+#define ALU_SHADOW_SIZE LCC_SHADOW_SIZE /* 8KB */
+#define REG_SHADOW_SIZE 0x1000 /* 4KB */
+#ifdef DISABLE_SHADOW_WRITES
+#define CMD_BUFFER_SIZE 0x9000 /* 36KB */
+#else
+#define CMD_BUFFER_SIZE 0x3000 /* 12KB */
+#endif
+#define TEX_SHADOW_SIZE (TEX_CONSTANTS*4) /* 768 bytes */
+#define SHADER_SHADOW_SIZE (SHADER_INSTRUCT*12) /* 6KB */
+
+#define REG_OFFSET LCC_SHADOW_SIZE
+#define CMD_OFFSET (REG_OFFSET + REG_SHADOW_SIZE)
+#define TEX_OFFSET (CMD_OFFSET + CMD_BUFFER_SIZE)
+#define SHADER_OFFSET ((TEX_OFFSET + TEX_SHADOW_SIZE + 32) & ~31)
+
+#define CONTEXT_SIZE (SHADER_OFFSET + 3 * SHADER_SHADOW_SIZE)
+
+
+
+/* temporary work structure */
+struct tmp_ctx {
+ unsigned int *start; /* Command & Vertex buffer start */
+ unsigned int *cmd; /* Next available dword in C&V buffer */
+
+ /* address of buffers, needed when creating IB1 command buffers. */
+ uint32_t bool_shadow; /* bool constants */
+ uint32_t loop_shadow; /* loop constants */
+
+#if defined(PM4_IM_STORE)
+ uint32_t shader_shared; /* shared shader instruction shadow */
+ uint32_t shader_vertex; /* vertex shader instruction shadow */
+ uint32_t shader_pixel; /* pixel shader instruction shadow */
+#endif
+
+ /* Addresses in command buffer where separately handled registers
+ * are saved
+ */
+ uint32_t reg_values[4];
+ uint32_t chicken_restore;
+
+ uint32_t gmem_base; /* Base gpu address of GMEM */
+
+};
+
+/* Helper function to calculate IEEE754 single precision float values
+* without FPU
+*/
+unsigned int uint2float(unsigned int uintval)
+{
+ unsigned int exp = 0;
+ unsigned int frac = 0;
+ unsigned int u = uintval;
+
+ /* Handle zero separately */
+ if (uintval == 0)
+ return 0;
+ /* Find log2 of u */
+ if (u >= 0x10000) {
+ exp += 16;
+ u >>= 16;
+ }
+ if (u >= 0x100) {
+ exp += 8;
+ u >>= 8;
+ }
+ if (u >= 0x10) {
+ exp += 4;
+ u >>= 4;
+ }
+ if (u >= 0x4) {
+ exp += 2;
+ u >>= 2;
+ }
+ if (u >= 0x2) {
+ exp += 1;
+ u >>= 1;
+ }
+
+ /* Calculate fraction */
+ frac = (uintval & (~(1 << exp))) << (23 - exp);
+
+ /* Exp is biased by 127 and shifted 23 bits */
+ exp = (exp + 127) << 23;
+
+ return exp | frac;
+}
+
+/* context save (gmem -> sys) */
+
+/* pre-compiled vertex shader program
+*
+* attribute vec4 P;
+* void main(void)
+* {
+* gl_Position = P;
+* }
+*/
+#define GMEM2SYS_VTX_PGM_LEN 0x12
+
+static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = {
+ 0x00011003, 0x00001000, 0xc2000000,
+ 0x00001004, 0x00001000, 0xc4000000,
+ 0x00001005, 0x00002000, 0x00000000,
+ 0x1cb81000, 0x00398a88, 0x00000003,
+ 0x140f803e, 0x00000000, 0xe2010100,
+ 0x14000000, 0x00000000, 0xe2000000
+};
+
+/* pre-compiled fragment shader program
+*
+* precision highp float;
+* uniform vec4 clear_color;
+* void main(void)
+* {
+* gl_FragColor = clear_color;
+* }
+*/
+
+#define GMEM2SYS_FRAG_PGM_LEN 0x0c
+
+static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = {
+ 0x00000000, 0x1002c400, 0x10000000,
+ 0x00001003, 0x00002000, 0x00000000,
+ 0x140f8000, 0x00000000, 0x22000000,
+ 0x14000000, 0x00000000, 0xe2000000
+};
+
+/* context restore (sys -> gmem) */
+/* pre-compiled vertex shader program
+*
+* attribute vec4 position;
+* attribute vec4 texcoord;
+* varying vec4 texcoord0;
+* void main()
+* {
+* gl_Position = position;
+* texcoord0 = texcoord;
+* }
+*/
+
+#define SYS2GMEM_VTX_PGM_LEN 0x18
+
+static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = {
+ 0x00052003, 0x00001000, 0xc2000000, 0x00001005,
+ 0x00001000, 0xc4000000, 0x00001006, 0x10071000,
+ 0x20000000, 0x18981000, 0x0039ba88, 0x00000003,
+ 0x12982000, 0x40257b08, 0x00000002, 0x140f803e,
+ 0x00000000, 0xe2010100, 0x140f8000, 0x00000000,
+ 0xe2020200, 0x14000000, 0x00000000, 0xe2000000
+};
+
+/* pre-compiled fragment shader program
+*
+* precision mediump float;
+* uniform sampler2D tex0;
+* varying vec4 texcoord0;
+* void main()
+* {
+* gl_FragColor = texture2D(tex0, texcoord0.xy);
+* }
+*/
+
+#define SYS2GMEM_FRAG_PGM_LEN 0x0f
+
+static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = {
+ 0x00011002, 0x00001000, 0xc4000000, 0x00001003,
+ 0x10041000, 0x20000000, 0x10000001, 0x1ffff688,
+ 0x00000002, 0x140f8000, 0x00000000, 0xe2000000,
+ 0x14000000, 0x00000000, 0xe2000000
+};
+
+/* shader texture constants (sysmem -> gmem) */
+#define SYS2GMEM_TEX_CONST_LEN 6
+
+static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = {
+ /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat,
+ * RFMode=ZeroClamp-1, Dim=1:2d
+ */
+ 0x00000002, /* Pitch = TBD */
+
+ /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0,
+ * NearestClamp=1:OGL Mode
+ */
+ 0x00000800, /* Address[31:12] = TBD */
+
+ /* Width, Height, EndianSwap=0:None */
+ 0, /* Width & Height = TBD */
+
+ /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point,
+ * Mip=2:BaseMap
+ */
+ 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23,
+
+ /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0,
+ * Dim3d=0
+ */
+ 0,
+
+ /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0,
+ * Dim=1:2d, MipPacking=0
+ */
+ 1 << 9 /* Mip Address[31:12] = TBD */
+};
+
+/* quad for copying GMEM to context shadow */
+#define QUAD_LEN 12
+
+static unsigned int gmem_copy_quad[QUAD_LEN] = {
+ 0x00000000, 0x00000000, 0x3f800000,
+ 0x00000000, 0x00000000, 0x3f800000,
+ 0x00000000, 0x00000000, 0x3f800000,
+ 0x00000000, 0x00000000, 0x3f800000
+};
+
+#define TEXCOORD_LEN 8
+
+static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = {
+ 0x00000000, 0x3f800000,
+ 0x3f800000, 0x3f800000,
+ 0x00000000, 0x00000000,
+ 0x3f800000, 0x00000000
+};
+
+#define NUM_COLOR_FORMATS 13
+
+static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = {
+ FMT_4_4_4_4, /* COLORX_4_4_4_4 */
+ FMT_1_5_5_5, /* COLORX_1_5_5_5 */
+ FMT_5_6_5, /* COLORX_5_6_5 */
+ FMT_8, /* COLORX_8 */
+ FMT_8_8, /* COLORX_8_8 */
+ FMT_8_8_8_8, /* COLORX_8_8_8_8 */
+ FMT_8_8_8_8, /* COLORX_S8_8_8_8 */
+ FMT_16_FLOAT, /* COLORX_16_FLOAT */
+ FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */
+ FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */
+ FMT_32_FLOAT, /* COLORX_32_FLOAT */
+ FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */
+ FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */
+};
+
+static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = {
+ 2, /* COLORX_4_4_4_4 */
+ 2, /* COLORX_1_5_5_5 */
+ 2, /* COLORX_5_6_5 */
+ 1, /* COLORX_8 */
+ 2, /* COLORX_8_8 8*/
+ 4, /* COLORX_8_8_8_8 */
+ 4, /* COLORX_S8_8_8_8 */
+ 2, /* COLORX_16_FLOAT */
+ 4, /* COLORX_16_16_FLOAT */
+ 8, /* COLORX_16_16_16_16_FLOAT */
+ 4, /* COLORX_32_FLOAT */
+ 8, /* COLORX_32_32_FLOAT */
+ 16, /* COLORX_32_32_32_32_FLOAT */
+};
+
+/* shader linkage info */
+#define SHADER_CONST_ADDR (11 * 6 + 3)
+
+/* gmem command buffer length */
+#define PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg)))
+
+/* functions */
+static void config_gmemsize(struct gmem_shadow_t *shadow, int gmem_size)
+{
+ int w = 64, h = 64; /* 16KB surface, minimum */
+
+ shadow->format = COLORX_8_8_8_8;
+ /* convert from bytes to 32-bit words */
+ gmem_size = (gmem_size + 3) / 4;
+
+ /* find the right surface size, close to a square. */
+ while (w * h < gmem_size)
+ if (w < h)
+ w *= 2;
+ else
+ h *= 2;
+
+ shadow->width = w;
+ shadow->pitch = w;
+ shadow->height = h;
+ shadow->gmem_pitch = shadow->pitch;
+
+ shadow->size = shadow->pitch * shadow->height * 4;
+}
+
+static unsigned int gpuaddr(unsigned int *cmd, struct kgsl_memdesc *memdesc)
+{
+ return memdesc->gpuaddr + ((char *)cmd - (char *)memdesc->hostptr);
+}
+
+static void
+create_ib1(struct kgsl_drawctxt *drawctxt, unsigned int *cmd,
+ unsigned int *start, unsigned int *end)
+{
+ cmd[0] = PM4_HDR_INDIRECT_BUFFER_PFD;
+ cmd[1] = gpuaddr(start, &drawctxt->gpustate);
+ cmd[2] = end - start;
+}
+
+static unsigned int *program_shader(unsigned int *cmds, int vtxfrag,
+ unsigned int *shader_pgm, int dwords)
+{
+ /* load the patched vertex shader stream */
+ *cmds++ = pm4_type3_packet(PM4_IM_LOAD_IMMEDIATE, 2 + dwords);
+ /* 0=vertex shader, 1=fragment shader */
+ *cmds++ = vtxfrag;
+ /* instruction start & size (in 32-bit words) */
+ *cmds++ = ((0 << 16) | dwords);
+
+ memcpy(cmds, shader_pgm, dwords << 2);
+ cmds += dwords;
+
+ return cmds;
+}
+
+static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst,
+ uint32_t src, int dwords)
+{
+ while (dwords-- > 0) {
+ *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmds++ = src++;
+ *cmds++ = dst;
+ dst += 4;
+ }
+
+ return cmds;
+}
+
+#ifdef DISABLE_SHADOW_WRITES
+
+static void build_reg_to_mem_range(unsigned int start, unsigned int end,
+ unsigned int **cmd,
+ struct kgsl_drawctxt *drawctxt)
+{
+ unsigned int i = start;
+
+ for (i = start; i <= end; i++) {
+ *(*cmd)++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *(*cmd)++ = i | (1 << 30);
+ *(*cmd)++ =
+ ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) +
+ (i - 0x2000) * 4;
+ }
+}
+
+#endif
+
+/* chicken restore */
+static unsigned int *build_chicken_restore_cmds(struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx)
+{
+ unsigned int *start = ctx->cmd;
+ unsigned int *cmds = start;
+
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+
+ *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1);
+ ctx->chicken_restore = gpuaddr(cmds, &drawctxt->gpustate);
+ *cmds++ = 0x00000000;
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds);
+
+ return cmds;
+}
+
+/* save h/w regs, alu constants, texture contants, etc. ...
+* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr
+*/
+static void build_regsave_cmds(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx)
+{
+ unsigned int *start = ctx->cmd;
+ unsigned int *cmd = start;
+ unsigned int pm_override1;
+
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1);
+
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+
+#ifdef DISABLE_SHADOW_WRITES
+ /* Make sure the HW context has the correct register values
+ * before reading them. */
+ *cmd++ = pm4_type3_packet(PM4_CONTEXT_UPDATE, 1);
+ *cmd++ = 0;
+#endif
+
+ /* Enable clock override for REG_FIFOS_SCLK */
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmd++ = pm_override1 | (1 << 6);
+
+#ifdef DISABLE_SHADOW_WRITES
+ /* Write HW registers into shadow */
+ build_reg_to_mem_range(REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_COHER_DEST_BASE_0,
+ REG_PA_SC_SCREEN_SCISSOR_BR, &cmd, drawctxt);
+ build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET,
+ REG_PA_SC_WINDOW_SCISSOR_BR, &cmd, drawctxt);
+ build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_RB_STENCILREFMASK_BF,
+ REG_PA_CL_VPORT_ZOFFSET, &cmd, drawctxt);
+ build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, REG_PA_SC_LINE_STIPPLE,
+ &cmd, drawctxt);
+ build_reg_to_mem_range(REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, &cmd,
+ drawctxt);
+ build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL,
+ REG_RB_DEPTH_CLEAR, &cmd, drawctxt);
+ build_reg_to_mem_range(REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK,
+ &cmd, drawctxt);
+ build_reg_to_mem_range(REG_PA_SU_POLY_OFFSET_FRONT_SCALE,
+ REG_PA_SU_POLY_OFFSET_BACK_OFFSET, &cmd,
+ drawctxt);
+
+ /* Copy ALU constants */
+ cmd =
+ reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000,
+ REG_SQ_CONSTANT_0, ALU_CONSTANTS);
+
+ /* Copy Tex constants */
+ cmd =
+ reg_to_mem(cmd,
+ (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000,
+ REG_SQ_FETCH_0, TEX_CONSTANTS);
+#else
+
+ /* Insert a wait for idle packet before reading the registers.
+ * This is to fix a hang/reset seen during stress testing. In this
+ * hang, CP encountered a timeout reading SQ's boolean constant
+ * register. There is logic in the HW that blocks reading of this
+ * register when the SQ block is not idle, which we believe is
+ * contributing to the hang.*/
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+ /* H/w registers are already shadowed; just need to disable shadowing
+ * to prevent corruption.
+ */
+ *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3);
+ *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+ *cmd++ = 4 << 16; /* regs, start=0 */
+ *cmd++ = 0x0; /* count = 0 */
+
+ /* ALU constants are already shadowed; just need to disable shadowing
+ * to prevent corruption.
+ */
+ *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3);
+ *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000;
+ *cmd++ = 0 << 16; /* ALU, start=0 */
+ *cmd++ = 0x0; /* count = 0 */
+
+ /* Tex constants are already shadowed; just need to disable shadowing
+ * to prevent corruption.
+ */
+ *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3);
+ *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000;
+ *cmd++ = 1 << 16; /* Tex, start=0 */
+ *cmd++ = 0x0; /* count = 0 */
+#endif
+
+ /* Need to handle some of the registers separately */
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_SQ_GPR_MANAGEMENT;
+ *cmd++ = ctx->reg_values[0];
+
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_TP0_CHICKEN;
+ *cmd++ = ctx->reg_values[1];
+
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_RBBM_PM_OVERRIDE1;
+ *cmd++ = ctx->reg_values[2];
+
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_RBBM_PM_OVERRIDE2;
+ *cmd++ = ctx->reg_values[3];
+
+ /* Copy Boolean constants */
+ cmd = reg_to_mem(cmd, ctx->bool_shadow, REG_SQ_CF_BOOLEANS,
+ BOOL_CONSTANTS);
+
+ /* Copy Loop constants */
+ cmd = reg_to_mem(cmd, ctx->loop_shadow, REG_SQ_CF_LOOP, LOOP_CONSTANTS);
+
+ /* Restore RBBM_PM_OVERRIDE1 */
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmd++ = pm_override1;
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->reg_save, start, cmd);
+
+ ctx->cmd = cmd;
+}
+
+/*copy colour, depth, & stencil buffers from graphics memory to system memory*/
+static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx,
+ struct gmem_shadow_t *shadow)
+{
+ unsigned int *cmds = shadow->gmem_save_commands;
+ unsigned int *start = cmds;
+ unsigned int pm_override1;
+ /* Calculate the new offset based on the adjusted base */
+ unsigned int bytesperpixel = format2bytesperpixel[shadow->format];
+ unsigned int addr =
+ (shadow->gmemshadow.gpuaddr + shadow->offset * bytesperpixel);
+ unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel;
+
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1);
+
+ /* Store TP0_CHICKEN register */
+ *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmds++ = REG_TP0_CHICKEN;
+ if (ctx)
+ *cmds++ = ctx->chicken_restore;
+ else
+ cmds++;
+
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+
+ /* Enable clock override for REG_FIFOS_SCLK */
+ *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmds++ = pm_override1 | (1 << 6);
+
+ /* Set TP0_CHICKEN to zero */
+ *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1);
+ *cmds++ = 0x00000000;
+
+ /* Set PA_SC_AA_CONFIG to 0 */
+ *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1);
+ *cmds++ = 0x00000000;
+
+ /* program shader */
+
+ /* load shader vtx constants ... 5 dwords */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4);
+ *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR;
+ *cmds++ = 0;
+ /* valid(?) vtx constant flag & addr */
+ *cmds++ = shadow->quad_vertices.gpuaddr | 0x3;
+ /* limit = 12 dwords */
+ *cmds++ = 0x00000030;
+
+ /* Invalidate L2 cache to make sure vertices are updated */
+ *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1);
+ *cmds++ = 0x1;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4);
+ *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX);
+ *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */
+ *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */
+ *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SC_AA_MASK);
+ *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */
+
+ /* load the patched vertex shader stream */
+ cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN);
+
+ /* Load the patched fragment shader stream */
+ cmds =
+ program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN);
+
+ /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL);
+ *cmds++ = 0x10010001;
+ *cmds++ = 0x00000008;
+
+ /* resolve */
+
+ /* PA_CL_VTE_CNTL */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL);
+ /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */
+ *cmds++ = 0x00000b00;
+
+ /* program surface info */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_RB_SURFACE_INFO);
+ *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */
+
+ /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0,
+ * Base=gmem_base
+ */
+ /* gmem base assumed 4K aligned. */
+ if (ctx) {
+ BUG_ON(ctx->gmem_base & 0xFFF);
+ *cmds++ =
+ (shadow->
+ format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx->
+ gmem_base;
+ } else {
+ unsigned int temp = *cmds;
+ *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) |
+ (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT);
+ }
+
+ /* disable Z */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL);
+ *cmds++ = 0;
+
+ /* set REG_PA_SU_SC_MODE_CNTL
+ * Front_ptype = draw triangles
+ * Back_ptype = draw triangles
+ * Provoking vertex = last
+ */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL);
+ *cmds++ = 0x00080240;
+
+ /* Use maximum scissor values -- quad vertices already have the
+ * correct bounds */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL);
+ *cmds++ = (0 << 16) | 0;
+ *cmds++ = (0x1fff << 16) | (0x1fff);
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL);
+ *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0);
+ *cmds++ = (0x1fff << 16) | (0x1fff);
+
+ /* load the viewport so that z scale = clear depth and
+ * z offset = 0.0f
+ */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE);
+ *cmds++ = 0xbf800000; /* -1.0f */
+ *cmds++ = 0x0;
+
+ /* load the stencil ref value
+ * $AAM - do this later
+ */
+
+ /* load the COPY state */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 6);
+ *cmds++ = PM4_REG(REG_RB_COPY_CONTROL);
+ *cmds++ = 0; /* RB_COPY_CONTROL */
+ *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */
+ *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */
+
+ /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither,
+ * MaskWrite:R=G=B=A=1
+ */
+ *cmds++ = 0x0003c008 |
+ (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT);
+ /* Make sure we stay in offsetx field. */
+ BUG_ON(offset & 0xfffff000);
+ *cmds++ = offset;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_MODECONTROL);
+ *cmds++ = 0x6; /* EDRAM copy */
+
+ /* queue the draw packet */
+ *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2);
+ *cmds++ = 0; /* viz query info. */
+ /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */
+ *cmds++ = 0x00030088;
+
+ /* Restore RBBM_PM_OVERRIDE1 */
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+ *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmds++ = pm_override1;
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, shadow->gmem_save, start, cmds);
+
+ return cmds;
+}
+
+/* context restore */
+
+/*copy colour, depth, & stencil buffers from system memory to graphics memory*/
+static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx,
+ struct gmem_shadow_t *shadow)
+{
+ unsigned int *cmds = shadow->gmem_restore_commands;
+ unsigned int *start = cmds;
+ unsigned int pm_override1;
+
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1);
+
+ /* Store TP0_CHICKEN register */
+ *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmds++ = REG_TP0_CHICKEN;
+ if (ctx)
+ *cmds++ = ctx->chicken_restore;
+ else
+ cmds++;
+
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+
+ /* Enable clock override for REG_FIFOS_SCLK */
+ *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmds++ = pm_override1 | (1 << 6);
+
+ /* Set TP0_CHICKEN to zero */
+ *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1);
+ *cmds++ = 0x00000000;
+
+ /* Set PA_SC_AA_CONFIG to 0 */
+ *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1);
+ *cmds++ = 0x00000000;
+ /* shader constants */
+
+ /* vertex buffer constants */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 7);
+
+ *cmds++ = (0x1 << 16) | (9 * 6);
+ /* valid(?) vtx constant flag & addr */
+ *cmds++ = shadow->quad_vertices.gpuaddr | 0x3;
+ /* limit = 12 dwords */
+ *cmds++ = 0x00000030;
+ /* valid(?) vtx constant flag & addr */
+ *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3;
+ /* limit = 8 dwords */
+ *cmds++ = 0x00000020;
+ *cmds++ = 0;
+ *cmds++ = 0;
+
+ /* Invalidate L2 cache to make sure vertices are updated */
+ *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1);
+ *cmds++ = 0x1;
+
+ cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN);
+
+ /* Load the patched fragment shader stream */
+ cmds =
+ program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN);
+
+ /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL);
+ *cmds++ = 0x10030002;
+ *cmds++ = 0x00000008;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SC_AA_MASK);
+ *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */
+
+ /* PA_SC_VIZ_QUERY */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY);
+ *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */
+
+ /* RB_COLORCONTROL */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_COLORCONTROL);
+ *cmds++ = 0x00000c20;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4);
+ *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX);
+ *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */
+ *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */
+ *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL);
+ *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */
+ *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_SQ_INTERPOLATOR_CNTL);
+ //*cmds++ = 0x0000ffff; //mmSQ_INTERPOLATOR_CNTL
+ *cmds++ = 0xffffffff; //mmSQ_INTERPOLATOR_CNTL
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SC_AA_CONFIG);
+ *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */
+
+ /* set REG_PA_SU_SC_MODE_CNTL
+ * Front_ptype = draw triangles
+ * Back_ptype = draw triangles
+ * Provoking vertex = last
+ */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL);
+ *cmds++ = 0x00080240;
+
+ /* texture constants */
+ *cmds++ =
+ pm4_type3_packet(PM4_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1));
+ *cmds++ = (0x1 << 16) | (0 * 6);
+ memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2);
+ cmds[0] |= (shadow->pitch >> 5) << 22;
+ cmds[1] |=
+ shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format];
+ cmds[2] |=
+ (shadow->width + shadow->offset_x - 1) | (shadow->height +
+ shadow->offset_y -
+ 1) << 13;
+ cmds += SYS2GMEM_TEX_CONST_LEN;
+
+ /* program surface info */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_RB_SURFACE_INFO);
+ *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */
+
+ /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0,
+ * Base=gmem_base
+ */
+ if (ctx)
+ *cmds++ =
+ (shadow->
+ format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx->
+ gmem_base;
+ else {
+ unsigned int temp = *cmds;
+ *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) |
+ (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT);
+ }
+
+ /* RB_DEPTHCONTROL */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL);
+ *cmds++ = 0; /* disable Z */
+
+ /* Use maximum scissor values -- quad vertices already
+ * have the correct bounds */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL);
+ *cmds++ = (0 << 16) | 0;
+ *cmds++ = ((0x1fff) << 16) | 0x1fff;
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL);
+ *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0);
+ *cmds++ = ((0x1fff) << 16) | 0x1fff;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL);
+ /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */
+ *cmds++ = 0x00000b00;
+
+ /*load the viewport so that z scale = clear depth and z offset = 0.0f */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE);
+ *cmds++ = 0xbf800000;
+ *cmds++ = 0x0;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_COLOR_MASK);
+ *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_COLOR_DEST_MASK);
+ *cmds++ = 0xffffffff;
+
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3);
+ *cmds++ = PM4_REG(REG_SQ_WRAPPING_0);
+ *cmds++ = 0x00000000;
+ *cmds++ = 0x00000000;
+
+ /* load the stencil ref value
+ * $AAM - do this later
+ */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = PM4_REG(REG_RB_MODECONTROL);
+ /* draw pixels with color and depth/stencil component */
+ *cmds++ = 0x4;
+
+ /* queue the draw packet */
+ *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2);
+ *cmds++ = 0; /* viz query info. */
+ /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */
+ *cmds++ = 0x00030088;
+
+ /* Restore RBBM_PM_OVERRIDE1 */
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0;
+ *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmds++ = pm_override1;
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, shadow->gmem_restore, start, cmds);
+
+ return cmds;
+}
+
+/* restore h/w regs, alu constants, texture constants, etc. ... */
+static unsigned *reg_range(unsigned int *cmd, unsigned int start,
+ unsigned int end)
+{
+ *cmd++ = PM4_REG(start); /* h/w regs, start addr */
+ *cmd++ = end - start + 1; /* count */
+ return cmd;
+}
+
+static void build_regrestore_cmds(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx)
+{
+ unsigned int *start = ctx->cmd;
+ unsigned int *cmd = start;
+ unsigned int pm_override1;
+
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1);
+
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+
+ /* Enable clock override for REG_FIFOS_SCLK */
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmd++ = pm_override1 | (1 << 6);
+
+ /* H/W Registers */
+ /* deferred pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, ???); */
+ cmd++;
+#ifdef DISABLE_SHADOW_WRITES
+ /* Force mismatch */
+ *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1;
+#else
+ *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000;
+#endif
+
+ cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_PA_SC_SCREEN_SCISSOR_BR);
+ cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET,
+ REG_PA_SC_WINDOW_SCISSOR_BR);
+ cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, REG_PA_CL_VPORT_ZOFFSET);
+ cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1);
+ cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL);
+ cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE,
+ REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */
+ cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, REG_RB_COLOR_DEST_MASK);
+ cmd = reg_range(cmd, REG_PA_SU_POLY_OFFSET_FRONT_SCALE,
+ REG_PA_SU_POLY_OFFSET_BACK_OFFSET);
+
+ /* Now we know how many register blocks we have, we can compute command
+ * length
+ */
+ start[4] =
+ pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, (cmd - start) - 5);
+ /* Enable shadowing for the entire register block. */
+#ifdef DISABLE_SHADOW_WRITES
+ start[6] |= (0 << 24) | (4 << 16); /* Disable shadowing. */
+#else
+ start[6] |= (1 << 24) | (4 << 16);
+#endif
+
+ /* Need to handle some of the registers separately */
+ *cmd++ = pm4_type0_packet(REG_SQ_GPR_MANAGEMENT, 1);
+ ctx->reg_values[0] = gpuaddr(cmd, &drawctxt->gpustate);
+ *cmd++ = 0x00040400;
+
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+ *cmd++ = pm4_type0_packet(REG_TP0_CHICKEN, 1);
+ ctx->reg_values[1] = gpuaddr(cmd, &drawctxt->gpustate);
+ *cmd++ = 0x00000000;
+
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate);
+ *cmd++ = 0x00000000;
+
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1);
+ ctx->reg_values[3] = gpuaddr(cmd, &drawctxt->gpustate);
+ *cmd++ = 0x00000000;
+
+ /* ALU Constants */
+ *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3);
+ *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000;
+#ifdef DISABLE_SHADOW_WRITES
+ *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */
+#else
+ *cmd++ = (1 << 24) | (0 << 16) | 0;
+#endif
+ *cmd++ = ALU_CONSTANTS;
+
+ /* Texture Constants */
+ *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3);
+ *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000;
+#ifdef DISABLE_SHADOW_WRITES
+ /* Disable shadowing */
+ *cmd++ = (0 << 24) | (1 << 16) | 0;
+#else
+ *cmd++ = (1 << 24) | (1 << 16) | 0;
+#endif
+ *cmd++ = TEX_CONSTANTS;
+
+ /* Boolean Constants */
+ *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + BOOL_CONSTANTS);
+ *cmd++ = (2 << 16) | 0;
+
+ /* the next BOOL_CONSTANT dwords is the shadow area for
+ * boolean constants.
+ */
+ ctx->bool_shadow = gpuaddr(cmd, &drawctxt->gpustate);
+ cmd += BOOL_CONSTANTS;
+
+ /* Loop Constants */
+ *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + LOOP_CONSTANTS);
+ *cmd++ = (3 << 16) | 0;
+
+ /* the next LOOP_CONSTANTS dwords is the shadow area for
+ * loop constants.
+ */
+ ctx->loop_shadow = gpuaddr(cmd, &drawctxt->gpustate);
+ cmd += LOOP_CONSTANTS;
+
+ /* Restore RBBM_PM_OVERRIDE1 */
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+ *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1);
+ *cmd++ = pm_override1;
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->reg_restore, start, cmd);
+
+ ctx->cmd = cmd;
+}
+
+/* quad for saving/restoring gmem */
+static void set_gmem_copy_quad(struct gmem_shadow_t *shadow)
+{
+ /* set vertex buffer values */
+ gmem_copy_quad[1] = uint2float(shadow->height + shadow->gmem_offset_y);
+ gmem_copy_quad[3] = uint2float(shadow->width + shadow->gmem_offset_x);
+ gmem_copy_quad[4] = uint2float(shadow->height + shadow->gmem_offset_y);
+ gmem_copy_quad[9] = uint2float(shadow->width + shadow->gmem_offset_x);
+
+ gmem_copy_quad[0] = uint2float(shadow->gmem_offset_x);
+ gmem_copy_quad[6] = uint2float(shadow->gmem_offset_x);
+ gmem_copy_quad[7] = uint2float(shadow->gmem_offset_y);
+ gmem_copy_quad[10] = uint2float(shadow->gmem_offset_y);
+
+ BUG_ON(shadow->offset_x);
+ BUG_ON(shadow->offset_y);
+
+ memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2);
+
+ memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord,
+ TEXCOORD_LEN << 2);
+}
+
+/* quad for saving/restoring gmem */
+static void build_quad_vtxbuff(struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx, struct gmem_shadow_t *shadow)
+{
+ unsigned int *cmd = ctx->cmd;
+
+ /* quad vertex buffer location (in GPU space) */
+ shadow->quad_vertices.hostptr = cmd;
+ shadow->quad_vertices.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate);
+
+ cmd += QUAD_LEN;
+
+ /* tex coord buffer location (in GPU space) */
+ shadow->quad_texcoords.hostptr = cmd;
+ shadow->quad_texcoords.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate);
+
+ cmd += TEXCOORD_LEN;
+
+ set_gmem_copy_quad(shadow);
+
+ ctx->cmd = cmd;
+}
+
+static void
+build_shader_save_restore_cmds(struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx)
+{
+ unsigned int *cmd = ctx->cmd;
+ unsigned int *save, *restore, *fixup;
+#if defined(PM4_IM_STORE)
+ unsigned int *startSizeVtx, *startSizePix, *startSizeShared;
+#endif
+ unsigned int *partition1;
+ unsigned int *shaderBases, *partition2;
+
+#if defined(PM4_IM_STORE)
+ /* compute vertex, pixel and shared instruction shadow GPU addresses */
+ ctx->shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET;
+ ctx->shader_pixel = ctx->shader_vertex + SHADER_SHADOW_SIZE;
+ ctx->shader_shared = ctx->shader_pixel + SHADER_SHADOW_SIZE;
+#endif
+
+ /* restore shader partitioning and instructions */
+
+ restore = cmd; /* start address */
+
+ /* Invalidate Vertex & Pixel instruction code address and sizes */
+ *cmd++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1);
+ *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */
+
+ /* Restore previous shader vertex & pixel instruction bases. */
+ *cmd++ = pm4_type3_packet(PM4_SET_SHADER_BASES, 1);
+ shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */
+
+ /* write the shader partition information to a scratch register */
+ *cmd++ = pm4_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1);
+ partition1 = cmd++; /* TBD #4a: partition info (from save) */
+
+#if defined(PM4_IM_STORE)
+ /* load vertex shader instructions from the shadow. */
+ *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2);
+ *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */
+ startSizeVtx = cmd++; /* TBD #1: start/size (from save) */
+
+ /* load pixel shader instructions from the shadow. */
+ *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2);
+ *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */
+ startSizePix = cmd++; /* TBD #2: start/size (from save) */
+
+ /* load shared shader instructions from the shadow. */
+ *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2);
+ *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */
+ startSizeShared = cmd++; /* TBD #3: start/size (from save) */
+#endif
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd);
+
+ /*
+ * fixup SET_SHADER_BASES data
+ *
+ * since self-modifying PM4 code is being used here, a seperate
+ * command buffer is used for this fixup operation, to ensure the
+ * commands are not read by the PM4 engine before the data fields
+ * have been written.
+ */
+
+ fixup = cmd; /* start address */
+
+ /* write the shader partition information to a scratch register */
+ *cmd++ = pm4_type0_packet(REG_SCRATCH_REG2, 1);
+ partition2 = cmd++; /* TBD #4b: partition info (from save) */
+
+ /* mask off unused bits, then OR with shader instruction memory size */
+ *cmd++ = pm4_type3_packet(PM4_REG_RMW, 3);
+ *cmd++ = REG_SCRATCH_REG2;
+ /* AND off invalid bits. */
+ *cmd++ = 0x0FFF0FFF;
+ /* OR in instruction memory size */
+ *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29);
+
+ /* write the computed value to the SET_SHADER_BASES data field */
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_SCRATCH_REG2;
+ /* TBD #5: shader bases (to restore) */
+ *cmd++ = gpuaddr(shaderBases, &drawctxt->gpustate);
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd);
+
+ /* save shader partitioning and instructions */
+
+ save = cmd; /* start address */
+
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+
+ /* fetch the SQ_INST_STORE_MANAGMENT register value,
+ * store the value in the data fields of the SET_CONSTANT commands
+ * above.
+ */
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_SQ_INST_STORE_MANAGMENT;
+ /* TBD #4a: partition info (to restore) */
+ *cmd++ = gpuaddr(partition1, &drawctxt->gpustate);
+ *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2);
+ *cmd++ = REG_SQ_INST_STORE_MANAGMENT;
+ /* TBD #4b: partition info (to fixup) */
+ *cmd++ = gpuaddr(partition2, &drawctxt->gpustate);
+
+#if defined(PM4_IM_STORE)
+
+ /* store the vertex shader instructions */
+ *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2);
+ *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */
+ /* TBD #1: start/size (to restore) */
+ *cmd++ = gpuaddr(startSizeVtx, &drawctxt->gpustate);
+
+ /* store the pixel shader instructions */
+ *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2);
+ *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */
+ /* TBD #2: start/size (to restore) */
+ *cmd++ = gpuaddr(startSizePix, &drawctxt->gpustate);
+
+ /* store the shared shader instructions if vertex base is nonzero */
+
+ *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2);
+ *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */
+ /* TBD #3: start/size (to restore) */
+ *cmd++ = gpuaddr(startSizeShared, &drawctxt->gpustate);
+
+#endif
+
+ *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmd++ = 0;
+
+ /* create indirect buffer command for above command sequence */
+ create_ib1(drawctxt, drawctxt->shader_save, save, cmd);
+
+ ctx->cmd = cmd;
+}
+
+/* create buffers for saving/restoring registers, constants, & GMEM */
+static int
+create_gpustate_shadow(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt, struct tmp_ctx *ctx)
+{
+ uint32_t flags;
+
+ flags = (KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K);
+
+ /* allocate memory to allow HW to save sub-blocks for efficient context
+ * save/restore
+ */
+ if (kgsl_sharedmem_alloc(flags, CONTEXT_SIZE, &drawctxt->gpustate) != 0)
+ return -ENOMEM;
+ if (kgsl_mmu_map(drawctxt->pagetable, drawctxt->gpustate.physaddr,
+ drawctxt->gpustate.size,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV,
+ &drawctxt->gpustate.gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K))
+ return -EINVAL;
+
+ drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW;
+
+ /* Blank out h/w register, constant, and command buffer shadows. */
+ kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE);
+
+ /* set-up command and vertex buffer pointers */
+ ctx->cmd = ctx->start
+ = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET);
+
+ /* build indirect command buffers to save & restore regs/constants */
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+ build_regrestore_cmds(device, drawctxt, ctx);
+ build_regsave_cmds(device, drawctxt, ctx);
+
+ build_shader_save_restore_cmds(drawctxt, ctx);
+
+ return 0;
+}
+
+/* create buffers for saving/restoring registers, constants, & GMEM */
+static int
+create_gmem_shadow(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt,
+ struct tmp_ctx *ctx)
+{
+ unsigned int flags = KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K, i;
+
+ config_gmemsize(&drawctxt->context_gmem_shadow,
+ device->gmemspace.sizebytes);
+ ctx->gmem_base = device->gmemspace.gpu_base;
+
+ /* allocate memory for GMEM shadow */
+ if (kgsl_sharedmem_alloc(flags, drawctxt->context_gmem_shadow.size,
+ &drawctxt->context_gmem_shadow.gmemshadow) !=
+ 0)
+ return -ENOMEM;
+ if (kgsl_mmu_map(drawctxt->pagetable,
+ drawctxt->context_gmem_shadow.gmemshadow.physaddr,
+ drawctxt->context_gmem_shadow.gmemshadow.size,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV,
+ &drawctxt->context_gmem_shadow.gmemshadow.gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K))
+ return -EINVAL;
+
+ /* we've allocated the shadow, when swapped out, GMEM must be saved. */
+ drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE;
+
+ /* blank out gmem shadow. */
+ kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0,
+ drawctxt->context_gmem_shadow.size);
+
+ /* build quad vertex buffer */
+ build_quad_vtxbuff(drawctxt, ctx, &drawctxt->context_gmem_shadow);
+
+ /* build TP0_CHICKEN register restore command buffer */
+ ctx->cmd = build_chicken_restore_cmds(drawctxt, ctx);
+
+ /* build indirect command buffers to save & restore gmem */
+ /* Idle because we are reading PM override registers */
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+ drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd;
+ ctx->cmd =
+ build_gmem2sys_cmds(device, drawctxt, ctx,
+ &drawctxt->context_gmem_shadow);
+ drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd;
+ ctx->cmd =
+ build_sys2gmem_cmds(device, drawctxt, ctx,
+ &drawctxt->context_gmem_shadow);
+
+ for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) {
+ build_quad_vtxbuff(drawctxt, ctx,
+ &drawctxt->user_gmem_shadow[i]);
+
+ drawctxt->user_gmem_shadow[i].gmem_save_commands = ctx->cmd;
+ ctx->cmd =
+ build_gmem2sys_cmds(device, drawctxt, ctx,
+ &drawctxt->user_gmem_shadow[i]);
+
+ drawctxt->user_gmem_shadow[i].gmem_restore_commands = ctx->cmd;
+ ctx->cmd =
+ build_sys2gmem_cmds(device, drawctxt, ctx,
+ &drawctxt->user_gmem_shadow[i]);
+ }
+
+ return 0;
+}
+
+/* init draw context */
+
+int kgsl_drawctxt_init(struct kgsl_device *device)
+{
+ return 0;
+}
+
+/* close draw context */
+int kgsl_drawctxt_close(struct kgsl_device *device)
+{
+ return 0;
+}
+
+/* create a new drawing context */
+
+int
+kgsl_drawctxt_create(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable,
+ unsigned int flags, unsigned int *drawctxt_id)
+{
+ struct kgsl_drawctxt *drawctxt;
+ int index;
+ struct tmp_ctx ctx;
+
+ KGSL_CTXT_INFO("pt %p flags %08x\n", pagetable, flags);
+ if (device->drawctxt_count >= KGSL_CONTEXT_MAX)
+ return -EINVAL;
+
+ /* find a free context slot */
+ index = 0;
+ while (index < KGSL_CONTEXT_MAX) {
+ if (device->drawctxt[index].flags == CTXT_FLAGS_NOT_IN_USE)
+ break;
+
+ index++;
+ }
+
+ if (index >= KGSL_CONTEXT_MAX)
+ return -EINVAL;
+
+ drawctxt = &device->drawctxt[index];
+ drawctxt->pagetable = pagetable;
+ drawctxt->flags = CTXT_FLAGS_IN_USE;
+ drawctxt->bin_base_offset = 0;
+
+ device->drawctxt_count++;
+
+ if (create_gpustate_shadow(device, drawctxt, &ctx)
+ != 0) {
+ kgsl_drawctxt_destroy(device, index);
+ return -EINVAL;
+ }
+
+ /* Save the shader instruction memory on context switching */
+ drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE;
+
+ if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) {
+ /* create gmem shadow */
+ memset(drawctxt->user_gmem_shadow, 0,
+ sizeof(struct gmem_shadow_t) *
+ KGSL_MAX_GMEM_SHADOW_BUFFERS);
+
+ if (create_gmem_shadow(device, drawctxt, &ctx)
+ != 0) {
+ kgsl_drawctxt_destroy(device, index);
+ return -EINVAL;
+ }
+ }
+
+ BUG_ON(ctx.cmd - ctx.start > CMD_BUFFER_LEN);
+
+ *drawctxt_id = index;
+
+ KGSL_CTXT_INFO("return drawctxt_id %d\n", *drawctxt_id);
+ return 0;
+}
+
+/* destroy a drawing context */
+
+int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id)
+{
+ struct kgsl_drawctxt *drawctxt;
+
+ drawctxt = &device->drawctxt[drawctxt_id];
+
+ KGSL_CTXT_INFO("drawctxt_id %d ptr %p\n", drawctxt_id, drawctxt);
+ if (drawctxt->flags != CTXT_FLAGS_NOT_IN_USE) {
+ /* deactivate context */
+ if (device->drawctxt_active == drawctxt) {
+ /* no need to save GMEM or shader, the context is
+ * being destroyed.
+ */
+ drawctxt->flags &= ~(CTXT_FLAGS_GMEM_SAVE |
+ CTXT_FLAGS_SHADER_SAVE |
+ CTXT_FLAGS_GMEM_SHADOW |
+ CTXT_FLAGS_STATE_SHADOW);
+
+ kgsl_drawctxt_switch(device, NULL, 0);
+ }
+
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+ /* destroy state shadow, if allocated */
+ if (drawctxt->gpustate.gpuaddr != 0) {
+ kgsl_mmu_unmap(drawctxt->pagetable,
+ drawctxt->gpustate.gpuaddr,
+ drawctxt->gpustate.size);
+ drawctxt->gpustate.gpuaddr = 0;
+ }
+ if (drawctxt->gpustate.physaddr != 0)
+ kgsl_sharedmem_free(&drawctxt->gpustate);
+
+ /* destroy gmem shadow, if allocated */
+ if (drawctxt->context_gmem_shadow.gmemshadow.gpuaddr != 0) {
+ kgsl_mmu_unmap(drawctxt->pagetable,
+ drawctxt->context_gmem_shadow.gmemshadow.gpuaddr,
+ drawctxt->context_gmem_shadow.gmemshadow.size);
+ drawctxt->context_gmem_shadow.gmemshadow.gpuaddr = 0;
+ }
+
+ if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0)
+ kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.
+ gmemshadow);
+
+ drawctxt->flags = CTXT_FLAGS_NOT_IN_USE;
+
+ BUG_ON(device->drawctxt_count == 0);
+ device->drawctxt_count--;
+ }
+ KGSL_CTXT_INFO("return\n");
+ return 0;
+}
+
+/* Binds a user specified buffer as GMEM shadow area */
+int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device,
+ unsigned int drawctxt_id,
+ const struct kgsl_gmem_desc *gmem_desc,
+ unsigned int shadow_x,
+ unsigned int shadow_y,
+ const struct kgsl_buffer_desc
+ *shadow_buffer, unsigned int buffer_id)
+{
+ struct kgsl_drawctxt *drawctxt;
+
+ /* Shadow struct being modified */
+ struct gmem_shadow_t *shadow;
+ unsigned int i;
+
+ if (device->flags & KGSL_FLAGS_SAFEMODE)
+ /* No need to bind any buffers since safe mode
+ * skips context switch */
+ return 0;
+
+ drawctxt = &device->drawctxt[drawctxt_id];
+
+ shadow = &drawctxt->user_gmem_shadow[buffer_id];
+
+ if (!shadow_buffer->enabled) {
+ /* Disable shadow */
+ KGSL_MEM_ERR("shadow is disabled in bind_gmem\n");
+ shadow->gmemshadow.size = 0;
+ } else {
+ /* Binding to a buffer */
+ unsigned int width, height;
+
+ BUG_ON(gmem_desc->x % 2); /* Needs to be a multiple of 2 */
+ BUG_ON(gmem_desc->y % 2); /* Needs to be a multiple of 2 */
+ BUG_ON(gmem_desc->width % 2); /* Needs to be a multiple of 2 */
+ /* Needs to be a multiple of 2 */
+ BUG_ON(gmem_desc->height % 2);
+ /* Needs to be a multiple of 32 */
+ BUG_ON(gmem_desc->pitch % 32);
+
+ BUG_ON(shadow_x % 2); /* Needs to be a multiple of 2 */
+ BUG_ON(shadow_y % 2); /* Needs to be a multiple of 2 */
+
+ BUG_ON(shadow_buffer->format < COLORX_4_4_4_4);
+ BUG_ON(shadow_buffer->format > COLORX_32_32_32_32_FLOAT);
+ /* Needs to be a multiple of 32 */
+ BUG_ON(shadow_buffer->pitch % 32);
+
+ BUG_ON(buffer_id < 0);
+ BUG_ON(buffer_id > KGSL_MAX_GMEM_SHADOW_BUFFERS);
+
+ width = gmem_desc->width;
+ height = gmem_desc->height;
+
+ shadow->width = width;
+ shadow->format = shadow_buffer->format;
+
+ shadow->height = height;
+ shadow->pitch = shadow_buffer->pitch;
+
+ memset(&shadow->gmemshadow, 0, sizeof(struct kgsl_memdesc));
+ shadow->gmemshadow.hostptr = shadow_buffer->hostptr;
+ shadow->gmemshadow.gpuaddr = shadow_buffer->gpuaddr;
+ shadow->gmemshadow.physaddr = shadow->gmemshadow.gpuaddr;
+ shadow->gmemshadow.size = shadow_buffer->size;
+
+ /* Calculate offset */
+ shadow->offset =
+ (int)(shadow_buffer->pitch) * ((int)shadow_y -
+ (int)gmem_desc->y) +
+ (int)shadow_x - (int)gmem_desc->x;
+
+ shadow->offset_x = shadow_x;
+ shadow->offset_y = shadow_y;
+ shadow->gmem_offset_x = gmem_desc->x;
+ shadow->gmem_offset_y = gmem_desc->y;
+
+ shadow->size = shadow->gmemshadow.size;
+
+ shadow->gmem_pitch = gmem_desc->pitch;
+
+ /* Modify quad vertices */
+ set_gmem_copy_quad(shadow);
+
+ /* Idle because we are reading PM override registers */
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+ /* Modify commands */
+ build_gmem2sys_cmds(device, drawctxt, NULL, shadow);
+ build_sys2gmem_cmds(device, drawctxt, NULL, shadow);
+
+ /* Release context GMEM shadow if found */
+ if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) {
+ kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.
+ gmemshadow);
+ drawctxt->context_gmem_shadow.gmemshadow.physaddr = 0;
+ }
+ }
+
+ /* Enable GMEM shadowing if we have any of the user buffers enabled */
+ drawctxt->flags &= ~CTXT_FLAGS_GMEM_SHADOW;
+ for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) {
+ if (drawctxt->user_gmem_shadow[i].gmemshadow.size > 0)
+ drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW;
+ }
+
+ return 0;
+}
+
+/* set bin base offset */
+int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device,
+ unsigned int drawctxt_id,
+ unsigned int offset)
+{
+ struct kgsl_drawctxt *drawctxt;
+
+ drawctxt = &device->drawctxt[drawctxt_id];
+
+ drawctxt->bin_base_offset = offset;
+
+ return 0;
+}
+
+/* switch drawing contexts */
+void
+kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt,
+ unsigned int flags)
+{
+ struct kgsl_drawctxt *active_ctxt = device->drawctxt_active;
+ unsigned int cmds[2];
+
+ if (drawctxt) {
+ /* Set the flag in context so that the save is done
+ * when this context is switched out. */
+ if (flags & KGSL_CONTEXT_SAVE_GMEM)
+ drawctxt->flags |= CTXT_FLAGS_GMEM_SAVE;
+ else
+ drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE;
+ }
+ /* already current? */
+ if (active_ctxt == drawctxt)
+ return;
+
+ KGSL_CTXT_INFO("from %p to %p flags %d\n",
+ device->drawctxt_active, drawctxt, flags);
+ /* save old context*/
+ if (active_ctxt != NULL) {
+ KGSL_CTXT_INFO("active_ctxt flags %08x\n", active_ctxt->flags);
+ /* save registers and constants. */
+ KGSL_CTXT_DBG("save regs");
+ kgsl_ringbuffer_issuecmds(device, 0, active_ctxt->reg_save, 3);
+
+ if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) {
+ /* save shader partitioning and instructions. */
+ KGSL_CTXT_DBG("save shader");
+ kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+ active_ctxt->shader_save, 3);
+
+ /* fixup shader partitioning parameter for
+ * SET_SHADER_BASES.
+ */
+ KGSL_CTXT_DBG("save shader fixup");
+ kgsl_ringbuffer_issuecmds(device, 0,
+ active_ctxt->shader_fixup, 3);
+
+ active_ctxt->flags |= CTXT_FLAGS_SHADER_RESTORE;
+ }
+
+ if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE
+ && active_ctxt->flags & CTXT_FLAGS_GMEM_SHADOW) {
+ /* save gmem.
+ * (note: changes shader. shader must already be saved.)
+ */
+ unsigned int i, numbuffers = 0;
+ KGSL_CTXT_DBG("save gmem");
+ for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) {
+ if (active_ctxt->user_gmem_shadow[i].gmemshadow.
+ size > 0) {
+ kgsl_ringbuffer_issuecmds(device,
+ KGSL_CMD_FLAGS_PMODE,
+ active_ctxt->user_gmem_shadow[i].
+ gmem_save, 3);
+
+ /* Restore TP0_CHICKEN */
+ kgsl_ringbuffer_issuecmds(device, 0,
+ active_ctxt->chicken_restore, 3);
+
+ numbuffers++;
+ }
+ }
+ if (numbuffers == 0) {
+ kgsl_ringbuffer_issuecmds(device,
+ KGSL_CMD_FLAGS_PMODE,
+ active_ctxt->context_gmem_shadow.gmem_save,
+ 3);
+
+ /* Restore TP0_CHICKEN */
+ kgsl_ringbuffer_issuecmds(device, 0,
+ active_ctxt->chicken_restore, 3);
+ }
+
+ active_ctxt->flags |= CTXT_FLAGS_GMEM_RESTORE;
+ }
+ }
+
+ device->drawctxt_active = drawctxt;
+
+ /* restore new context */
+ if (drawctxt != NULL) {
+
+ KGSL_CTXT_INFO("drawctxt flags %08x\n", drawctxt->flags);
+ KGSL_CTXT_DBG("restore pagetable");
+ kgsl_mmu_setstate(device, drawctxt->pagetable);
+
+ /* restore gmem.
+ * (note: changes shader. shader must not already be restored.)
+ */
+ if (drawctxt->flags & CTXT_FLAGS_GMEM_RESTORE) {
+ unsigned int i, numbuffers = 0;
+ KGSL_CTXT_DBG("restore gmem");
+
+ for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) {
+ if (drawctxt->user_gmem_shadow[i].gmemshadow.
+ size > 0) {
+ kgsl_ringbuffer_issuecmds(device,
+ KGSL_CMD_FLAGS_PMODE,
+ drawctxt->user_gmem_shadow[i].
+ gmem_restore, 3);
+
+ /* Restore TP0_CHICKEN */
+ kgsl_ringbuffer_issuecmds(device, 0,
+ drawctxt->chicken_restore, 3);
+ numbuffers++;
+ }
+ }
+ if (numbuffers == 0) {
+ kgsl_ringbuffer_issuecmds(device,
+ KGSL_CMD_FLAGS_PMODE,
+ drawctxt->context_gmem_shadow.gmem_restore,
+ 3);
+
+ /* Restore TP0_CHICKEN */
+ kgsl_ringbuffer_issuecmds(device, 0,
+ drawctxt->chicken_restore, 3);
+ }
+ drawctxt->flags &= ~CTXT_FLAGS_GMEM_RESTORE;
+ }
+
+ /* restore registers and constants. */
+ KGSL_CTXT_DBG("restore regs");
+ kgsl_ringbuffer_issuecmds(device, 0,
+ drawctxt->reg_restore, 3);
+
+ /* restore shader instructions & partitioning. */
+ if (drawctxt->flags & CTXT_FLAGS_SHADER_RESTORE)
+ KGSL_CTXT_DBG("restore shader");
+ kgsl_ringbuffer_issuecmds(device, 0,
+ drawctxt->shader_restore, 3);
+
+ cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1);
+ cmds[1] = drawctxt->bin_base_offset;
+ kgsl_ringbuffer_issuecmds(device, 0, cmds, 2);
+
+ } else
+ kgsl_mmu_setstate(device, device->mmu.defaultpagetable);
+ KGSL_CTXT_INFO("return\n");
+}
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h
new file mode 100644
index 0000000..3090373
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h
@@ -0,0 +1,120 @@
+/*
+* (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+* Copyright (c) 2008-2009 QUALCOMM USA, INC.
+*
+* All source code in this file is licensed under the following license
+*
+* 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, you can find it at http://www.fsf.org
+*/
+#ifndef __GSL_DRAWCTXT_H
+#define __GSL_DRAWCTXT_H
+
+/* Flags */
+
+#define CTXT_FLAGS_NOT_IN_USE 0x00000000
+#define CTXT_FLAGS_IN_USE 0x00000001
+
+/* state shadow memory allocated */
+#define CTXT_FLAGS_STATE_SHADOW 0x00000010
+
+/* gmem shadow memory allocated */
+#define CTXT_FLAGS_GMEM_SHADOW 0x00000100
+/* gmem must be copied to shadow */
+#define CTXT_FLAGS_GMEM_SAVE 0x00000200
+/* gmem can be restored from shadow */
+#define CTXT_FLAGS_GMEM_RESTORE 0x00000400
+/* shader must be copied to shadow */
+#define CTXT_FLAGS_SHADER_SAVE 0x00002000
+/* shader can be restored from shadow */
+#define CTXT_FLAGS_SHADER_RESTORE 0x00004000
+
+#include "kgsl_sharedmem.h"
+#include "yamato_reg.h"
+
+#define KGSL_MAX_GMEM_SHADOW_BUFFERS 2
+
+struct kgsl_device;
+
+/* types */
+
+/* draw context */
+struct gmem_shadow_t {
+ struct kgsl_memdesc gmemshadow; /* Shadow buffer address */
+
+ /* 256 KB GMEM surface = 4 bytes-per-pixel x 256 pixels/row x
+ * 256 rows. */
+ /* width & height must be a multiples of 32, in case tiled textures
+ * are used. */
+ enum COLORFORMATX format;
+ unsigned int size; /* Size of surface used to store GMEM */
+ unsigned int width; /* Width of surface used to store GMEM */
+ unsigned int height; /* Height of surface used to store GMEM */
+ unsigned int pitch; /* Pitch of surface used to store GMEM */
+ int offset;
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int gmem_offset_x;
+ unsigned int gmem_offset_y;
+ unsigned int gmem_pitch; /* Pitch value used for GMEM */
+ unsigned int *gmem_save_commands;
+ unsigned int *gmem_restore_commands;
+ unsigned int gmem_save[3];
+ unsigned int gmem_restore[3];
+ struct kgsl_memdesc quad_vertices;
+ struct kgsl_memdesc quad_texcoords;
+};
+
+struct kgsl_drawctxt {
+ uint32_t flags;
+ struct kgsl_pagetable *pagetable;
+ struct kgsl_memdesc gpustate;
+ unsigned int reg_save[3];
+ unsigned int reg_restore[3];
+ unsigned int shader_save[3];
+ unsigned int shader_fixup[3];
+ unsigned int shader_restore[3];
+ unsigned int chicken_restore[3];
+ unsigned int bin_base_offset;
+ /* Information of the GMEM shadow that is created in context create */
+ struct gmem_shadow_t context_gmem_shadow;
+ /* User defined GMEM shadow buffers */
+ struct gmem_shadow_t user_gmem_shadow[KGSL_MAX_GMEM_SHADOW_BUFFERS];
+};
+
+
+int kgsl_drawctxt_create(struct kgsl_device *, struct kgsl_pagetable *,
+ unsigned int flags,
+ unsigned int *drawctxt_id);
+
+int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id);
+
+int kgsl_drawctxt_init(struct kgsl_device *device);
+
+int kgsl_drawctxt_close(struct kgsl_device *device);
+
+void kgsl_drawctxt_switch(struct kgsl_device *device,
+ struct kgsl_drawctxt *drawctxt,
+ unsigned int flags);
+int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device,
+ unsigned int drawctxt_id,
+ const struct kgsl_gmem_desc *gmem_desc,
+ unsigned int shadow_x,
+ unsigned int shadow_y,
+ const struct kgsl_buffer_desc
+ *shadow_buffer, unsigned int buffer_id);
+
+int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device,
+ unsigned int drawctxt_id,
+ unsigned int offset);
+
+#endif /* __GSL_DRAWCTXT_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.c b/drivers/video/msm/gpu/kgsl/kgsl_log.c
new file mode 100644
index 0000000..6308087
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_log.c
@@ -0,0 +1,292 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/debugfs.h>
+#include "kgsl_log.h"
+#include "kgsl_ringbuffer.h"
+#include "kgsl_device.h"
+#include "kgsl.h"
+
+/*default log levels is error for everything*/
+#define KGSL_LOG_LEVEL_DEFAULT 3
+#define KGSL_LOG_LEVEL_MAX 7
+unsigned int kgsl_drv_log = KGSL_LOG_LEVEL_DEFAULT;
+unsigned int kgsl_cmd_log = KGSL_LOG_LEVEL_DEFAULT;
+unsigned int kgsl_ctxt_log = KGSL_LOG_LEVEL_DEFAULT;
+unsigned int kgsl_mem_log = KGSL_LOG_LEVEL_DEFAULT;
+
+unsigned int kgsl_cache_enable;
+
+#ifdef CONFIG_DEBUG_FS
+static int kgsl_log_set(unsigned int *log_val, void *data, u64 val)
+{
+ *log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX);
+ return 0;
+}
+
+static int kgsl_drv_log_set(void *data, u64 val)
+{
+ return kgsl_log_set(&kgsl_drv_log, data, val);
+}
+
+static int kgsl_drv_log_get(void *data, u64 *val)
+{
+ *val = kgsl_drv_log;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_drv_log_fops, kgsl_drv_log_get,
+ kgsl_drv_log_set, "%llu\n");
+
+static int kgsl_cmd_log_set(void *data, u64 val)
+{
+ return kgsl_log_set(&kgsl_cmd_log, data, val);
+}
+
+static int kgsl_cmd_log_get(void *data, u64 *val)
+{
+ *val = kgsl_cmd_log;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_cmd_log_fops, kgsl_cmd_log_get,
+ kgsl_cmd_log_set, "%llu\n");
+
+static int kgsl_ctxt_log_set(void *data, u64 val)
+{
+ return kgsl_log_set(&kgsl_ctxt_log, data, val);
+}
+
+static int kgsl_ctxt_log_get(void *data, u64 *val)
+{
+ *val = kgsl_ctxt_log;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_ctxt_log_fops, kgsl_ctxt_log_get,
+ kgsl_ctxt_log_set, "%llu\n");
+
+static int kgsl_mem_log_set(void *data, u64 val)
+{
+ return kgsl_log_set(&kgsl_mem_log, data, val);
+}
+
+static int kgsl_mem_log_get(void *data, u64 *val)
+{
+ *val = kgsl_mem_log;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_mem_log_fops, kgsl_mem_log_get,
+ kgsl_mem_log_set, "%llu\n");
+
+#ifdef DEBUG
+static ssize_t rb_regs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t rb_regs_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct kgsl_device *device = NULL;
+ struct kgsl_ringbuffer *rb = NULL;
+ struct kgsl_rb_debug rb_debug;
+
+ device = &kgsl_driver.yamato_device;
+
+ rb = &device->ringbuffer;
+
+ kgsl_ringbuffer_debug(rb, &rb_debug);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rbbm_status %08x mem_rptr %08x mem_wptr_poll %08x\n",
+ rb_debug.rbbm_status,
+ rb_debug.mem_rptr,
+ rb_debug.mem_wptr_poll);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rb_base %08x rb_cntl %08x rb_rptr_addr %08x"
+ " rb_rptr %08x rb_rptr_wr %08x\n",
+ rb_debug.cp_rb_base,
+ rb_debug.cp_rb_cntl,
+ rb_debug.cp_rb_rptr_addr,
+ rb_debug.cp_rb_rptr,
+ rb_debug.cp_rb_rptr_wr);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x"
+ " ib1_base %08x ib1_bufsz %08x\n",
+ rb_debug.cp_rb_wptr,
+ rb_debug.cp_rb_wptr_delay,
+ rb_debug.cp_rb_wptr_base,
+ rb_debug.cp_ib1_base,
+ rb_debug.cp_ib1_bufsz);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "ib2_base %08x ib2_bufsz %08x st_base %08x"
+ " st_bufsz %08x cp_me_cntl %08x cp_me_status %08x\n",
+ rb_debug.cp_ib2_base,
+ rb_debug.cp_ib2_bufsz,
+ rb_debug.cp_st_base,
+ rb_debug.cp_st_bufsz,
+ rb_debug.cp_me_cntl,
+ rb_debug.cp_me_status);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "csq_cp_rb %08x csq_cp_ib1 %08x csq_cp_ib2 %08x\n",
+ rb_debug.cp_csq_rb_stat,
+ rb_debug.cp_csq_ib1_stat,
+ rb_debug.cp_csq_ib2_stat);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "cp_debug %08x cp_stat %08x cp_int_status %08x"
+ " cp_int_cntl %08x\n",
+ rb_debug.cp_debug,
+ rb_debug.cp_stat,
+ rb_debug.cp_int_status,
+ rb_debug.cp_int_cntl);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "sop_timestamp: %0d eop_timestamp: %d\n",
+ rb_debug.sop_timestamp,
+ rb_debug.eop_timestamp);
+ n++;
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static struct file_operations kgsl_rb_regs_fops = {
+ .read = rb_regs_read,
+ .open = rb_regs_open,
+};
+#endif /*DEBUG*/
+
+#ifdef DEBUG
+static ssize_t mmu_regs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t mmu_regs_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct kgsl_device *device = NULL;
+ struct kgsl_mmu *mmu = NULL;
+ struct kgsl_mmu_debug mmu_debug;
+
+ device = &kgsl_driver.yamato_device;
+
+ mmu = &device->mmu;
+
+ kgsl_mmu_debug(mmu, &mmu_debug);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "config %08x mpu_base %08x mpu_end %08x\n",
+ mmu_debug.config,
+ mmu_debug.mpu_base,
+ mmu_debug.mpu_end);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "va_range %08x pt_base %08x\n",
+ mmu_debug.va_range,
+ mmu_debug.pt_base);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "page_fault %08x trans_error %08x axi_error %08x\n",
+ mmu_debug.page_fault,
+ mmu_debug.trans_error,
+ mmu_debug.axi_error);
+
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "interrupt_mask %08x interrupt_status %08x\n",
+ mmu_debug.interrupt_mask,
+ mmu_debug.interrupt_status);
+
+ n++;
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static struct file_operations kgsl_mmu_regs_fops = {
+ .read = mmu_regs_read,
+ .open = mmu_regs_open,
+};
+#endif /*DEBUG*/
+
+#ifdef CONFIG_MSM_KGSL_MMU
+static int kgsl_cache_enable_set(void *data, u64 val)
+{
+ kgsl_cache_enable = (val != 0);
+ return 0;
+}
+
+static int kgsl_cache_enable_get(void *data, u64 *val)
+{
+ *val = kgsl_cache_enable;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_cache_enable_fops, kgsl_cache_enable_get,
+ kgsl_cache_enable_set, "%llu\n");
+#endif
+
+#endif /* CONFIG_DEBUG_FS */
+
+int kgsl_debug_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent;
+ dent = debugfs_create_dir("kgsl", 0);
+ if (IS_ERR(dent))
+ return 0;
+
+ debugfs_create_file("log_level_cmd", 0644, dent, 0,
+ &kgsl_cmd_log_fops);
+ debugfs_create_file("log_level_ctxt", 0644, dent, 0,
+ &kgsl_ctxt_log_fops);
+ debugfs_create_file("log_level_drv", 0644, dent, 0,
+ &kgsl_drv_log_fops);
+ debugfs_create_file("log_level_mem", 0644, dent, 0,
+ &kgsl_mem_log_fops);
+#ifdef DEBUG
+ debugfs_create_file("rb_regs", 0444, dent, 0,
+ &kgsl_rb_regs_fops);
+#endif
+
+#ifdef DEBUG
+ debugfs_create_file("mmu_regs", 0444, dent, 0,
+ &kgsl_mmu_regs_fops);
+#endif
+
+#ifdef CONFIG_MSM_KGSL_MMU
+ debugfs_create_file("cache_enable", 0644, dent, 0,
+ &kgsl_cache_enable_fops);
+#endif
+
+#endif /* CONFIG_DEBUG_FS */
+ return 0;
+}
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.h b/drivers/video/msm/gpu/kgsl/kgsl_log.h
new file mode 100644
index 0000000..3869ff7
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_log.h
@@ -0,0 +1,105 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef _GSL_LOG_H
+#define _GSL_LOG_H
+
+#include <linux/bug.h>
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+#include <linux/device.h>
+
+extern unsigned int kgsl_drv_log;
+extern unsigned int kgsl_cmd_log;
+extern unsigned int kgsl_ctxt_log;
+extern unsigned int kgsl_mem_log;
+
+struct device *kgsl_driver_getdevnode(void);
+int kgsl_debug_init(void);
+
+#define KGSL_LOG_VDBG(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 7) \
+ dev_vdbg(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_LOG_DBG(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 7) \
+ dev_dbg(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_LOG_INFO(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 6) \
+ dev_info(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_LOG_WARN(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 4) \
+ dev_warn(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_LOG_ERR(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 3) \
+ dev_err(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_LOG_FATAL(lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 2) \
+ dev_crit(kgsl_driver_getdevnode(), "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
+#define KGSL_DRV_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_drv_log, fmt, ##args)
+#define KGSL_DRV_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_drv_log, fmt, ##args)
+#define KGSL_DRV_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_drv_log, fmt, ##args)
+#define KGSL_DRV_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_drv_log, fmt, ##args)
+#define KGSL_DRV_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_drv_log, fmt, ##args)
+#define KGSL_DRV_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_drv_log, fmt, ##args)
+
+#define KGSL_CMD_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_cmd_log, fmt, ##args)
+#define KGSL_CMD_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_cmd_log, fmt, ##args)
+#define KGSL_CMD_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_cmd_log, fmt, ##args)
+#define KGSL_CMD_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_cmd_log, fmt, ##args)
+#define KGSL_CMD_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_cmd_log, fmt, ##args)
+#define KGSL_CMD_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_cmd_log, fmt, ##args)
+
+#define KGSL_CTXT_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_ctxt_log, fmt, ##args)
+#define KGSL_CTXT_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_ctxt_log, fmt, ##args)
+#define KGSL_CTXT_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_ctxt_log, fmt, ##args)
+#define KGSL_CTXT_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_ctxt_log, fmt, ##args)
+#define KGSL_CTXT_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_ctxt_log, fmt, ##args)
+#define KGSL_CTXT_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_ctxt_log, fmt, ##args)
+
+#define KGSL_MEM_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_mem_log, fmt, ##args)
+#define KGSL_MEM_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_mem_log, fmt, ##args)
+#define KGSL_MEM_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_mem_log, fmt, ##args)
+#define KGSL_MEM_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_mem_log, fmt, ##args)
+#define KGSL_MEM_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_mem_log, fmt, ##args)
+#define KGSL_MEM_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_mem_log, fmt, ##args)
+
+#endif /* _GSL_LOG_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c
new file mode 100644
index 0000000..c32558f
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c
@@ -0,0 +1,664 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+#include "kgsl_mmu.h"
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "yamato_reg.h"
+
+struct kgsl_pte_debug {
+ unsigned int read:1;
+ unsigned int write:1;
+ unsigned int dirty:1;
+ unsigned int reserved:9;
+ unsigned int phyaddr:20;
+};
+
+#define GSL_PTE_SIZE 4
+#define GSL_PT_EXTRA_ENTRIES 16
+
+
+#define GSL_PT_PAGE_BITS_MASK 0x00000007
+#define GSL_PT_PAGE_ADDR_MASK (~(KGSL_PAGESIZE - 1))
+
+#define GSL_MMU_INT_MASK \
+ (MH_INTERRUPT_MASK__AXI_READ_ERROR | \
+ MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
+
+uint32_t kgsl_pt_entry_get(struct kgsl_pagetable *pt, uint32_t va)
+{
+ return (va - pt->va_base) >> KGSL_PAGESIZE_SHIFT;
+}
+
+uint32_t kgsl_pt_map_get(struct kgsl_pagetable *pt, uint32_t pte)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ return baseptr[pte];
+}
+
+void kgsl_pt_map_set(struct kgsl_pagetable *pt, uint32_t pte, uint32_t val)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ baseptr[pte] = val;
+}
+#define GSL_PT_MAP_DEBUG(pte) ((struct kgsl_pte_debug *) \
+ &gsl_pt_map_get(pagetable, pte))
+
+void kgsl_pt_map_setbits(struct kgsl_pagetable *pt, uint32_t pte, uint32_t bits)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ baseptr[pte] |= bits;
+}
+
+void kgsl_pt_map_setaddr(struct kgsl_pagetable *pt, uint32_t pte,
+ uint32_t pageaddr)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ uint32_t val = baseptr[pte];
+ val &= ~GSL_PT_PAGE_ADDR_MASK;
+ val |= (pageaddr & GSL_PT_PAGE_ADDR_MASK);
+ baseptr[pte] = val;
+}
+
+void kgsl_pt_map_resetall(struct kgsl_pagetable *pt, uint32_t pte)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ baseptr[pte] &= GSL_PT_PAGE_DIRTY;
+}
+
+void kgsl_pt_map_resetbits(struct kgsl_pagetable *pt, uint32_t pte,
+ uint32_t bits)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ baseptr[pte] &= ~(bits & GSL_PT_PAGE_BITS_MASK);
+}
+
+int kgsl_pt_map_isdirty(struct kgsl_pagetable *pt, uint32_t pte)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ return baseptr[pte] & GSL_PT_PAGE_DIRTY;
+}
+
+uint32_t kgsl_pt_map_getaddr(struct kgsl_pagetable *pt, uint32_t pte)
+{
+ uint32_t *baseptr = (uint32_t *)pt->base.hostptr;
+ return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK;
+}
+
+void kgsl_mh_intrcallback(struct kgsl_device *device)
+{
+ unsigned int status = 0;
+ unsigned int reg;
+ unsigned int axi_error;
+ struct kgsl_mmu_debug dbg;
+
+ KGSL_MEM_VDBG("enter (device=%p)\n", device);
+
+ kgsl_yamato_regread(device, REG_MH_INTERRUPT_STATUS, &status);
+
+ if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) {
+ kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error);
+ KGSL_MEM_FATAL("axi read error interrupt (%08x)\n", axi_error);
+ kgsl_mmu_debug(&device->mmu, &dbg);
+ } else if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) {
+ kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error);
+ KGSL_MEM_FATAL("axi write error interrupt (%08x)\n", axi_error);
+ kgsl_mmu_debug(&device->mmu, &dbg);
+ } else if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) {
+ kgsl_yamato_regread(device, REG_MH_MMU_PAGE_FAULT, ®);
+ KGSL_MEM_FATAL("mmu page fault interrupt: %08x\n", reg);
+ kgsl_mmu_debug(&device->mmu, &dbg);
+ } else {
+ KGSL_MEM_DBG("bad bits in REG_MH_INTERRUPT_STATUS %08x\n",
+ status);
+ }
+
+ kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_CLEAR, status);
+
+ /*TODO: figure out how to handle errror interupts.
+ * specifically, page faults should probably nuke the client that
+ * caused them, but we don't have enough info to figure that out yet.
+ */
+
+ KGSL_MEM_VDBG("return\n");
+}
+
+#ifdef DEBUG
+void kgsl_mmu_debug(struct kgsl_mmu *mmu, struct kgsl_mmu_debug *regs)
+{
+ memset(regs, 0, sizeof(struct kgsl_mmu_debug));
+
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_CONFIG,
+ ®s->config);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_BASE,
+ ®s->mpu_base);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_END,
+ ®s->mpu_end);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_VA_RANGE,
+ ®s->va_range);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_PT_BASE,
+ ®s->pt_base);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_PAGE_FAULT,
+ ®s->page_fault);
+ kgsl_yamato_regread(mmu->device, REG_MH_MMU_TRAN_ERROR,
+ ®s->trans_error);
+ kgsl_yamato_regread(mmu->device, REG_MH_AXI_ERROR,
+ ®s->axi_error);
+ kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_MASK,
+ ®s->interrupt_mask);
+ kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_STATUS,
+ ®s->interrupt_status);
+
+ KGSL_MEM_DBG("mmu config %08x mpu_base %08x mpu_end %08x\n",
+ regs->config, regs->mpu_base, regs->mpu_end);
+ KGSL_MEM_DBG("mmu va_range %08x pt_base %08x \n",
+ regs->va_range, regs->pt_base);
+ KGSL_MEM_DBG("mmu page_fault %08x tran_err %08x\n",
+ regs->page_fault, regs->trans_error);
+ KGSL_MEM_DBG("mmu int mask %08x int status %08x\n",
+ regs->interrupt_mask, regs->interrupt_status);
+}
+#endif
+
+struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu)
+{
+ int status = 0;
+ struct kgsl_pagetable *pagetable = NULL;
+ uint32_t flags;
+
+ KGSL_MEM_VDBG("enter (mmu=%p)\n", mmu);
+
+ pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL);
+ if (pagetable == NULL) {
+ KGSL_MEM_ERR("Unable to allocate pagetable object.\n");
+ return NULL;
+ }
+
+ pagetable->mmu = mmu;
+ pagetable->va_base = mmu->va_base;
+ pagetable->va_range = mmu->va_range;
+ pagetable->last_superpte = 0;
+ pagetable->max_entries = (mmu->va_range >> KGSL_PAGESIZE_SHIFT)
+ + GSL_PT_EXTRA_ENTRIES;
+
+ pagetable->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1);
+ if (pagetable->pool == NULL) {
+ KGSL_MEM_ERR("Unable to allocate virtualaddr pool.\n");
+ goto err_gen_pool_create;
+ }
+
+ if (gen_pool_add(pagetable->pool, pagetable->va_base,
+ pagetable->va_range, -1)) {
+ KGSL_MEM_ERR("gen_pool_create failed for pagetable %p\n",
+ pagetable);
+ goto err_gen_pool_add;
+ }
+
+ /* allocate page table memory */
+ flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS
+ | KGSL_MEMFLAGS_STRICTREQUEST);
+ status = kgsl_sharedmem_alloc(flags,
+ pagetable->max_entries * GSL_PTE_SIZE,
+ &pagetable->base);
+
+ if (status) {
+ KGSL_MEM_ERR("cannot alloc page tables\n");
+ goto err_kgsl_sharedmem_alloc;
+ }
+
+ /* reset page table entries
+ * -- all pte's are marked as not dirty initially
+ */
+ kgsl_sharedmem_set(&pagetable->base, 0, 0, pagetable->base.size);
+ pagetable->base.gpuaddr = pagetable->base.physaddr;
+
+ KGSL_MEM_VDBG("return %p\n", pagetable);
+
+ return pagetable;
+
+err_kgsl_sharedmem_alloc:
+err_gen_pool_add:
+ gen_pool_destroy(pagetable->pool);
+err_gen_pool_create:
+ kfree(pagetable);
+ return NULL;
+}
+
+int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable)
+{
+ KGSL_MEM_VDBG("enter (pagetable=%p)\n", pagetable);
+
+ if (pagetable) {
+ if (pagetable->base.gpuaddr)
+ kgsl_sharedmem_free(&pagetable->base);
+
+ if (pagetable->pool) {
+ gen_pool_destroy(pagetable->pool);
+ pagetable->pool = NULL;
+ }
+
+ kfree(pagetable);
+
+ }
+ KGSL_MEM_VDBG("return 0x%08x\n", 0);
+
+ return 0;
+}
+
+int kgsl_mmu_setstate(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable)
+{
+ int status = 0;
+ struct kgsl_mmu *mmu = &device->mmu;
+
+ KGSL_MEM_VDBG("enter (device=%p, pagetable=%p)\n", device, pagetable);
+
+ if (mmu->flags & KGSL_FLAGS_STARTED) {
+ /* page table not current, then setup mmu to use new
+ * specified page table
+ */
+ KGSL_MEM_INFO("from %p to %p\n", mmu->hwpagetable, pagetable);
+ if (mmu->hwpagetable != pagetable) {
+ mmu->hwpagetable = pagetable;
+
+ /* call device specific set page table */
+ status = kgsl_yamato_setstate(mmu->device,
+ KGSL_MMUFLAGS_TLBFLUSH |
+ KGSL_MMUFLAGS_PTUPDATE);
+ }
+ }
+
+ KGSL_MEM_VDBG("return %d\n", status);
+
+ return status;
+}
+
+int kgsl_mmu_init(struct kgsl_device *device)
+{
+ /*
+ * intialize device mmu
+ *
+ * call this with the global lock held
+ */
+ int status;
+ uint32_t flags;
+ struct kgsl_mmu *mmu = &device->mmu;
+#ifdef _DEBUG
+ struct kgsl_mmu_debug regs;
+#endif /* _DEBUG */
+
+ KGSL_MEM_VDBG("enter (device=%p)\n", device);
+
+ if (mmu->flags & KGSL_FLAGS_INITIALIZED0) {
+ KGSL_MEM_INFO("MMU already initialized.\n");
+ return 0;
+ }
+
+ mmu->device = device;
+
+#ifndef CONFIG_MSM_KGSL_MMU
+ mmu->config = 0x00000000;
+#endif
+
+ /* setup MMU and sub-client behavior */
+ kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, mmu->config);
+
+ /* enable axi interrupts */
+ KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n",
+ GSL_MMU_INT_MASK);
+ kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, GSL_MMU_INT_MASK);
+
+ mmu->flags |= KGSL_FLAGS_INITIALIZED0;
+
+ /* MMU not enabled */
+ if ((mmu->config & 0x1) == 0) {
+ KGSL_MEM_VDBG("return %d\n", 0);
+ return 0;
+ }
+
+ /* idle device */
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+ /* make sure aligned to pagesize */
+ BUG_ON(mmu->mpu_base & (KGSL_PAGESIZE - 1));
+ BUG_ON((mmu->mpu_base + mmu->mpu_range) & (KGSL_PAGESIZE - 1));
+
+ /* define physical memory range accessible by the core */
+ kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_BASE,
+ mmu->mpu_base);
+ kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_END,
+ mmu->mpu_base + mmu->mpu_range);
+
+ /* enable axi interrupts */
+ KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n",
+ GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT);
+ kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK,
+ GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT);
+
+ mmu->flags |= KGSL_FLAGS_INITIALIZED;
+
+ /* sub-client MMU lookups require address translation */
+ if ((mmu->config & ~0x1) > 0) {
+ /*make sure virtual address range is a multiple of 64Kb */
+ BUG_ON(mmu->va_range & ((1 << 16) - 1));
+
+ /* allocate memory used for completing r/w operations that
+ * cannot be mapped by the MMU
+ */
+ flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS
+ | KGSL_MEMFLAGS_STRICTREQUEST);
+ status = kgsl_sharedmem_alloc(flags, 64, &mmu->dummyspace);
+ if (status != 0) {
+ KGSL_MEM_ERR
+ ("Unable to allocate dummy space memory.\n");
+ kgsl_mmu_close(device);
+ return status;
+ }
+
+ kgsl_sharedmem_set(&mmu->dummyspace, 0, 0,
+ mmu->dummyspace.size);
+ /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory
+ * to complete transactions in case of an MMU fault. Note that
+ * we'll leave the bottom 32 bytes of the dummyspace for other
+ * purposes (e.g. use it when dummy read cycles are needed
+ * for other blocks */
+ kgsl_yamato_regwrite(device,
+ REG_MH_MMU_TRAN_ERROR,
+ mmu->dummyspace.physaddr + 32);
+
+ mmu->defaultpagetable = kgsl_mmu_createpagetableobject(mmu);
+ if (!mmu->defaultpagetable) {
+ KGSL_MEM_ERR("Failed to create global page table\n");
+ kgsl_mmu_close(device);
+ return -ENOMEM;
+ }
+ mmu->hwpagetable = mmu->defaultpagetable;
+ kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE,
+ mmu->hwpagetable->base.gpuaddr);
+ kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE,
+ (mmu->hwpagetable->va_base |
+ (mmu->hwpagetable->va_range >> 16)));
+ status = kgsl_yamato_setstate(device, KGSL_MMUFLAGS_TLBFLUSH);
+ if (status) {
+ kgsl_mmu_close(device);
+ return status;
+ }
+
+ mmu->flags |= KGSL_FLAGS_STARTED;
+ }
+
+ KGSL_MEM_VDBG("return %d\n", 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_MSM_KGSL_MMU
+pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr)
+{
+ pgd_t *pgd_ptr = NULL;
+ pmd_t *pmd_ptr = NULL;
+ pte_t *pte_ptr = NULL;
+
+ pgd_ptr = pgd_offset(current->mm, vaddr);
+ if (pgd_none(*pgd) || pgd_bad(*pgd)) {
+ KGSL_MEM_ERR
+ ("Invalid pgd entry found while trying to convert virtual "
+ "address to physical\n");
+ return 0;
+ }
+
+ pmd_ptr = pmd_offset(pgd_ptr, vaddr);
+ if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) {
+ KGSL_MEM_ERR
+ ("Invalid pmd entry found while trying to convert virtual "
+ "address to physical\n");
+ return 0;
+ }
+
+ pte_ptr = pte_offset_map(pmd_ptr, vaddr);
+ if (!pte_ptr) {
+ KGSL_MEM_ERR
+ ("Unable to map pte entry while trying to convert virtual "
+ "address to physical\n");
+ return 0;
+ }
+ return pte_ptr;
+}
+
+int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
+ unsigned int address,
+ int range,
+ unsigned int protflags,
+ unsigned int *gpuaddr,
+ unsigned int flags)
+{
+ int numpages;
+ unsigned int pte, superpte, ptefirst, ptelast, physaddr;
+ int flushtlb, alloc_size;
+ struct kgsl_mmu *mmu = NULL;
+ int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS;
+ unsigned int align = flags & KGSL_MEMFLAGS_ALIGN_MASK;
+
+ KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n",
+ pagetable, address, range, gpuaddr);
+
+ mmu = pagetable->mmu;
+
+ BUG_ON(mmu == NULL);
+ BUG_ON(protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV));
+ BUG_ON(protflags == 0);
+ BUG_ON(range <= 0);
+
+ /* Only support 4K and 8K alignment for now */
+ if (align != KGSL_MEMFLAGS_ALIGN8K && align != KGSL_MEMFLAGS_ALIGN4K) {
+ KGSL_MEM_ERR("Cannot map memory according to "
+ "requested flags: %08x\n", flags);
+ return -EINVAL;
+ }
+
+ /* Make sure address being mapped is at 4K boundary */
+ if (!IS_ALIGNED(address, KGSL_PAGESIZE) || range & ~KGSL_PAGEMASK) {
+ KGSL_MEM_ERR("Cannot map address not aligned "
+ "at page boundary: address: %08x, range: %08x\n",
+ address, range);
+ return -EINVAL;
+ }
+ alloc_size = range;
+ if (align == KGSL_MEMFLAGS_ALIGN8K)
+ alloc_size += KGSL_PAGESIZE;
+
+ *gpuaddr = gen_pool_alloc(pagetable->pool, alloc_size);
+ if (*gpuaddr == 0) {
+ KGSL_MEM_ERR("gen_pool_alloc failed: %d\n", alloc_size);
+ return -ENOMEM;
+ }
+
+ if (align == KGSL_MEMFLAGS_ALIGN8K) {
+ if (*gpuaddr & ((1 << 13) - 1)) {
+ /* Not 8k aligned, align it */
+ gen_pool_free(pagetable->pool, *gpuaddr, KGSL_PAGESIZE);
+ *gpuaddr = *gpuaddr + KGSL_PAGESIZE;
+ } else
+ gen_pool_free(pagetable->pool, *gpuaddr + range,
+ KGSL_PAGESIZE);
+ }
+
+ numpages = (range >> KGSL_PAGESIZE_SHIFT);
+
+ ptefirst = kgsl_pt_entry_get(pagetable, *gpuaddr);
+ ptelast = ptefirst + numpages;
+
+ pte = ptefirst;
+ flushtlb = 0;
+
+ superpte = ptefirst & (GSL_PT_SUPER_PTE - 1);
+ for (pte = superpte; pte < ptefirst; pte++) {
+ /* tlb needs to be flushed only when a dirty superPTE
+ gets backed */
+ if (kgsl_pt_map_isdirty(pagetable, pte)) {
+ flushtlb = 1;
+ break;
+ }
+ }
+
+ for (pte = ptefirst; pte < ptelast; pte++) {
+#ifdef VERBOSE_DEBUG
+ /* check if PTE exists */
+ uint32_t val = kgsl_pt_map_getaddr(pagetable, pte);
+ BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY);
+#endif
+ if (kgsl_pt_map_isdirty(pagetable, pte))
+ flushtlb = 1;
+ /* mark pte as in use */
+ if (phys_contiguous)
+ physaddr = address;
+ else {
+ physaddr = vmalloc_to_pfn((void *)address);
+ physaddr <<= PAGE_SHIFT;
+ }
+
+ if (physaddr)
+ kgsl_pt_map_set(pagetable, pte, physaddr | protflags);
+ else {
+ KGSL_MEM_ERR
+ ("Unable to find physaddr for vmallloc address: %x\n",
+ address);
+ kgsl_mmu_unmap(pagetable, *gpuaddr, range);
+ return -EFAULT;
+ }
+ address += KGSL_PAGESIZE;
+ }
+
+ /* set superpte to end of next superpte */
+ superpte = (ptelast + (GSL_PT_SUPER_PTE - 1))
+ & (GSL_PT_SUPER_PTE - 1);
+ for (pte = ptelast; pte < superpte; pte++) {
+ /* tlb needs to be flushed only when a dirty superPTE
+ gets backed */
+ if (kgsl_pt_map_isdirty(pagetable, pte)) {
+ flushtlb = 1;
+ break;
+ }
+ }
+ KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n",
+ pagetable, address, *gpuaddr, ptefirst, ptelast,
+ numpages, flushtlb);
+
+ dmb();
+
+ /* Invalidate tlb only if current page table used by GPU is the
+ * pagetable that we used to allocate */
+ if (pagetable == mmu->hwpagetable)
+ kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH);
+
+
+ KGSL_MEM_VDBG("return %d\n", 0);
+
+ return 0;
+}
+
+int
+kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr,
+ int range)
+{
+ unsigned int numpages;
+ unsigned int pte, ptefirst, ptelast;
+
+ KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n",
+ pagetable, gpuaddr, range);
+
+ BUG_ON(range <= 0);
+
+ numpages = (range >> KGSL_PAGESIZE_SHIFT);
+ if (range & (KGSL_PAGESIZE - 1))
+ numpages++;
+
+ ptefirst = kgsl_pt_entry_get(pagetable, gpuaddr);
+ ptelast = ptefirst + numpages;
+
+ KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n",
+ pagetable, gpuaddr, ptefirst, ptelast, numpages);
+
+ for (pte = ptefirst; pte < ptelast; pte++) {
+#ifdef VERBOSE_DEBUG
+ /* check if PTE exists */
+ BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte));
+#endif
+ kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY);
+ }
+
+ dmb();
+
+ /* Invalidate tlb only if current page table used by GPU is the
+ * pagetable that we used to allocate */
+ if (pagetable == pagetable->mmu->hwpagetable)
+ kgsl_yamato_setstate(pagetable->mmu->device,
+ KGSL_MMUFLAGS_TLBFLUSH);
+
+ gen_pool_free(pagetable->pool, gpuaddr, range);
+
+ KGSL_MEM_VDBG("return %d\n", 0);
+
+ return 0;
+}
+#endif
+
+int kgsl_mmu_close(struct kgsl_device *device)
+{
+ /*
+ * close device mmu
+ *
+ * call this with the global lock held
+ */
+ struct kgsl_mmu *mmu = &device->mmu;
+#ifdef _DEBUG
+ int i;
+#endif /* _DEBUG */
+
+ KGSL_MEM_VDBG("enter (device=%p)\n", device);
+
+ if (mmu->flags & KGSL_FLAGS_INITIALIZED0) {
+ /* disable mh interrupts */
+ KGSL_MEM_DBG("disabling mmu interrupts\n");
+ kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, 0);
+
+ /* disable MMU */
+ kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, 0x00000000);
+
+ if (mmu->dummyspace.gpuaddr)
+ kgsl_sharedmem_free(&mmu->dummyspace);
+
+ mmu->flags &= ~KGSL_FLAGS_STARTED;
+ mmu->flags &= ~KGSL_FLAGS_INITIALIZED;
+ mmu->flags &= ~KGSL_FLAGS_INITIALIZED0;
+ kgsl_mmu_destroypagetableobject(mmu->defaultpagetable);
+ mmu->defaultpagetable = NULL;
+ }
+
+ KGSL_MEM_VDBG("return %d\n", 0);
+
+ return 0;
+}
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h
new file mode 100644
index 0000000..d70f24a
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h
@@ -0,0 +1,147 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef __GSL_MMU_H
+#define __GSL_MMU_H
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+#include "kgsl_log.h"
+#include "kgsl_sharedmem.h"
+
+#define GSL_PT_SUPER_PTE 8
+#define GSL_PT_PAGE_WV 0x00000001
+#define GSL_PT_PAGE_RV 0x00000002
+#define GSL_PT_PAGE_DIRTY 0x00000004
+/* MMU Flags */
+#define KGSL_MMUFLAGS_TLBFLUSH 0x10000000
+#define KGSL_MMUFLAGS_PTUPDATE 0x20000000
+
+extern unsigned int kgsl_cache_enable;
+
+struct kgsl_device;
+
+struct kgsl_mmu_debug {
+ unsigned int config;
+ unsigned int mpu_base;
+ unsigned int mpu_end;
+ unsigned int va_range;
+ unsigned int pt_base;
+ unsigned int page_fault;
+ unsigned int trans_error;
+ unsigned int axi_error;
+ unsigned int interrupt_mask;
+ unsigned int interrupt_status;
+};
+
+struct kgsl_ptstats {
+ int64_t maps;
+ int64_t unmaps;
+ int64_t superpteallocs;
+ int64_t superptefrees;
+ int64_t ptswitches;
+ int64_t tlbflushes[KGSL_DEVICE_MAX];
+};
+
+struct kgsl_pagetable {
+ unsigned int refcnt;
+ struct kgsl_mmu *mmu;
+ struct kgsl_memdesc base;
+ uint32_t va_base;
+ unsigned int va_range;
+ unsigned int last_superpte;
+ unsigned int max_entries;
+ struct gen_pool *pool;
+};
+
+struct kgsl_mmu {
+ unsigned int refcnt;
+ uint32_t flags;
+ struct kgsl_device *device;
+ unsigned int config;
+ uint32_t mpu_base;
+ int mpu_range;
+ uint32_t va_base;
+ unsigned int va_range;
+ struct kgsl_memdesc dummyspace;
+ /* current page table object being used by device mmu */
+ struct kgsl_pagetable *defaultpagetable;
+ struct kgsl_pagetable *hwpagetable;
+};
+
+
+static inline int
+kgsl_mmu_isenabled(struct kgsl_mmu *mmu)
+{
+ return ((mmu)->flags & KGSL_FLAGS_STARTED) ? 1 : 0;
+}
+
+
+int kgsl_mmu_init(struct kgsl_device *device);
+
+int kgsl_mmu_close(struct kgsl_device *device);
+
+struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu);
+
+int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable);
+
+int kgsl_mmu_setstate(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable);
+
+#ifdef CONFIG_MSM_KGSL_MMU
+int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
+ unsigned int address,
+ int range,
+ unsigned int protflags,
+ unsigned int *gpuaddr,
+ unsigned int flags);
+
+int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
+ unsigned int gpuaddr, int range);
+
+pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr);
+#else
+static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
+ unsigned int address,
+ int range,
+ unsigned int protflags,
+ unsigned int *gpuaddr,
+ unsigned int flags)
+{
+ *gpuaddr = address;
+ return 0;
+}
+
+static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
+ unsigned int gpuaddr, int range) { return 0; }
+
+static inline pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) {return NULL;}
+#endif
+
+int kgsl_mmu_querystats(struct kgsl_pagetable *pagetable,
+ struct kgsl_ptstats *stats);
+
+void kgsl_mh_intrcallback(struct kgsl_device *device);
+
+#ifdef DEBUG
+void kgsl_mmu_debug(struct kgsl_mmu *, struct kgsl_mmu_debug*);
+#else
+static inline void kgsl_mmu_debug(struct kgsl_mmu *mmu,
+ struct kgsl_mmu_debug *mmu_debug) { }
+#endif /* DEBUG */
+
+#endif /* __GSL_MMU_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h
new file mode 100644
index 0000000..bc224b4
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h
@@ -0,0 +1,182 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef __GSL_PM4TYPES_H
+#define __GSL_PM4TYPES_H
+
+
+#define PM4_PKT_MASK 0xc0000000
+
+#define PM4_TYPE0_PKT ((unsigned int)0 << 30)
+#define PM4_TYPE1_PKT ((unsigned int)1 << 30)
+#define PM4_TYPE2_PKT ((unsigned int)2 << 30)
+#define PM4_TYPE3_PKT ((unsigned int)3 << 30)
+
+
+/* type3 packets */
+/* initialize CP's micro-engine */
+#define PM4_ME_INIT 0x48
+
+/* skip N 32-bit words to get to the next packet */
+#define PM4_NOP 0x10
+
+/* indirect buffer dispatch. prefetch parser uses this packet type to determine
+* whether to pre-fetch the IB
+*/
+#define PM4_INDIRECT_BUFFER 0x3f
+
+/* indirect buffer dispatch. same as IB, but init is pipelined */
+#define PM4_INDIRECT_BUFFER_PFD 0x37
+
+/* wait for the IDLE state of the engine */
+#define PM4_WAIT_FOR_IDLE 0x26
+
+/* wait until a register or memory location is a specific value */
+#define PM4_WAIT_REG_MEM 0x3c
+
+/* wait until a register location is equal to a specific value */
+#define PM4_WAIT_REG_EQ 0x52
+
+/* wait until a register location is >= a specific value */
+#define PM4_WAT_REG_GTE 0x53
+
+/* wait until a read completes */
+#define PM4_WAIT_UNTIL_READ 0x5c
+
+/* wait until all base/size writes from an IB_PFD packet have completed */
+#define PM4_WAIT_IB_PFD_COMPLETE 0x5d
+
+/* register read/modify/write */
+#define PM4_REG_RMW 0x21
+
+/* reads register in chip and writes to memory */
+#define PM4_REG_TO_MEM 0x3e
+
+/* write N 32-bit words to memory */
+#define PM4_MEM_WRITE 0x3d
+
+/* write CP_PROG_COUNTER value to memory */
+#define PM4_MEM_WRITE_CNTR 0x4f
+
+/* conditional execution of a sequence of packets */
+#define PM4_COND_EXEC 0x44
+
+/* conditional write to memory or register */
+#define PM4_COND_WRITE 0x45
+
+/* generate an event that creates a write to memory when completed */
+#define PM4_EVENT_WRITE 0x46
+
+/* generate a VS|PS_done event */
+#define PM4_EVENT_WRITE_SHD 0x58
+
+/* generate a cache flush done event */
+#define PM4_EVENT_WRITE_CFL 0x59
+
+/* generate a z_pass done event */
+#define PM4_EVENT_WRITE_ZPD 0x5b
+
+
+/* initiate fetch of index buffer and draw */
+#define PM4_DRAW_INDX 0x22
+
+/* draw using supplied indices in packet */
+#define PM4_DRAW_INDX_2 0x36
+
+/* initiate fetch of index buffer and binIDs and draw */
+#define PM4_DRAW_INDX_BIN 0x34
+
+/* initiate fetch of bin IDs and draw using supplied indices */
+#define PM4_DRAW_INDX_2_BIN 0x35
+
+
+/* begin/end initiator for viz query extent processing */
+#define PM4_VIZ_QUERY 0x23
+
+/* fetch state sub-blocks and initiate shader code DMAs */
+#define PM4_SET_STATE 0x25
+
+/* load constant into chip and to memory */
+#define PM4_SET_CONSTANT 0x2d
+
+/* load sequencer instruction memory (pointer-based) */
+#define PM4_IM_LOAD 0x27
+
+/* load sequencer instruction memory (code embedded in packet) */
+#define PM4_IM_LOAD_IMMEDIATE 0x2b
+
+/* load constants from a location in memory */
+#define PM4_LOAD_CONSTANT_CONTEXT 0x2e
+
+/* selective invalidation of state pointers */
+#define PM4_INVALIDATE_STATE 0x3b
+
+
+/* dynamically changes shader instruction memory partition */
+#define PM4_SET_SHADER_BASES 0x4A
+
+/* sets the 64-bit BIN_MASK register in the PFP */
+#define PM4_SET_BIN_MASK 0x50
+
+/* sets the 64-bit BIN_SELECT register in the PFP */
+#define PM4_SET_BIN_SELECT 0x51
+
+
+/* updates the current context, if needed */
+#define PM4_CONTEXT_UPDATE 0x5e
+
+/* generate interrupt from the command stream */
+#define PM4_INTERRUPT 0x40
+
+
+/* copy sequencer instruction memory to system memory */
+#define PM4_IM_STORE 0x2c
+
+/* program an offset that will added to the BIN_BASE value of
+ * the 3D_DRAW_INDX_BIN packet */
+#define PM4_SET_BIN_BASE_OFFSET 0x4B
+
+#define PM4_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */
+
+/* packet header building macros */
+#define pm4_type0_packet(regindx, cnt) \
+ (PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF))
+
+#define pm4_type0_packet_for_sameregister(regindx, cnt) \
+ ((PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \
+ ((regindx) & 0x7FFF)))
+
+#define pm4_type1_packet(reg0, reg1) \
+ (PM4_TYPE1_PKT | ((reg1) << 12) | (reg0))
+
+#define pm4_type3_packet(opcode, cnt) \
+ (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8))
+
+#define pm4_predicated_type3_packet(opcode, cnt) \
+ (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1)
+
+#define pm4_nop_packet(cnt) \
+ (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (PM4_NOP << 8))
+
+
+/* packet headers */
+#define PM4_HDR_ME_INIT pm4_type3_packet(PM4_ME_INIT, 18)
+#define PM4_HDR_INDIRECT_BUFFER_PFD pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2)
+#define PM4_HDR_INDIRECT_BUFFER pm4_type3_packet(PM4_INDIRECT_BUFFER, 2)
+
+#endif /* __GSL_PM4TYPES_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c
new file mode 100644
index 0000000..472d10c
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c
@@ -0,0 +1,837 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "kgsl.h"
+#include "kgsl_device.h"
+#include "kgsl_log.h"
+#include "kgsl_pm4types.h"
+#include "kgsl_ringbuffer.h"
+#include "kgsl_cmdstream.h"
+
+#include "yamato_reg.h"
+
+#define GSL_RB_NOP_SIZEDWORDS 2
+/* protected mode error checking below register address 0x800
+* note: if CP_INTERRUPT packet is used then checking needs
+* to change to below register address 0x7C8
+*/
+#define GSL_RB_PROTECTED_MODE_CONTROL 0x200001F2
+
+#define GSL_CP_INT_MASK \
+ (CP_INT_CNTL__SW_INT_MASK | \
+ CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \
+ CP_INT_CNTL__OPCODE_ERROR_MASK | \
+ CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \
+ CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \
+ CP_INT_CNTL__IB_ERROR_MASK | \
+ CP_INT_CNTL__IB2_INT_MASK | \
+ CP_INT_CNTL__IB1_INT_MASK | \
+ CP_INT_CNTL__RB_INT_MASK)
+
+#define YAMATO_PFP_FW "yamato_pfp.fw"
+#define YAMATO_PM4_FW "yamato_pm4.fw"
+
+/* ringbuffer size log2 quadwords equivalent */
+inline unsigned int kgsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords)
+{
+ unsigned int sizelog2quadwords = 0;
+ int i = sizedwords >> 1;
+
+ while (i >>= 1)
+ sizelog2quadwords++;
+
+ return sizelog2quadwords;
+}
+
+
+/* functions */
+void kgsl_cp_intrcallback(struct kgsl_device *device)
+{
+ unsigned int status = 0;
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+
+ KGSL_CMD_VDBG("enter (device=%p)\n", device);
+
+ kgsl_yamato_regread(device, REG_CP_INT_STATUS, &status);
+
+ if (status & CP_INT_CNTL__RB_INT_MASK) {
+ /* signal intr completion event */
+ int init_reftimestamp = 0x7fffffff;
+ int enableflag = 0;
+ kgsl_sharedmem_write(&rb->device->memstore,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable),
+ &enableflag, 4);
+ kgsl_sharedmem_write(&rb->device->memstore,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts),
+ &init_reftimestamp, 4);
+ KGSL_CMD_WARN("ringbuffer rb interrupt\n");
+ }
+
+ if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) {
+ KGSL_CMD_WARN("ringbuffer ib1/rb interrupt\n");
+ wake_up_interruptible_all(&device->ib1_wq);
+ }
+ if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) {
+ KGSL_CMD_FATAL("ringbuffer TO packet in IB interrupt\n");
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+ kgsl_ringbuffer_dump(rb);
+ }
+ if (status & CP_INT_CNTL__OPCODE_ERROR_MASK) {
+ KGSL_CMD_FATAL("ringbuffer opcode error interrupt\n");
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+ kgsl_ringbuffer_dump(rb);
+ }
+ if (status & CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK) {
+ KGSL_CMD_FATAL("ringbuffer protected mode error interrupt\n");
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+ kgsl_ringbuffer_dump(rb);
+ }
+ if (status & CP_INT_CNTL__RESERVED_BIT_ERROR_MASK) {
+ KGSL_CMD_FATAL("ringbuffer reserved bit error interrupt\n");
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+ kgsl_ringbuffer_dump(rb);
+ }
+ if (status & CP_INT_CNTL__IB_ERROR_MASK) {
+ KGSL_CMD_FATAL("ringbuffer IB error interrupt\n");
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+ kgsl_ringbuffer_dump(rb);
+ }
+ if (status & CP_INT_CNTL__SW_INT_MASK)
+ KGSL_CMD_DBG("ringbuffer software interrupt\n");
+
+ if (status & CP_INT_CNTL__IB2_INT_MASK)
+ KGSL_CMD_DBG("ringbuffer ib2 interrupt\n");
+
+ if (status & (~GSL_CP_INT_MASK))
+ KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status);
+
+ /* only ack bits we understand */
+ status &= GSL_CP_INT_MASK;
+ kgsl_yamato_regwrite(device, REG_CP_INT_ACK, status);
+
+ KGSL_CMD_VDBG("return\n");
+}
+
+
+void kgsl_ringbuffer_watchdog()
+{
+ struct kgsl_device *device = NULL;
+ struct kgsl_ringbuffer *rb = NULL;
+
+ device = &kgsl_driver.yamato_device;
+
+ BUG_ON(device == NULL);
+
+ rb = &device->ringbuffer;
+
+ KGSL_CMD_VDBG("enter\n");
+
+ if ((rb->flags & KGSL_FLAGS_STARTED) == 0) {
+ KGSL_CMD_VDBG("not started\n");
+ return;
+ }
+
+ GSL_RB_GET_READPTR(rb, &rb->rptr);
+
+ if (rb->rptr == rb->wptr) {
+ /* clear rptr sample for interval n */
+ rb->watchdog.flags &= ~KGSL_FLAGS_ACTIVE;
+ goto done;
+ }
+ /* ringbuffer is currently not empty */
+ /* and a rptr sample was taken during interval n-1 */
+ if (rb->watchdog.flags & KGSL_FLAGS_ACTIVE) {
+ /* and the rptr did not advance between
+ * interval n-1 and n */
+ if (rb->rptr == rb->watchdog.rptr_sample) {
+ /* then the core has hung */
+ KGSL_CMD_FATAL("Watchdog detected core hung.\n");
+ goto done;
+ }
+ /* save rptr sample for interval n */
+ rb->watchdog.flags |= KGSL_FLAGS_ACTIVE;
+ rb->watchdog.rptr_sample = rb->rptr;
+ }
+done:
+ KGSL_CMD_VDBG("return\n");
+}
+
+static void kgsl_ringbuffer_submit(struct kgsl_ringbuffer *rb)
+{
+ BUG_ON(rb->wptr == 0);
+
+ GSL_RB_UPDATE_WPTR_POLLING(rb);
+ /* Drain write buffer and data memory barrier */
+ dsb();
+ dmb();
+
+ /* Memory fence to ensure all data has posted. On some systems,
+ * like 7x27, the register block is not allocated as strongly ordered
+ * memory. Adding a memory fence ensures ordering during ringbuffer
+ * submits.*/
+ mb();
+
+ kgsl_yamato_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr);
+
+ rb->flags |= KGSL_FLAGS_ACTIVE;
+}
+
+static int
+kgsl_ringbuffer_waitspace(struct kgsl_ringbuffer *rb, unsigned int numcmds,
+ int wptr_ahead)
+{
+ int nopcount;
+ unsigned int freecmds;
+ unsigned int *cmds;
+
+ KGSL_CMD_VDBG("enter (rb=%p, numcmds=%d, wptr_ahead=%d)\n",
+ rb, numcmds, wptr_ahead);
+
+ /* if wptr ahead, fill the remaining with NOPs */
+ if (wptr_ahead) {
+ /* -1 for header */
+ nopcount = rb->sizedwords - rb->wptr - 1;
+
+ cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
+ GSL_RB_WRITE(cmds, pm4_nop_packet(nopcount));
+ rb->wptr++;
+
+ kgsl_ringbuffer_submit(rb);
+
+ rb->wptr = 0;
+ }
+
+ /* wait for space in ringbuffer */
+ do {
+ GSL_RB_GET_READPTR(rb, &rb->rptr);
+
+ freecmds = rb->rptr - rb->wptr;
+
+ } while ((freecmds != 0) && (freecmds < numcmds));
+
+ KGSL_CMD_VDBG("return %d\n", 0);
+
+ return 0;
+}
+
+
+static unsigned int *kgsl_ringbuffer_allocspace(struct kgsl_ringbuffer *rb,
+ unsigned int numcmds)
+{
+ unsigned int *ptr = NULL;
+ int status = 0;
+
+ BUG_ON(numcmds >= rb->sizedwords);
+
+ /* check for available space */
+ if (rb->wptr >= rb->rptr) {
+ /* wptr ahead or equal to rptr */
+ /* reserve dwords for nop packet */
+ if ((rb->wptr + numcmds) > (rb->sizedwords -
+ GSL_RB_NOP_SIZEDWORDS))
+ status = kgsl_ringbuffer_waitspace(rb, numcmds, 1);
+ } else {
+ /* wptr behind rptr */
+ if ((rb->wptr + numcmds) >= rb->rptr)
+ status = kgsl_ringbuffer_waitspace(rb, numcmds, 0);
+ /* check for remaining space */
+ /* reserve dwords for nop packet */
+ if ((rb->wptr + numcmds) > (rb->sizedwords -
+ GSL_RB_NOP_SIZEDWORDS))
+ status = kgsl_ringbuffer_waitspace(rb, numcmds, 1);
+ }
+
+ if (status == 0) {
+ ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr;
+ rb->wptr += numcmds;
+ }
+
+ return ptr;
+}
+
+static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
+{
+ int status = 0;
+ int i;
+ const struct firmware *fw = NULL;
+ unsigned int *fw_ptr = NULL;
+ size_t fw_word_size = 0;
+
+ status = request_firmware(&fw, YAMATO_PM4_FW,
+ kgsl_driver.misc.this_device);
+ if (status != 0) {
+ KGSL_DRV_ERR("request_firmware failed for %s with error %d\n",
+ YAMATO_PM4_FW, status);
+ goto done;
+ }
+ /*this firmware must come in 3 word chunks. plus 1 word of version*/
+ if ((fw->size % (sizeof(uint32_t)*3)) != 4) {
+ KGSL_DRV_ERR("bad firmware size %d.\n", fw->size);
+ status = -EINVAL;
+ goto done;
+ }
+ fw_ptr = (unsigned int *)fw->data;
+ fw_word_size = fw->size/sizeof(uint32_t);
+ KGSL_DRV_INFO("loading pm4 ucode version: %d\n", fw_ptr[0]);
+
+ kgsl_yamato_regwrite(device, REG_CP_DEBUG, 0x02000000);
+ kgsl_yamato_regwrite(device, REG_CP_ME_RAM_WADDR, 0);
+ for (i = 1; i < fw_word_size; i++)
+ kgsl_yamato_regwrite(device, REG_CP_ME_RAM_DATA, fw_ptr[i]);
+
+done:
+ release_firmware(fw);
+ return status;
+}
+
+static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device)
+{
+ int status = 0;
+ int i;
+ const struct firmware *fw = NULL;
+ unsigned int *fw_ptr = NULL;
+ size_t fw_word_size = 0;
+
+ status = request_firmware(&fw, YAMATO_PFP_FW,
+ kgsl_driver.misc.this_device);
+ if (status != 0) {
+ KGSL_DRV_ERR("request_firmware for %s failed with error %d\n",
+ YAMATO_PFP_FW, status);
+ return status;
+ }
+ /*this firmware must come in 1 word chunks. */
+ if ((fw->size % sizeof(uint32_t)) != 0) {
+ KGSL_DRV_ERR("bad firmware size %d.\n", fw->size);
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ fw_ptr = (unsigned int *)fw->data;
+ fw_word_size = fw->size/sizeof(uint32_t);
+
+ KGSL_DRV_INFO("loading pfp ucode version: %d\n", fw_ptr[0]);
+
+ kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_ADDR, 0);
+ for (i = 1; i < fw_word_size; i++)
+ kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_DATA, fw_ptr[i]);
+
+ release_firmware(fw);
+ return status;
+}
+
+static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb)
+{
+ int status;
+ /*cp_rb_cntl_u cp_rb_cntl; */
+ union reg_cp_rb_cntl cp_rb_cntl;
+ unsigned int *cmds, rb_cntl;
+ struct kgsl_device *device = rb->device;
+
+ KGSL_CMD_VDBG("enter (rb=%p)\n", rb);
+
+ if (rb->flags & KGSL_FLAGS_STARTED) {
+ KGSL_CMD_VDBG("return %d\n", 0);
+ return 0;
+ }
+ kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0,
+ sizeof(struct kgsl_rbmemptrs));
+
+ kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA,
+ (rb->sizedwords << 2));
+
+ kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_BASE,
+ (rb->memptrs_desc.gpuaddr
+ + GSL_RB_MEMPTRS_WPTRPOLL_OFFSET));
+
+ /* setup WPTR delay */
+ kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_DELAY, 0 /*0x70000010 */);
+
+ /*setup REG_CP_RB_CNTL */
+ kgsl_yamato_regread(device, REG_CP_RB_CNTL, &rb_cntl);
+ cp_rb_cntl.val = rb_cntl;
+ /* size of ringbuffer */
+ cp_rb_cntl.f.rb_bufsz =
+ kgsl_ringbuffer_sizelog2quadwords(rb->sizedwords);
+ /* quadwords to read before updating mem RPTR */
+ cp_rb_cntl.f.rb_blksz = rb->blksizequadwords;
+ cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; /* WPTR polling */
+ /* mem RPTR writebacks */
+ cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE;
+
+ kgsl_yamato_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val);
+
+ kgsl_yamato_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr);
+
+ kgsl_yamato_regwrite(device, REG_CP_RB_RPTR_ADDR,
+ rb->memptrs_desc.gpuaddr +
+ GSL_RB_MEMPTRS_RPTR_OFFSET);
+
+ /* explicitly clear all cp interrupts */
+ kgsl_yamato_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF);
+
+ /* setup scratch/timestamp */
+ kgsl_yamato_regwrite(device, REG_SCRATCH_ADDR,
+ device->memstore.gpuaddr +
+ KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp));
+
+ kgsl_yamato_regwrite(device, REG_SCRATCH_UMSK,
+ GSL_RB_MEMPTRS_SCRATCH_MASK);
+
+ /* load the CP ucode */
+
+ status = kgsl_ringbuffer_load_pm4_ucode(device);
+ if (status != 0) {
+ KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n",
+ status);
+ return status;
+ }
+
+
+ /* load the prefetch parser ucode */
+ status = kgsl_ringbuffer_load_pfp_ucode(device);
+ if (status != 0) {
+ KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n",
+ status);
+ return status;
+ }
+
+ kgsl_yamato_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000C0804);
+
+ rb->rptr = 0;
+ rb->wptr = 0;
+
+ rb->timestamp = 0;
+ GSL_RB_INIT_TIMESTAMP(rb);
+
+ INIT_LIST_HEAD(&rb->memqueue);
+
+ /* clear ME_HALT to start micro engine */
+ kgsl_yamato_regwrite(device, REG_CP_ME_CNTL, 0);
+
+ /* ME_INIT */
+ cmds = kgsl_ringbuffer_allocspace(rb, 19);
+
+ GSL_RB_WRITE(cmds, PM4_HDR_ME_INIT);
+ /* All fields present (bits 9:0) */
+ GSL_RB_WRITE(cmds, 0x000003ff);
+ /* Disable/Enable Real-Time Stream processing (present but ignored) */
+ GSL_RB_WRITE(cmds, 0x00000000);
+ /* Enable (2D <-> 3D) implicit synchronization (present but ignored) */
+ GSL_RB_WRITE(cmds, 0x00000000);
+
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE));
+ GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL));
+ GSL_RB_WRITE(cmds,
+ GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE));
+
+ /* Vertex and Pixel Shader Start Addresses in instructions
+ * (3 DWORDS per instruction) */
+ GSL_RB_WRITE(cmds, 0x80000180);
+ /* Maximum Contexts */
+ GSL_RB_WRITE(cmds, 0x00000001);
+ /* Write Confirm Interval and The CP will wait the
+ * wait_interval * 16 clocks between polling */
+ GSL_RB_WRITE(cmds, 0x00000000);
+
+ /* NQ and External Memory Swap */
+ GSL_RB_WRITE(cmds, 0x00000000);
+ /* Protected mode error checking */
+ GSL_RB_WRITE(cmds, GSL_RB_PROTECTED_MODE_CONTROL);
+ /* Disable header dumping and Header dump address */
+ GSL_RB_WRITE(cmds, 0x00000000);
+ /* Header dump size */
+ GSL_RB_WRITE(cmds, 0x00000000);
+
+ kgsl_ringbuffer_submit(rb);
+
+ /* idle device to validate ME INIT */
+ status = kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+
+ KGSL_CMD_DBG("enabling CP interrupts: mask %08lx\n", GSL_CP_INT_MASK);
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, GSL_CP_INT_MASK);
+ if (status == 0)
+ rb->flags |= KGSL_FLAGS_STARTED;
+
+ KGSL_CMD_VDBG("return %d\n", status);
+
+ return status;
+}
+
+static int kgsl_ringbuffer_stop(struct kgsl_ringbuffer *rb)
+{
+ KGSL_CMD_VDBG("enter (rb=%p)\n", rb);
+
+ if (rb->flags & KGSL_FLAGS_STARTED) {
+ KGSL_CMD_DBG("disabling CP interrupts: mask %08x\n", 0);
+ kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0);
+
+ /* ME_HALT */
+ kgsl_yamato_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000);
+
+ rb->flags &= ~KGSL_FLAGS_STARTED;
+ kgsl_ringbuffer_dump(rb);
+ }
+
+ KGSL_CMD_VDBG("return %d\n", 0);
+
+ return 0;
+}
+
+int kgsl_ringbuffer_init(struct kgsl_device *device)
+{
+ int status;
+ uint32_t flags;
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+
+ KGSL_CMD_VDBG("enter (device=%p)\n", device);
+
+ rb->device = device;
+ rb->sizedwords = (2 << kgsl_cfg_rb_sizelog2quadwords);
+ rb->blksizequadwords = kgsl_cfg_rb_blksizequadwords;
+
+ /* allocate memory for ringbuffer, needs to be double octword aligned
+ * align on page from contiguous physical memory
+ */
+ flags =
+ (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS |
+ KGSL_MEMFLAGS_STRICTREQUEST);
+
+ status = kgsl_sharedmem_alloc(flags, (rb->sizedwords << 2),
+ &rb->buffer_desc);
+
+ if (status != 0) {
+ kgsl_ringbuffer_close(rb);
+ KGSL_CMD_VDBG("return %d\n", status);
+ return status;
+ }
+
+ /* allocate memory for polling and timestamps */
+ /* This really can be at 4 byte alignment boundry but for using MMU
+ * we need to make it at page boundary */
+ flags = (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS);
+
+ status = kgsl_sharedmem_alloc(flags, sizeof(struct kgsl_rbmemptrs),
+ &rb->memptrs_desc);
+
+ if (status != 0) {
+ kgsl_ringbuffer_close(rb);
+ KGSL_CMD_VDBG("return %d\n", status);
+ return status;
+ }
+
+ /* last allocation of init process is made here so map all
+ * allocations to MMU */
+ status = kgsl_yamato_setup_pt(device, device->mmu.defaultpagetable);
+ if (status != 0) {
+ kgsl_ringbuffer_close(rb);
+ KGSL_CMD_VDBG("return %d\n", status);
+ return status;
+ }
+
+ /* overlay structure on memptrs memory */
+ rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr;
+
+ rb->flags |= KGSL_FLAGS_INITIALIZED;
+
+ status = kgsl_ringbuffer_start(rb);
+ if (status != 0) {
+ kgsl_ringbuffer_close(rb);
+ KGSL_CMD_VDBG("return %d\n", status);
+ return status;
+ }
+
+ KGSL_CMD_VDBG("return %d\n", 0);
+ return 0;
+}
+
+int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb)
+{
+ KGSL_CMD_VDBG("enter (rb=%p)\n", rb);
+
+ kgsl_cmdstream_memqueue_drain(rb->device);
+
+ kgsl_ringbuffer_stop(rb);
+
+ /* this must happen before first sharedmem_free */
+ kgsl_yamato_cleanup_pt(rb->device, rb->device->mmu.defaultpagetable);
+
+ if (rb->buffer_desc.hostptr)
+ kgsl_sharedmem_free(&rb->buffer_desc);
+
+ if (rb->memptrs_desc.hostptr)
+ kgsl_sharedmem_free(&rb->memptrs_desc);
+
+ rb->flags &= ~KGSL_FLAGS_INITIALIZED;
+
+ memset(rb, 0, sizeof(struct kgsl_ringbuffer));
+
+ KGSL_CMD_VDBG("return %d\n", 0);
+ return 0;
+}
+
+static uint32_t
+kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb,
+ int flags, unsigned int *cmds,
+ int sizedwords)
+{
+ unsigned int *ringcmds;
+ unsigned int timestamp;
+ unsigned int total_sizedwords = sizedwords + 6;
+
+ /* reserve space to temporarily turn off protected mode
+ * error checking if needed
+ */
+ total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
+ total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 9 : 0;
+
+ ringcmds = kgsl_ringbuffer_allocspace(rb, total_sizedwords);
+
+ if (flags & KGSL_CMD_FLAGS_PMODE) {
+ /* disable protected mode error checking */
+ *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1);
+ *ringcmds++ = 0;
+ }
+
+ memcpy(ringcmds, cmds, (sizedwords << 2));
+
+ ringcmds += sizedwords;
+
+ if (flags & KGSL_CMD_FLAGS_PMODE) {
+ /* re-enable protected mode error checking */
+ *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1);
+ *ringcmds++ = 1;
+ }
+
+ rb->timestamp++;
+ timestamp = rb->timestamp;
+
+ /* start-of-pipeline and end-of-pipeline timestamps */
+ *ringcmds++ = pm4_type0_packet(REG_CP_TIMESTAMP, 1);
+ *ringcmds++ = rb->timestamp;
+ *ringcmds++ = pm4_type3_packet(PM4_EVENT_WRITE, 3);
+ *ringcmds++ = CACHE_FLUSH_TS;
+ *ringcmds++ =
+ (rb->device->memstore.gpuaddr +
+ KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp));
+ *ringcmds++ = rb->timestamp;
+
+ if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) {
+ /* Add idle packet so avoid RBBM errors */
+ *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *ringcmds++ = 0x00000000;
+ /* Conditional execution based on memory values */
+ *ringcmds++ = pm4_type3_packet(PM4_COND_EXEC, 4);
+ *ringcmds++ = (rb->device->memstore.gpuaddr +
+ KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)) >> 2;
+ *ringcmds++ = (rb->device->memstore.gpuaddr +
+ KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)) >> 2;
+ *ringcmds++ = rb->timestamp;
+ /* # of conditional command DWORDs */
+ *ringcmds++ = 2;
+ *ringcmds++ = pm4_type3_packet(PM4_INTERRUPT, 1);
+ *ringcmds++ = CP_INT_CNTL__RB_INT_MASK;
+ }
+
+ kgsl_ringbuffer_submit(rb);
+
+ GSL_RB_STATS(rb->stats.words_total += sizedwords);
+ GSL_RB_STATS(rb->stats.issues++);
+
+ KGSL_CMD_VDBG("return %d\n", timestamp);
+
+ /* return timestamp of issued coREG_ands */
+ return timestamp;
+}
+
+uint32_t
+kgsl_ringbuffer_issuecmds(struct kgsl_device *device,
+ int flags,
+ unsigned int *cmds,
+ int sizedwords)
+{
+ unsigned int timestamp;
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+
+ KGSL_CMD_VDBG("enter (device->id=%d, flags=%d, cmds=%p, "
+ "sizedwords=%d)\n", device->id, flags, cmds, sizedwords);
+
+ timestamp = kgsl_ringbuffer_addcmds(rb, flags, cmds, sizedwords);
+
+ KGSL_CMD_VDBG("return %d\n)", timestamp);
+ return timestamp;
+}
+
+int
+kgsl_ringbuffer_issueibcmds(struct kgsl_device *device,
+ int drawctxt_index,
+ uint32_t ibaddr,
+ int sizedwords,
+ uint32_t *timestamp,
+ unsigned int flags)
+{
+ unsigned int link[3];
+
+ KGSL_CMD_VDBG("enter (device_id=%d, drawctxt_index=%d, ibaddr=0x%08x,"
+ " sizedwords=%d, timestamp=%p)\n",
+ device->id, drawctxt_index, ibaddr,
+ sizedwords, timestamp);
+
+ if (!(device->ringbuffer.flags & KGSL_FLAGS_STARTED)) {
+ KGSL_CMD_VDBG("return %d\n", -EINVAL);
+ return -EINVAL;
+ }
+
+ BUG_ON(ibaddr == 0);
+ BUG_ON(sizedwords == 0);
+
+ link[0] = PM4_HDR_INDIRECT_BUFFER_PFD;
+ link[1] = ibaddr;
+ link[2] = sizedwords;
+
+ kgsl_drawctxt_switch(device, &device->drawctxt[drawctxt_index], flags);
+
+ *timestamp = kgsl_ringbuffer_addcmds(&device->ringbuffer,
+ 0, &link[0], 3);
+
+
+ KGSL_CMD_INFO("ctxt %d g %08x sd %d ts %d\n",
+ drawctxt_index, ibaddr, sizedwords, *timestamp);
+
+ KGSL_CMD_VDBG("return %d\n", 0);
+
+ return 0;
+}
+
+
+#ifdef DEBUG
+void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb,
+ struct kgsl_rb_debug *rb_debug)
+{
+ memset(rb_debug, 0, sizeof(struct kgsl_rb_debug));
+
+ rb_debug->mem_rptr = rb->memptrs->rptr;
+ rb_debug->mem_wptr_poll = rb->memptrs->wptr_poll;
+ kgsl_yamato_regread(rb->device, REG_CP_RB_BASE,
+ (unsigned int *)&rb_debug->cp_rb_base);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_CNTL,
+ (unsigned int *)&rb_debug->cp_rb_cntl);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_ADDR,
+ (unsigned int *)&rb_debug->cp_rb_rptr_addr);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR,
+ (unsigned int *)&rb_debug->cp_rb_rptr);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_WR,
+ (unsigned int *)&rb_debug->cp_rb_rptr_wr);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR,
+ (unsigned int *)&rb_debug->cp_rb_wptr);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_DELAY,
+ (unsigned int *)&rb_debug->cp_rb_wptr_delay);
+ kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_BASE,
+ (unsigned int *)&rb_debug->cp_rb_wptr_base);
+ kgsl_yamato_regread(rb->device, REG_CP_IB1_BASE,
+ (unsigned int *)&rb_debug->cp_ib1_base);
+ kgsl_yamato_regread(rb->device, REG_CP_IB1_BUFSZ,
+ (unsigned int *)&rb_debug->cp_ib1_bufsz);
+ kgsl_yamato_regread(rb->device, REG_CP_IB2_BASE,
+ (unsigned int *)&rb_debug->cp_ib2_base);
+ kgsl_yamato_regread(rb->device, REG_CP_IB2_BUFSZ,
+ (unsigned int *)&rb_debug->cp_ib2_bufsz);
+ kgsl_yamato_regread(rb->device, REG_CP_ST_BASE,
+ (unsigned int *)&rb_debug->cp_st_base);
+ kgsl_yamato_regread(rb->device, REG_CP_ST_BUFSZ,
+ (unsigned int *)&rb_debug->cp_st_bufsz);
+ kgsl_yamato_regread(rb->device, REG_CP_CSQ_RB_STAT,
+ (unsigned int *)&rb_debug->cp_csq_rb_stat);
+ kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB1_STAT,
+ (unsigned int *)&rb_debug->cp_csq_ib1_stat);
+ kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB2_STAT,
+ (unsigned int *)&rb_debug->cp_csq_ib2_stat);
+ kgsl_yamato_regread(rb->device, REG_SCRATCH_UMSK,
+ (unsigned int *)&rb_debug->scratch_umsk);
+ kgsl_yamato_regread(rb->device, REG_SCRATCH_ADDR,
+ (unsigned int *)&rb_debug->scratch_addr);
+ kgsl_yamato_regread(rb->device, REG_CP_ME_CNTL,
+ (unsigned int *)&rb_debug->cp_me_cntl);
+ kgsl_yamato_regread(rb->device, REG_CP_ME_STATUS,
+ (unsigned int *)&rb_debug->cp_me_status);
+ kgsl_yamato_regread(rb->device, REG_CP_DEBUG,
+ (unsigned int *)&rb_debug->cp_debug);
+ kgsl_yamato_regread(rb->device, REG_CP_STAT,
+ (unsigned int *)&rb_debug->cp_stat);
+ kgsl_yamato_regread(rb->device, REG_CP_INT_STATUS,
+ (unsigned int *)&rb_debug->cp_int_status);
+ kgsl_yamato_regread(rb->device, REG_CP_INT_CNTL,
+ (unsigned int *)&rb_debug->cp_int_cntl);
+ kgsl_yamato_regread(rb->device, REG_RBBM_STATUS,
+ (unsigned int *)&rb_debug->rbbm_status);
+ kgsl_yamato_regread(rb->device, REG_RBBM_INT_STATUS,
+ (unsigned int *)&rb_debug->rbbm_int_status);
+ GSL_RB_GET_SOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->sop_timestamp);
+ GSL_RB_GET_EOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->eop_timestamp);
+
+}
+#endif /*DEBUG*/
+
+#ifdef DEBUG
+void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb)
+{
+ struct kgsl_rb_debug rb_debug;
+ kgsl_ringbuffer_debug(rb, &rb_debug);
+
+ KGSL_CMD_DBG("rbbm_status %08x rbbm_int_status %08x"
+ " mem_rptr %08x mem_wptr_poll %08x\n",
+ rb_debug.rbbm_status,
+ rb_debug.rbbm_int_status,
+ rb_debug.mem_rptr, rb_debug.mem_wptr_poll);
+
+ KGSL_CMD_DBG("rb_base %08x rb_cntl %08x rb_rptr_addr %08x rb_rptr %08x"
+ " rb_rptr_wr %08x\n",
+ rb_debug.cp_rb_base, rb_debug.cp_rb_cntl,
+ rb_debug.cp_rb_rptr_addr, rb_debug.cp_rb_rptr,
+ rb_debug.cp_rb_rptr_wr);
+
+ KGSL_CMD_DBG("rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x"
+ " ib1_base %08x ib1_bufsz %08x\n",
+ rb_debug.cp_rb_wptr, rb_debug.cp_rb_wptr_delay,
+ rb_debug.cp_rb_wptr_base, rb_debug.cp_ib1_base,
+ rb_debug.cp_ib1_bufsz);
+
+ KGSL_CMD_DBG("ib2_base %08x ib2_bufsz %08x st_base %08x st_bufsz %08x"
+ " cp_me_cntl %08x cp_me_status %08x\n",
+ rb_debug.cp_ib2_base, rb_debug.cp_ib2_bufsz,
+ rb_debug.cp_st_base, rb_debug.cp_st_bufsz,
+ rb_debug.cp_me_cntl, rb_debug.cp_me_status);
+
+ KGSL_CMD_DBG("cp_debug %08x cp_stat %08x cp_int_status %08x"
+ " cp_int_cntl %08x\n",
+ rb_debug.cp_debug, rb_debug.cp_stat,
+ rb_debug.cp_int_status, rb_debug.cp_int_cntl);
+
+ KGSL_CMD_DBG("sop_timestamp: %d eop_timestamp: %d\n",
+ rb_debug.sop_timestamp, rb_debug.eop_timestamp);
+
+}
+#endif /* DEBUG */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h
new file mode 100644
index 0000000..0d06020
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h
@@ -0,0 +1,254 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef __GSL_RINGBUFFER_H
+#define __GSL_RINGBUFFER_H
+
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+#include <linux/mutex.h>
+#include "kgsl_log.h"
+#include "kgsl_sharedmem.h"
+#include "yamato_reg.h"
+
+#define GSL_STATS_RINGBUFFER
+
+#define GSL_RB_USE_MEM_RPTR
+#define GSL_RB_USE_MEM_TIMESTAMP
+#define GSL_DEVICE_SHADOW_MEMSTORE_TO_USER
+
+/* ringbuffer sizes log2quadword */
+#define GSL_RB_SIZE_8 0
+#define GSL_RB_SIZE_16 1
+#define GSL_RB_SIZE_32 2
+#define GSL_RB_SIZE_64 3
+#define GSL_RB_SIZE_128 4
+#define GSL_RB_SIZE_256 5
+#define GSL_RB_SIZE_512 6
+#define GSL_RB_SIZE_1K 7
+#define GSL_RB_SIZE_2K 8
+#define GSL_RB_SIZE_4K 9
+#define GSL_RB_SIZE_8K 10
+#define GSL_RB_SIZE_16K 11
+#define GSL_RB_SIZE_32K 12
+#define GSL_RB_SIZE_64K 13
+#define GSL_RB_SIZE_128K 14
+#define GSL_RB_SIZE_256K 15
+#define GSL_RB_SIZE_512K 16
+#define GSL_RB_SIZE_1M 17
+#define GSL_RB_SIZE_2M 18
+#define GSL_RB_SIZE_4M 19
+
+/* Yamato ringbuffer config*/
+static const unsigned int kgsl_cfg_rb_sizelog2quadwords = GSL_RB_SIZE_32K;
+static const unsigned int kgsl_cfg_rb_blksizequadwords = GSL_RB_SIZE_16;
+
+/* CP timestamp register */
+#define REG_CP_TIMESTAMP REG_SCRATCH_REG0
+
+
+struct kgsl_device;
+struct kgsl_drawctxt;
+struct kgsl_ringbuffer;
+
+struct kgsl_rb_debug {
+ unsigned int pm4_ucode_rel;
+ unsigned int pfp_ucode_rel;
+ unsigned int mem_wptr_poll;
+ unsigned int mem_rptr;
+ unsigned int cp_rb_base;
+ unsigned int cp_rb_cntl;
+ unsigned int cp_rb_rptr_addr;
+ unsigned int cp_rb_rptr;
+ unsigned int cp_rb_rptr_wr;
+ unsigned int cp_rb_wptr;
+ unsigned int cp_rb_wptr_delay;
+ unsigned int cp_rb_wptr_base;
+ unsigned int cp_ib1_base;
+ unsigned int cp_ib1_bufsz;
+ unsigned int cp_ib2_base;
+ unsigned int cp_ib2_bufsz;
+ unsigned int cp_st_base;
+ unsigned int cp_st_bufsz;
+ unsigned int cp_csq_rb_stat;
+ unsigned int cp_csq_ib1_stat;
+ unsigned int cp_csq_ib2_stat;
+ unsigned int scratch_umsk;
+ unsigned int scratch_addr;
+ unsigned int cp_me_cntl;
+ unsigned int cp_me_status;
+ unsigned int cp_debug;
+ unsigned int cp_stat;
+ unsigned int cp_int_status;
+ unsigned int cp_int_cntl;
+ unsigned int rbbm_status;
+ unsigned int rbbm_int_status;
+ unsigned int sop_timestamp;
+ unsigned int eop_timestamp;
+};
+#ifdef DEBUG
+void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb,
+ struct kgsl_rb_debug *rb_debug);
+
+void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb);
+#else
+static inline void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb,
+ struct kgsl_rb_debug *rb_debug)
+{
+}
+
+static inline void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb)
+{
+}
+#endif
+
+struct kgsl_rbwatchdog {
+ uint32_t flags;
+ unsigned int rptr_sample;
+};
+
+#define GSL_RB_MEMPTRS_SCRATCH_COUNT 8
+struct kgsl_rbmemptrs {
+ volatile int rptr;
+ volatile int wptr_poll;
+} __attribute__ ((packed));
+
+#define GSL_RB_MEMPTRS_RPTR_OFFSET \
+ (offsetof(struct kgsl_rbmemptrs, rptr))
+
+#define GSL_RB_MEMPTRS_WPTRPOLL_OFFSET \
+ (offsetof(struct kgsl_rbmemptrs, wptr_poll))
+
+struct kgsl_rbstats {
+ int64_t issues;
+ int64_t words_total;
+};
+
+
+struct kgsl_ringbuffer {
+ struct kgsl_device *device;
+ uint32_t flags;
+
+ struct kgsl_memdesc buffer_desc;
+
+ struct kgsl_memdesc memptrs_desc;
+ struct kgsl_rbmemptrs *memptrs;
+
+ /*ringbuffer size */
+ unsigned int sizedwords;
+ unsigned int blksizequadwords;
+
+ unsigned int wptr; /* write pointer offset in dwords from baseaddr */
+ unsigned int rptr; /* read pointer offset in dwords from baseaddr */
+ uint32_t timestamp;
+
+ /* queue of memfrees pending timestamp elapse */
+ struct list_head memqueue;
+
+ struct kgsl_rbwatchdog watchdog;
+
+#ifdef GSL_STATS_RINGBUFFER
+ struct kgsl_rbstats stats;
+#endif /* GSL_STATS_RINGBUFFER */
+
+};
+
+/* dword base address of the GFX decode space */
+#define GSL_HAL_SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000)))
+
+#define GSL_RB_WRITE(ring, data) \
+ do { \
+ mb(); \
+ writel(data, ring); \
+ ring++; \
+ } while (0)
+
+/* timestamp */
+#ifdef GSL_DEVICE_SHADOW_MEMSTORE_TO_USER
+#define GSL_RB_USE_MEM_TIMESTAMP
+#endif /* GSL_DEVICE_SHADOW_MEMSTORE_TO_USER */
+
+#ifdef GSL_RB_USE_MEM_TIMESTAMP
+/* enable timestamp (...scratch0) memory shadowing */
+#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x1
+#define GSL_RB_INIT_TIMESTAMP(rb)
+
+#else
+#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x0
+#define GSL_RB_INIT_TIMESTAMP(rb) \
+ kgsl_yamato_regwrite((rb)->device->id, REG_CP_TIMESTAMP, 0)
+
+#endif /* GSL_RB_USE_MEMTIMESTAMP */
+
+/* mem rptr */
+#ifdef GSL_RB_USE_MEM_RPTR
+#define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */
+#define GSL_RB_GET_READPTR(rb, data) \
+ do { \
+ *(data) = (rb)->memptrs->rptr; \
+ } while (0)
+#else
+#define GSL_RB_CNTL_NO_UPDATE 0x1 /* disable */
+#define GSL_RB_GET_READPTR(rb, data) \
+ do { \
+ kgsl_yamato_regread((rb)->device->id, REG_CP_RB_RPTR, (data)); \
+ } while (0)
+#endif /* GSL_RB_USE_MEMRPTR */
+
+/* wptr polling */
+#ifdef GSL_RB_USE_WPTR_POLLING
+#define GSL_RB_CNTL_POLL_EN 0x1 /* enable */
+#define GSL_RB_UPDATE_WPTR_POLLING(rb) \
+ do { (rb)->memptrs->wptr_poll = (rb)->wptr; } while (0)
+#else
+#define GSL_RB_CNTL_POLL_EN 0x0 /* disable */
+#define GSL_RB_UPDATE_WPTR_POLLING(rb)
+#endif /* GSL_RB_USE_WPTR_POLLING */
+
+/* stats */
+#ifdef GSL_STATS_RINGBUFFER
+#define GSL_RB_STATS(x) x
+#else
+#define GSL_RB_STATS(x)
+#endif /* GSL_STATS_RINGBUFFER */
+
+struct kgsl_pmem_entry;
+
+int kgsl_ringbuffer_issueibcmds(struct kgsl_device *, int drawctxt_index,
+ uint32_t ibaddr, int sizedwords,
+ uint32_t *timestamp,
+ unsigned int flags);
+
+int kgsl_ringbuffer_init(struct kgsl_device *device);
+
+int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb);
+
+uint32_t kgsl_ringbuffer_issuecmds(struct kgsl_device *device,
+ int pmodeoff,
+ unsigned int *cmdaddr,
+ int sizedwords);
+
+int kgsl_ringbuffer_gettimestampshadow(struct kgsl_device *device,
+ unsigned int *sopaddr,
+ unsigned int *eopaddr);
+
+void kgsl_ringbuffer_watchdog(void);
+
+void kgsl_cp_intrcallback(struct kgsl_device *device);
+
+#endif /* __GSL_RINGBUFFER_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c
new file mode 100644
index 0000000..4a1b421
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c
@@ -0,0 +1,300 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+
+#include "kgsl_sharedmem.h"
+#include "kgsl_device.h"
+#include "kgsl.h"
+#include "kgsl_log.h"
+
+/* block alignment shift count */
+static inline unsigned int
+kgsl_memarena_get_order(uint32_t flags)
+{
+ unsigned int alignshift;
+ alignshift = ((flags & KGSL_MEMFLAGS_ALIGN_MASK)
+ >> KGSL_MEMFLAGS_ALIGN_SHIFT);
+ return alignshift;
+}
+
+/* block alignment shift count */
+static inline unsigned int
+kgsl_memarena_align(unsigned int address, unsigned int shift)
+{
+ unsigned int alignedbaseaddr = ((address) >> shift) << shift;
+ if (alignedbaseaddr < address)
+ alignedbaseaddr += (1 << shift);
+
+ return alignedbaseaddr;
+}
+
+int
+kgsl_sharedmem_init(struct kgsl_sharedmem *shmem)
+{
+ int result = -EINVAL;
+
+ if (!request_mem_region(shmem->physbase, shmem->size, DRIVER_NAME)) {
+ KGSL_MEM_ERR("request_mem_region failed\n");
+ goto error;
+ }
+
+ shmem->baseptr = ioremap(shmem->physbase, shmem->size);
+ KGSL_MEM_INFO("ioremap(shm) = %p\n", shmem->baseptr);
+
+ if (shmem->baseptr == NULL) {
+ KGSL_MEM_ERR("ioremap failed for address %08x size %d\n",
+ shmem->physbase, shmem->size);
+ result = -ENODEV;
+ goto error_release_mem;
+ }
+
+ shmem->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1);
+ if (shmem->pool == NULL) {
+ KGSL_MEM_ERR("gen_pool_create failed\n");
+ result = -ENOMEM;
+ goto error_iounmap;
+ }
+
+ if (gen_pool_add(shmem->pool, shmem->physbase, shmem->size, -1)) {
+ KGSL_MEM_ERR("gen_pool_create failed\n");
+ result = -ENOMEM;
+ goto error_pool_destroy;
+ }
+ result = 0;
+ KGSL_MEM_INFO("physbase 0x%08x size 0x%08x baseptr 0x%p\n",
+ shmem->physbase, shmem->size, shmem->baseptr);
+ return 0;
+
+error_pool_destroy:
+ gen_pool_destroy(shmem->pool);
+error_iounmap:
+ iounmap(shmem->baseptr);
+ shmem->baseptr = NULL;
+error_release_mem:
+ release_mem_region(shmem->physbase, shmem->size);
+error:
+ return result;
+}
+
+int
+kgsl_sharedmem_close(struct kgsl_sharedmem *shmem)
+{
+ if (shmem->pool) {
+ gen_pool_destroy(shmem->pool);
+ shmem->pool = NULL;
+ }
+
+ if (shmem->baseptr != NULL) {
+ KGSL_MEM_INFO("iounmap(shm) = %p\n", shmem->baseptr);
+ iounmap(shmem->baseptr);
+ shmem->baseptr = NULL;
+ release_mem_region(shmem->physbase, shmem->size);
+ }
+
+ return 0;
+}
+/*
+* get the host mapped address for a hardware device address
+*/
+static void *kgsl_memarena_gethostptr(struct kgsl_sharedmem *shmem,
+ uint32_t physaddr)
+{
+ void *result;
+
+ KGSL_MEM_VDBG("enter (memarena=%p, physaddr=0x%08x)\n",
+ shmem, physaddr);
+
+ BUG_ON(shmem == NULL);
+
+ /* check address range */
+ if (physaddr < shmem->physbase)
+ return NULL;
+
+ if (physaddr >= shmem->physbase + shmem->size)
+ return NULL;
+
+ if (shmem->baseptr == NULL) {
+ KGSL_MEM_VDBG("return: %p\n", NULL);
+ return NULL;
+ }
+
+ result = ((physaddr - shmem->physbase) + shmem->baseptr);
+
+ KGSL_MEM_VDBG("return: %p\n", result);
+
+ return result;
+}
+
+
+int
+kgsl_sharedmem_alloc(uint32_t flags, int size,
+ struct kgsl_memdesc *memdesc)
+{
+ struct kgsl_sharedmem *shmem;
+ int result = -ENOMEM;
+ unsigned int blksize;
+ unsigned int baseaddr;
+ unsigned int alignshift;
+ unsigned int alignedbaseaddr;
+
+ KGSL_MEM_VDBG("enter (flags=0x%08x, size=%d, memdesc=%p)\n",
+ flags, size, memdesc);
+
+ shmem = &kgsl_driver.shmem;
+ BUG_ON(memdesc == NULL);
+ BUG_ON(size <= 0);
+
+ alignshift = kgsl_memarena_get_order(flags);
+
+ size = ALIGN(size, KGSL_PAGESIZE);
+ blksize = size;
+ if (alignshift > KGSL_PAGESIZE_SHIFT)
+ blksize += (1 << alignshift) - KGSL_PAGESIZE;
+
+ baseaddr = gen_pool_alloc(shmem->pool, blksize);
+ if (baseaddr == 0) {
+ KGSL_MEM_ERR("gen_pool_alloc failed\n");
+ result = -ENOMEM;
+ goto done;
+ }
+ result = 0;
+
+ if (alignshift > KGSL_PAGESIZE_SHIFT) {
+ alignedbaseaddr = ALIGN(baseaddr, (1 << alignshift));
+
+ KGSL_MEM_VDBG("ba %x al %x as %d m->as %d bs %x s %x\n",
+ baseaddr, alignedbaseaddr, alignshift,
+ KGSL_PAGESIZE_SHIFT, blksize, size);
+ if (alignedbaseaddr > baseaddr) {
+ KGSL_MEM_VDBG("physaddr %x free before %x size %x\n",
+ alignedbaseaddr,
+ baseaddr, alignedbaseaddr - baseaddr);
+ gen_pool_free(shmem->pool, baseaddr,
+ alignedbaseaddr - baseaddr);
+ blksize -= alignedbaseaddr - baseaddr;
+ }
+ if (blksize > size) {
+ KGSL_MEM_VDBG("physaddr %x free after %x size %x\n",
+ alignedbaseaddr,
+ alignedbaseaddr + size,
+ blksize - size);
+ gen_pool_free(shmem->pool,
+ alignedbaseaddr + size,
+ blksize - size);
+ }
+ } else {
+ alignedbaseaddr = baseaddr;
+ }
+
+ memdesc->physaddr = alignedbaseaddr;
+ memdesc->hostptr = kgsl_memarena_gethostptr(shmem, memdesc->physaddr);
+ memdesc->size = size;
+
+ KGSL_MEM_VDBG("ashift %d m->ashift %d blksize %d base %x abase %x\n",
+ alignshift, KGSL_PAGESIZE_SHIFT, blksize, baseaddr,
+ alignedbaseaddr);
+
+done:
+ if (result)
+ memset(memdesc, 0, sizeof(*memdesc));
+
+
+ KGSL_MEM_VDBG("return: %d\n", result);
+ return result;
+}
+
+void
+kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
+{
+ struct kgsl_sharedmem *shmem = &kgsl_driver.shmem;
+
+ KGSL_MEM_VDBG("enter (shmem=%p, memdesc=%p, physaddr=%08x, size=%d)\n",
+ shmem, memdesc, memdesc->physaddr, memdesc->size);
+
+ BUG_ON(memdesc == NULL);
+ BUG_ON(memdesc->size <= 0);
+ BUG_ON(shmem->physbase > memdesc->physaddr);
+ BUG_ON((shmem->physbase + shmem->size)
+ < (memdesc->physaddr + memdesc->size));
+
+ gen_pool_free(shmem->pool, memdesc->physaddr, memdesc->size);
+
+ memset(memdesc, 0, sizeof(struct kgsl_memdesc));
+ KGSL_MEM_VDBG("return\n");
+}
+
+int
+kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst,
+ unsigned int offsetbytes, unsigned int sizebytes)
+{
+ if (memdesc == NULL || memdesc->hostptr == NULL || dst == NULL) {
+ KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p dst %p\n",
+ memdesc,
+ (memdesc ? memdesc->hostptr : NULL),
+ dst);
+ return -EINVAL;
+ }
+ if (offsetbytes + sizebytes > memdesc->size) {
+ KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n",
+ offsetbytes, sizebytes, memdesc->size);
+ return -ERANGE;
+ }
+ memcpy(dst, memdesc->hostptr + offsetbytes, sizebytes);
+ return 0;
+}
+
+int
+kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc,
+ unsigned int offsetbytes,
+ void *value, unsigned int sizebytes)
+{
+ if (memdesc == NULL || memdesc->hostptr == NULL) {
+ KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc,
+ (memdesc ? memdesc->hostptr : NULL));
+ return -EINVAL;
+ }
+ if (offsetbytes + sizebytes > memdesc->size) {
+ KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n",
+ offsetbytes, sizebytes, memdesc->size);
+ return -ERANGE;
+ }
+ memcpy(memdesc->hostptr + offsetbytes, value, sizebytes);
+ return 0;
+}
+
+int
+kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes,
+ unsigned int value, unsigned int sizebytes)
+{
+ if (memdesc == NULL || memdesc->hostptr == NULL) {
+ KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc,
+ (memdesc ? memdesc->hostptr : NULL));
+ return -EINVAL;
+ }
+ if (offsetbytes + sizebytes > memdesc->size) {
+ KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n",
+ offsetbytes, sizebytes, memdesc->size);
+ return -ERANGE;
+ }
+ memset(memdesc->hostptr + offsetbytes, value, sizebytes);
+ return 0;
+}
+
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h
new file mode 100644
index 0000000..eaf8406
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef __GSL_SHAREDMEM_H
+#define __GSL_SHAREDMEM_H
+
+#include <linux/types.h>
+#include <linux/msm_kgsl.h>
+
+#define KGSL_PAGESIZE 0x1000
+#define KGSL_PAGESIZE_SHIFT 12
+#define KGSL_PAGEMASK (~(KGSL_PAGESIZE - 1))
+
+struct kgsl_pagetable;
+
+struct platform_device;
+struct gen_pool;
+
+/* memory allocation flags */
+#define KGSL_MEMFLAGS_ANY 0x00000000 /*dont care*/
+
+#define KGSL_MEMFLAGS_APERTUREANY 0x00000000
+#define KGSL_MEMFLAGS_EMEM 0x00000000
+#define KGSL_MEMFLAGS_CONPHYS 0x00001000
+
+#define KGSL_MEMFLAGS_ALIGNANY 0x00000000
+#define KGSL_MEMFLAGS_ALIGN32 0x00000000
+#define KGSL_MEMFLAGS_ALIGN64 0x00060000
+#define KGSL_MEMFLAGS_ALIGN128 0x00070000
+#define KGSL_MEMFLAGS_ALIGN256 0x00080000
+#define KGSL_MEMFLAGS_ALIGN512 0x00090000
+#define KGSL_MEMFLAGS_ALIGN1K 0x000A0000
+#define KGSL_MEMFLAGS_ALIGN2K 0x000B0000
+#define KGSL_MEMFLAGS_ALIGN4K 0x000C0000
+#define KGSL_MEMFLAGS_ALIGN8K 0x000D0000
+#define KGSL_MEMFLAGS_ALIGN16K 0x000E0000
+#define KGSL_MEMFLAGS_ALIGN32K 0x000F0000
+#define KGSL_MEMFLAGS_ALIGN64K 0x00100000
+#define KGSL_MEMFLAGS_ALIGNPAGE KGSL_MEMFLAGS_ALIGN4K
+
+/* fail the alloc if the flags cannot be honored */
+#define KGSL_MEMFLAGS_STRICTREQUEST 0x80000000
+
+#define KGSL_MEMFLAGS_APERTURE_MASK 0x0000F000
+#define KGSL_MEMFLAGS_ALIGN_MASK 0x00FF0000
+
+#define KGSL_MEMFLAGS_APERTURE_SHIFT 12
+#define KGSL_MEMFLAGS_ALIGN_SHIFT 16
+
+
+/* shared memory allocation */
+struct kgsl_memdesc {
+ struct kgsl_pagetable *pagetable;
+ void *hostptr;
+ unsigned int gpuaddr;
+ unsigned int physaddr;
+ unsigned int size;
+ unsigned int priv;
+};
+
+struct kgsl_sharedmem {
+ void *baseptr;
+ unsigned int physbase;
+ unsigned int size;
+ struct gen_pool *pool;
+};
+
+int kgsl_sharedmem_alloc(uint32_t flags, int size,
+ struct kgsl_memdesc *memdesc);
+
+/*TODO: add protection flags */
+int kgsl_sharedmem_import(struct kgsl_pagetable *,
+ uint32_t phys_addr,
+ uint32_t size,
+ struct kgsl_memdesc *memdesc);
+
+
+void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc);
+
+
+int kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst,
+ unsigned int offsetbytes, unsigned int sizebytes);
+
+int kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc,
+ unsigned int offsetbytes, void *value,
+ unsigned int sizebytes);
+
+int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc,
+ unsigned int offsetbytes, unsigned int value,
+ unsigned int sizebytes);
+
+int kgsl_sharedmem_init(struct kgsl_sharedmem *shmem);
+
+int kgsl_sharedmem_close(struct kgsl_sharedmem *shmem);
+
+#endif /* __GSL_SHAREDMEM_H */
diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c
new file mode 100644
index 0000000..af5a6ff
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c
@@ -0,0 +1,1004 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "kgsl_pm4types.h"
+#include "kgsl_cmdstream.h"
+
+#include "yamato_reg.h"
+
+#define GSL_RBBM_INT_MASK \
+ (RBBM_INT_CNTL__RDERR_INT_MASK | \
+ RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK)
+
+#define GSL_SQ_INT_MASK \
+ (SQ_INT_CNTL__PS_WATCHDOG_MASK | \
+ SQ_INT_CNTL__VS_WATCHDOG_MASK)
+
+/* Yamato MH arbiter config*/
+#define KGSL_CFG_YAMATO_MHARB \
+ (0x10 \
+ | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \
+ | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \
+ | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \
+ | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \
+ | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT))
+
+void kgsl_register_dump(struct kgsl_device *device)
+{
+ unsigned int regValue;
+
+ kgsl_yamato_regread(device, REG_RBBM_STATUS, ®Value);
+ KGSL_CMD_ERR("RBBM_STATUS = %8.8X\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_BASE, ®Value);
+ KGSL_CMD_ERR("CP_RB_BASE = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_CNTL, ®Value);
+ KGSL_CMD_ERR("CP_RB_CNTL = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_RPTR_ADDR, ®Value);
+ KGSL_CMD_ERR("CP_RB_RPTR_ADDR = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_RPTR, ®Value);
+ KGSL_CMD_ERR("CP_RB_RPTR = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_WPTR, ®Value);
+ KGSL_CMD_ERR("CP_RB_WPTR = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_RB_RPTR_WR, ®Value);
+ KGSL_CMD_ERR("CP_RB_RPTR_WR = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_INT_CNTL, ®Value);
+ KGSL_CMD_ERR("CP_INT_CNTL = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_INT_STATUS, ®Value);
+ KGSL_CMD_ERR("CP_INT_STATUS = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_ME_CNTL, ®Value);
+ KGSL_CMD_ERR("CP_ME_CNTL = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_ME_STATUS, ®Value);
+ KGSL_CMD_ERR("CP_ME_STATUS = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, ®Value);
+ KGSL_CMD_ERR("RBBM_PM_OVERRIDE1 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE2, ®Value);
+ KGSL_CMD_ERR("RBBM_PM_OVERRIDE2 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_RBBM_INT_CNTL, ®Value);
+ KGSL_CMD_ERR("RBBM_INT_CNTL = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, ®Value);
+ KGSL_CMD_ERR("RBBM_INT_STATUS = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, ®Value);
+ KGSL_CMD_ERR("MASTER_INT_SIGNAL = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_IB1_BASE, ®Value);
+ KGSL_CMD_ERR("CP_IB1_BASE = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_IB1_BUFSZ, ®Value);
+ KGSL_CMD_ERR("CP_IB1_BUFSZ = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_IB2_BASE, ®Value);
+ KGSL_CMD_ERR("CP_IB2_BASE = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_IB2_BUFSZ, ®Value);
+ KGSL_CMD_ERR("CP_IB2_BUFSZ = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_CP_STAT, ®Value);
+ KGSL_CMD_ERR("CP_STAT = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_SCRATCH_REG0, ®Value);
+ KGSL_CMD_ERR("SCRATCH_REG0 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_COHER_SIZE_PM4, ®Value);
+ KGSL_CMD_ERR("COHER_SIZE_PM4 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_COHER_BASE_PM4, ®Value);
+ KGSL_CMD_ERR("COHER_BASE_PM4 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_COHER_STATUS_PM4, ®Value);
+ KGSL_CMD_ERR("COHER_STATUS_PM4 = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, ®Value);
+ KGSL_CMD_ERR("RBBM_READ_ERROR = %08x\n", regValue);
+ kgsl_yamato_regread(device, REG_MH_AXI_ERROR, ®Value);
+ KGSL_CMD_ERR("MH_AXI_ERROR = %08x\n", regValue);
+}
+
+static int kgsl_yamato_gmeminit(struct kgsl_device *device)
+{
+ union reg_rb_edram_info rb_edram_info;
+ unsigned int gmem_size;
+ unsigned int edram_value = 0;
+
+ /* make sure edram range is aligned to size */
+ BUG_ON(device->gmemspace.gpu_base & (device->gmemspace.sizebytes - 1));
+
+ /* get edram_size value equivalent */
+ gmem_size = (device->gmemspace.sizebytes >> 14);
+ while (gmem_size >>= 1)
+ edram_value++;
+
+ rb_edram_info.val = 0;
+
+ rb_edram_info.f.edram_size = edram_value;
+ rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */
+ /* must be aligned to size */
+ rb_edram_info.f.edram_range = (device->gmemspace.gpu_base >> 14);
+
+ kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val);
+
+ return 0;
+}
+
+static int kgsl_yamato_gmemclose(struct kgsl_device *device)
+{
+ kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000);
+
+ return 0;
+}
+
+void kgsl_yamato_rbbm_intrcallback(struct kgsl_device *device)
+{
+ unsigned int status = 0;
+ unsigned int rderr = 0;
+
+ KGSL_DRV_VDBG("enter (device=%p)\n", device);
+
+ kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, &status);
+
+ if (status & RBBM_INT_CNTL__RDERR_INT_MASK) {
+ kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, &rderr);
+ KGSL_DRV_FATAL("rbbm read error interrupt: %08x\n", rderr);
+ } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) {
+ KGSL_DRV_DBG("rbbm display update interrupt\n");
+ } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) {
+ KGSL_DRV_DBG("rbbm gui idle interrupt\n");
+ } else {
+ KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status);
+ }
+
+ status &= GSL_RBBM_INT_MASK;
+ kgsl_yamato_regwrite(device, REG_RBBM_INT_ACK, status);
+
+ KGSL_DRV_VDBG("return\n");
+}
+
+void kgsl_yamato_sq_intrcallback(struct kgsl_device *device)
+{
+ unsigned int status = 0;
+
+ KGSL_DRV_VDBG("enter (device=%p)\n", device);
+
+ kgsl_yamato_regread(device, REG_SQ_INT_STATUS, &status);
+
+ if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK)
+ KGSL_DRV_DBG("sq ps watchdog interrupt\n");
+ else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK)
+ KGSL_DRV_DBG("sq vs watchdog interrupt\n");
+ else
+ KGSL_DRV_DBG("bad bits in REG_SQ_INT_STATUS %08x\n", status);
+
+
+ status &= GSL_SQ_INT_MASK;
+ kgsl_yamato_regwrite(device, REG_SQ_INT_ACK, status);
+
+ KGSL_DRV_VDBG("return\n");
+}
+
+irqreturn_t kgsl_yamato_isr(int irq, void *data)
+{
+ irqreturn_t result = IRQ_NONE;
+
+ struct kgsl_device *device = &kgsl_driver.yamato_device;
+ unsigned int status;
+
+ kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, &status);
+
+ if (status & MASTER_INT_SIGNAL__MH_INT_STAT) {
+ kgsl_mh_intrcallback(device);
+ result = IRQ_HANDLED;
+ }
+
+ if (status & MASTER_INT_SIGNAL__CP_INT_STAT) {
+ kgsl_cp_intrcallback(device);
+ result = IRQ_HANDLED;
+ }
+
+ if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) {
+ kgsl_yamato_rbbm_intrcallback(device);
+ result = IRQ_HANDLED;
+ }
+
+ if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) {
+ kgsl_yamato_sq_intrcallback(device);
+ result = IRQ_HANDLED;
+ }
+
+
+ return result;
+}
+
+int kgsl_yamato_cleanup_pt(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable)
+{
+ kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr,
+ device->ringbuffer.buffer_desc.size);
+
+ kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr,
+ device->ringbuffer.memptrs_desc.size);
+
+ kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr,
+ device->memstore.size);
+
+ kgsl_mmu_unmap(pagetable, device->mmu.dummyspace.gpuaddr,
+ device->mmu.dummyspace.size);
+
+ return 0;
+}
+
+int kgsl_yamato_setup_pt(struct kgsl_device *device,
+ struct kgsl_pagetable *pagetable)
+{
+ int result = 0;
+ unsigned int gpuaddr;
+
+ BUG_ON(device->ringbuffer.buffer_desc.physaddr == 0);
+ BUG_ON(device->ringbuffer.memptrs_desc.physaddr == 0);
+ BUG_ON(device->memstore.physaddr == 0);
+ BUG_ON(device->mmu.dummyspace.physaddr == 0);
+
+ result = kgsl_mmu_map(pagetable,
+ device->ringbuffer.buffer_desc.physaddr,
+ device->ringbuffer.buffer_desc.size,
+ GSL_PT_PAGE_RV, &gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K);
+
+ if (result)
+ goto error;
+
+ if (device->ringbuffer.buffer_desc.gpuaddr == 0)
+ device->ringbuffer.buffer_desc.gpuaddr = gpuaddr;
+ BUG_ON(device->ringbuffer.buffer_desc.gpuaddr != gpuaddr);
+
+ result = kgsl_mmu_map(pagetable,
+ device->ringbuffer.memptrs_desc.physaddr,
+ device->ringbuffer.memptrs_desc.size,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K);
+ if (result)
+ goto unmap_buffer_desc;
+
+ if (device->ringbuffer.memptrs_desc.gpuaddr == 0)
+ device->ringbuffer.memptrs_desc.gpuaddr = gpuaddr;
+ BUG_ON(device->ringbuffer.memptrs_desc.gpuaddr != gpuaddr);
+
+ result = kgsl_mmu_map(pagetable, device->memstore.physaddr,
+ device->memstore.size,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K);
+ if (result)
+ goto unmap_memptrs_desc;
+
+ if (device->memstore.gpuaddr == 0)
+ device->memstore.gpuaddr = gpuaddr;
+ BUG_ON(device->memstore.gpuaddr != gpuaddr);
+
+ result = kgsl_mmu_map(pagetable,
+ device->mmu.dummyspace.physaddr,
+ device->mmu.dummyspace.size,
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr,
+ KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K);
+
+ if (result)
+ goto unmap_memstore_desc;
+
+ if (device->mmu.dummyspace.gpuaddr == 0)
+ device->mmu.dummyspace.gpuaddr = gpuaddr;
+ BUG_ON(device->mmu.dummyspace.gpuaddr != gpuaddr);
+
+ return result;
+
+unmap_memstore_desc:
+ kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr,
+ device->memstore.size);
+
+unmap_memptrs_desc:
+ kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr,
+ device->ringbuffer.memptrs_desc.size);
+unmap_buffer_desc:
+ kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr,
+ device->ringbuffer.buffer_desc.size);
+error:
+ return result;
+
+}
+
+#ifdef CONFIG_MSM_KGSL_MMU
+int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags)
+{
+ unsigned int link[32];
+ unsigned int *cmds = &link[0];
+ int sizedwords = 0;
+ unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */
+
+ KGSL_MEM_DBG("device %p ctxt %p pt %p\n",
+ device,
+ device->drawctxt_active,
+ device->mmu.hwpagetable);
+ /* if possible, set via command stream,
+ * otherwise set via direct register writes
+ */
+ if (device->drawctxt_active) {
+ KGSL_MEM_DBG("cmds\n");
+ if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+ /* wait for graphics pipe to be idle */
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0x00000000;
+
+ /* set page table base */
+ *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1);
+ *cmds++ = device->mmu.hwpagetable->base.gpuaddr;
+ sizedwords += 4;
+ }
+
+ if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+ *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1);
+ *cmds++ = mh_mmu_invalidate;
+ sizedwords += 2;
+ }
+
+ if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+ /* HW workaround: to resolve MMU page fault interrupts
+ * caused by the VGT.It prevents the CP PFP from filling
+ * the VGT DMA request fifo too early,thereby ensuring
+ * that the VGT will not fetch vertex/bin data until
+ * after the page table base register has been updated.
+ *
+ * Two null DRAW_INDX_BIN packets are inserted right
+ * after the page table base update, followed by a
+ * wait for idle. The null packets will fill up the
+ * VGT DMA request fifo and prevent any further
+ * vertex/bin updates from occurring until the wait
+ * has finished. */
+ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2);
+ *cmds++ = (0x4 << 16) |
+ (REG_PA_SU_SC_MODE_CNTL - 0x2000);
+ *cmds++ = 0; /* disable faceness generation */
+ *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1);
+ *cmds++ = device->mmu.dummyspace.gpuaddr;
+ *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6);
+ *cmds++ = 0; /* viz query info */
+ *cmds++ = 0x0003C004; /* draw indicator */
+ *cmds++ = 0; /* bin base */
+ *cmds++ = 3; /* bin size */
+ *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */
+ *cmds++ = 6; /* dma size */
+ *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6);
+ *cmds++ = 0; /* viz query info */
+ *cmds++ = 0x0003C004; /* draw indicator */
+ *cmds++ = 0; /* bin base */
+ *cmds++ = 3; /* bin size */
+ /* dma base */
+ *cmds++ = device->mmu.dummyspace.gpuaddr;
+ *cmds++ = 6; /* dma size */
+ *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0x00000000;
+ sizedwords += 21;
+ }
+
+ if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) {
+ *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1);
+ *cmds++ = 0x7fff; /* invalidate all base pointers */
+ sizedwords += 2;
+ }
+
+ kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
+ &link[0], sizedwords);
+ } else {
+ KGSL_MEM_DBG("regs\n");
+
+ if (flags & KGSL_MMUFLAGS_PTUPDATE) {
+ kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT);
+ kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE,
+ device->mmu.hwpagetable->base.gpuaddr);
+ }
+
+ if (flags & KGSL_MMUFLAGS_TLBFLUSH) {
+ kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE,
+ mh_mmu_invalidate);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static unsigned int
+kgsl_yamato_getchipid(struct kgsl_device *device)
+{
+ unsigned int chipid;
+ unsigned int coreid, majorid, minorid, patchid, revid;
+
+ /* YDX */
+ kgsl_yamato_regread(device, REG_RBBM_PERIPHID1, &coreid);
+ coreid &= 0xF;
+
+ kgsl_yamato_regread(device, REG_RBBM_PERIPHID2, &majorid);
+ majorid = (majorid >> 4) & 0xF;
+
+ kgsl_yamato_regread(device, REG_RBBM_PATCH_RELEASE, &revid);
+ /* this is a 16bit field, but extremely unlikely it would ever get
+ * this high
+ */
+ minorid = ((revid >> 0) & 0xFF);
+
+
+ patchid = ((revid >> 16) & 0xFF);
+
+ chipid = ((coreid << 24) | (majorid << 16) |
+ (minorid << 8) | (patchid << 0));
+
+ /* Hardware revision 211 (8650) returns the wrong chip ID */
+ if (chipid == KGSL_CHIPID_YAMATODX_REV21)
+ chipid = KGSL_CHIPID_YAMATODX_REV211;
+
+ return chipid;
+}
+
+int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config)
+{
+ int status = -EINVAL;
+ int init_reftimestamp = 0x7fffffff;
+ struct kgsl_memregion *regspace = &device->regspace;
+ unsigned int memflags = KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS;
+
+ KGSL_DRV_VDBG("enter (device=%p, config=%p)\n", device, config);
+
+ if (device->flags & KGSL_FLAGS_INITIALIZED) {
+ KGSL_DRV_VDBG("return %d\n", 0);
+ return 0;
+ }
+ memset(device, 0, sizeof(*device));
+
+ init_waitqueue_head(&device->ib1_wq);
+
+ memcpy(regspace, &config->regspace, sizeof(device->regspace));
+ if (regspace->mmio_phys_base == 0 || regspace->sizebytes == 0) {
+ KGSL_DRV_ERR("dev %d invalid regspace\n", device->id);
+ goto error;
+ }
+ if (!request_mem_region(regspace->mmio_phys_base,
+ regspace->sizebytes, DRIVER_NAME)) {
+ KGSL_DRV_ERR("request_mem_region failed for register memory\n");
+ status = -ENODEV;
+ goto error;
+ }
+
+ regspace->mmio_virt_base = ioremap(regspace->mmio_phys_base,
+ regspace->sizebytes);
+ KGSL_MEM_INFO("ioremap(regs) = %p\n", regspace->mmio_virt_base);
+ if (regspace->mmio_virt_base == NULL) {
+ KGSL_DRV_ERR("ioremap failed for register memory\n");
+ status = -ENODEV;
+ goto error_release_mem;
+ }
+
+ KGSL_DRV_INFO("dev %d regs phys 0x%08x size 0x%08x virt %p\n",
+ device->id, regspace->mmio_phys_base,
+ regspace->sizebytes, regspace->mmio_virt_base);
+
+ memcpy(&device->gmemspace, &config->gmemspace,
+ sizeof(device->gmemspace));
+
+ device->id = KGSL_DEVICE_YAMATO;
+
+ if (config->mmu_config) {
+ device->mmu.config = config->mmu_config;
+ device->mmu.mpu_base = config->mpu_base;
+ device->mmu.mpu_range = config->mpu_range;
+ device->mmu.va_base = config->va_base;
+ device->mmu.va_range = config->va_range;
+ }
+
+ device->chip_id = kgsl_yamato_getchipid(device);
+
+ /*We need to make sure all blocks are powered up and clocked before
+ *issuing a soft reset. The overrides will be turned off (set to 0)
+ *later in kgsl_yamato_start.
+ */
+ kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe);
+ kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff);
+
+ kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF);
+ msleep(50);
+ kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000);
+
+ kgsl_yamato_regwrite(device, REG_RBBM_CNTL, 0x00004442);
+
+ kgsl_yamato_regwrite(device, REG_MH_ARBITER_CONFIG,
+ KGSL_CFG_YAMATO_MHARB);
+
+ kgsl_yamato_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000);
+ kgsl_yamato_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000);
+
+
+ status = kgsl_mmu_init(device);
+ if (status != 0) {
+ status = -ENODEV;
+ goto error_iounmap;
+ }
+
+ status = kgsl_cmdstream_init(device);
+ if (status != 0) {
+ status = -ENODEV;
+ goto error_close_mmu;
+ }
+
+ status = kgsl_sharedmem_alloc(memflags, sizeof(device->memstore),
+ &device->memstore);
+ if (status != 0) {
+ status = -ENODEV;
+ goto error_close_cmdstream;
+ }
+ kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size);
+
+ kgsl_sharedmem_write(&device->memstore,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts),
+ &init_reftimestamp, 4);
+
+ kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000);
+ pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id,
+ kgsl_mmu_isenabled(&device->mmu) ? "on" : "off");
+
+ device->flags |= KGSL_FLAGS_INITIALIZED;
+ return 0;
+
+error_close_cmdstream:
+ kgsl_cmdstream_close(device);
+error_close_mmu:
+ kgsl_mmu_close(device);
+error_iounmap:
+ iounmap(regspace->mmio_virt_base);
+ regspace->mmio_virt_base = NULL;
+error_release_mem:
+ release_mem_region(regspace->mmio_phys_base, regspace->sizebytes);
+error:
+ return status;
+}
+
+int kgsl_yamato_close(struct kgsl_device *device)
+{
+ struct kgsl_memregion *regspace = &device->regspace;
+
+ if (device->memstore.hostptr)
+ kgsl_sharedmem_free(&device->memstore);
+
+ kgsl_mmu_close(device);
+
+ kgsl_cmdstream_close(device);
+
+ if (regspace->mmio_virt_base != NULL) {
+ KGSL_MEM_INFO("iounmap(regs) = %p\n", regspace->mmio_virt_base);
+ iounmap(regspace->mmio_virt_base);
+ regspace->mmio_virt_base = NULL;
+ release_mem_region(regspace->mmio_phys_base,
+ regspace->sizebytes);
+ }
+
+ KGSL_DRV_VDBG("return %d\n", 0);
+ device->flags &= ~KGSL_FLAGS_INITIALIZED;
+ return 0;
+}
+
+int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags)
+{
+ int status = -EINVAL;
+
+ KGSL_DRV_VDBG("enter (device=%p)\n", device);
+
+ if (!(device->flags & KGSL_FLAGS_INITIALIZED)) {
+ KGSL_DRV_ERR("Trying to start uninitialized device.\n");
+ return -EINVAL;
+ }
+
+ device->refcnt++;
+
+ if (device->flags & KGSL_FLAGS_STARTED) {
+ KGSL_DRV_VDBG("already started");
+ return 0;
+ }
+
+ kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0);
+ kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0);
+
+ KGSL_DRV_DBG("enabling RBBM interrupts mask 0x%08lx\n",
+ GSL_RBBM_INT_MASK);
+ kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK);
+
+ /* make sure SQ interrupts are disabled */
+ kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0);
+
+ kgsl_yamato_gmeminit(device);
+
+ status = kgsl_ringbuffer_init(device);
+ if (status != 0) {
+ kgsl_yamato_stop(device);
+ return status;
+ }
+
+ status = kgsl_drawctxt_init(device);
+ if (status != 0) {
+ kgsl_yamato_stop(device);
+ return status;
+ }
+
+ device->flags |= KGSL_FLAGS_STARTED;
+
+ KGSL_DRV_VDBG("return %d\n", status);
+ return status;
+}
+
+int kgsl_yamato_stop(struct kgsl_device *device)
+{
+ if (device->flags & KGSL_FLAGS_STARTED) {
+
+ kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, 0);
+
+ kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0);
+
+ kgsl_drawctxt_close(device);
+
+ kgsl_ringbuffer_close(&device->ringbuffer);
+
+ kgsl_yamato_gmemclose(device);
+
+ device->flags &= ~KGSL_FLAGS_STARTED;
+ }
+
+ return 0;
+}
+
+int kgsl_yamato_getproperty(struct kgsl_device *device,
+ enum kgsl_property_type type,
+ void *value,
+ unsigned int sizebytes)
+{
+ int status = -EINVAL;
+
+ switch (type) {
+ case KGSL_PROP_DEVICE_INFO:
+ {
+ struct kgsl_devinfo devinfo;
+
+ if (sizebytes != sizeof(devinfo)) {
+ status = -EINVAL;
+ break;
+ }
+
+ memset(&devinfo, 0, sizeof(devinfo));
+ devinfo.device_id = device->id;
+ devinfo.chip_id = device->chip_id;
+ devinfo.mmu_enabled = kgsl_mmu_isenabled(&device->mmu);
+ devinfo.gmem_hostbaseaddr =
+ (unsigned int)device->gmemspace.mmio_virt_base;
+ devinfo.gmem_gpubaseaddr = device->gmemspace.gpu_base;
+ devinfo.gmem_sizebytes = device->gmemspace.sizebytes;
+
+ if (copy_to_user(value, &devinfo, sizeof(devinfo)) !=
+ 0) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
+ case KGSL_PROP_DEVICE_SHADOW:
+ {
+ struct kgsl_shadowprop shadowprop;
+
+ if (sizebytes != sizeof(shadowprop)) {
+ status = -EINVAL;
+ break;
+ }
+ memset(&shadowprop, 0, sizeof(shadowprop));
+ if (device->memstore.hostptr) {
+ /*NOTE: with mmu enabled, gpuaddr doesn't mean
+ * anything to mmap().
+ */
+ shadowprop.gpuaddr = device->memstore.physaddr;
+ shadowprop.size = device->memstore.size;
+ shadowprop.flags = KGSL_FLAGS_INITIALIZED;
+ }
+ if (copy_to_user(value, &shadowprop,
+ sizeof(shadowprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
+ case KGSL_PROP_MMU_ENABLE:
+ {
+#ifdef CONFIG_MSM_KGSL_MMU
+ int mmuProp = 1;
+#else
+ int mmuProp = 0;
+#endif
+ if (sizebytes != sizeof(int)) {
+ status = -EINVAL;
+ break;
+ }
+ if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
+ case KGSL_PROP_INTERRUPT_WAITS:
+ {
+ int int_waits = 1;
+ if (sizebytes != sizeof(int)) {
+ status = -EINVAL;
+ break;
+ }
+ if (copy_to_user(value, &int_waits, sizeof(int))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
+ default:
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+/* Note: This is either called from the standby timer, or while holding the
+ * driver mutex.
+ *
+ * The reader may obseve that this function may be called without holding the
+ * driver mutex (in the timer), which can cause the ringbuffer write pointer
+ * to change, when a user submits a command. However, the user must be holding
+ * the driver mutex when doing so, and then must
+ * have canceled the timer. If the timer was executing at the time of
+ * cancellation, the active flag would have been cleared, which the user
+ * ioctl checks for after cancelling the timer.
+ */
+bool kgsl_yamato_is_idle(struct kgsl_device *device)
+{
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+ unsigned int rbbm_status;
+
+ BUG_ON(!(rb->flags & KGSL_FLAGS_STARTED));
+
+ GSL_RB_GET_READPTR(rb, &rb->rptr);
+ if (rb->rptr == rb->wptr) {
+ kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status);
+ if (rbbm_status == 0x110)
+ return true;
+ }
+ return false;
+}
+
+int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout)
+{
+ int status = -EINVAL;
+ struct kgsl_ringbuffer *rb = &device->ringbuffer;
+ struct kgsl_mmu_debug mmu_dbg;
+ unsigned int rbbm_status;
+ int idle_count = 0;
+#define IDLE_COUNT_MAX 1000000
+
+ KGSL_DRV_VDBG("enter (device=%p, timeout=%d)\n", device, timeout);
+
+ (void)timeout;
+
+ /* first, wait until the CP has consumed all the commands in
+ * the ring buffer
+ */
+ if (rb->flags & KGSL_FLAGS_STARTED) {
+ do {
+ idle_count++;
+ GSL_RB_GET_READPTR(rb, &rb->rptr);
+
+ } while (rb->rptr != rb->wptr && idle_count < IDLE_COUNT_MAX);
+ if (idle_count == IDLE_COUNT_MAX)
+ goto err;
+ }
+ /* now, wait for the GPU to finish its operations */
+ for (idle_count = 0; idle_count < IDLE_COUNT_MAX; idle_count++) {
+ kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status);
+
+ if (rbbm_status == 0x110) {
+ status = 0;
+ goto done;
+ }
+ }
+
+err:
+ KGSL_DRV_ERR("spun too long waiting for RB to idle\n");
+ kgsl_register_dump(device);
+ kgsl_ringbuffer_dump(rb);
+ kgsl_mmu_debug(&device->mmu, &mmu_dbg);
+ BUG();
+
+done:
+ KGSL_DRV_VDBG("return %d\n", status);
+
+ return status;
+}
+
+int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords,
+ unsigned int *value)
+{
+ unsigned int *reg;
+
+ if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) {
+ KGSL_DRV_ERR("invalid offset %d\n", offsetwords);
+ return -ERANGE;
+ }
+
+ reg = (unsigned int *)(device->regspace.mmio_virt_base
+ + (offsetwords << 2));
+ *value = readl(reg);
+
+ return 0;
+}
+
+int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords,
+ unsigned int value)
+{
+ unsigned int *reg;
+
+ if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) {
+ KGSL_DRV_ERR("invalid offset %d\n", offsetwords);
+ return -ERANGE;
+ }
+
+ reg = (unsigned int *)(device->regspace.mmio_virt_base
+ + (offsetwords << 2));
+ writel(value, reg);
+
+ return 0;
+}
+
+static inline int _wait_timestamp(struct kgsl_device *device,
+ unsigned int timestamp,
+ unsigned int msecs)
+{
+ long status;
+
+ status = wait_event_interruptible_timeout(device->ib1_wq,
+ kgsl_cmdstream_check_timestamp(device, timestamp),
+ msecs_to_jiffies(msecs));
+
+ if (status > 0)
+ status = 0;
+ else if (status == 0) {
+ if (!kgsl_cmdstream_check_timestamp(device, timestamp)) {
+ status = -ETIMEDOUT;
+ kgsl_register_dump(device);
+ }
+ }
+
+ return (int)status;
+}
+
+/* MUST be called with the kgsl_driver.mutex held */
+int kgsl_yamato_waittimestamp(struct kgsl_device *device,
+ unsigned int timestamp,
+ unsigned int msecs)
+{
+ long status = 0;
+ uint32_t ref_ts;
+ int enableflag = 1;
+ unsigned int cmd[2];
+
+ KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n",
+ device, timestamp, msecs);
+
+ if (!kgsl_cmdstream_check_timestamp(device, timestamp)) {
+ kgsl_sharedmem_read(&device->memstore, &ref_ts,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), 4);
+ if (timestamp_cmp(ref_ts, timestamp)) {
+ kgsl_sharedmem_write(&device->memstore,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts),
+ ×tamp, 4);
+ }
+
+ cmd[0] = pm4_type3_packet(PM4_INTERRUPT, 1);
+ cmd[1] = CP_INT_CNTL__IB1_INT_MASK;
+ kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NO_TS_CMP,
+ cmd, 2);
+ kgsl_sharedmem_write(&device->memstore,
+ KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable),
+ &enableflag, 4);
+
+ mutex_unlock(&kgsl_driver.mutex);
+ status = _wait_timestamp(device, timestamp, msecs);
+ mutex_lock(&kgsl_driver.mutex);
+ }
+
+ KGSL_DRV_INFO("return %ld\n", status);
+ return (int)status;
+}
+
+int kgsl_yamato_runpending(struct kgsl_device *device)
+{
+ if (device->flags & KGSL_FLAGS_INITIALIZED)
+ kgsl_cmdstream_memqueue_drain(device);
+ return 0;
+}
+
+int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig,
+ struct platform_device *pdev)
+{
+ int result = 0;
+ struct resource *res = NULL;
+
+ memset(devconfig, 0, sizeof(*devconfig));
+
+ /*find memory regions */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "kgsl_reg_memory");
+ if (res == NULL) {
+ KGSL_DRV_ERR("platform_get_resource_byname failed\n");
+ result = -EINVAL;
+ goto done;
+ }
+ KGSL_DRV_DBG("registers at %08x to %08x\n", res->start, res->end);
+ devconfig->regspace.mmio_phys_base = res->start;
+ devconfig->regspace.sizebytes = resource_size(res);
+
+ devconfig->gmemspace.gpu_base = 0;
+ devconfig->gmemspace.sizebytes = SZ_256K;
+
+ /*note: for all of these behavior masks:
+ * 0 = do not translate
+ * 1 = translate within va_range, otherwise use physical
+ * 2 = translate within va_range, otherwise fault
+ */
+ devconfig->mmu_config = 1 /* mmu enable */
+ | (2 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT)
+ | (2 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT);
+
+ /*TODO: these should probably be configurable from platform device
+ * stuff */
+ devconfig->va_base = 0x66000000;
+ devconfig->va_range = SZ_128M;
+
+ /* turn off memory protection unit by setting acceptable physical
+ * address range to include all pages. Apparrently MPU causing
+ * problems.
+ */
+ devconfig->mpu_base = 0x00000000;
+ devconfig->mpu_range = 0xFFFFF000;
+
+ result = 0;
+done:
+ return result;
+}
diff --git a/drivers/video/msm/gpu/kgsl/yamato_reg.h b/drivers/video/msm/gpu/kgsl/yamato_reg.h
new file mode 100644
index 0000000..828b95a
--- /dev/null
+++ b/drivers/video/msm/gpu/kgsl/yamato_reg.h
@@ -0,0 +1,400 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef _YAMATO_REG_H
+#define _YAMATO_REG_H
+
+enum VGT_EVENT_TYPE {
+ VS_DEALLOC = 0,
+ PS_DEALLOC = 1,
+ VS_DONE_TS = 2,
+ PS_DONE_TS = 3,
+ CACHE_FLUSH_TS = 4,
+ CONTEXT_DONE = 5,
+ CACHE_FLUSH = 6,
+ VIZQUERY_START = 7,
+ VIZQUERY_END = 8,
+ SC_WAIT_WC = 9,
+ RST_PIX_CNT = 13,
+ RST_VTX_CNT = 14,
+ TILE_FLUSH = 15,
+ CACHE_FLUSH_AND_INV_TS_EVENT = 20,
+ ZPASS_DONE = 21,
+ CACHE_FLUSH_AND_INV_EVENT = 22,
+ PERFCOUNTER_START = 23,
+ PERFCOUNTER_STOP = 24,
+ VS_FETCH_DONE = 27,
+ FACENESS_FLUSH = 28,
+};
+
+enum COLORFORMATX {
+ COLORX_4_4_4_4 = 0,
+ COLORX_1_5_5_5 = 1,
+ COLORX_5_6_5 = 2,
+ COLORX_8 = 3,
+ COLORX_8_8 = 4,
+ COLORX_8_8_8_8 = 5,
+ COLORX_S8_8_8_8 = 6,
+ COLORX_16_FLOAT = 7,
+ COLORX_16_16_FLOAT = 8,
+ COLORX_16_16_16_16_FLOAT = 9,
+ COLORX_32_FLOAT = 10,
+ COLORX_32_32_FLOAT = 11,
+ COLORX_32_32_32_32_FLOAT = 12,
+ COLORX_2_3_3 = 13,
+ COLORX_8_8_8 = 14,
+};
+
+enum SURFACEFORMAT {
+ FMT_1_REVERSE = 0,
+ FMT_1 = 1,
+ FMT_8 = 2,
+ FMT_1_5_5_5 = 3,
+ FMT_5_6_5 = 4,
+ FMT_6_5_5 = 5,
+ FMT_8_8_8_8 = 6,
+ FMT_2_10_10_10 = 7,
+ FMT_8_A = 8,
+ FMT_8_B = 9,
+ FMT_8_8 = 10,
+ FMT_Cr_Y1_Cb_Y0 = 11,
+ FMT_Y1_Cr_Y0_Cb = 12,
+ FMT_5_5_5_1 = 13,
+ FMT_8_8_8_8_A = 14,
+ FMT_4_4_4_4 = 15,
+ FMT_10_11_11 = 16,
+ FMT_11_11_10 = 17,
+ FMT_DXT1 = 18,
+ FMT_DXT2_3 = 19,
+ FMT_DXT4_5 = 20,
+ FMT_24_8 = 22,
+ FMT_24_8_FLOAT = 23,
+ FMT_16 = 24,
+ FMT_16_16 = 25,
+ FMT_16_16_16_16 = 26,
+ FMT_16_EXPAND = 27,
+ FMT_16_16_EXPAND = 28,
+ FMT_16_16_16_16_EXPAND = 29,
+ FMT_16_FLOAT = 30,
+ FMT_16_16_FLOAT = 31,
+ FMT_16_16_16_16_FLOAT = 32,
+ FMT_32 = 33,
+ FMT_32_32 = 34,
+ FMT_32_32_32_32 = 35,
+ FMT_32_FLOAT = 36,
+ FMT_32_32_FLOAT = 37,
+ FMT_32_32_32_32_FLOAT = 38,
+ FMT_32_AS_8 = 39,
+ FMT_32_AS_8_8 = 40,
+ FMT_16_MPEG = 41,
+ FMT_16_16_MPEG = 42,
+ FMT_8_INTERLACED = 43,
+ FMT_32_AS_8_INTERLACED = 44,
+ FMT_32_AS_8_8_INTERLACED = 45,
+ FMT_16_INTERLACED = 46,
+ FMT_16_MPEG_INTERLACED = 47,
+ FMT_16_16_MPEG_INTERLACED = 48,
+ FMT_DXN = 49,
+ FMT_8_8_8_8_AS_16_16_16_16 = 50,
+ FMT_DXT1_AS_16_16_16_16 = 51,
+ FMT_DXT2_3_AS_16_16_16_16 = 52,
+ FMT_DXT4_5_AS_16_16_16_16 = 53,
+ FMT_2_10_10_10_AS_16_16_16_16 = 54,
+ FMT_10_11_11_AS_16_16_16_16 = 55,
+ FMT_11_11_10_AS_16_16_16_16 = 56,
+ FMT_32_32_32_FLOAT = 57,
+ FMT_DXT3A = 58,
+ FMT_DXT5A = 59,
+ FMT_CTX1 = 60,
+ FMT_DXT3A_AS_1_1_1_1 = 61
+};
+
+#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4
+#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2
+#define RB_EDRAM_INFO_UNUSED0_SIZE 8
+#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18
+
+struct rb_edram_info_t {
+ unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE;
+ unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE;
+ unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE;
+ unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE;
+};
+
+union reg_rb_edram_info {
+ unsigned int val:32;
+ struct rb_edram_info_t f;
+};
+
+#define CP_RB_CNTL_RB_BUFSZ_SIZE 6
+#define CP_RB_CNTL_UNUSED0_SIZE 2
+#define CP_RB_CNTL_RB_BLKSZ_SIZE 6
+#define CP_RB_CNTL_UNUSED1_SIZE 2
+#define CP_RB_CNTL_BUF_SWAP_SIZE 2
+#define CP_RB_CNTL_UNUSED2_SIZE 2
+#define CP_RB_CNTL_RB_POLL_EN_SIZE 1
+#define CP_RB_CNTL_UNUSED3_SIZE 6
+#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1
+#define CP_RB_CNTL_UNUSED4_SIZE 3
+#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1
+
+struct cp_rb_cntl_t {
+ unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE;
+ unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE;
+ unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE;
+ unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE;
+ unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE;
+ unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE;
+ unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE;
+ unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE;
+ unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE;
+ unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE;
+ unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE;
+};
+
+union reg_cp_rb_cntl {
+ unsigned int val:32;
+ struct cp_rb_cntl_t f;
+};
+
+#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL
+#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004
+
+
+#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L
+#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L
+
+#define MH_INTERRUPT_MASK__AXI_READ_ERROR 0x00000001L
+#define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L
+#define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L
+
+#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L
+#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L
+#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L
+
+#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL
+#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L
+#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L
+#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L
+#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L
+#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L
+#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L
+#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L
+#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L
+#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L
+#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L
+#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L
+#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L
+#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L
+#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L
+#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L
+#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L
+#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L
+#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L
+#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L
+
+#define CP_INT_CNTL__SW_INT_MASK 0x00080000L
+#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L
+#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L
+#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L
+#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L
+#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L
+#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L
+#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L
+#define CP_INT_CNTL__RB_INT_MASK 0x80000000L
+
+#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L
+#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L
+#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L
+#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L
+
+#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL
+#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L
+
+#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006
+#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007
+#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008
+#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009
+#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a
+#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d
+#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e
+#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f
+#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010
+#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016
+#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017
+#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018
+#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019
+#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a
+
+#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004
+#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006
+#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008
+#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a
+#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c
+#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e
+#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010
+#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012
+#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014
+#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016
+#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018
+
+#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000
+#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008
+#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014
+#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b
+
+#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000
+#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004
+#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e
+
+#define REG_CP_CSQ_IB1_STAT 0x01FE
+#define REG_CP_CSQ_IB2_STAT 0x01FF
+#define REG_CP_CSQ_RB_STAT 0x01FD
+#define REG_CP_DEBUG 0x01FC
+#define REG_CP_IB1_BASE 0x0458
+#define REG_CP_IB1_BUFSZ 0x0459
+#define REG_CP_IB2_BASE 0x045A
+#define REG_CP_IB2_BUFSZ 0x045B
+#define REG_CP_INT_ACK 0x01F4
+#define REG_CP_INT_CNTL 0x01F2
+#define REG_CP_INT_STATUS 0x01F3
+#define REG_CP_ME_CNTL 0x01F6
+#define REG_CP_ME_RAM_DATA 0x01FA
+#define REG_CP_ME_RAM_WADDR 0x01F8
+#define REG_CP_ME_STATUS 0x01F7
+#define REG_CP_PFP_UCODE_ADDR 0x00C0
+#define REG_CP_PFP_UCODE_DATA 0x00C1
+#define REG_CP_QUEUE_THRESHOLDS 0x01D5
+#define REG_CP_RB_BASE 0x01C0
+#define REG_CP_RB_CNTL 0x01C1
+#define REG_CP_RB_RPTR 0x01C4
+#define REG_CP_RB_RPTR_ADDR 0x01C3
+#define REG_CP_RB_RPTR_WR 0x01C7
+#define REG_CP_RB_WPTR 0x01C5
+#define REG_CP_RB_WPTR_BASE 0x01C8
+#define REG_CP_RB_WPTR_DELAY 0x01C6
+#define REG_CP_STAT 0x047F
+#define REG_CP_STATE_DEBUG_DATA 0x01ED
+#define REG_CP_STATE_DEBUG_INDEX 0x01EC
+#define REG_CP_ST_BASE 0x044D
+#define REG_CP_ST_BUFSZ 0x044E
+
+#define REG_MASTER_INT_SIGNAL 0x03B7
+
+#define REG_MH_ARBITER_CONFIG 0x0A40
+#define REG_MH_INTERRUPT_CLEAR 0x0A44
+#define REG_MH_INTERRUPT_MASK 0x0A42
+#define REG_MH_INTERRUPT_STATUS 0x0A43
+#define REG_MH_MMU_CONFIG 0x0040
+#define REG_MH_MMU_INVALIDATE 0x0045
+#define REG_MH_MMU_MPU_BASE 0x0046
+#define REG_MH_MMU_MPU_END 0x0047
+#define REG_MH_MMU_PAGE_FAULT 0x0043
+#define REG_MH_MMU_PT_BASE 0x0042
+#define REG_MH_MMU_TRAN_ERROR 0x0044
+#define REG_MH_MMU_VA_RANGE 0x0041
+
+#define REG_PA_CL_VPORT_XSCALE 0x210F
+#define REG_PA_CL_VPORT_ZOFFSET 0x2114
+#define REG_PA_CL_VPORT_ZSCALE 0x2113
+#define REG_PA_CL_VTE_CNTL 0x2206
+#define REG_PA_SC_AA_MASK 0x2312
+#define REG_PA_SC_LINE_CNTL 0x2300
+#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F
+#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E
+#define REG_PA_SC_VIZ_QUERY 0x2293
+#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44
+#define REG_PA_SC_WINDOW_OFFSET 0x2080
+#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082
+#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081
+#define REG_PA_SU_FACE_DATA 0x0C86
+#define REG_PA_SU_POINT_SIZE 0x2280
+#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383
+#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380
+#define REG_PA_SU_SC_MODE_CNTL 0x2205
+
+#define REG_RBBM_CNTL 0x003B
+#define REG_RBBM_INT_ACK 0x03B6
+#define REG_RBBM_INT_CNTL 0x03B4
+#define REG_RBBM_INT_STATUS 0x03B5
+#define REG_RBBM_PATCH_RELEASE 0x0001
+#define REG_RBBM_PERIPHID1 0x03F9
+#define REG_RBBM_PERIPHID2 0x03FA
+#define REG_RBBM_DEBUG 0x039B
+#define REG_RBBM_PM_OVERRIDE1 0x039C
+#define REG_RBBM_PM_OVERRIDE2 0x039D
+#define REG_RBBM_READ_ERROR 0x03B3
+#define REG_RBBM_SOFT_RESET 0x003C
+#define REG_RBBM_STATUS 0x05D0
+
+#define REG_RB_COLORCONTROL 0x2202
+#define REG_RB_COLOR_DEST_MASK 0x2326
+#define REG_RB_COLOR_MASK 0x2104
+#define REG_RB_COPY_CONTROL 0x2318
+#define REG_RB_DEPTHCONTROL 0x2200
+#define REG_RB_EDRAM_INFO 0x0F02
+#define REG_RB_MODECONTROL 0x2208
+#define REG_RB_SURFACE_INFO 0x2000
+
+#define REG_SCRATCH_ADDR 0x01DD
+#define REG_SCRATCH_REG0 0x0578
+#define REG_SCRATCH_REG2 0x057A
+#define REG_SCRATCH_UMSK 0x01DC
+
+#define REG_SQ_CF_BOOLEANS 0x4900
+#define REG_SQ_CF_LOOP 0x4908
+#define REG_SQ_GPR_MANAGEMENT 0x0D00
+#define REG_SQ_INST_STORE_MANAGMENT 0x0D02
+#define REG_SQ_INT_ACK 0x0D36
+#define REG_SQ_INT_CNTL 0x0D34
+#define REG_SQ_INT_STATUS 0x0D35
+#define REG_SQ_PROGRAM_CNTL 0x2180
+#define REG_SQ_PS_PROGRAM 0x21F6
+#define REG_SQ_VS_PROGRAM 0x21F7
+#define REG_SQ_WRAPPING_0 0x2183
+#define REG_SQ_WRAPPING_1 0x2184
+
+#define REG_VGT_ENHANCE 0x2294
+#define REG_VGT_INDX_OFFSET 0x2102
+#define REG_VGT_MAX_VTX_INDX 0x2100
+#define REG_VGT_MIN_VTX_INDX 0x2101
+
+#define REG_TP0_CHICKEN 0x0E1E
+#define REG_TC_CNTL_STATUS 0x0E00
+#define REG_PA_SC_AA_CONFIG 0x2301
+#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316
+#define REG_SQ_INTERPOLATOR_CNTL 0x2182
+#define REG_RB_DEPTH_INFO 0x2002
+#define REG_COHER_DEST_BASE_0 0x2006
+#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F
+#define REG_RB_FOG_COLOR 0x2109
+#define REG_RB_STENCILREFMASK_BF 0x210C
+#define REG_PA_SC_LINE_STIPPLE 0x2283
+#define REG_SQ_PS_CONST 0x2308
+#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316
+#define REG_RB_DEPTH_CLEAR 0x231D
+#define REG_RB_SAMPLE_COUNT_CTL 0x2324
+#define REG_SQ_CONSTANT_0 0x4000
+#define REG_SQ_FETCH_0 0x4800
+
+#define REG_MH_AXI_ERROR 0xA45
+#define REG_COHER_BASE_PM4 0xA2A
+#define REG_COHER_STATUS_PM4 0xA2B
+#define REG_COHER_SIZE_PM4 0xA29
+
+#endif /* _YAMATO_REG_H */
diff --git a/drivers/video/msm/logo.c b/drivers/video/msm/logo.c
new file mode 100644
index 0000000..7272765
--- /dev/null
+++ b/drivers/video/msm/logo.c
@@ -0,0 +1,98 @@
+/* drivers/video/msm/logo.c
+ *
+ * Show Logo in RLE 565 format
+ *
+ * Copyright (C) 2008 Google Incorporated
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/unistd.h>
+#include <linux/syscalls.h>
+
+#include <linux/irq.h>
+#include <asm/system.h>
+
+#define fb_width(fb) ((fb)->var.xres)
+#define fb_height(fb) ((fb)->var.yres)
+#define fb_size(fb) ((fb)->var.xres * (fb)->var.yres * 2)
+
+static void memset16(void *_ptr, unsigned short val, unsigned count)
+{
+ unsigned short *ptr = _ptr;
+ count >>= 1;
+ while (count--)
+ *ptr++ = val;
+}
+
+/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
+int load_565rle_image(char *filename)
+{
+ struct fb_info *info;
+ int fd, err = 0;
+ unsigned count, max;
+ unsigned short *data, *bits, *ptr;
+
+ info = registered_fb[0];
+ if (!info) {
+ printk(KERN_WARNING "%s: Can not access framebuffer\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ fd = sys_open(filename, O_RDONLY, 0);
+ if (fd < 0) {
+ printk(KERN_WARNING "%s: Can not open %s\n",
+ __func__, filename);
+ return -ENOENT;
+ }
+ count = (unsigned)sys_lseek(fd, (off_t)0, 2);
+ if (count == 0) {
+ sys_close(fd);
+ err = -EIO;
+ goto err_logo_close_file;
+ }
+ sys_lseek(fd, (off_t)0, 0);
+ data = kmalloc(count, GFP_KERNEL);
+ if (!data) {
+ printk(KERN_WARNING "%s: Can not alloc data\n", __func__);
+ err = -ENOMEM;
+ goto err_logo_close_file;
+ }
+ if ((unsigned)sys_read(fd, (char *)data, count) != count) {
+ err = -EIO;
+ goto err_logo_free_data;
+ }
+
+ max = fb_width(info) * fb_height(info);
+ ptr = data;
+ bits = (unsigned short *)(info->screen_base);
+ while (count > 3) {
+ unsigned n = ptr[0];
+ if (n > max)
+ break;
+ memset16(bits, ptr[1], n << 1);
+ bits += n;
+ max -= n;
+ ptr += 2;
+ count -= 4;
+ }
+
+err_logo_free_data:
+ kfree(data);
+err_logo_close_file:
+ sys_close(fd);
+ return err;
+}
+EXPORT_SYMBOL(load_565rle_image);
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c
index c1ff271..927bbc4 100644
--- a/drivers/video/msm/mddi.c
+++ b/drivers/video/msm/mddi.c
@@ -29,6 +29,10 @@
#include <mach/msm_iomap.h>
#include <mach/irqs.h>
#include <mach/board.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/wakelock.h>
+
#include <mach/msm_fb.h>
#include "mddi_hw.h"
@@ -40,6 +44,8 @@
#define CMD_GET_CLIENT_CAP 0x0601
#define CMD_GET_CLIENT_STATUS 0x0602
+static uint32_t mddi_debug_flags;
+
union mddi_rev {
unsigned char raw[MDDI_REV_BUFFER_SIZE];
struct mddi_rev_packet hdr;
@@ -61,6 +67,8 @@
char __iomem *base;
int irq;
struct clk *clk;
+ struct clk *pclk;
+ unsigned long clk_rate;
struct msm_mddi_client_data client_data;
/* buffer for rev encap packets */
@@ -84,14 +92,21 @@
struct mddi_client_caps caps;
struct mddi_client_status status;
+ struct wake_lock idle_lock;
+ struct wake_lock link_active_idle_lock;
+
void (*power_client)(struct msm_mddi_client_data *, int);
+ /* used to save/restore pad config during suspend */
+ uint32_t pad_ctrl;
+
/* client device published to bind us to the
* appropriate mddi_client driver
*/
char client_name[20];
struct platform_device client_pdev;
+ struct resource client_vsync_res;
};
static void mddi_init_rev_encap(struct mddi_info *mddi);
@@ -103,7 +118,7 @@
{
struct mddi_info *mddi = container_of(cdata, struct mddi_info,
client_data);
-
+ wake_lock(&mddi->link_active_idle_lock);
mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
}
@@ -127,6 +142,8 @@
if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) &&
(rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) {
+ /* printk(KERN_INFO "rev: len=%04x type=%04x\n",
+ * rev->hdr.length, rev->hdr.type); */
switch (rev->hdr.type) {
case TYPE_CLIENT_CAPS:
@@ -142,6 +159,9 @@
wake_up(&mddi->int_wait);
break;
case TYPE_REGISTER_ACCESS:
+ /* printk(KERN_INFO "rev: reg %x = %x\n",
+ * rev->reg.register_address,
+ * rev->reg.register_data_list); */
ri = mddi->reg_read;
if (ri == 0) {
printk(KERN_INFO "rev: got reg %x = %x without "
@@ -203,6 +223,8 @@
rev_crc_err_count = mddi_readl(REV_CRC_ERR);
if (rev_data_count > 1)
printk(KERN_INFO "rev_data_count %d\n", rev_data_count);
+ /* printk(KERN_INFO "rev_data_count %d, INT %x\n", rev_data_count,
+ * mddi_readl(INT)); */
if (rev_crc_err_count) {
printk(KERN_INFO "rev_crc_err_count %d, INT %x\n",
@@ -222,6 +244,20 @@
if (rev_data_count == 0)
return;
+ if (mddi_debug_flags & 1) {
+ printk(KERN_INFO "INT %x, STAT %x, CURR_REV_PTR %x\n",
+ mddi_readl(INT), mddi_readl(STAT),
+ mddi_readl(CURR_REV_PTR));
+ for (i = 0; i < MDDI_REV_BUFFER_SIZE; i++) {
+ if ((i % 16) == 0)
+ printk(KERN_INFO "\n");
+ printk(KERN_INFO " %02x", rev->raw[i]);
+ }
+ printk(KERN_INFO "\n");
+ }
+
+ /* printk(KERN_INFO "rev_data_curr %d + %d\n", mddi->rev_data_curr,
+ * crev->hdr.length); */
prev_offset = mddi->rev_data_curr;
length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr);
@@ -247,12 +283,23 @@
memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem);
memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem);
mddi_handle_rev_data(mddi, &tmprev);
+ if (mddi_debug_flags & 2) {
+ memset(mddi->rev_data + prev_offset, 0xee, rem);
+ memset(mddi->rev_data, 0xee, mddi->rev_data_curr);
+ }
} else {
mddi_handle_rev_data(mddi, crev);
+ if (mddi_debug_flags & 2)
+ memset(mddi->rev_data + prev_offset, 0xee,
+ mddi->rev_data_curr - prev_offset);
}
+ /* if(mddi->rev_data_curr + MDDI_MAX_REV_PKT_SIZE >=
+ * MDDI_REV_BUFFER_SIZE) { */
if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 &&
mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) {
+ /* printk(KERN_INFO "passed buffer half full: rev_data_curr
+ * %d\n", mddi->rev_data_curr); */
mddi_writel(mddi->rev_addr, REV_PTR);
}
}
@@ -271,6 +318,9 @@
mddi_writel(active, INT);
+ /* printk(KERN_INFO "%s: isr a=%08x e=%08x s=%08x\n",
+ mddi->name, active, mddi->int_enable, status); */
+
/* ignore any interrupts we have disabled */
active &= mddi->int_enable;
@@ -290,11 +340,13 @@
if (active & MDDI_INT_LINK_ACTIVE) {
mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE);
mddi->int_enable |= MDDI_INT_IN_HIBERNATION;
+ wake_lock(&mddi->link_active_idle_lock);
}
if (active & MDDI_INT_IN_HIBERNATION) {
mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION);
mddi->int_enable |= MDDI_INT_LINK_ACTIVE;
+ wake_unlock(&mddi->link_active_idle_lock);
}
mddi_writel(mddi->int_enable, INTEN);
@@ -354,7 +406,10 @@
mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN);
mddi_writel(0x0096, DRIVE_HI);
/* 0x32 normal, 0x50 for Toshiba display */
+
+ /* XXX: should we use 0x32? */
mddi_writel(0x0050, DRIVE_LO);
+
mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */
mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV);
@@ -372,16 +427,26 @@
udelay(5);
}
+ /* XXX: NEED SUPPORT FOR 1.2 */
+
/* Recommendation from PAD hw team */
mddi_writel(0xa850f, PAD_CTL);
+#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40)
+ mddi_writel(0x00320000, PAD_IO_CTL);
+ mddi_writel(0x00220020, PAD_CAL);
+#endif
/* Need an even number for counts */
mddi_writel(0x60006, DRIVER_START_CNT);
mddi_set_auto_hibernate(&mddi->client_data, 0);
+#if 1 /* ignore listen */
mddi_writel(MDDI_CMD_DISP_IGNORE, CMD);
+#else
+ mddi_writel(MDDI_CMD_DISP_LISTEN, CMD);
+#endif
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
mddi_init_rev_encap(mddi);
@@ -392,26 +457,39 @@
{
struct mddi_info *mddi = container_of(cdata, struct mddi_info,
client_data);
+ wake_lock(&mddi->idle_lock);
/* turn off the client */
if (mddi->power_client)
mddi->power_client(&mddi->client_data, 0);
/* turn off the link */
mddi_writel(MDDI_CMD_RESET, CMD);
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
+ /* save pad ctrl and power down the drivers */
+ mddi->pad_ctrl = mddi_readl(PAD_CTL);
+ mddi_writel(0, PAD_CTL);
+ /* release rate request to not hold any high speed plls */
+ clk_set_rate(mddi->clk, 0);
/* turn off the clock */
+ if (mddi->pclk)
+ clk_disable(mddi->pclk);
clk_disable(mddi->clk);
+ wake_unlock(&mddi->idle_lock);
}
static void mddi_resume(struct msm_mddi_client_data *cdata)
{
struct mddi_info *mddi = container_of(cdata, struct mddi_info,
client_data);
+ wake_lock(&mddi->idle_lock);
+ clk_enable(mddi->clk);
+ if (mddi->pclk)
+ clk_enable(mddi->pclk);
+ clk_set_rate(mddi->clk, mddi->clk_rate);
+ mddi_writel(mddi->pad_ctrl, PAD_CTL);
mddi_set_auto_hibernate(&mddi->client_data, 0);
/* turn on the client */
if (mddi->power_client)
mddi->power_client(&mddi->client_data, 1);
- /* turn on the clock */
- clk_enable(mddi->clk);
/* set up the local registers */
mddi->rev_data_curr = 0;
mddi_init_registers(mddi);
@@ -420,6 +498,7 @@
mddi_writel(MDDI_CMD_SEND_RTD, CMD);
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
mddi_set_auto_hibernate(&mddi->client_data, 1);
+ wake_unlock(&mddi->idle_lock);
}
static int __init mddi_get_client_caps(struct mddi_info *mddi)
@@ -518,6 +597,7 @@
client_data);
struct mddi_llentry *ll;
struct mddi_register_access *ra;
+ /* unsigned s; */
mutex_lock(&mddi->reg_write_lock);
@@ -541,9 +621,19 @@
ll->next = 0;
ll->reserved = 0;
+ /* s = mddi_readl(STAT); */
+ /* printk(KERN_INFO "mddi_remote_write(%x, %x), stat = %x\n", val,
+ * reg, s); */
+
mddi_writel(mddi->reg_write_addr, PRI_PTR);
+ /* s = mddi_readl(STAT); */
+ /* printk(KERN_INFO "mddi_remote_write(%x, %x) sent, stat = %x\n",
+ * val, reg, s); */
+
mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
+ /* printk(KERN_INFO "mddi_remote_write(%x, %x) done, stat = %x\n",
+ * val, reg, s); */
mutex_unlock(&mddi->reg_write_lock);
}
@@ -579,6 +669,7 @@
ll->reserved = 0;
s = mddi_readl(STAT);
+ /* printk(KERN_INFO "mddi_remote_read(%x), stat = %x\n", reg, s); */
ri.reg = reg;
ri.status = -1;
@@ -589,6 +680,14 @@
mddi_writel(mddi->reg_read_addr, PRI_PTR);
mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
+ /* s = mddi_readl(STAT); */
+ /* printk(KERN_INFO "mddi_remote_read(%x) sent, stat = %x\n",
+ * reg, s); */
+
+ /* s = mddi_readl(STAT); */
+ /* while((s & MDDI_STAT_PRI_LINK_LIST_DONE) == 0){ */
+ /* s = mddi_readl(STAT); */
+ /* } */
/* Enable Periodic Reverse Encapsulation. */
mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
@@ -607,8 +706,14 @@
if (ri.status == 0)
break;
+ /* printk(KERN_INFO "mddi_remote_read: failed, sent
+ * MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x\n",
+ * mddi_readl(INT), mddi_readl(STAT), mddi_readl(RTD_VAL)); */
mddi_writel(MDDI_CMD_SEND_RTD, CMD);
mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
+ /* printk(KERN_INFO "mddi_remote_read: failed, sent
+ * MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x\n",
+ * mddi_readl(INT), mddi_readl(STAT), mddi_readl(RTD_VAL)); */
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
printk(KERN_INFO "mddi_remote_read: failed, sent "
"MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x "
@@ -618,6 +723,8 @@
/* Disable Periodic Reverse Encapsulation. */
mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
+ /* printk(KERN_INFO "mddi_remote_read(%x) done, stat = %x,
+ * return %x\n", reg, s, ri.result); */
mddi->reg_read = NULL;
mutex_unlock(&mddi->reg_read_lock);
return ri.result;
@@ -637,15 +744,26 @@
printk(KERN_INFO "mddi: failed to get clock\n");
return PTR_ERR(mddi->clk);
}
- ret = clk_enable(mddi->clk);
- if (ret)
- goto fail;
- ret = clk_set_rate(mddi->clk, clk_rate);
+ mddi->pclk = clk_get(&pdev->dev, "mddi_pclk");
+ if (IS_ERR(mddi->pclk))
+ mddi->pclk = NULL;
+
+ clk_enable(mddi->clk);
+ if (mddi->pclk)
+ clk_enable(mddi->pclk);
+
+ mddi->clk_rate = clk_rate;
+ ret = clk_set_rate(mddi->clk, mddi->clk_rate);
if (ret)
goto fail;
return 0;
fail:
+ if (mddi->pclk) {
+ clk_disable(mddi->pclk);
+ clk_put(mddi->pclk);
+ }
+ clk_disable(mddi->clk);
clk_put(mddi->clk);
return ret;
}
@@ -670,7 +788,7 @@
return 0;
}
-static int __init mddi_probe(struct platform_device *pdev)
+static int mddi_probe(struct platform_device *pdev)
{
struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
struct mddi_info *mddi = &mddi_info[pdev->id];
@@ -704,6 +822,10 @@
spin_lock_init(&mddi->int_lock);
init_waitqueue_head(&mddi->int_wait);
+ wake_lock_init(&mddi->idle_lock, WAKE_LOCK_IDLE, "mddi_idle_lock");
+ wake_lock_init(&mddi->link_active_idle_lock, WAKE_LOCK_IDLE,
+ "mddi_link_active_idle_lock");
+
ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate);
if (ret) {
printk(KERN_ERR "mddi: failed to setup clock!\n");
@@ -741,6 +863,8 @@
goto error_mddi_version;
}
+ printk(KERN_INFO "mddi: host core version: 0x%02x\n", mddi->version);
+
/* read the capabilities off the client */
if (!mddi_get_client_caps(mddi)) {
printk(KERN_INFO "mddi: no client found\n");
@@ -753,6 +877,9 @@
}
mddi_set_auto_hibernate(&mddi->client_data, 1);
+ pr_info("%s: got mfr %04x product %04x\n", __func__,
+ mddi->caps.Mfr_Name, mddi->caps.Product_Code);
+
if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code);
@@ -794,6 +921,15 @@
goto error_mddi_interface;
}
+ if (pdata->vsync_irq) {
+ mddi->client_vsync_res.start = pdata->vsync_irq;
+ mddi->client_vsync_res.end = pdata->vsync_irq;
+ mddi->client_vsync_res.flags = IORESOURCE_IRQ;
+ mddi->client_vsync_res.name = "vsync";
+ mddi->client_pdev.resource = &mddi->client_vsync_res;
+ mddi->client_pdev.num_resources = 1;
+ }
+
mddi->client_pdev.dev.platform_data = &mddi->client_data;
printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name);
platform_device_register(&mddi->client_pdev);
@@ -806,6 +942,8 @@
dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr);
error_rev_data:
error_clk_setup:
+ wake_lock_destroy(&mddi->idle_lock);
+ wake_lock_destroy(&mddi->link_active_idle_lock);
error_get_irq_resource:
iounmap(mddi->base);
error_ioremap:
@@ -814,6 +952,37 @@
return ret;
}
+#if 0 /* read/write mddi registers from userspace */
+module_param_named(debug, mddi_debug_flags, uint, 0644);
+
+static uint32_t selected_register;
+module_param_named(reg, selected_register, uint, 0644);
+
+static int set_reg(const char *val, struct kernel_param *kp)
+{
+ char *endp;
+ uint32_t l;
+
+ if (!val)
+ return -EINVAL;
+ l = simple_strtoul(val, &endp, 0);
+ if (endp == val || ((uint32_t)l != l))
+ return -EINVAL;
+ mddi_remote_write(kp->arg, l, selected_register);
+ return 0;
+}
+
+static int get_reg(char *buffer, struct kernel_param *kp)
+{
+ int val;
+ val = mddi_remote_read(kp->arg, selected_register);
+ return sprintf(buffer, "%x", val);
+}
+
+module_param_call(pmdh_val, set_reg, get_reg, &mddi_info[0], 0644);
+module_param_call(emdh_val, set_reg, get_reg, &mddi_info[1], 0644);
+
+#endif
static struct platform_driver mddi_driver = {
.probe = mddi_probe,
diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_dummy.c
deleted file mode 100644
index d2a091c..0000000
--- a/drivers/video/msm/mddi_client_dummy.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_dummy.c
- *
- * Support for "dummy" mddi client devices which require no
- * special initialization code.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * 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/slab.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-
-#include <mach/msm_fb.h>
-
-struct panel_info {
- struct platform_device pdev;
- struct msm_panel_data panel_data;
-};
-
-static int mddi_dummy_suspend(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_resume(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_blank(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_unblank(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_probe(struct platform_device *pdev)
-{
- struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
- struct panel_info *panel =
- kzalloc(sizeof(struct panel_info), GFP_KERNEL);
- int ret;
- if (!panel)
- return -ENOMEM;
- platform_set_drvdata(pdev, panel);
- panel->panel_data.suspend = mddi_dummy_suspend;
- panel->panel_data.resume = mddi_dummy_resume;
- panel->panel_data.blank = mddi_dummy_blank;
- panel->panel_data.unblank = mddi_dummy_unblank;
- panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
- panel->pdev.name = "msm_panel";
- panel->pdev.id = pdev->id;
- platform_device_add_resources(&panel->pdev,
- client_data->fb_resource, 1);
- panel->panel_data.fb_data = client_data->private_client_data;
- panel->pdev.dev.platform_data = &panel->panel_data;
- ret = platform_device_register(&panel->pdev);
- if (ret) {
- kfree(panel);
- return ret;
- }
- return 0;
-}
-
-static int mddi_dummy_remove(struct platform_device *pdev)
-{
- struct panel_info *panel = platform_get_drvdata(pdev);
- kfree(panel);
- return 0;
-}
-
-static struct platform_driver mddi_client_dummy = {
- .probe = mddi_dummy_probe,
- .remove = mddi_dummy_remove,
- .driver = { .name = "mddi_c_dummy" },
-};
-
-static int __init mddi_client_dummy_init(void)
-{
- platform_driver_register(&mddi_client_dummy);
- return 0;
-}
-
-module_init(mddi_client_dummy_init);
-
diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c
deleted file mode 100644
index f239f4a..0000000
--- a/drivers/video/msm/mddi_client_nt35399.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_nt35399.c
- *
- * Support for Novatek NT35399 MDDI client of Sapphire
- *
- * Copyright (C) 2008 HTC Incorporated
- * Author: Solomon Chiu (solomon_chiu@htc.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.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <mach/msm_fb.h>
-
-static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait);
-
-struct panel_info {
- struct msm_mddi_client_data *client_data;
- struct platform_device pdev;
- struct msm_panel_data panel_data;
- struct msmfb_callback *fb_callback;
- struct work_struct panel_work;
- struct workqueue_struct *fb_wq;
- int nt35399_got_int;
-};
-
-static void
-nt35399_request_vsync(struct msm_panel_data *panel_data,
- struct msmfb_callback *callback)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- panel->fb_callback = callback;
- if (panel->nt35399_got_int) {
- panel->nt35399_got_int = 0;
- client_data->activate_link(client_data); /* clears interrupt */
- }
-}
-
-static void nt35399_wait_vsync(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- if (panel->nt35399_got_int) {
- panel->nt35399_got_int = 0;
- client_data->activate_link(client_data); /* clears interrupt */
- }
-
- if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int,
- HZ/2) == 0)
- printk(KERN_ERR "timeout waiting for VSYNC\n");
-
- panel->nt35399_got_int = 0;
- /* interrupt clears when screen dma starts */
-}
-
-static int nt35399_suspend(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- ret = bridge_data->uninit(bridge_data, client_data);
- if (ret) {
- printk(KERN_INFO "mddi nt35399 client: non zero return from "
- "uninit\n");
- return ret;
- }
- client_data->suspend(client_data);
- return 0;
-}
-
-static int nt35399_resume(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- client_data->resume(client_data);
- ret = bridge_data->init(bridge_data, client_data);
- if (ret)
- return ret;
- return 0;
-}
-
-static int nt35399_blank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->blank(bridge_data, client_data);
-}
-
-static int nt35399_unblank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->unblank(bridge_data, client_data);
-}
-
-irqreturn_t nt35399_vsync_interrupt(int irq, void *data)
-{
- struct panel_info *panel = data;
-
- panel->nt35399_got_int = 1;
-
- if (panel->fb_callback) {
- panel->fb_callback->func(panel->fb_callback);
- panel->fb_callback = NULL;
- }
-
- wake_up(&nt35399_vsync_wait);
-
- return IRQ_HANDLED;
-}
-
-static int setup_vsync(struct panel_info *panel, int init)
-{
- int ret;
- int gpio = 97;
- unsigned int irq;
-
- if (!init) {
- ret = 0;
- goto uninit;
- }
- ret = gpio_request(gpio, "vsync");
- if (ret)
- goto err_request_gpio_failed;
-
- ret = gpio_direction_input(gpio);
- if (ret)
- goto err_gpio_direction_input_failed;
-
- ret = irq = gpio_to_irq(gpio);
- if (ret < 0)
- goto err_get_irq_num_failed;
-
- ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING,
- "vsync", panel);
- if (ret)
- goto err_request_irq_failed;
-
- printk(KERN_INFO "vsync on gpio %d now %d\n",
- gpio, gpio_get_value(gpio));
- return 0;
-
-uninit:
- free_irq(gpio_to_irq(gpio), panel->client_data);
-err_request_irq_failed:
-err_get_irq_num_failed:
-err_gpio_direction_input_failed:
- gpio_free(gpio);
-err_request_gpio_failed:
- return ret;
-}
-
-static int mddi_nt35399_probe(struct platform_device *pdev)
-{
- struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- int ret;
-
- struct panel_info *panel = kzalloc(sizeof(struct panel_info),
- GFP_KERNEL);
-
- printk(KERN_DEBUG "%s: enter.\n", __func__);
-
- if (!panel)
- return -ENOMEM;
- platform_set_drvdata(pdev, panel);
-
- ret = setup_vsync(panel, 1);
- if (ret) {
- dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n");
- return ret;
- }
-
- panel->client_data = client_data;
- panel->panel_data.suspend = nt35399_suspend;
- panel->panel_data.resume = nt35399_resume;
- panel->panel_data.wait_vsync = nt35399_wait_vsync;
- panel->panel_data.request_vsync = nt35399_request_vsync;
- panel->panel_data.blank = nt35399_blank;
- panel->panel_data.unblank = nt35399_unblank;
- panel->panel_data.fb_data = &bridge_data->fb_data;
- panel->panel_data.caps = 0;
-
- panel->pdev.name = "msm_panel";
- panel->pdev.id = pdev->id;
- panel->pdev.resource = client_data->fb_resource;
- panel->pdev.num_resources = 1;
- panel->pdev.dev.platform_data = &panel->panel_data;
-
- if (bridge_data->init)
- bridge_data->init(bridge_data, client_data);
-
- platform_device_register(&panel->pdev);
-
- return 0;
-}
-
-static int mddi_nt35399_remove(struct platform_device *pdev)
-{
- struct panel_info *panel = platform_get_drvdata(pdev);
-
- setup_vsync(panel, 0);
- kfree(panel);
- return 0;
-}
-
-static struct platform_driver mddi_client_0bda_8a47 = {
- .probe = mddi_nt35399_probe,
- .remove = mddi_nt35399_remove,
- .driver = { .name = "mddi_c_0bda_8a47" },
-};
-
-static int __init mddi_client_nt35399_init(void)
-{
- return platform_driver_register(&mddi_client_0bda_8a47);
-}
-
-module_init(mddi_client_nt35399_init);
-
diff --git a/drivers/video/msm/mddi_client_simple.c b/drivers/video/msm/mddi_client_simple.c
new file mode 100644
index 0000000..1613fca
--- /dev/null
+++ b/drivers/video/msm/mddi_client_simple.c
@@ -0,0 +1,236 @@
+/* drivers/video/msm_fb/mddi_client_simple.c
+ *
+ * Support for simple mddi client devices which require no special
+ * initialization code except for what may be provided in the board file.
+ * If the clients do not provide board specific code, this driver's
+ * panel operations are no-ops.
+ *
+ * Copyright (C) 2007-2010, Google Incorporated
+ *
+ * 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/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <mach/msm_fb.h>
+
+struct panel_info {
+ struct platform_device pdev;
+ struct msm_mddi_client_data *client_data;
+ struct msm_panel_data panel_data;
+ struct msmfb_callback *fb_callback;
+ wait_queue_head_t vsync_wait;
+ int got_vsync;
+ int irq;
+};
+
+#define to_panel_info(pd) container_of((pd), struct panel_info, panel_data)
+
+static void mddi_simple_request_vsync(struct msm_panel_data *panel_data,
+ struct msmfb_callback *callback)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+
+ panel->fb_callback = callback;
+ if (panel->got_vsync) {
+ panel->got_vsync = 0;
+ client_data->activate_link(client_data); /* clears interrupt */
+ }
+}
+
+static void mddi_simple_wait_vsync(struct msm_panel_data *panel_data)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+ int ret;
+
+ if (panel->got_vsync) {
+ panel->got_vsync = 0;
+ client_data->activate_link(client_data); /* clears interrupt */
+ }
+
+ ret = wait_event_timeout(panel->vsync_wait, panel->got_vsync, HZ/2);
+ if (!ret && !panel->got_vsync)
+ pr_err("mddi_client_simple: timeout waiting for vsync\n");
+
+ panel->got_vsync = 0;
+ /* interrupt clears when screen dma starts */
+}
+
+
+static int mddi_simple_suspend(struct msm_panel_data *panel_data)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+ struct msm_mddi_bridge_platform_data *bridge_data =
+ client_data->private_client_data;
+ int ret;
+
+ if (!bridge_data->uninit)
+ return 0;
+
+ ret = bridge_data->uninit(bridge_data, client_data);
+ if (ret) {
+ pr_info("%s: non zero return from uninit\n", __func__);
+ return ret;
+ }
+ client_data->suspend(client_data);
+ return 0;
+}
+
+static int mddi_simple_resume(struct msm_panel_data *panel_data)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+ struct msm_mddi_bridge_platform_data *bridge_data =
+ client_data->private_client_data;
+
+ if (!bridge_data->init)
+ return 0;
+
+ client_data->resume(client_data);
+ return bridge_data->init(bridge_data, client_data);
+}
+
+static int mddi_simple_blank(struct msm_panel_data *panel_data)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+ struct msm_mddi_bridge_platform_data *bridge_data =
+ client_data->private_client_data;
+
+ if (!bridge_data->blank)
+ return 0;
+ return bridge_data->blank(bridge_data, client_data);
+}
+
+static int mddi_simple_unblank(struct msm_panel_data *panel_data)
+{
+ struct panel_info *panel = to_panel_info(panel_data);
+ struct msm_mddi_client_data *client_data = panel->client_data;
+ struct msm_mddi_bridge_platform_data *bridge_data =
+ client_data->private_client_data;
+
+ if (!bridge_data->unblank)
+ return 0;
+ return bridge_data->unblank(bridge_data, client_data);
+}
+
+static irqreturn_t handle_vsync_irq(int irq, void *data)
+{
+ struct panel_info *panel = data;
+
+ panel->got_vsync = 1;
+ if (panel->fb_callback) {
+ panel->fb_callback->func(panel->fb_callback);
+ panel->fb_callback = NULL;
+ }
+
+ wake_up(&panel->vsync_wait);
+ return IRQ_HANDLED;
+}
+
+static int mddi_simple_probe(struct platform_device *pdev)
+{
+ struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
+ struct msm_mddi_bridge_platform_data *bridge_data =
+ client_data->private_client_data;
+ struct panel_info *panel;
+ int ret;
+
+ pr_debug("%s()\n", __func__);
+
+ panel = kzalloc(sizeof(struct panel_info), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, panel);
+
+ init_waitqueue_head(&panel->vsync_wait);
+
+ panel->irq = platform_get_irq_byname(pdev, "vsync");
+ if (panel->irq >= 0) {
+ ret = request_irq(panel->irq, handle_vsync_irq,
+ IRQF_TRIGGER_RISING, "mddi_c_simple_vsync",
+ panel);
+ if (ret) {
+ pr_err("%s: request vsync irq %d failed (%d)\n",
+ __func__, panel->irq, ret);
+ goto err_req_irq;
+ }
+
+ panel->panel_data.wait_vsync = mddi_simple_wait_vsync;
+ panel->panel_data.request_vsync = mddi_simple_request_vsync;
+ }
+
+ panel->client_data = client_data;
+ panel->panel_data.suspend = mddi_simple_suspend;
+ panel->panel_data.resume = mddi_simple_resume;
+ panel->panel_data.blank = mddi_simple_blank;
+ panel->panel_data.unblank = mddi_simple_unblank;
+ panel->panel_data.caps = bridge_data->panel_caps;
+ panel->panel_data.fb_data = &bridge_data->fb_data;
+
+ panel->pdev.name = "msm_panel";
+ panel->pdev.id = pdev->id;
+ platform_device_add_resources(&panel->pdev,
+ client_data->fb_resource, 1);
+ panel->pdev.dev.platform_data = &panel->panel_data;
+
+ if (bridge_data->init)
+ bridge_data->init(bridge_data, client_data);
+
+ ret = platform_device_register(&panel->pdev);
+ if (ret) {
+ pr_err("%s: Can't register platform device\n", __func__);
+ goto err_plat_dev_reg;
+ }
+
+ return 0;
+
+err_plat_dev_reg:
+ if (panel->irq >= 0)
+ free_irq(panel->irq, panel);
+err_req_irq:
+ platform_set_drvdata(pdev, NULL);
+ kfree(panel);
+ return ret;
+}
+
+static int mddi_simple_remove(struct platform_device *pdev)
+{
+ struct panel_info *panel = platform_get_drvdata(pdev);
+ kfree(panel);
+ return 0;
+}
+
+static struct platform_driver mddi_client_simple = {
+ .probe = mddi_simple_probe,
+ .remove = mddi_simple_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mddi_c_simple"
+ },
+};
+
+static int __init mddi_client_simple_init(void)
+{
+ platform_driver_register(&mddi_client_simple);
+ return 0;
+}
+
+module_init(mddi_client_simple_init);
diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c
index f9bc932..21e0ac7 100644
--- a/drivers/video/msm/mddi_client_toshiba.c
+++ b/drivers/video/msm/mddi_client_toshiba.c
@@ -60,6 +60,7 @@
struct msm_panel_data panel_data;
struct msmfb_callback *toshiba_callback;
int toshiba_got_int;
+ int irq;
};
@@ -175,47 +176,6 @@
return IRQ_HANDLED;
}
-static int setup_vsync(struct panel_info *panel,
- int init)
-{
- int ret;
- int gpio = 97;
- unsigned int irq;
-
- if (!init) {
- ret = 0;
- goto uninit;
- }
- ret = gpio_request(gpio, "vsync");
- if (ret)
- goto err_request_gpio_failed;
-
- ret = gpio_direction_input(gpio);
- if (ret)
- goto err_gpio_direction_input_failed;
-
- ret = irq = gpio_to_irq(gpio);
- if (ret < 0)
- goto err_get_irq_num_failed;
-
- ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING,
- "vsync", panel);
- if (ret)
- goto err_request_irq_failed;
- printk(KERN_INFO "vsync on gpio %d now %d\n",
- gpio, gpio_get_value(gpio));
- return 0;
-
-uninit:
- free_irq(gpio_to_irq(gpio), panel);
-err_request_irq_failed:
-err_get_irq_num_failed:
-err_gpio_direction_input_failed:
- gpio_free(gpio);
-err_request_gpio_failed:
- return ret;
-}
-
static int mddi_toshiba_probe(struct platform_device *pdev)
{
int ret;
@@ -232,10 +192,16 @@
client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL);
client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK);
- ret = setup_vsync(panel, 1);
+ ret = platform_get_irq_byname(pdev, "vsync");
+ if (ret < 0)
+ goto err_plat_get_irq;
+
+ panel->irq = ret;
+ ret = request_irq(panel->irq, toshiba_vsync_interrupt,
+ IRQF_TRIGGER_RISING, "vsync", panel);
if (ret) {
dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
- return ret;
+ goto err_req_irq;
}
panel->client_data = client_data;
@@ -258,13 +224,19 @@
platform_device_register(&panel->pdev);
return 0;
+
+err_req_irq:
+err_plat_get_irq:
+ kfree(panel);
+ return ret;
}
static int mddi_toshiba_remove(struct platform_device *pdev)
{
struct panel_info *panel = platform_get_drvdata(pdev);
- setup_vsync(panel, 0);
+ platform_set_drvdata(pdev, NULL);
+ free_irq(panel->irq, panel);
kfree(panel);
return 0;
}
diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h
index 45cc01f..47bb449 100644
--- a/drivers/video/msm/mddi_hw.h
+++ b/drivers/video/msm/mddi_hw.h
@@ -53,6 +53,9 @@
#define MDDI_MF_CNT 0x0084
#define MDDI_CURR_REV_PTR 0x0088
#define MDDI_CORE_VER 0x008c
+#define MDDI_FIFO_ALLOC 0x0090
+#define MDDI_PAD_IO_CTL 0x00a0
+#define MDDI_PAD_CAL 0x00a4
#define MDDI_INT_PRI_PTR_READ 0x0001
#define MDDI_INT_SEC_PTR_READ 0x0002
@@ -125,8 +128,14 @@
/* MDP sends 256 pixel packets, so lower value hibernates more without
* significantly increasing latency of waiting for next subframe */
#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00
+
+#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40)
+#define MDDI_HOST_TA2_LEN 0x001a
+#define MDDI_HOST_REV_RATE_DIV 0x0004
+#else
#define MDDI_HOST_TA2_LEN 0x000c
#define MDDI_HOST_REV_RATE_DIV 0x0002
+#endif
struct __attribute__((packed)) mddi_rev_packet {
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 19c01c6..9f32396a 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -22,6 +22,7 @@
#include <linux/wait.h>
#include <linux/clk.h>
#include <linux/file.h>
+#include <linux/android_pmem.h>
#include <linux/major.h>
#include <linux/slab.h>
@@ -30,50 +31,52 @@
#include <linux/platform_device.h>
#include "mdp_hw.h"
+#include "mdp_ppp.h"
struct class *mdp_class;
#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
-static uint16_t mdp_default_ccs[] = {
- 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000,
- 0x010, 0x080, 0x080
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue);
-static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
-static struct msmfb_callback *dma_callback;
-static struct clk *clk;
static unsigned int mdp_irq_mask;
-static DEFINE_SPINLOCK(mdp_lock);
-DEFINE_MUTEX(mdp_mutex);
+static struct mdp_info *the_mdp;
-static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
+static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
{
- unsigned long irq_flags;
- int ret = 0;
-
BUG_ON(!mask);
- spin_lock_irqsave(&mdp_lock, irq_flags);
/* if the mask bits are already set return an error, this interrupt
* is already enabled */
if (mdp_irq_mask & mask) {
- printk(KERN_ERR "mdp irq already on already on %x %x\n",
- mdp_irq_mask, mask);
- ret = -1;
+ pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
+ return -1;
}
/* if the mdp irq is not already enabled enable it */
if (!mdp_irq_mask) {
- if (clk)
- clk_enable(clk);
+ clk_set_rate(mdp->ebi1_clk, 128000000);
+ clk_enable(mdp->clk);
+ if (mdp->pclk)
+ clk_enable(mdp->pclk);
enable_irq(mdp->irq);
}
+ /* clear out any previous irqs for the requested mask*/
+ mdp_writel(mdp, mask, MDP_INTR_CLEAR);
+
/* update the irq mask to reflect the fact that the interrupt is
* enabled */
mdp_irq_mask |= mask;
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE);
+ return 0;
+}
+
+static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&mdp->lock, flags);
+ ret = locked_enable_mdp_irq(mdp, mask);
+ spin_unlock_irqrestore(&mdp->lock, flags);
return ret;
}
@@ -88,23 +91,28 @@
/* update the irq mask to reflect the fact that the interrupt is
* disabled */
mdp_irq_mask &= ~(mask);
+ mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE);
+
/* if no one is waiting on the interrupt, disable it */
if (!mdp_irq_mask) {
- disable_irq(mdp->irq);
- if (clk)
- clk_disable(clk);
+ disable_irq_nosync(mdp->irq);
+ if (mdp->pclk)
+ clk_disable(mdp->pclk);
+ if (mdp->clk)
+ clk_disable(mdp->clk);
+ clk_set_rate(mdp->ebi1_clk, 0);
}
return 0;
}
-static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
+int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
{
unsigned long irq_flags;
int ret;
- spin_lock_irqsave(&mdp_lock, irq_flags);
+ spin_lock_irqsave(&mdp->lock, irq_flags);
ret = locked_disable_mdp_irq(mdp, mask);
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ spin_unlock_irqrestore(&mdp->lock, irq_flags);
return ret;
}
@@ -113,68 +121,109 @@
uint32_t status;
unsigned long irq_flags;
struct mdp_info *mdp = data;
+ int i;
- spin_lock_irqsave(&mdp_lock, irq_flags);
+ spin_lock_irqsave(&mdp->lock, irq_flags);
status = mdp_readl(mdp, MDP_INTR_STATUS);
mdp_writel(mdp, status, MDP_INTR_CLEAR);
+// pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status,
+// mdp_irq_mask);
status &= mdp_irq_mask;
- if (status & DL0_DMA2_TERM_DONE) {
- if (dma_callback) {
- dma_callback->func(dma_callback);
- dma_callback = NULL;
+
+ for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) {
+ struct mdp_out_interface *out_if = &mdp->out_if[i];
+ if (status & out_if->dma_mask) {
+ if (out_if->dma_cb) {
+ out_if->dma_cb->func(out_if->dma_cb);
+ out_if->dma_cb = NULL;
+ }
+ wake_up(&out_if->dma_waitqueue);
}
- wake_up(&mdp_dma2_waitqueue);
+ if (status & out_if->irq_mask) {
+ out_if->irq_cb->func(out_if->irq_cb);
+ out_if->irq_cb = NULL;
+ }
}
- if (status & DL0_ROI_DONE)
- wake_up(&mdp_ppp_waitqueue);
+ mdp_ppp_handle_isr(mdp, status);
if (status)
locked_disable_mdp_irq(mdp, status);
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ spin_unlock_irqrestore(&mdp->lock, irq_flags);
return IRQ_HANDLED;
}
-static uint32_t mdp_check_mask(uint32_t mask)
+static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask)
{
uint32_t ret;
unsigned long irq_flags;
- spin_lock_irqsave(&mdp_lock, irq_flags);
+ spin_lock_irqsave(&mdp->lock, irq_flags);
ret = mdp_irq_mask & mask;
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ spin_unlock_irqrestore(&mdp->lock, irq_flags);
return ret;
}
-static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
+int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
{
int ret = 0;
unsigned long irq_flags;
- wait_event_timeout(*wq, !mdp_check_mask(mask), HZ);
+// pr_info("%s: WAITING for 0x%x\n", __func__, mask);
+ wait_event_timeout(*wq, !mdp_check_mask(mdp, mask),
+ msecs_to_jiffies(1000));
- spin_lock_irqsave(&mdp_lock, irq_flags);
+ spin_lock_irqsave(&mdp->lock, irq_flags);
if (mdp_irq_mask & mask) {
+ pr_warning("%s: timeout waiting for mdp to complete 0x%x\n",
+ __func__, mask);
+ pr_info("GLBL_CLK_ENA: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x0000));
+ pr_info("GLBL_CLK_STATE: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x0004));
+ pr_info("GLBL_SLEEP_EN: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x001C));
+ pr_info("GLBL_CLK_ENA_2: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x0220));
+ pr_info("GLBL_CLK_STATE_2: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x0224));
+ pr_info("GLBL_CLK_SLEEP_EN_2: %08X\n",
+ readl(MSM_CLK_CTL_BASE + 0x023C));
locked_disable_mdp_irq(mdp, mask);
- printk(KERN_WARNING "timeout waiting for mdp to complete %x\n",
- mask);
ret = -ETIMEDOUT;
+ } else {
+// pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask);
}
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ spin_unlock_irqrestore(&mdp->lock, irq_flags);
return ret;
}
-void mdp_dma_wait(struct mdp_device *mdp_dev)
+static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface)
{
#define MDP_MAX_TIMEOUTS 20
static int timeout_count;
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ unsigned int mask = 0;
+ wait_queue_head_t *wq;
- if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT)
+ switch (interface) {
+ case MSM_MDDI_PMDH_INTERFACE:
+ case MSM_MDDI_EMDH_INTERFACE:
+ case MSM_LCDC_INTERFACE:
+ BUG_ON(!mdp->out_if[interface].registered);
+ mask = mdp->out_if[interface].dma_mask;
+ wq = &mdp->out_if[interface].dma_waitqueue;
+ break;
+ default:
+ pr_err("%s: Unknown interface %d\n", __func__, interface);
+ BUG();
+ }
+
+ if (mdp_wait(mdp, mask, wq) == -ETIMEDOUT)
timeout_count++;
else
timeout_count = 0;
@@ -186,180 +235,106 @@
}
}
-static int mdp_ppp_wait(struct mdp_info *mdp)
-{
- return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
-}
-
-void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride,
- uint32_t width, uint32_t height, uint32_t x, uint32_t y,
- struct msmfb_callback *callback)
-{
- uint32_t dma2_cfg;
- uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
-
- if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) {
- printk(KERN_ERR "mdp_dma_to_mddi: busy\n");
- return;
- }
-
- dma_callback = callback;
-
- dma2_cfg = DMA_PACK_TIGHT |
- DMA_PACK_ALIGN_LSB |
- DMA_PACK_PATTERN_RGB |
- DMA_OUT_SEL_AHB |
- DMA_IBUF_NONCONTIGUOUS;
-
- dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
-
- dma2_cfg |= DMA_OUT_SEL_MDDI;
-
- dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
-
- dma2_cfg |= DMA_DITHER_EN;
-
- /* setup size, address, and stride */
- mdp_writel(mdp, (height << 16) | (width),
- MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
- mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
- mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
-
- /* 666 18BPP */
- dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
-
- /* set y & x offset and MDDI transaction parameters */
- mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
- mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
- mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
- MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
-
- mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
-
- /* start DMA2 */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
-}
-
-void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
+static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x, uint32_t y,
struct msmfb_callback *callback, int interface)
{
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ struct mdp_out_interface *out_if;
+ unsigned long flags;
- if (interface == MSM_MDDI_PMDH_INTERFACE) {
- mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y,
- callback);
+ if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES ||
+ !mdp->out_if[interface].registered) {
+ pr_err("%s: Unknown interface: %d\n", __func__, interface);
+ BUG();
}
+ out_if = &mdp->out_if[interface];
+
+ spin_lock_irqsave(&mdp->lock, flags);
+ if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) {
+ pr_err("%s: busy\n", __func__);
+ goto done;
+ }
+
+ out_if->dma_cb = callback;
+ out_if->dma_start(out_if->priv, addr, stride, width, height, x, y);
+done:
+ spin_unlock_irqrestore(&mdp->lock, flags);
}
-int get_img(struct mdp_img *img, struct fb_info *info,
- unsigned long *start, unsigned long *len,
- struct file **filep)
+void mdp_configure_dma_format(struct mdp_device *mdp_dev)
{
- int put_needed, ret = 0;
- struct file *file;
- unsigned long vstart;
+ struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ uint32_t dma_cfg;
- file = fget_light(img->memory_id, &put_needed);
- if (file == NULL)
- return -1;
+ if (!mdp->dma_format_dirty)
+ return;
+ dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG);
+ dma_cfg &= ~DMA_IBUF_FORMAT_MASK;
+ dma_cfg &= ~DMA_PACK_PATTERN_MASK;
+ dma_cfg |= (mdp->dma_format | mdp->dma_pack_pattern);
+ mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG);
+ mdp->dma_format_dirty = false;
- if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
- *start = info->fix.smem_start;
- *len = info->fix.smem_len;
- } else
- ret = -1;
- fput_light(file, put_needed);
-
- return ret;
+ return;
}
-void put_img(struct file *src_file, struct file *dst_file)
+int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp)
{
+ switch (bpp) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp)
+{
+ struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ uint32_t format, pack_pattern;
+
+ switch (bpp) {
+ case 16:
+ format = DMA_IBUF_FORMAT_RGB565;
+ pack_pattern = DMA_PACK_PATTERN_RGB;
+ break;
+#ifdef CONFIG_MSM_MDP22
+ case 24:
+ case 32:
+ format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888;
+ break;
+#else
+ case 24:
+ format = DMA_IBUF_FORMAT_RGB888;
+ pack_pattern = DMA_PACK_PATTERN_BGR;
+ break;
+ case 32:
+ format = DMA_IBUF_FORMAT_XRGB8888;
+ pack_pattern = DMA_PACK_PATTERN_BGR;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+ if (format != mdp->dma_format ||
+ pack_pattern != mdp->dma_pack_pattern) {
+ mdp->dma_format = format;
+ mdp->dma_pack_pattern = pack_pattern;
+ mdp->dma_format_dirty = true;
+ }
+
+ return 0;
}
int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
struct mdp_blit_req *req)
{
- int ret;
- unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
- struct file *src_file = 0, *dst_file = 0;
-
- /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
- if (unlikely(req->src_rect.h == 0 ||
- req->src_rect.w == 0)) {
- printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
- return -EINVAL;
- }
- if (unlikely(req->dst_rect.h == 0 ||
- req->dst_rect.w == 0))
- return -EINVAL;
-
- /* do this first so that if this fails, the caller can always
- * safely call put_img */
- if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
- printk(KERN_ERR "mpd_ppp: could not retrieve src image from "
- "memory\n");
- return -EINVAL;
- }
-
- if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
- printk(KERN_ERR "mpd_ppp: could not retrieve dst image from "
- "memory\n");
- return -EINVAL;
- }
- mutex_lock(&mdp_mutex);
-
- /* transp_masking unimplemented */
- req->transp_mask = MDP_TRANSP_NOP;
- if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
- req->alpha != MDP_ALPHA_NOP ||
- HAS_ALPHA(req->src.format)) &&
- (req->flags & MDP_ROT_90 &&
- req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
- int i;
- unsigned int tiles = req->dst_rect.h / 16;
- unsigned int remainder = req->dst_rect.h % 16;
- req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
- req->dst_rect.h = 16;
- for (i = 0; i < tiles; i++) {
- enable_mdp_irq(mdp, DL0_ROI_DONE);
- ret = mdp_ppp_blit(mdp, req, src_file, src_start,
- src_len, dst_file, dst_start,
- dst_len);
- if (ret)
- goto err_bad_blit;
- ret = mdp_ppp_wait(mdp);
- if (ret)
- goto err_wait_failed;
- req->dst_rect.y += 16;
- req->src_rect.x += req->src_rect.w;
- }
- if (!remainder)
- goto end;
- req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
- req->dst_rect.h = remainder;
- }
- enable_mdp_irq(mdp, DL0_ROI_DONE);
- ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file,
- dst_start,
- dst_len);
- if (ret)
- goto err_bad_blit;
- ret = mdp_ppp_wait(mdp);
- if (ret)
- goto err_wait_failed;
-end:
- put_img(src_file, dst_file);
- mutex_unlock(&mdp_mutex);
- return 0;
-err_bad_blit:
- disable_mdp_irq(mdp, DL0_ROI_DONE);
-err_wait_failed:
- put_img(src_file, dst_file);
- mutex_unlock(&mdp_mutex);
- return ret;
+ return mdp_ppp_blit(mdp, fb, req);
}
void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id)
@@ -370,6 +345,78 @@
mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43);
}
+/* used by output interface drivers like mddi and lcdc */
+int mdp_out_if_register(struct mdp_device *mdp_dev, int interface,
+ void *private_data, uint32_t dma_mask,
+ mdp_dma_start_func_t dma_start)
+{
+ struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ unsigned long flags;
+ int ret = 0;
+
+ if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) {
+ pr_err("%s: invalid interface (%d)\n", __func__, interface);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mdp->lock, flags);
+
+ if (mdp->out_if[interface].registered) {
+ pr_err("%s: interface (%d) already registered\n", __func__,
+ interface);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ init_waitqueue_head(&mdp->out_if[interface].dma_waitqueue);
+ mdp->out_if[interface].registered = 1;
+ mdp->out_if[interface].priv = private_data;
+ mdp->out_if[interface].dma_mask = dma_mask;
+ mdp->out_if[interface].dma_start = dma_start;
+ mdp->out_if[interface].dma_cb = NULL;
+
+done:
+ spin_unlock_irqrestore(&mdp->lock, flags);
+ return ret;
+}
+
+int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
+ uint32_t mask, struct msmfb_callback *cb)
+{
+ struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+ unsigned long flags;
+ int ret = 0;
+
+ if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) {
+ pr_err("%s: invalid interface (%d)\n", __func__, interface);
+ BUG();
+ } else if (!mdp->out_if[interface].registered) {
+ pr_err("%s: interface (%d) not registered\n", __func__,
+ interface);
+ BUG();
+ }
+
+ spin_lock_irqsave(&mdp->lock, flags);
+
+ if (mask) {
+ ret = locked_enable_mdp_irq(mdp, mask);
+ if (ret) {
+ pr_err("%s: busy\n", __func__);
+ goto done;
+ }
+ mdp->out_if[interface].irq_mask = mask;
+ mdp->out_if[interface].irq_cb = cb;
+ } else {
+ locked_disable_mdp_irq(mdp, mask);
+ mdp->out_if[interface].irq_mask = 0;
+ mdp->out_if[interface].irq_cb = NULL;
+ }
+
+done:
+ spin_unlock_irqrestore(&mdp->lock, flags);
+ return ret;
+}
+
int register_mdp_client(struct class_interface *cint)
{
if (!mdp_class) {
@@ -380,14 +427,10 @@
return class_interface_register(cint);
}
-#include "mdp_csc_table.h"
-#include "mdp_scale_tables.h"
-
int mdp_probe(struct platform_device *pdev)
{
struct resource *resource;
int ret;
- int n;
struct mdp_info *mdp;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -400,6 +443,8 @@
if (!mdp)
return -ENOMEM;
+ spin_lock_init(&mdp->lock);
+
mdp->irq = platform_get_irq(pdev, 0);
if (mdp->irq < 0) {
pr_err("mdp: can not get mdp irq\n");
@@ -419,70 +464,45 @@
mdp->mdp_dev.dma_wait = mdp_dma_wait;
mdp->mdp_dev.blit = mdp_blit;
mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
+ mdp->mdp_dev.set_output_format = mdp_set_output_format;
+ mdp->mdp_dev.check_output_format = mdp_check_output_format;
- clk = clk_get(&pdev->dev, "mdp_clk");
- if (IS_ERR(clk)) {
+ mdp->enable_irq = enable_mdp_irq;
+ mdp->disable_irq = disable_mdp_irq;
+
+ mdp->clk = clk_get(&pdev->dev, "mdp_clk");
+ if (IS_ERR(mdp->clk)) {
printk(KERN_INFO "mdp: failed to get mdp clk");
- return PTR_ERR(clk);
+ ret = PTR_ERR(mdp->clk);
+ goto error_get_mdp_clk;
}
+ mdp->pclk = clk_get(&pdev->dev, "mdp_pclk");
+ if (IS_ERR(mdp->pclk))
+ mdp->pclk = NULL;
+
+ mdp->ebi1_clk = clk_get(NULL, "ebi1_clk");
+ if (IS_ERR(mdp->ebi1_clk)) {
+ pr_err("mdp: failed to get ebi1 clk\n");
+ ret = PTR_ERR(mdp->ebi1_clk);
+ goto error_get_ebi1_clk;
+ }
+
+
ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp);
if (ret)
goto error_request_irq;
disable_irq(mdp->irq);
- mdp_irq_mask = 0;
- /* debug interface write access */
- mdp_writel(mdp, 1, 0x60);
-
- mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE);
- mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
-
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
-
- for (n = 0; n < ARRAY_SIZE(csc_table); n++)
- mdp_writel(mdp, csc_table[n].val, csc_table[n].reg);
-
- /* clear up unused fg/main registers */
- /* comp.plane 2&3 ystride */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
-
- /* unpacked pattern */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
-
- /* comp.plane 2 & 3 */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
-
- /* clear unused bg registers */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
-
- for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++)
- mdp_writel(mdp, mdp_upscale_table[n].val,
- mdp_upscale_table[n].reg);
-
- for (n = 0; n < 9; n++)
- mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n);
- mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0);
- mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0);
- mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0);
+ clk_enable(mdp->clk);
+ if (mdp->pclk)
+ clk_enable(mdp->pclk);
+ mdp_hw_init(mdp);
/* register mdp device */
mdp->mdp_dev.dev.parent = &pdev->dev;
mdp->mdp_dev.dev.class = mdp_class;
+ dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id);
/* if you can remove the platform device you'd have to implement
* this:
@@ -491,14 +511,28 @@
ret = device_register(&mdp->mdp_dev.dev);
if (ret)
goto error_device_register;
+
+ the_mdp = mdp;
+
+ pr_info("%s: initialized\n", __func__);
+
return 0;
error_device_register:
+ if (mdp->pclk)
+ clk_disable(mdp->pclk);
+ clk_disable(mdp->clk);
free_irq(mdp->irq, mdp);
error_request_irq:
+ clk_put(mdp->ebi1_clk);
+error_get_ebi1_clk:
+ if (mdp->pclk)
+ clk_put(mdp->pclk);
+ clk_put(mdp->clk);
+error_get_mdp_clk:
iounmap(mdp->base);
-error_get_irq:
error_ioremap:
+error_get_irq:
kfree(mdp);
return ret;
}
@@ -508,6 +542,17 @@
.driver = {.name = "msm_mdp"},
};
+static int __init mdp_lateinit(void)
+{
+ struct mdp_info *mdp = the_mdp;
+ if (the_mdp) {
+ if (mdp->pclk)
+ clk_disable(mdp->pclk);
+ clk_disable(mdp->clk);
+ }
+ return 0;
+}
+
static int __init mdp_init(void)
{
mdp_class = class_create(THIS_MODULE, "msm_mdp");
@@ -519,3 +564,4 @@
}
subsys_initcall(mdp_init);
+late_initcall(mdp_lateinit);
diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h
index d1cde30..a0f72c0 100644
--- a/drivers/video/msm/mdp_csc_table.h
+++ b/drivers/video/msm/mdp_csc_table.h
@@ -1,4 +1,4 @@
-/* drivers/video/msm_fb/mdp_csc_table.h
+/* drivers/video/msm/mdp_csc_table.h
*
* Copyright (C) 2007 QUALCOMM Incorporated
* Copyright (C) 2007 Google Incorporated
@@ -16,57 +16,116 @@
static struct {
uint32_t reg;
uint32_t val;
-} csc_table[] = {
- { 0x40400, 0x83 },
- { 0x40404, 0x102 },
- { 0x40408, 0x32 },
- { 0x4040c, 0xffffffb5 },
- { 0x40410, 0xffffff6c },
- { 0x40414, 0xe1 },
- { 0x40418, 0xe1 },
- { 0x4041c, 0xffffff45 },
- { 0x40420, 0xffffffdc },
- { 0x40440, 0x254 },
- { 0x40444, 0x0 },
- { 0x40448, 0x331 },
- { 0x4044c, 0x254 },
- { 0x40450, 0xffffff38 },
- { 0x40454, 0xfffffe61 },
- { 0x40458, 0x254 },
- { 0x4045c, 0x409 },
- { 0x40460, 0x0 },
- { 0x40480, 0x5d },
- { 0x40484, 0x13a },
- { 0x40488, 0x20 },
- { 0x4048c, 0xffffffcd },
- { 0x40490, 0xffffff54 },
- { 0x40494, 0xe1 },
- { 0x40498, 0xe1 },
- { 0x4049c, 0xffffff35 },
- { 0x404a0, 0xffffffec },
- { 0x404c0, 0x254 },
- { 0x404c4, 0x0 },
- { 0x404c8, 0x396 },
- { 0x404cc, 0x254 },
- { 0x404d0, 0xffffff94 },
- { 0x404d4, 0xfffffef0 },
- { 0x404d8, 0x254 },
- { 0x404dc, 0x43a },
- { 0x404e0, 0x0 },
- { 0x40500, 0x10 },
- { 0x40504, 0x80 },
- { 0x40508, 0x80 },
- { 0x40540, 0x10 },
- { 0x40544, 0x80 },
- { 0x40548, 0x80 },
- { 0x40580, 0x10 },
- { 0x40584, 0xeb },
- { 0x40588, 0x10 },
- { 0x4058c, 0xf0 },
- { 0x405c0, 0x10 },
- { 0x405c4, 0xeb },
- { 0x405c8, 0x10 },
- { 0x405cc, 0xf0 },
+} csc_matrix_config_table[] = {
+ /* RGB -> YUV primary forward matrix (set1). */
+ { MDP_CSC_PFMVn(0), 0x83 },
+ { MDP_CSC_PFMVn(1), 0x102 },
+ { MDP_CSC_PFMVn(2), 0x32 },
+ { MDP_CSC_PFMVn(3), 0xffffffb5 },
+ { MDP_CSC_PFMVn(4), 0xffffff6c },
+ { MDP_CSC_PFMVn(5), 0xe1 },
+ { MDP_CSC_PFMVn(6), 0xe1 },
+ { MDP_CSC_PFMVn(7), 0xffffff45 },
+ { MDP_CSC_PFMVn(8), 0xffffffdc },
+
+ /* YUV -> RGB primary reverse matrix (set2) */
+ { MDP_CSC_PRMVn(0), 0x254 },
+ { MDP_CSC_PRMVn(1), 0x0 },
+ { MDP_CSC_PRMVn(2), 0x331 },
+ { MDP_CSC_PRMVn(3), 0x254 },
+ { MDP_CSC_PRMVn(4), 0xffffff38 },
+ { MDP_CSC_PRMVn(5), 0xfffffe61 },
+ { MDP_CSC_PRMVn(6), 0x254 },
+ { MDP_CSC_PRMVn(7), 0x409 },
+ { MDP_CSC_PRMVn(8), 0x0 },
+
+#ifndef CONFIG_MSM_MDP31
+ /* For MDP 2.2/3.0 */
+
+ /* primary limit vector */
+ { MDP_CSC_PLVn(0), 0x10 },
+ { MDP_CSC_PLVn(1), 0xeb },
+ { MDP_CSC_PLVn(2), 0x10 },
+ { MDP_CSC_PLVn(3), 0xf0 },
+
+ /* primary bias vector */
+ { MDP_CSC_PBVn(0), 0x10 },
+ { MDP_CSC_PBVn(1), 0x80 },
+ { MDP_CSC_PBVn(2), 0x80 },
+
+#else /* CONFIG_MSM_MDP31 */
+
+ /* limit vectors configuration */
+ /* rgb -> yuv (set1) pre-limit vector */
+ { MDP_PPP_CSC_PRE_LV1n(0), 0x10 },
+ { MDP_PPP_CSC_PRE_LV1n(1), 0xeb },
+ { MDP_PPP_CSC_PRE_LV1n(2), 0x10 },
+ { MDP_PPP_CSC_PRE_LV1n(3), 0xf0 },
+ { MDP_PPP_CSC_PRE_LV1n(4), 0x10 },
+ { MDP_PPP_CSC_PRE_LV1n(5), 0xf0 },
+
+ /* rgb -> yuv (set1) post-limit vector */
+ { MDP_PPP_CSC_POST_LV1n(0), 0x0 },
+ { MDP_PPP_CSC_POST_LV1n(1), 0xff },
+ { MDP_PPP_CSC_POST_LV1n(2), 0x0 },
+ { MDP_PPP_CSC_POST_LV1n(3), 0xff },
+ { MDP_PPP_CSC_POST_LV1n(4), 0x0 },
+ { MDP_PPP_CSC_POST_LV1n(5), 0xff },
+
+ /* yuv -> rgb (set2) pre-limit vector */
+ { MDP_PPP_CSC_PRE_LV2n(0), 0x0 },
+ { MDP_PPP_CSC_PRE_LV2n(1), 0xff },
+ { MDP_PPP_CSC_PRE_LV2n(2), 0x0 },
+ { MDP_PPP_CSC_PRE_LV2n(3), 0xff },
+ { MDP_PPP_CSC_PRE_LV2n(4), 0x0 },
+ { MDP_PPP_CSC_PRE_LV2n(5), 0xff },
+
+ /* yuv -> rgb (set2) post-limit vector */
+ { MDP_PPP_CSC_POST_LV2n(0), 0x10 },
+ { MDP_PPP_CSC_POST_LV2n(1), 0xeb },
+ { MDP_PPP_CSC_POST_LV2n(2), 0x10 },
+ { MDP_PPP_CSC_POST_LV2n(3), 0xf0 },
+ { MDP_PPP_CSC_POST_LV2n(4), 0x10 },
+ { MDP_PPP_CSC_POST_LV2n(5), 0xf0 },
+
+ /* bias vectors configuration */
+
+ /* XXX: why is set2 used for rgb->yuv, but set1 */
+ /* used for yuv -> rgb??!? Seems to be the reverse of the
+ * other vectors. */
+
+ /* RGB -> YUV pre-bias vector... */
+ { MDP_PPP_CSC_PRE_BV2n(0), 0 },
+ { MDP_PPP_CSC_PRE_BV2n(1), 0 },
+ { MDP_PPP_CSC_PRE_BV2n(2), 0 },
+
+ /* RGB -> YUV post-bias vector */
+ { MDP_PPP_CSC_POST_BV2n(0), 0x10 },
+ { MDP_PPP_CSC_POST_BV2n(1), 0x80 },
+ { MDP_PPP_CSC_POST_BV2n(2), 0x80 },
+
+ /* YUV -> RGB pre-bias vector... */
+ { MDP_PPP_CSC_PRE_BV1n(0), 0x1f0 },
+ { MDP_PPP_CSC_PRE_BV1n(1), 0x180 },
+ { MDP_PPP_CSC_PRE_BV1n(2), 0x180 },
+
+ /* YUV -> RGB post-bias vector */
+ { MDP_PPP_CSC_POST_BV1n(0), 0 },
+ { MDP_PPP_CSC_POST_BV1n(1), 0 },
+ { MDP_PPP_CSC_POST_BV1n(2), 0 },
+
+ /* luma filter coefficients */
+ { MDP_PPP_DEINT_COEFFn(0), 0x3e0 },
+ { MDP_PPP_DEINT_COEFFn(1), 0x360 },
+ { MDP_PPP_DEINT_COEFFn(2), 0x120 },
+ { MDP_PPP_DEINT_COEFFn(3), 0x140 },
+#endif
+};
+
+static struct {
+ uint32_t reg;
+ uint32_t val;
+} csc_color_lut[] = {
{ 0x40800, 0x0 },
{ 0x40804, 0x151515 },
{ 0x40808, 0x1d1d1d },
diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h
index 4e3deb4..ba29454 100644
--- a/drivers/video/msm/mdp_hw.h
+++ b/drivers/video/msm/mdp_hw.h
@@ -15,23 +15,70 @@
#ifndef _MDP_HW_H_
#define _MDP_HW_H_
+#include <linux/platform_device.h>
+#include <linux/wait.h>
#include <mach/msm_iomap.h>
#include <mach/msm_fb.h>
+typedef void (*mdp_dma_start_func_t)(void *private_data, uint32_t addr,
+ uint32_t stride, uint32_t width,
+ uint32_t height, uint32_t x, uint32_t y);
+
+struct mdp_out_interface {
+ uint32_t registered:1;
+ void *priv;
+
+ /* If the interface client wants to get DMA_DONE events */
+ uint32_t dma_mask;
+ mdp_dma_start_func_t dma_start;
+
+ struct msmfb_callback *dma_cb;
+ wait_queue_head_t dma_waitqueue;
+
+ /* If the interface client wants to be notified of non-DMA irqs,
+ * e.g. LCDC/TV-out frame start */
+ uint32_t irq_mask;
+ struct msmfb_callback *irq_cb;
+};
+
struct mdp_info {
+ spinlock_t lock;
struct mdp_device mdp_dev;
char * __iomem base;
int irq;
+ struct clk *clk;
+ struct clk *pclk;
+ struct clk *ebi1_clk;
+ struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES];
+ int dma_format;
+ int dma_pack_pattern;
+ bool dma_format_dirty;
+ struct mdp_blit_req *req;
+
+ int (*enable_irq)(struct mdp_info *mdp, uint32_t mask);
+ int (*disable_irq)(struct mdp_info *mdp, uint32_t mask);
};
+
+void mdp_configure_dma_format(struct mdp_device *mdp_dev);
+
+extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface,
+ void *private_data, uint32_t dma_mask,
+ mdp_dma_start_func_t dma_start);
+
+extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
+ uint32_t mask, struct msmfb_callback *cb);
+
struct mdp_blit_req;
struct mdp_device;
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct file *src_file, unsigned long src_start,
- unsigned long src_len, struct file *dst_file,
- unsigned long dst_start, unsigned long dst_len);
+
+int mdp_hw_init(struct mdp_info *mdp);
+
+int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq);
+
#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
#define mdp_readl(mdp, offset) readl(mdp->base + offset)
+#if defined(CONFIG_MSM_MDP22)
#define MDP_SYNC_CONFIG_0 (0x00000)
#define MDP_SYNC_CONFIG_1 (0x00004)
#define MDP_SYNC_CONFIG_2 (0x00008)
@@ -40,6 +87,18 @@
#define MDP_SYNC_STATUS_2 (0x00014)
#define MDP_SYNC_THRESH_0 (0x00018)
#define MDP_SYNC_THRESH_1 (0x0001c)
+#endif
+
+#if defined(CONFIG_MSM_MDP40)
+#define MDP_DISP_INTF_SEL (0x00038)
+#define MDP_MAX_RD_PENDING_CMD_CONFIG (0x0004c)
+#define MDP_INTR_ENABLE (0x00050)
+#define MDP_INTR_STATUS (0x00054)
+#define MDP_INTR_CLEAR (0x00058)
+#define MDP_EBI2_LCD0 (0x00060)
+#define MDP_EBI2_LCD1 (0x00064)
+#define MDP_EBI2_PORTMAP_MODE (0x00070)
+#else
#define MDP_INTR_ENABLE (0x00020)
#define MDP_INTR_STATUS (0x00024)
#define MDP_INTR_CLEAR (0x00028)
@@ -48,10 +107,20 @@
#define MDP_DISPLAY_STATUS (0x00038)
#define MDP_EBI2_LCD0 (0x0003c)
#define MDP_EBI2_LCD1 (0x00040)
+#define MDP_EBI2_PORTMAP_MODE (0x0005c)
+#endif
+
+#if defined(CONFIG_MSM_MDP22)
#define MDP_DISPLAY0_ADDR (0x00054)
#define MDP_DISPLAY1_ADDR (0x00058)
-#define MDP_EBI2_PORTMAP_MODE (0x0005c)
-#define MDP_MODE (0x00060)
+#define MDP_PPP_CMD_MODE (0x00060)
+#elif defined(CONFIG_MSM_MDP31)
+#define MDP_DISPLAY0_ADDR (0x10000)
+#define MDP_DISPLAY1_ADDR (0x10004)
+#define MDP_PPP_CMD_MODE (0x10060)
+#endif
+
+#if defined(CONFIG_MSM_MDP22)
#define MDP_TV_OUT_STATUS (0x00064)
#define MDP_HW_VERSION (0x00070)
#define MDP_SW_RESET (0x00074)
@@ -61,6 +130,20 @@
#define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084)
#define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088)
#define MDP_VSYNC_CTRL (0x0008c)
+#endif
+
+#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40)
+#define MDP_MDDI_PARAM_WR_SEL (0x00090)
+#define MDP_MDDI_PARAM (0x00094)
+#define MDP_MDDI_DATA_XFR (0x00098)
+#endif
+
+#if defined(CONFIG_MSM_MDP40)
+#define MDP_LAYERMIXER_IN_CFG (0x10100)
+#define MDP_OVERLAYPROC0_CFG (0x10004)
+#define MDP_OVERLAYPROC1_CFG (0x18004)
+#endif
+
#define MDP_CGC_EN (0x00100)
#define MDP_CMD_STATUS (0x10008)
#define MDP_PROFILE_EN (0x10010)
@@ -107,6 +190,7 @@
#define MDP_FULL_BYPASS_WORD35 (0x1018c)
#define MDP_FULL_BYPASS_WORD37 (0x10194)
#define MDP_FULL_BYPASS_WORD39 (0x1019c)
+#define MDP_PPP_OUT_XY (0x1019c)
#define MDP_FULL_BYPASS_WORD40 (0x101a0)
#define MDP_FULL_BYPASS_WORD41 (0x101a4)
#define MDP_FULL_BYPASS_WORD43 (0x101ac)
@@ -129,11 +213,27 @@
#define MDP_FULL_BYPASS_WORD61 (0x101f4)
#define MDP_FULL_BYPASS_WORD62 (0x101f8)
#define MDP_FULL_BYPASS_WORD63 (0x101fc)
+
+#ifdef CONFIG_MSM_MDP31
+#define MDP_PPP_SRC_XY (0x10200)
+#define MDP_PPP_BG_XY (0x10204)
+#define MDP_PPP_SRC_IMAGE_SIZE (0x10208)
+#define MDP_PPP_BG_IMAGE_SIZE (0x1020c)
+#define MDP_PPP_SCALE_CONFIG (0x10230)
+#define MDP_PPP_CSC_CONFIG (0x10240)
+#define MDP_PPP_BLEND_BG_ALPHA_SEL (0x70010)
+#endif
+
#define MDP_TFETCH_TEST_MODE (0x20004)
#define MDP_TFETCH_STATUS (0x20008)
#define MDP_TFETCH_TILE_COUNT (0x20010)
#define MDP_TFETCH_FETCH_COUNT (0x20014)
#define MDP_TFETCH_CONSTANT_COLOR (0x20040)
+#define MDP_BGTFETCH_TEST_MODE (0x28004)
+#define MDP_BGTFETCH_STATUS (0x28008)
+#define MDP_BGTFETCH_TILE_COUNT (0x28010)
+#define MDP_BGTFETCH_FETCH_COUNT (0x28014)
+#define MDP_BGTFETCH_CONSTANT_COLOR (0x28040)
#define MDP_CSC_BYPASS (0x40004)
#define MDP_SCALE_COEFF_LSB (0x5fffc)
#define MDP_TV_OUT_CTL (0xc0000)
@@ -158,55 +258,68 @@
#define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c)
#define MDP_TEST_CAPTURED_DCLK (0xd0210)
#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214)
-#define MDP_LCDC_CTL (0xe0000)
-#define MDP_LCDC_HSYNC_CTL (0xe0004)
-#define MDP_LCDC_VSYNC_CTL (0xe0008)
-#define MDP_LCDC_ACTIVE_HCTL (0xe000c)
-#define MDP_LCDC_ACTIVE_VCTL (0xe0010)
-#define MDP_LCDC_BORDER_CLR (0xe0014)
-#define MDP_LCDC_H_BLANK (0xe0018)
-#define MDP_LCDC_V_BLANK (0xe001c)
-#define MDP_LCDC_UNDERFLOW_CLR (0xe0020)
-#define MDP_LCDC_HSYNC_SKEW (0xe0024)
-#define MDP_LCDC_TEST_CTL (0xe0028)
-#define MDP_LCDC_LINE_IRQ (0xe002c)
-#define MDP_LCDC_CTL_POLARITY (0xe0030)
-#define MDP_LCDC_DMA_CONFIG (0xe1000)
-#define MDP_LCDC_DMA_SIZE (0xe1004)
-#define MDP_LCDC_DMA_IBUF_ADDR (0xe1008)
-#define MDP_LCDC_DMA_IBUF_Y_STRIDE (0xe100c)
+#define MDP_DMA_P_CONFIG (0x90000)
+#define MDP_DMA_P_SIZE (0x90004)
+#define MDP_DMA_P_IBUF_ADDR (0x90008)
+#define MDP_DMA_P_IBUF_Y_STRIDE (0x9000c)
+#define MDP_DMA_P_OUT_XY (0x90010)
-#define MDP_DMA2_TERM 0x1
-#define MDP_DMA3_TERM 0x2
-#define MDP_PPP_TERM 0x3
+#ifdef CONFIG_MSM_MDP40
+#define MDP_DMA_P_START (0x0000c)
+#define MDP_DMA_P_FETCH_CFG (0x91004)
+#define MDP_DMA_P_HIST_INTR_STATUS (0x95014)
+#define MDP_DMA_P_HIST_INTR_CLEAR (0x95018)
+#define MDP_DMA_P_HIST_INTR_ENABLE (0x9501C)
+#else
+#define MDP_DMA_P_START (0x00044)
+#define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070)
+#endif
+
+#if defined(CONFIG_MSM_MDP31)
+#define MDP_LCDC_BASE (0xe0000)
+#elif defined(CONFIG_MSM_MDP40)
+#define MDP_LCDC_BASE (0xc0000)
+#endif
+
+#ifdef MDP_LCDC_BASE
+#define MDP_LCDC_EN (MDP_LCDC_BASE + 0x00)
+#define MDP_LCDC_HSYNC_CTL (MDP_LCDC_BASE + 0x04)
+#define MDP_LCDC_VSYNC_PERIOD (MDP_LCDC_BASE + 0x08)
+#define MDP_LCDC_VSYNC_PULSE_WIDTH (MDP_LCDC_BASE + 0x0c)
+#define MDP_LCDC_DISPLAY_HCTL (MDP_LCDC_BASE + 0x10)
+#define MDP_LCDC_DISPLAY_V_START (MDP_LCDC_BASE + 0x14)
+#define MDP_LCDC_DISPLAY_V_END (MDP_LCDC_BASE + 0x18)
+#define MDP_LCDC_ACTIVE_HCTL (MDP_LCDC_BASE + 0x1c)
+#define MDP_LCDC_ACTIVE_V_START (MDP_LCDC_BASE + 0x20)
+#define MDP_LCDC_ACTIVE_V_END (MDP_LCDC_BASE + 0x24)
+#define MDP_LCDC_BORDER_CLR (MDP_LCDC_BASE + 0x28)
+#define MDP_LCDC_UNDERFLOW_CTL (MDP_LCDC_BASE + 0x2c)
+#define MDP_LCDC_HSYNC_SKEW (MDP_LCDC_BASE + 0x30)
+#define MDP_LCDC_TEST_CTL (MDP_LCDC_BASE + 0x34)
+#define MDP_LCDC_CTL_POLARITY (MDP_LCDC_BASE + 0x38)
+#endif
+
+#define MDP_PPP_SCALE_STATUS (0x50000)
+#define MDP_PPP_BLEND_STATUS (0x70000)
+
+/* MDP_SW_RESET */
+#define MDP_PPP_SW_RESET (1<<4)
/* MDP_INTR_ENABLE */
-#define DL0_ROI_DONE (1<<0)
-#define DL1_ROI_DONE (1<<1)
-#define DL0_DMA2_TERM_DONE (1<<2)
-#define DL1_DMA2_TERM_DONE (1<<3)
-#define DL0_PPP_TERM_DONE (1<<4)
-#define DL1_PPP_TERM_DONE (1<<5)
-#define TV_OUT_DMA3_DONE (1<<6)
-#define TV_ENC_UNDERRUN (1<<7)
-#define DL0_FETCH_DONE (1<<11)
-#define DL1_FETCH_DONE (1<<12)
+#define DL0_ROI_DONE (1<<0)
+#define TV_OUT_DMA3_DONE (1<<6)
+#define TV_ENC_UNDERRUN (1<<7)
-#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \
- DL1_ROI_DONE| \
- DL0_PPP_TERM_DONE| \
- DL1_PPP_TERM_DONE)
-
-#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \
- DL1_ROI_DONE| \
- DL0_DMA2_TERM_DONE| \
- DL1_DMA2_TERM_DONE| \
- DL0_PPP_TERM_DONE| \
- DL1_PPP_TERM_DONE| \
- DL0_FETCH_DONE| \
- DL1_FETCH_DONE| \
- TV_ENC_UNDERRUN)
+#if defined(CONFIG_MSM_MDP40)
+#define MDP_DMA_P_DONE (1 << 4)
+#elif defined(CONFIG_MSM_MDP22)
+#define MDP_DMA_P_DONE (1 << 2)
+#elif defined(CONFIG_MSM_MDP31)
+#define MDP_DMA_P_DONE (1 << 14)
+#define MDP_LCDC_UNDERFLOW (1 << 16)
+#define MDP_LCDC_FRAME_START (1 << 15)
+#endif
#define MDP_TOP_LUMA 16
#define MDP_TOP_CHROMA 0
@@ -316,7 +429,12 @@
#define PPP_OP_SCALE_X_ON (1<<0)
#define PPP_OP_SCALE_Y_ON (1<<1)
+#ifndef CONFIG_MSM_MDP31
#define PPP_OP_CONVERT_RGB2YCBCR 0
+#else
+#define PPP_OP_CONVERT_RGB2YCBCR (1<<30)
+#endif
+
#define PPP_OP_CONVERT_YCBCR2RGB (1<<2)
#define PPP_OP_CONVERT_ON (1<<3)
@@ -372,6 +490,13 @@
#define PPP_OP_BG_CHROMA_SITE_COSITE 0
#define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27)
+#define PPP_BLEND_BG_USE_ALPHA_SEL (1 << 0)
+#define PPP_BLEND_BG_ALPHA_REVERSE (1 << 3)
+#define PPP_BLEND_BG_SRCPIXEL_ALPHA (0 << 1)
+#define PPP_BLEND_BG_DSTPIXEL_ALPHA (1 << 1)
+#define PPP_BLEND_BG_CONSTANT_ALPHA (2 << 1)
+#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24)
+
/* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */
#define PPP_DST_C0G_8BIT ((1<<0)|(1<<1))
#define PPP_DST_C1B_8BIT ((1<<3)|(1<<2))
@@ -449,6 +574,7 @@
#define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
#define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
#define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
+#define PPP_CFG_MDP_RGBX_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
#define PPP_CFG_MDP_Y_CBCR_H2V2(dir) (PPP_##dir##_C2R_8BIT | \
PPP_##dir##_C0G_8BIT | \
@@ -488,12 +614,14 @@
MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8)
#define PPP_PACK_PATTERN_MDP_RGB_888 PPP_PACK_PATTERN_MDP_RGB_565
#define PPP_PACK_PATTERN_MDP_XRGB_8888 \
- MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8)
+ MDP_GET_PACK_PATTERN(CLR_B, CLR_G, CLR_R, CLR_ALPHA, 8)
#define PPP_PACK_PATTERN_MDP_ARGB_8888 PPP_PACK_PATTERN_MDP_XRGB_8888
#define PPP_PACK_PATTERN_MDP_RGBA_8888 \
MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
#define PPP_PACK_PATTERN_MDP_BGRA_8888 \
MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8)
+#define PPP_PACK_PATTERN_MDP_RGBX_8888 \
+ MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \
MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8)
#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1
@@ -509,6 +637,7 @@
#define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB
#define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
#define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
+#define PPP_CHROMA_SAMP_MDP_RGBX_8888(dir) PPP_OP_##dir##_CHROMA_RGB
#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420
#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
@@ -523,6 +652,7 @@
[MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\
[MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\
[MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\
+ [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888,\
[MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\
[MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\
[MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\
@@ -536,6 +666,7 @@
[MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\
[MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\
[MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\
+ [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888(dir),\
[MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\
[MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\
[MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\
@@ -547,7 +678,8 @@
(img == MDP_YCRYCB_H2V1))
#define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \
(img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
- (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888))
+ (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888) | \
+ (img == MDP_RGBX_8888))
#define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
(img == MDP_BGRA_8888))
@@ -582,20 +714,71 @@
#define PPP_ADDR_BG_CFG MDP_FULL_BYPASS_WORD53
#define PPP_ADDR_BG_PACK_PATTERN MDP_FULL_BYPASS_WORD54
+/* color conversion matrix configuration registers */
+/* pfmv is mv1, prmv is mv2 */
+#define MDP_CSC_PFMVn(n) (0x40400 + (4 * (n)))
+#define MDP_CSC_PRMVn(n) (0x40440 + (4 * (n)))
+
+#ifdef CONFIG_MSM_MDP31
+#define MDP_PPP_CSC_PRE_BV1n(n) (0x40500 + (4 * (n)))
+#define MDP_PPP_CSC_PRE_BV2n(n) (0x40540 + (4 * (n)))
+#define MDP_PPP_CSC_POST_BV1n(n) (0x40580 + (4 * (n)))
+#define MDP_PPP_CSC_POST_BV2n(n) (0x405c0 + (4 * (n)))
+
+#define MDP_PPP_CSC_PRE_LV1n(n) (0x40600 + (4 * (n)))
+#define MDP_PPP_CSC_PRE_LV2n(n) (0x40640 + (4 * (n)))
+#define MDP_PPP_CSC_POST_LV1n(n) (0x40680 + (4 * (n)))
+#define MDP_PPP_CSC_POST_LV2n(n) (0x406c0 + (4 * (n)))
+
+#define MDP_PPP_SCALE_COEFF_D0_SET (0)
+#define MDP_PPP_SCALE_COEFF_D1_SET (1)
+#define MDP_PPP_SCALE_COEFF_D2_SET (2)
+#define MDP_PPP_SCALE_COEFF_U1_SET (3)
+#define MDP_PPP_SCALE_COEFF_LSBn(n) (0x50400 + (8 * (n)))
+#define MDP_PPP_SCALE_COEFF_MSBn(n) (0x50404 + (8 * (n)))
+
+#define MDP_PPP_DEINT_COEFFn(n) (0x30010 + (4 * (n)))
+
+#define MDP_PPP_SCALER_FIR (0)
+#define MDP_PPP_SCALER_MN (1)
+
+#else /* !defined(CONFIG_MSM_MDP31) */
+
+#define MDP_CSC_PBVn(n) (0x40500 + (4 * (n)))
+#define MDP_CSC_SBVn(n) (0x40540 + (4 * (n)))
+#define MDP_CSC_PLVn(n) (0x40580 + (4 * (n)))
+#define MDP_CSC_SLVn(n) (0x405c0 + (4 * (n)))
+
+#endif
+
+
/* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */
-#define DMA_DSTC0G_6BITS (1<<1)
-#define DMA_DSTC1B_6BITS (1<<3)
-#define DMA_DSTC2R_6BITS (1<<5)
#define DMA_DSTC0G_5BITS (1<<0)
#define DMA_DSTC1B_5BITS (1<<2)
#define DMA_DSTC2R_5BITS (1<<4)
+#define DMA_DSTC0G_6BITS (2<<0)
+#define DMA_DSTC1B_6BITS (2<<2)
+#define DMA_DSTC2R_6BITS (2<<4)
+
+#define DMA_DSTC0G_8BITS (3<<0)
+#define DMA_DSTC1B_8BITS (3<<2)
+#define DMA_DSTC2R_8BITS (3<<4)
+
+#define DMA_DST_BITS_MASK 0x3F
+
#define DMA_PACK_TIGHT (1<<6)
#define DMA_PACK_LOOSE 0
#define DMA_PACK_ALIGN_LSB 0
#define DMA_PACK_ALIGN_MSB (1<<7)
+#define DMA_PACK_PATTERN_MASK (0x3f<<8)
#define DMA_PACK_PATTERN_RGB \
(MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8)
+#define DMA_PACK_PATTERN_BGR \
+ (MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 2)<<8)
+
+
+#ifdef CONFIG_MSM_MDP22
#define DMA_OUT_SEL_AHB 0
#define DMA_OUT_SEL_MDDI (1<<14)
@@ -603,16 +786,32 @@
#define DMA_AHBM_LCD_SEL_SECONDARY (1<<15)
#define DMA_IBUF_C3ALPHA_EN (1<<16)
#define DMA_DITHER_EN (1<<17)
-
#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18)
#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19)
-
#define DMA_IBUF_FORMAT_RGB565 (1<<20)
#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0
-
+#define DMA_IBUF_FORMAT_MASK (1 << 20)
#define DMA_IBUF_NONCONTIGUOUS (1<<21)
+#else /* CONFIG_MSM_MDP31 */
+
+#define DMA_OUT_SEL_AHB (0 << 19)
+#define DMA_OUT_SEL_MDDI (1 << 19)
+#define DMA_OUT_SEL_LCDC (2 << 19)
+#define DMA_OUT_SEL_LCDC_MDDI (3 << 19)
+#define DMA_DITHER_EN (1 << 24)
+#define DMA_IBUF_FORMAT_RGB888 (0 << 25)
+#define DMA_IBUF_FORMAT_RGB565 (1 << 25)
+#define DMA_IBUF_FORMAT_XRGB8888 (2 << 25)
+#define DMA_IBUF_FORMAT_MASK (3 << 25)
+#define DMA_IBUF_NONCONTIGUOUS (0)
+
+#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY (0)
+#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (0)
+#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (0)
+#endif
+
/* MDDI REGISTER ? */
#define MDDI_VDO_PACKET_DESC 0x5666
#define MDDI_VDO_PACKET_PRIM 0xC3
diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c
new file mode 100644
index 0000000..a642c9b
--- /dev/null
+++ b/drivers/video/msm/mdp_hw40.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.com>
+ *
+ * Based on code from Code Aurora Forum.
+ *
+ * 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/io.h>
+
+#include "mdp_hw.h"
+
+static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
+ uint32_t width, uint32_t height, uint32_t x,
+ uint32_t y)
+{
+ struct mdp_info *mdp = priv;
+ uint32_t dma2_cfg;
+ uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
+
+ dma2_cfg = DMA_PACK_TIGHT |
+ DMA_PACK_ALIGN_LSB;
+
+ dma2_cfg |= mdp->dma_format;
+ dma2_cfg |= mdp->dma_pack_pattern;
+ dma2_cfg |= DMA_DITHER_EN;
+
+ /* 666 18BPP */
+ dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+
+ /* setup size, address, and stride */
+ mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE);
+ mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR);
+ mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
+
+ /* set y & x offset and MDDI transaction parameters */
+ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
+ mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
+ mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
+ MDP_MDDI_PARAM);
+
+ mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR);
+ mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
+ mdp_writel(mdp, 0, MDP_DMA_P_START);
+}
+
+int mdp_hw_init(struct mdp_info *mdp)
+{
+ int ret;
+
+ ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp,
+ MDP_DMA_P_DONE, mdp_dma_to_mddi);
+ if (ret)
+ return ret;
+
+ mdp_writel(mdp, 0, MDP_INTR_ENABLE);
+ mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE);
+
+ /* XXX: why set this? QCT says it should be > mdp_pclk,
+ * but they never set the clkrate of pclk */
+ clk_set_rate(mdp->clk, 122880000); /* 122.88 Mhz */
+ pr_info("%s: mdp_clk=%lu\n", __func__, clk_get_rate(mdp->clk));
+
+ /* TODO: Configure the VG/RGB pipes fetch data */
+
+ /* this should work for any mdp_clk freq.
+ * TODO: use different value for mdp_clk freqs >= 90Mhz */
+ mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); /* 8 bytes-burst x 8 req */
+
+ mdp_writel(mdp, 0x3, MDP_EBI2_PORTMAP_MODE);
+
+ /* 3 pending requests */
+ mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG);
+
+ /* no overlay processing, sw controls everything */
+ mdp_writel(mdp, 0, MDP_LAYERMIXER_IN_CFG);
+ mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC0_CFG);
+ mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC1_CFG);
+
+ /* XXX: HACK! hardcode to do mddi on primary */
+ mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL);
+ return 0;
+}
+
diff --git a/drivers/video/msm/mdp_hw_legacy.c b/drivers/video/msm/mdp_hw_legacy.c
new file mode 100644
index 0000000..fdde9ff
--- /dev/null
+++ b/drivers/video/msm/mdp_hw_legacy.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.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.
+ *
+ */
+
+#include <linux/io.h>
+
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+#include "mdp_csc_table.h"
+
+#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
+
+#ifdef CONFIG_FB_MSM_MDDI
+static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
+ uint32_t width, uint32_t height, uint32_t x,
+ uint32_t y)
+{
+ struct mdp_info *mdp = priv;
+ uint32_t dma2_cfg;
+ uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
+
+ dma2_cfg = DMA_PACK_TIGHT |
+ DMA_PACK_ALIGN_LSB |
+ DMA_OUT_SEL_AHB |
+ DMA_IBUF_NONCONTIGUOUS;
+
+ dma2_cfg |= mdp->dma_format;
+ dma2_cfg |= mdp->dma_pack_pattern;
+
+ dma2_cfg |= DMA_OUT_SEL_MDDI;
+
+ dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
+
+ dma2_cfg |= DMA_DITHER_EN;
+
+ /* 666 18BPP */
+ dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+
+#ifdef CONFIG_MSM_MDP22
+ /* setup size, address, and stride */
+ mdp_writel(mdp, (height << 16) | (width),
+ MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
+ mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
+ mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
+
+ /* set y & x offset and MDDI transaction parameters */
+ mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
+ mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
+ mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
+ MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
+
+ mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
+
+ /* start DMA2 */
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
+#else
+ /* setup size, address, and stride */
+ mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE);
+ mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR);
+ mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
+
+ /* set y & x offset and MDDI transaction parameters */
+ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
+ mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
+ mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
+ MDP_MDDI_PARAM);
+
+ mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR);
+ mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
+ mdp_writel(mdp, 0, MDP_DMA_P_START);
+#endif
+}
+#endif
+
+int mdp_hw_init(struct mdp_info *mdp)
+{
+ int n;
+
+#ifdef CONFIG_FB_MSM_MDDI
+ n = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp,
+ MDP_DMA_P_DONE, mdp_dma_to_mddi);
+ if (n)
+ return n;
+#endif
+
+ mdp_writel(mdp, 0, MDP_INTR_ENABLE);
+
+ /* debug interface write access */
+ mdp_writel(mdp, 1, 0x60);
+ mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
+
+#ifndef CONFIG_MSM_MDP22
+ /* disable lcdc */
+ mdp_writel(mdp, 0, MDP_LCDC_EN);
+ /* enable auto clock gating for all blocks by default */
+ mdp_writel(mdp, 0xffffffff, MDP_CGC_EN);
+ /* reset color/gamma correct parms */
+ mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG);
+#endif
+
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
+ mdp_writel(mdp, 1, 0x60);
+
+ for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++)
+ mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg);
+
+ /* clear up unused fg/main registers */
+ /* comp.plane 2&3 ystride */
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
+
+ /* unpacked pattern */
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
+
+ /* comp.plane 2 & 3 */
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
+
+ /* clear unused bg registers */
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
+ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
+
+ for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++)
+ mdp_writel(mdp, csc_matrix_config_table[n].val,
+ csc_matrix_config_table[n].reg);
+
+ mdp_ppp_init_scale(mdp);
+
+#ifndef CONFIG_MSM_MDP31
+ mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG);
+#endif
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c
new file mode 100644
index 0000000..a88840aa
--- /dev/null
+++ b/drivers/video/msm/mdp_lcdc.c
@@ -0,0 +1,431 @@
+/* drivers/video/msm/mdp_lcdc.c
+ *
+ * Copyright (c) 2009 Google Inc.
+ * Copyright (c) 2009 QUALCOMM Incorporated
+ *
+ * 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.
+ *
+ * Author: Dima Zavin <dima@android.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+
+#include "mdp_hw.h"
+
+struct mdp_lcdc_info {
+ struct mdp_info *mdp;
+ struct clk *mdp_clk;
+ struct clk *pclk;
+ struct clk *pad_pclk;
+ struct msm_panel_data fb_panel_data;
+ struct platform_device fb_pdev;
+ struct msm_lcdc_platform_data *pdata;
+ uint32_t fb_start;
+
+ struct msmfb_callback frame_start_cb;
+ wait_queue_head_t vsync_waitq;
+ int got_vsync;
+
+ struct {
+ uint32_t clk_rate;
+ uint32_t hsync_ctl;
+ uint32_t vsync_period;
+ uint32_t vsync_pulse_width;
+ uint32_t display_hctl;
+ uint32_t display_vstart;
+ uint32_t display_vend;
+ uint32_t hsync_skew;
+ uint32_t polarity;
+ } parms;
+};
+
+static struct mdp_device *mdp_dev;
+
+#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
+
+static int lcdc_unblank(struct msm_panel_data *fb_panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+ struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
+
+ pr_info("%s: ()\n", __func__);
+ panel_ops->unblank(panel_ops);
+
+ return 0;
+}
+
+static int lcdc_blank(struct msm_panel_data *fb_panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+ struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
+
+ pr_info("%s: ()\n", __func__);
+ panel_ops->blank(panel_ops);
+
+ return 0;
+}
+
+static int lcdc_suspend(struct msm_panel_data *fb_panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+ pr_info("%s: suspending\n", __func__);
+
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
+ clk_disable(lcdc->pad_pclk);
+ clk_disable(lcdc->pclk);
+ clk_disable(lcdc->mdp_clk);
+
+ return 0;
+}
+
+static int lcdc_resume(struct msm_panel_data *fb_panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+ pr_info("%s: resuming\n", __func__);
+
+ clk_enable(lcdc->mdp_clk);
+ clk_enable(lcdc->pclk);
+ clk_enable(lcdc->pad_pclk);
+ mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+
+ return 0;
+}
+
+static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
+{
+ struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
+ uint32_t dma_cfg;
+
+ clk_enable(lcdc->mdp_clk);
+ clk_enable(lcdc->pclk);
+ clk_enable(lcdc->pad_pclk);
+
+ clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
+ clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
+
+ /* write the lcdc params */
+ mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
+ mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
+ mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
+ MDP_LCDC_VSYNC_PULSE_WIDTH);
+ mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
+ mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
+ MDP_LCDC_DISPLAY_V_START);
+ mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
+ mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
+
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
+ mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
+ mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
+
+ /* config the dma_p block that drives the lcdc data */
+ mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
+ mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
+ (fb_panel->fb_data->xres & 0x7ff)),
+ MDP_DMA_P_SIZE);
+
+ mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
+
+ dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
+ dma_cfg |= (DMA_PACK_ALIGN_LSB |
+ DMA_PACK_PATTERN_RGB |
+ DMA_DITHER_EN);
+ dma_cfg |= DMA_OUT_SEL_LCDC;
+ dma_cfg &= ~DMA_DST_BITS_MASK;
+
+ if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
+ dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+ else
+ dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+
+ mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
+
+ /* enable the lcdc timing generation */
+ mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+
+ return 0;
+}
+
+static void lcdc_wait_vsync(struct msm_panel_data *panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
+ int ret;
+
+ ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
+ if (!ret && !lcdc->got_vsync)
+ pr_err("%s: timeout waiting for VSYNC\n", __func__);
+ lcdc->got_vsync = 0;
+}
+
+static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
+ struct msmfb_callback *vsync_cb)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+
+ /* the vsync callback will start the dma */
+ vsync_cb->func(vsync_cb);
+ lcdc->got_vsync = 0;
+ mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
+ &lcdc->frame_start_cb);
+ lcdc_wait_vsync(fb_panel);
+}
+
+static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
+{
+ struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
+ lcdc->got_vsync = 0;
+ mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
+}
+
+/* called in irq context with mdp lock held, when mdp gets the
+ * MDP_LCDC_FRAME_START interrupt */
+static void lcdc_frame_start(struct msmfb_callback *cb)
+{
+ struct mdp_lcdc_info *lcdc;
+
+ lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
+
+ lcdc->got_vsync = 1;
+ wake_up(&lcdc->vsync_waitq);
+}
+
+static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
+ uint32_t width, uint32_t height, uint32_t x,
+ uint32_t y)
+{
+ struct mdp_lcdc_info *lcdc = priv;
+
+ if (lcdc->mdp->dma_format_dirty) {
+ mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
+ mdelay(20);
+ mdp_configure_dma_format(mdp_dev);
+ mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
+ }
+ mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
+ mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
+}
+
+static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
+{
+ struct msm_lcdc_timing *timing = lcdc->pdata->timing;
+ struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
+ unsigned int hsync_period;
+ unsigned int hsync_start_x;
+ unsigned int hsync_end_x;
+ unsigned int vsync_period;
+ unsigned int display_vstart;
+ unsigned int display_vend;
+
+ hsync_period = (timing->hsync_back_porch +
+ fb_data->xres + timing->hsync_front_porch);
+ hsync_start_x = timing->hsync_back_porch;
+ hsync_end_x = hsync_start_x + fb_data->xres - 1;
+
+ vsync_period = (timing->vsync_back_porch +
+ fb_data->yres + timing->vsync_front_porch);
+ vsync_period *= hsync_period;
+
+ display_vstart = timing->vsync_back_porch;
+ display_vstart *= hsync_period;
+ display_vstart += timing->hsync_skew;
+
+ display_vend = (timing->vsync_back_porch + fb_data->yres) *
+ hsync_period;
+ display_vend += timing->hsync_skew - 1;
+
+ /* register values we pre-compute at init time from the timing
+ * information in the panel info */
+ lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
+ (timing->hsync_pulse_width & 0xfff));
+ lcdc->parms.vsync_period = vsync_period & 0xffffff;
+ lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
+ hsync_period) & 0xffffff;
+
+ lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
+ (hsync_start_x & 0xfff));
+ lcdc->parms.display_vstart = display_vstart & 0xffffff;
+ lcdc->parms.display_vend = display_vend & 0xffffff;
+ lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
+ lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
+ (timing->vsync_act_low << 1) |
+ (timing->den_act_low << 2));
+ lcdc->parms.clk_rate = timing->clk_rate;
+}
+
+static int mdp_lcdc_probe(struct platform_device *pdev)
+{
+ struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
+ struct mdp_lcdc_info *lcdc;
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("%s: no LCDC platform data found\n", __func__);
+ return -EINVAL;
+ }
+
+ lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
+ if (!lcdc)
+ return -ENOMEM;
+
+ /* We don't actually own the clocks, the mdp does. */
+ lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
+ if (IS_ERR(lcdc->mdp_clk)) {
+ pr_err("%s: failed to get mdp_clk\n", __func__);
+ ret = PTR_ERR(lcdc->mdp_clk);
+ goto err_get_mdp_clk;
+ }
+
+ lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
+ if (IS_ERR(lcdc->pclk)) {
+ pr_err("%s: failed to get lcdc_pclk\n", __func__);
+ ret = PTR_ERR(lcdc->pclk);
+ goto err_get_pclk;
+ }
+
+ lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
+ if (IS_ERR(lcdc->pad_pclk)) {
+ pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
+ ret = PTR_ERR(lcdc->pad_pclk);
+ goto err_get_pad_pclk;
+ }
+
+ init_waitqueue_head(&lcdc->vsync_waitq);
+ lcdc->pdata = pdata;
+ lcdc->frame_start_cb.func = lcdc_frame_start;
+
+ platform_set_drvdata(pdev, lcdc);
+
+ mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
+ lcdc_dma_start);
+
+ precompute_timing_parms(lcdc);
+
+ lcdc->fb_start = pdata->fb_resource->start;
+ lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
+
+ lcdc->fb_panel_data.suspend = lcdc_suspend;
+ lcdc->fb_panel_data.resume = lcdc_resume;
+ lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
+ lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
+ lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
+ lcdc->fb_panel_data.blank = lcdc_blank;
+ lcdc->fb_panel_data.unblank = lcdc_unblank;
+ lcdc->fb_panel_data.fb_data = pdata->fb_data;
+ lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
+
+ ret = lcdc_hw_init(lcdc);
+ if (ret) {
+ pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
+ goto err_hw_init;
+ }
+
+ lcdc->fb_pdev.name = "msm_panel";
+ lcdc->fb_pdev.id = pdata->fb_id;
+ lcdc->fb_pdev.resource = pdata->fb_resource;
+ lcdc->fb_pdev.num_resources = 1;
+ lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
+
+ if (pdata->panel_ops->init)
+ pdata->panel_ops->init(pdata->panel_ops);
+
+ ret = platform_device_register(&lcdc->fb_pdev);
+ if (ret) {
+ pr_err("%s: Cannot register msm_panel pdev\n", __func__);
+ goto err_plat_dev_reg;
+ }
+
+ pr_info("%s: initialized\n", __func__);
+
+ return 0;
+
+err_plat_dev_reg:
+err_hw_init:
+ platform_set_drvdata(pdev, NULL);
+ clk_put(lcdc->pad_pclk);
+err_get_pad_pclk:
+ clk_put(lcdc->pclk);
+err_get_pclk:
+ clk_put(lcdc->mdp_clk);
+err_get_mdp_clk:
+ kfree(lcdc);
+ return ret;
+}
+
+static int mdp_lcdc_remove(struct platform_device *pdev)
+{
+ struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ clk_put(lcdc->pclk);
+ clk_put(lcdc->pad_pclk);
+ kfree(lcdc);
+
+ return 0;
+}
+
+static struct platform_driver mdp_lcdc_driver = {
+ .probe = mdp_lcdc_probe,
+ .remove = mdp_lcdc_remove,
+ .driver = {
+ .name = "msm_mdp_lcdc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int mdp_lcdc_add_mdp_device(struct device *dev,
+ struct class_interface *class_intf)
+{
+ /* might need locking if mulitple mdp devices */
+ if (mdp_dev)
+ return 0;
+ mdp_dev = container_of(dev, struct mdp_device, dev);
+ return platform_driver_register(&mdp_lcdc_driver);
+}
+
+static void mdp_lcdc_remove_mdp_device(struct device *dev,
+ struct class_interface *class_intf)
+{
+ /* might need locking if mulitple mdp devices */
+ if (dev != &mdp_dev->dev)
+ return;
+ platform_driver_unregister(&mdp_lcdc_driver);
+ mdp_dev = NULL;
+}
+
+static struct class_interface mdp_lcdc_interface = {
+ .add_dev = &mdp_lcdc_add_mdp_device,
+ .remove_dev = &mdp_lcdc_remove_mdp_device,
+};
+
+static int __init mdp_lcdc_init(void)
+{
+ return register_mdp_client(&mdp_lcdc_interface);
+}
+
+module_init(mdp_lcdc_init);
diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c
index 4ff001f..8f56323 100644
--- a/drivers/video/msm/mdp_ppp.c
+++ b/drivers/video/msm/mdp_ppp.c
@@ -15,40 +15,34 @@
#include <linux/fb.h>
#include <linux/file.h>
#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/msm_hw3d.h>
#include <linux/msm_mdp.h>
+#include <linux/mutex.h>
+#include <linux/android_pmem.h>
+#include <linux/wait.h>
#include <mach/msm_fb.h>
#include "mdp_hw.h"
-#include "mdp_scale_tables.h"
+#include "mdp_ppp.h"
+#define PPP_DUMP_BLITS 0
+
+#define PPP_DEBUG_MSGS 1
+#if PPP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, \
+ __LINE__, ##args); } \
+ while (0)
+#else
#define DLOG(x...) do {} while (0)
+#endif
-#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1)
-static int downscale_y_table = MDP_DOWNSCALE_MAX;
-static int downscale_x_table = MDP_DOWNSCALE_MAX;
+#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp)
-struct mdp_regs {
- uint32_t src0;
- uint32_t src1;
- uint32_t dst0;
- uint32_t dst1;
- uint32_t src_cfg;
- uint32_t dst_cfg;
- uint32_t src_pack;
- uint32_t dst_pack;
- uint32_t src_rect;
- uint32_t dst_rect;
- uint32_t src_ystride;
- uint32_t dst_ystride;
- uint32_t op;
- uint32_t src_bpp;
- uint32_t dst_bpp;
- uint32_t edge;
- uint32_t phasex_init;
- uint32_t phasey_init;
- uint32_t phasex_step;
- uint32_t phasey_step;
-};
+#define Y_TO_CRCB_RATIO(format) \
+ ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\
+ (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1)
static uint32_t pack_pattern[] = {
PPP_ARRAY0(PACK_PATTERN)
@@ -62,18 +56,19 @@
PPP_ARRAY1(CFG, DST)
};
-static uint32_t bytes_per_pixel[] = {
+static const uint32_t bytes_per_pixel[] = {
[MDP_RGB_565] = 2,
- [MDP_RGB_888] = 3,
[MDP_XRGB_8888] = 4,
+ [MDP_Y_CBCR_H2V2] = 1,
[MDP_ARGB_8888] = 4,
+ [MDP_RGB_888] = 3,
+ [MDP_Y_CRCB_H2V2] = 1,
+ [MDP_YCRYCB_H2V1] = 2,
+ [MDP_Y_CRCB_H2V1] = 1,
+ [MDP_Y_CBCR_H2V1] = 1,
[MDP_RGBA_8888] = 4,
[MDP_BGRA_8888] = 4,
- [MDP_Y_CBCR_H2V1] = 1,
- [MDP_Y_CBCR_H2V2] = 1,
- [MDP_Y_CRCB_H2V1] = 1,
- [MDP_Y_CRCB_H2V2] = 1,
- [MDP_YCRYCB_H2V1] = 2
+ [MDP_RGBX_8888] = 4,
};
static uint32_t dst_op_chroma[] = {
@@ -88,26 +83,108 @@
PPP_ARRAY1(CHROMA_SAMP, BG)
};
-static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs)
+static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
+DEFINE_MUTEX(mdp_mutex);
+
+static uint32_t get_luma_offset(struct mdp_img *img,
+ struct mdp_rect *rect, uint32_t bpp)
{
+#ifndef CONFIG_MSM_MDP31
+ return (rect->x + (rect->y * img->width)) * bpp;
+#else
+ return 0;
+#endif
+}
+
+static uint32_t get_chroma_offset(struct mdp_img *img,
+ struct mdp_rect *rect, uint32_t bpp)
+{
+#ifndef CONFIG_MSM_MDP31
+ uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
+ uint32_t compress_h = 2;
+ uint32_t offset = 0;
+
+ if (IS_PSEUDOPLNR(img->format)) {
+ offset = (rect->x / compress_h) * compress_h;
+ offset += rect->y == 0 ? 0 :
+ ((rect->y + 1) / compress_v) * img->width;
+ offset *= bpp;
+ }
+ return offset;
+#else
+ return 0;
+#endif
+}
+
+static void set_src_region(struct mdp_img *img, struct mdp_rect *rect,
+ struct ppp_regs *regs)
+{
+ regs->src_rect = (rect->h << 16) | (rect->w & 0x1fff);
+
+#ifdef CONFIG_MSM_MDP31
+ regs->src_xy = (rect->y << 16) | (rect->x & 0x1fff);
+ regs->src_img_sz = (img->height << 16) | (img->width & 0x1fff);
+#endif
+}
+
+static inline void set_dst_region(struct mdp_rect *rect, struct ppp_regs *regs)
+{
+ regs->dst_rect = (rect->h << 16) | (rect->w & 0xfff);
+
+#ifdef CONFIG_MSM_MDP31
+ regs->dst_xy = (rect->y << 16) | (rect->x & 0x1fff);
+#endif
+}
+
+static void set_blend_region(struct mdp_img *img, struct mdp_rect *rect,
+ struct ppp_regs *regs)
+{
+#ifdef CONFIG_MSM_MDP31
+ uint32_t rect_x = rect->x;
+ uint32_t rect_y = rect->y;
+ uint32_t img_w = img->width;
+ uint32_t img_h = img->height;
+
+ /* HW bug workaround */
+ if (img->format == MDP_YCRYCB_H2V1) {
+ regs->bg0 += (rect_x + (rect_y * img_w)) * regs->bg_bpp;
+ rect_x = 0;
+ rect_y = 0;
+ img_w = rect->w;
+ img_h = rect->h;
+ }
+
+ regs->bg_xy = (rect_y << 16) | (rect_x & 0x1fff);
+ regs->bg_img_sz = (img_h << 16) | (img_w & 0x1fff);
+#endif
+}
+
+static void rotate_dst_addr_x(struct mdp_blit_req *req,
+ struct ppp_regs *regs)
+{
+#ifndef CONFIG_MSM_MDP31
regs->dst0 += (req->dst_rect.w -
min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
regs->dst1 += (req->dst_rect.w -
min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
+#endif
}
-static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs)
+static void rotate_dst_addr_y(struct mdp_blit_req *req,
+ struct ppp_regs *regs)
{
+#ifndef CONFIG_MSM_MDP31
regs->dst0 += (req->dst_rect.h -
min((uint32_t)16, req->dst_rect.h)) *
regs->dst_ystride;
regs->dst1 += (req->dst_rect.h -
min((uint32_t)16, req->dst_rect.h)) *
regs->dst_ystride;
+#endif
}
static void blit_rotate(struct mdp_blit_req *req,
- struct mdp_regs *regs)
+ struct ppp_regs *regs)
{
if (req->flags == MDP_ROT_NOP)
return;
@@ -126,16 +203,24 @@
regs->op |= PPP_OP_FLIP_LR;
}
-static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs)
+static void blit_convert(struct mdp_blit_req *req, struct ppp_regs *regs)
{
if (req->src.format == req->dst.format)
return;
if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) {
regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON;
+#ifdef CONFIG_MSM_MDP31
+ /* primary really means set1 */
+ regs->op |= PPP_OP_CONVERT_MATRIX_PRIMARY;
+ regs->csc_cfg = 0x1e;
+#endif
} else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) {
regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON;
- if (req->dst.format == MDP_RGB_565)
- regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY;
+#ifdef CONFIG_MSM_MDP31
+ /* secondary really means set2 */
+ regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY;
+ regs->csc_cfg = 0;
+#endif
}
}
@@ -165,7 +250,7 @@
}
#undef GET_BIT_RANGE
-static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs)
+static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs)
{
/* TRANSP BLEND */
if (req->transp_mask != MDP_TRANSP_NOP) {
@@ -190,8 +275,22 @@
req->alpha &= 0xff;
/* ALPHA BLEND */
if (HAS_ALPHA(req->src.format)) {
- regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
- PPP_OP_BLEND_SRCPIXEL_ALPHA;
+ regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON;
+ if (req->flags & MDP_BLEND_FG_PREMULT) {
+#ifdef CONFIG_MSM_MDP31
+ /* premultiplied alpha:
+ * bg_alpha = (1 - fg_alpha)
+ * fg_alpha = 0xff
+ */
+ regs->bg_alpha_sel = PPP_BLEND_BG_USE_ALPHA_SEL |
+ PPP_BLEND_BG_ALPHA_REVERSE |
+ PPP_BLEND_BG_SRCPIXEL_ALPHA;
+ regs->op |= PPP_OP_BLEND_CONSTANT_ALPHA;
+ req->alpha = 0xff;
+#endif
+ } else {
+ regs->op |= PPP_OP_BLEND_SRCPIXEL_ALPHA;
+ }
} else if (req->alpha < MDP_ALPHA_NOP) {
/* just blend by alpha */
regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
@@ -200,254 +299,31 @@
}
regs->op |= bg_op_chroma[req->dst.format];
+
+ /* since we always blend src + dst -> dst, copy most of the
+ * configuration from dest to bg */
+ regs->bg0 = regs->dst0;
+ regs->bg1 = regs->dst1;
+ regs->bg_cfg = src_img_cfg[req->dst.format];
+ regs->bg_bpp = regs->dst_bpp;
+ regs->bg_pack = pack_pattern[req->dst.format];
+ regs->bg_ystride = regs->dst_ystride;
+ set_blend_region(&req->dst, &req->dst_rect, regs);
}
-#define ONE_HALF (1LL << 32)
-#define ONE (1LL << 33)
-#define TWO (2LL << 33)
-#define THREE (3LL << 33)
-#define FRAC_MASK (ONE - 1)
-#define INT_MASK (~FRAC_MASK)
-
-static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
- uint32_t *phase_init, uint32_t *phase_step)
+static int blit_scale(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct ppp_regs *regs)
{
- /* to improve precicsion calculations are done in U31.33 and converted
- * to U3.29 at the end */
- int64_t k1, k2, k3, k4, tmp;
- uint64_t n, d, os, os_p, od, od_p, oreq;
- unsigned rpa = 0;
- int64_t ip64, delta;
+ struct mdp_rect dst_rect;
- if (dim_out % 3 == 0)
- rpa = !(dim_in % (dim_out / 3));
-
- n = ((uint64_t)dim_out) << 34;
- d = dim_in;
- if (!d)
- return -1;
- do_div(n, d);
- k3 = (n + 1) >> 1;
- if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) {
- DLOG("crap bad scale\n");
- return -1;
- }
- n = ((uint64_t)dim_in) << 34;
- d = (uint64_t)dim_out;
- if (!d)
- return -1;
- do_div(n, d);
- k1 = (n + 1) >> 1;
- k2 = (k1 - ONE) >> 1;
-
- *phase_init = (int)(k2 >> 4);
- k4 = (k3 - ONE) >> 1;
-
- if (rpa) {
- os = ((uint64_t)origin << 33) - ONE_HALF;
- tmp = (dim_out * os) + ONE_HALF;
- if (!dim_in)
- return -1;
- do_div(tmp, dim_in);
- od = tmp - ONE_HALF;
- } else {
- os = ((uint64_t)origin << 1) - 1;
- od = (((k3 * os) >> 1) + k4);
- }
-
- od_p = od & INT_MASK;
- if (od_p != od)
- od_p += ONE;
-
- if (rpa) {
- tmp = (dim_in * od_p) + ONE_HALF;
- if (!dim_in)
- return -1;
- do_div(tmp, dim_in);
- os_p = tmp - ONE_HALF;
- } else {
- os_p = ((k1 * (od_p >> 33)) + k2);
- }
-
- oreq = (os_p & INT_MASK) - ONE;
-
- ip64 = os_p - oreq;
- delta = ((int64_t)(origin) << 33) - oreq;
- ip64 -= delta;
- /* limit to valid range before the left shift */
- delta = (ip64 & (1LL << 63)) ? 4 : -4;
- delta <<= 33;
- while (abs((int)(ip64 >> 33)) > 4)
- ip64 += delta;
- *phase_init = (int)(ip64 >> 4);
- *phase_step = (uint32_t)(k1 >> 4);
- return 0;
-}
-
-static void load_scale_table(const struct mdp_info *mdp,
- struct mdp_table_entry *table, int len)
-{
- int i;
- for (i = 0; i < len; i++)
- mdp_writel(mdp, table[i].val, table[i].reg);
-}
-
-enum {
-IMG_LEFT,
-IMG_RIGHT,
-IMG_TOP,
-IMG_BOTTOM,
-};
-
-static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
- uint32_t *interp1, uint32_t *interp2,
- uint32_t *repeat1, uint32_t *repeat2) {
- if (src > 3 * dst) {
- *interp1 = 0;
- *interp2 = src - 1;
- *repeat1 = 0;
- *repeat2 = 0;
- } else if (src == 3 * dst) {
- *interp1 = 0;
- *interp2 = src;
- *repeat1 = 0;
- *repeat2 = 1;
- } else if (src > dst && src < 3 * dst) {
- *interp1 = -1;
- *interp2 = src;
- *repeat1 = 1;
- *repeat2 = 1;
- } else if (src == dst) {
- *interp1 = -1;
- *interp2 = src + 1;
- *repeat1 = 1;
- *repeat2 = 2;
- } else {
- *interp1 = -2;
- *interp2 = src + 1;
- *repeat1 = 2;
- *repeat2 = 2;
- }
- *interp1 += src_coord;
- *interp2 += src_coord;
-}
-
-static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- int32_t luma_interp[4];
- int32_t luma_repeat[4];
- int32_t chroma_interp[4];
- int32_t chroma_bound[4];
- int32_t chroma_repeat[4];
- uint32_t dst_w, dst_h;
-
- memset(&luma_interp, 0, sizeof(int32_t) * 4);
- memset(&luma_repeat, 0, sizeof(int32_t) * 4);
- memset(&chroma_interp, 0, sizeof(int32_t) * 4);
- memset(&chroma_bound, 0, sizeof(int32_t) * 4);
- memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
- regs->edge = 0;
-
+ memcpy(&dst_rect, &req->dst_rect, sizeof(dst_rect));
if (req->flags & MDP_ROT_90) {
- dst_w = req->dst_rect.h;
- dst_h = req->dst_rect.w;
- } else {
- dst_w = req->dst_rect.w;
- dst_h = req->dst_rect.h;
+ dst_rect.w = req->dst_rect.h;
+ dst_rect.h = req->dst_rect.w;
}
- if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
- get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
- &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
- &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
- get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
- &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
- &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
- } else {
- luma_interp[IMG_LEFT] = req->src_rect.x;
- luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
- luma_interp[IMG_TOP] = req->src_rect.y;
- luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
- luma_repeat[IMG_LEFT] = 0;
- luma_repeat[IMG_TOP] = 0;
- luma_repeat[IMG_RIGHT] = 0;
- luma_repeat[IMG_BOTTOM] = 0;
- }
-
- chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
- chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
- chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
- chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
-
- chroma_bound[IMG_LEFT] = req->src_rect.x;
- chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
- chroma_bound[IMG_TOP] = req->src_rect.y;
- chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-
- if (IS_YCRCB(req->src.format)) {
- chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
- chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
-
- chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
- chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
- }
-
- if (req->src.format == MDP_Y_CBCR_H2V2 ||
- req->src.format == MDP_Y_CRCB_H2V2) {
- chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
- chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
- >> 1;
- chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
- chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
- }
-
- chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
- chroma_interp[IMG_LEFT];
- chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
- chroma_bound[IMG_RIGHT];
- chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
- chroma_interp[IMG_TOP];
- chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
- chroma_bound[IMG_BOTTOM];
-
- if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
- chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
- chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
- chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
- luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
- luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
- luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
- luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
- return -1;
-
- regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
- regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
- regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
- regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
- regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
- regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
- regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
- regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
- return 0;
-}
-
-static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs)
-{
- uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
- uint32_t scale_factor_x, scale_factor_y;
- uint32_t downscale;
- uint32_t dst_w, dst_h;
-
- if (req->flags & MDP_ROT_90) {
- dst_w = req->dst_rect.h;
- dst_h = req->dst_rect.w;
- } else {
- dst_w = req->dst_rect.w;
- dst_h = req->dst_rect.h;
- }
- if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) &&
- !(req->flags & MDP_BLUR)) {
+ if ((req->src_rect.w == dst_rect.w) && (req->src_rect.h == dst_rect.h)
+ && !(req->flags & MDP_BLUR)) {
regs->phasex_init = 0;
regs->phasey_init = 0;
regs->phasex_step = 0;
@@ -455,73 +331,30 @@
return 0;
}
- if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x,
- &phase_step_x) ||
- scale_params(req->src_rect.h, dst_h, 1, &phase_init_y,
- &phase_step_y))
+ if (mdp_ppp_cfg_scale(mdp, regs, &req->src_rect, &dst_rect,
+ req->src.format, req->dst.format)) {
+ DLOG("crap, bad scale\n");
return -1;
-
- scale_factor_x = (dst_w * 10) / req->src_rect.w;
- scale_factor_y = (dst_h * 10) / req->src_rect.h;
-
- if (scale_factor_x > 8)
- downscale = MDP_DOWNSCALE_PT8TO1;
- else if (scale_factor_x > 6)
- downscale = MDP_DOWNSCALE_PT6TOPT8;
- else if (scale_factor_x > 4)
- downscale = MDP_DOWNSCALE_PT4TOPT6;
- else
- downscale = MDP_DOWNSCALE_PT2TOPT4;
- if (downscale != downscale_x_table) {
- load_scale_table(mdp, mdp_downscale_x_table[downscale], 64);
- downscale_x_table = downscale;
}
- if (scale_factor_y > 8)
- downscale = MDP_DOWNSCALE_PT8TO1;
- else if (scale_factor_y > 6)
- downscale = MDP_DOWNSCALE_PT6TOPT8;
- else if (scale_factor_y > 4)
- downscale = MDP_DOWNSCALE_PT4TOPT6;
- else
- downscale = MDP_DOWNSCALE_PT2TOPT4;
- if (downscale != downscale_y_table) {
- load_scale_table(mdp, mdp_downscale_y_table[downscale], 64);
- downscale_y_table = downscale;
- }
-
- regs->phasex_init = phase_init_x;
- regs->phasey_init = phase_init_y;
- regs->phasex_step = phase_step_x;
- regs->phasey_step = phase_step_y;
regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
return 0;
-
}
-static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs)
+static void blit_blur(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct ppp_regs *regs)
{
+ int ret;
if (!(req->flags & MDP_BLUR))
return;
- if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
- downscale_y_table == MDP_DOWNSCALE_BLUR)) {
- load_scale_table(mdp, mdp_gaussian_blur_table, 128);
- downscale_x_table = MDP_DOWNSCALE_BLUR;
- downscale_y_table = MDP_DOWNSCALE_BLUR;
- }
+ ret = mdp_ppp_load_blur(mdp);
+ if (ret)
+ return;
regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
}
-
-#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp)
-
-#define Y_TO_CRCB_RATIO(format) \
- ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\
- (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1)
-
static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp,
uint32_t *len0, uint32_t *len1)
{
@@ -534,7 +367,7 @@
static int valid_src_dst(unsigned long src_start, unsigned long src_len,
unsigned long dst_start, unsigned long dst_len,
- struct mdp_blit_req *req, struct mdp_regs *regs)
+ struct mdp_blit_req *req, struct ppp_regs *regs)
{
unsigned long src_min_ok = src_start;
unsigned long src_max_ok = src_start + src_len;
@@ -574,83 +407,151 @@
return 1;
}
-
-static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs,
+static void flush_imgs(struct mdp_blit_req *req, struct ppp_regs *regs,
struct file *src_file, struct file *dst_file)
{
+#ifdef CONFIG_ANDROID_PMEM
+ uint32_t src0_len, src1_len, dst0_len, dst1_len;
+
+ /* flush src images to memory before dma to mdp */
+ get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len,
+ &src1_len);
+ flush_pmem_file(src_file, req->src.offset, src0_len);
+ if (IS_PSEUDOPLNR(req->src.format))
+ flush_pmem_file(src_file, req->src.offset + src0_len,
+ src1_len);
+
+ /* flush dst images */
+ get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len,
+ &dst1_len);
+ flush_pmem_file(dst_file, req->dst.offset, dst0_len);
+ if (IS_PSEUDOPLNR(req->dst.format))
+ flush_pmem_file(dst_file, req->dst.offset + dst0_len,
+ dst1_len);
+#endif
}
-static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect,
- uint32_t base, uint32_t bpp, uint32_t cfg,
- uint32_t *addr, uint32_t *ystride)
+static uint32_t get_chroma_base(struct mdp_img *img, uint32_t base,
+ uint32_t bpp)
{
- uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
- uint32_t compress_h = 2;
- uint32_t offset;
+ uint32_t addr = 0;
- if (IS_PSEUDOPLNR(img->format)) {
- offset = (rect->x / compress_h) * compress_h;
- offset += rect->y == 0 ? 0 :
- ((rect->y + 1) / compress_v) * img->width;
- *addr = base + (img->width * img->height * bpp);
- *addr += offset * bpp;
- *ystride |= *ystride << 16;
- } else {
- *addr = 0;
- }
+ if (IS_PSEUDOPLNR(img->format))
+ addr = base + (img->width * img->height * bpp);
+ return addr;
}
+int mdp_get_bytes_per_pixel(int format)
+{
+ if (format < 0 || format >= MDP_IMGTYPE_LIMIT)
+ return -1;
+ return bytes_per_pixel[format];
+}
+
+#if PPP_DUMP_BLITS
+#define mdp_writel_dbg(mdp, val, reg) do { \
+ pr_info("%s: writing 0x%08x=0x%08x\n", __func__, (reg), (val));\
+ mdp_writel((mdp), (val), (reg)); \
+ } while (0)
+#else
+#define mdp_writel_dbg(mdp, val, reg) mdp_writel((mdp), (val), (reg))
+#endif
+
+
static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs, struct file *src_file,
+ struct ppp_regs *regs, struct file *src_file,
struct file *dst_file)
{
- mdp_writel(mdp, 1, 0x060);
- mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
- mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0);
- mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1);
- mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
- mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
- mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
+#if 0
+ mdp_writel_dbg(mdp, 1, MDP_PPP_CMD_MODE);
+#endif
+ mdp_writel_dbg(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
+ mdp_writel_dbg(mdp, regs->src0, PPP_ADDR_SRC0);
+ mdp_writel_dbg(mdp, regs->src1, PPP_ADDR_SRC1);
+ mdp_writel_dbg(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
+ mdp_writel_dbg(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
+ mdp_writel_dbg(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
- mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION);
- mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
- mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
- mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
- mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
+ mdp_writel_dbg(mdp, regs->op, PPP_ADDR_OPERATION);
+ mdp_writel_dbg(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
+ mdp_writel_dbg(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
+ mdp_writel_dbg(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
+ mdp_writel_dbg(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
- mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
+#ifdef CONFIG_MSM_MDP31
+ mdp_writel_dbg(mdp, regs->scale_cfg, MDP_PPP_SCALE_CONFIG);
+ mdp_writel_dbg(mdp, regs->csc_cfg, MDP_PPP_CSC_CONFIG);
+ mdp_writel_dbg(mdp, regs->src_xy, MDP_PPP_SRC_XY);
+ mdp_writel_dbg(mdp, regs->src_img_sz, MDP_PPP_SRC_IMAGE_SIZE);
+ mdp_writel_dbg(mdp, regs->dst_xy, MDP_PPP_OUT_XY);
+#else
+ /* no edge conditions to set for MDP 3.1 */
+ mdp_writel_dbg(mdp, regs->edge, PPP_ADDR_EDGE);
+#endif
+
+ mdp_writel_dbg(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
PPP_ADDR_ALPHA_TRANSP);
- mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
- mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
- mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
- mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0);
- mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1);
- mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
+ mdp_writel_dbg(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
+ mdp_writel_dbg(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
+ mdp_writel_dbg(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
+ mdp_writel_dbg(mdp, regs->dst0, PPP_ADDR_DST0);
+ mdp_writel_dbg(mdp, regs->dst1, PPP_ADDR_DST1);
+ mdp_writel_dbg(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
- mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE);
if (regs->op & PPP_OP_BLEND_ON) {
- mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0);
- mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1);
- mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE);
- mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG);
- mdp_writel(mdp, pack_pattern[req->dst.format],
- PPP_ADDR_BG_PACK_PATTERN);
+ mdp_writel_dbg(mdp, regs->bg0, PPP_ADDR_BG0);
+ mdp_writel_dbg(mdp, regs->bg1, PPP_ADDR_BG1);
+ mdp_writel_dbg(mdp, regs->bg_ystride, PPP_ADDR_BG_YSTRIDE);
+ mdp_writel_dbg(mdp, regs->bg_cfg, PPP_ADDR_BG_CFG);
+ mdp_writel_dbg(mdp, regs->bg_pack, PPP_ADDR_BG_PACK_PATTERN);
+#ifdef CONFIG_MSM_MDP31
+ mdp_writel_dbg(mdp, regs->bg_xy, MDP_PPP_BG_XY);
+ mdp_writel_dbg(mdp, regs->bg_img_sz, MDP_PPP_BG_IMAGE_SIZE);
+ mdp_writel_dbg(mdp, regs->bg_alpha_sel,
+ MDP_PPP_BLEND_BG_ALPHA_SEL);
+#endif
}
flush_imgs(req, regs, src_file, dst_file);
- mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START);
+ mdp_writel_dbg(mdp, 0x1000, MDP_DISPLAY0_START);
return 0;
}
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
+#if PPP_DUMP_BLITS
+static void mdp_dump_blit(struct mdp_blit_req *req)
+{
+ pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__,
+ req->src.width, req->src.height, req->src.format,
+ req->src.offset, req->src.memory_id);
+ pr_info("%s: dst: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__,
+ req->dst.width, req->dst.height, req->dst.format,
+ req->dst.offset, req->dst.memory_id);
+ pr_info("%s: src_rect: x=%d y=%d w=%d h=%d\n", __func__,
+ req->src_rect.x, req->src_rect.y, req->src_rect.w,
+ req->src_rect.h);
+ pr_info("%s: dst_rect: x=%d y=%d w=%d h=%d\n", __func__,
+ req->dst_rect.x, req->dst_rect.y, req->dst_rect.w,
+ req->dst_rect.h);
+ pr_info("%s: alpha=0x%08x\n", __func__, req->alpha);
+ pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask);
+ pr_info("%s: flags=%08x\n", __func__, req->flags);
+}
+#endif
+
+static int process_blit(struct mdp_info *mdp, struct mdp_blit_req *req,
struct file *src_file, unsigned long src_start, unsigned long src_len,
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
{
- struct mdp_regs regs = {0};
+ struct ppp_regs regs = {0};
+ uint32_t luma_base;
+
+#if PPP_DUMP_BLITS
+ mdp_dump_blit(req);
+#endif
if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT ||
req->dst.format >= MDP_IMGTYPE_LIMIT)) {
- printk(KERN_ERR "mpd_ppp: img is of wrong format\n");
+ printk(KERN_ERR "mdp_ppp: img is of wrong format\n");
return -EINVAL;
}
@@ -658,7 +559,15 @@
req->src_rect.y > req->src.height ||
req->dst_rect.x > req->dst.width ||
req->dst_rect.y > req->dst.height)) {
- printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n");
+ printk(KERN_ERR "mdp_ppp: img rect is outside of img!\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(req->src_rect.x + req->src_rect.w > req->src.width ||
+ req->src_rect.y + req->src_rect.h > req->src.height ||
+ req->dst_rect.x + req->dst_rect.w > req->dst.width ||
+ req->dst_rect.y + req->dst_rect.h > req->dst.height)) {
+ printk(KERN_ERR "mdp_ppp: img rect extends outside of img!\n");
return -EINVAL;
}
@@ -666,35 +575,35 @@
regs.src_cfg = src_img_cfg[req->src.format];
regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0;
regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
- regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w;
regs.src_pack = pack_pattern[req->src.format];
/* set the dest image configuration */
regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI;
- regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w;
regs.dst_pack = pack_pattern[req->dst.format];
/* set src, bpp, start pixel and ystride */
- regs.src_bpp = bytes_per_pixel[req->src.format];
- regs.src0 = src_start + req->src.offset;
+ regs.src_bpp = mdp_get_bytes_per_pixel(req->src.format);
+ luma_base = src_start + req->src.offset;
+ regs.src0 = luma_base +
+ get_luma_offset(&req->src, &req->src_rect, regs.src_bpp);
+ regs.src1 = get_chroma_base(&req->src, luma_base, regs.src_bpp);
+ regs.src1 += get_chroma_offset(&req->src, &req->src_rect, regs.src_bpp);
regs.src_ystride = req->src.width * regs.src_bpp;
- get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp,
- regs.src_cfg, ®s.src1, ®s.src_ystride);
- regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) *
- regs.src_bpp;
+ set_src_region(&req->src, &req->src_rect, ®s);
/* set dst, bpp, start pixel and ystride */
- regs.dst_bpp = bytes_per_pixel[req->dst.format];
- regs.dst0 = dst_start + req->dst.offset;
+ regs.dst_bpp = mdp_get_bytes_per_pixel(req->dst.format);
+ luma_base = dst_start + req->dst.offset;
+ regs.dst0 = luma_base +
+ get_luma_offset(&req->dst, &req->dst_rect, regs.dst_bpp);
+ regs.dst1 = get_chroma_base(&req->dst, luma_base, regs.dst_bpp);
+ regs.dst1 += get_chroma_offset(&req->dst, &req->dst_rect, regs.dst_bpp);
regs.dst_ystride = req->dst.width * regs.dst_bpp;
- get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp,
- regs.dst_cfg, ®s.dst1, ®s.dst_ystride);
- regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) *
- regs.dst_bpp;
+ set_dst_region(&req->dst_rect, ®s);
if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req,
®s)) {
- printk(KERN_ERR "mpd_ppp: final src or dst location is "
+ printk(KERN_ERR "mdp_ppp: final src or dst location is "
"invalid, are you trying to make an image too large "
"or to place it outside the screen?\n");
return -EINVAL;
@@ -708,7 +617,7 @@
regs.op |= PPP_OP_DITHER_EN;
blit_blend(req, ®s);
if (blit_scale(mdp, req, ®s)) {
- printk(KERN_ERR "mpd_ppp: error computing scale for img.\n");
+ printk(KERN_ERR "mdp_ppp: error computing scale for img.\n");
return -EINVAL;
}
blit_blur(mdp, req, ®s);
@@ -722,9 +631,188 @@
req->dst_rect.x = req->dst_rect.x & (~0x1);
req->dst_rect.w = req->dst_rect.w & (~0x1);
}
- if (get_edge_cond(req, ®s))
+
+ if (mdp_ppp_cfg_edge_cond(req, ®s))
return -EINVAL;
+ /* for simplicity, always write the chroma stride */
+ regs.src_ystride &= 0x3fff;
+ regs.src_ystride |= regs.src_ystride << 16;
+ regs.dst_ystride &= 0x3fff;
+ regs.dst_ystride |= regs.dst_ystride << 16;
+ regs.bg_ystride &= 0x3fff;
+ regs.bg_ystride |= regs.bg_ystride << 16;
+
+#if PPP_DUMP_BLITS
+ pr_info("%s: sending blit\n", __func__);
+#endif
send_blit(mdp, req, ®s, src_file, dst_file);
return 0;
}
+
+#define mdp_dump_register(mdp, reg) \
+ printk(# reg ": %08x\n", mdp_readl((mdp), (reg)))
+
+void mdp_ppp_dump_debug(const struct mdp_info *mdp)
+{
+ mdp_dump_register(mdp, MDP_TFETCH_STATUS);
+ mdp_dump_register(mdp, MDP_TFETCH_TILE_COUNT);
+ mdp_dump_register(mdp, MDP_TFETCH_FETCH_COUNT);
+ mdp_dump_register(mdp, MDP_BGTFETCH_STATUS);
+ mdp_dump_register(mdp, MDP_BGTFETCH_TILE_COUNT);
+ mdp_dump_register(mdp, MDP_BGTFETCH_FETCH_COUNT);
+ mdp_dump_register(mdp, MDP_PPP_SCALE_STATUS);
+ mdp_dump_register(mdp, MDP_PPP_BLEND_STATUS);
+ mdp_dump_register(mdp, MDP_INTR_STATUS);
+ mdp_dump_register(mdp, MDP_INTR_ENABLE);
+}
+
+static int mdp_ppp_wait(struct mdp_info *mdp)
+{
+ int ret;
+
+ ret = mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
+ if (ret)
+ mdp_ppp_dump_debug(mdp);
+ return ret;
+}
+
+static int get_img(struct mdp_img *img, struct fb_info *info,
+ unsigned long *start, unsigned long *len,
+ struct file** filep)
+{
+ int put_needed, ret = 0;
+ struct file *file;
+ unsigned long vstart;
+
+ if (!get_pmem_file(img->memory_id, start, &vstart, len, filep))
+ return 0;
+ else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len,
+ filep))
+ return 0;
+
+ file = fget_light(img->memory_id, &put_needed);
+ if (file == NULL)
+ return -1;
+
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+ *start = info->fix.smem_start;
+ *len = info->fix.smem_len;
+ ret = 0;
+ } else
+ ret = -1;
+ fput_light(file, put_needed);
+
+ return ret;
+}
+
+static void put_img(struct file *file)
+{
+ if (file) {
+ if (is_pmem_file(file))
+ put_pmem_file(file);
+ else if (is_msm_hw3d_file(file))
+ put_msm_hw3d_file(file);
+ }
+}
+
+static void dump_req(struct mdp_blit_req *req,
+ unsigned long src_start, unsigned long src_len,
+ unsigned long dst_start, unsigned long dst_len)
+{
+ pr_err("flags: 0x%x\n", req->flags);
+ pr_err("src_start: 0x%08lx\n", src_start);
+ pr_err("src_len: 0x%08lx\n", src_len);
+ pr_err("src.offset: 0x%x\n", req->src.offset);
+ pr_err("src.format: 0x%x\n", req->src.format);
+ pr_err("src.width: %d\n", req->src.width);
+ pr_err("src.height: %d\n", req->src.height);
+ pr_err("src_rect.x: %d\n", req->src_rect.x);
+ pr_err("src_rect.y: %d\n", req->src_rect.y);
+ pr_err("src_rect.w: %d\n", req->src_rect.w);
+ pr_err("src_rect.h: %d\n", req->src_rect.h);
+
+ pr_err("dst_start: 0x%08lx\n", dst_start);
+ pr_err("dst_len: 0x%08lx\n", dst_len);
+ pr_err("dst.offset: 0x%x\n", req->dst.offset);
+ pr_err("dst.format: 0x%x\n", req->dst.format);
+ pr_err("dst.width: %d\n", req->dst.width);
+ pr_err("dst.height: %d\n", req->dst.height);
+ pr_err("dst_rect.x: %d\n", req->dst_rect.x);
+ pr_err("dst_rect.y: %d\n", req->dst_rect.y);
+ pr_err("dst_rect.w: %d\n", req->dst_rect.w);
+ pr_err("dst_rect.h: %d\n", req->dst_rect.h);
+}
+
+int mdp_ppp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start, unsigned long src_len,
+ struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
+{
+ int ret;
+ mdp->enable_irq(mdp, DL0_ROI_DONE);
+ ret = process_blit(mdp, req, src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ if (unlikely(ret)) {
+ mdp->disable_irq(mdp, DL0_ROI_DONE);
+ return ret;
+ }
+ ret = mdp_ppp_wait(mdp);
+ if (unlikely(ret)) {
+ printk(KERN_ERR "%s: failed!\n", __func__);
+ pr_err("original request:\n");
+ dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
+ pr_err("dead request:\n");
+ dump_req(req, src_start, src_len, dst_start, dst_len);
+ BUG();
+ return ret;
+ }
+ return 0;
+}
+
+int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb,
+ struct mdp_blit_req *req)
+{
+ int ret;
+ unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
+ struct file *src_file = 0, *dst_file = 0;
+
+ ret = mdp_ppp_validate_blit(mdp, req);
+ if (ret)
+ return ret;
+
+ /* do this first so that if this fails, the caller can always
+ * safely call put_img */
+ if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
+ printk(KERN_ERR "mdp_ppp: could not retrieve src image from "
+ "memory\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
+ printk(KERN_ERR "mdp_ppp: could not retrieve dst image from "
+ "memory\n");
+ put_img(src_file);
+ return -EINVAL;
+ }
+ mutex_lock(&mdp_mutex);
+
+ /* transp_masking unimplemented */
+ req->transp_mask = MDP_TRANSP_NOP;
+ mdp->req = req;
+
+ ret = mdp_ppp_do_blit(mdp, req, src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+
+ put_img(src_file);
+ put_img(dst_file);
+ mutex_unlock(&mdp_mutex);
+ return ret;
+}
+
+void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask)
+{
+ if (mask & DL0_ROI_DONE)
+ wake_up(&mdp_ppp_waitqueue);
+}
+
+
diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h
new file mode 100644
index 0000000..03a1506
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp.h
@@ -0,0 +1,118 @@
+/* drivers/video/msm/mdp_ppp.h
+ *
+ * Copyright (C) 2009 Google Incorporated
+ *
+ * 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 _VIDEO_MSM_MDP_PPP_H_
+#define _VIDEO_MSM_MDP_PPP_H_
+
+#include <linux/types.h>
+
+struct ppp_regs {
+ uint32_t src0;
+ uint32_t src1;
+ uint32_t dst0;
+ uint32_t dst1;
+ uint32_t src_cfg;
+ uint32_t dst_cfg;
+ uint32_t src_pack;
+ uint32_t dst_pack;
+ uint32_t src_rect;
+ uint32_t dst_rect;
+ uint32_t src_ystride;
+ uint32_t dst_ystride;
+ uint32_t op;
+ uint32_t src_bpp;
+ uint32_t dst_bpp;
+ uint32_t edge;
+ uint32_t phasex_init;
+ uint32_t phasey_init;
+ uint32_t phasex_step;
+ uint32_t phasey_step;
+
+ uint32_t bg0;
+ uint32_t bg1;
+ uint32_t bg_cfg;
+ uint32_t bg_bpp;
+ uint32_t bg_pack;
+ uint32_t bg_ystride;
+
+#ifdef CONFIG_MSM_MDP31
+ uint32_t src_xy;
+ uint32_t src_img_sz;
+ uint32_t dst_xy;
+ uint32_t bg_xy;
+ uint32_t bg_img_sz;
+ uint32_t bg_alpha_sel;
+
+ uint32_t scale_cfg;
+ uint32_t csc_cfg;
+#endif
+};
+
+struct mdp_info;
+struct mdp_rect;
+struct mdp_blit_req;
+struct fb_info;
+
+#ifdef CONFIG_FB_MSM_MDP_PPP
+int mdp_get_bytes_per_pixel(int format);
+int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb,
+ struct mdp_blit_req *req);
+void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask);
+int mdp_ppp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start,
+ unsigned long src_len, struct file *dst_file,
+ unsigned long dst_start, unsigned long dst_len);
+
+/* these must be provided by h/w specific ppp files */
+void mdp_ppp_init_scale(struct mdp_info *mdp);
+int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs,
+ struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+ uint32_t src_format, uint32_t dst_format);
+int mdp_ppp_load_blur(struct mdp_info *mdp);
+int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
+int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req);
+int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start,
+ unsigned long src_len, struct file *dst_file,
+ unsigned long dst_start, unsigned long dst_len);
+
+#else
+
+static inline int mdp_get_bytes_per_pixel(int format) { return -1; }
+static inline int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb,
+ struct mdp_blit_req *req) { return -EINVAL; }
+static inline void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask) {}
+static inline int mdp_ppp_blit_and_wait(struct mdp_info *mdp,
+ struct mdp_blit_req *req, struct file *src_file,
+ unsigned long src_start, unsigned long src_len,
+ struct file *dst_file, unsigned long dst_start,
+ unsigned long dst_len) { return 0; }
+
+static inline void mdp_ppp_init_scale(struct mdp_info *mdp) {}
+static inline int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs,
+ struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+ uint32_t src_format, uint32_t dst_format) { return 0; }
+static inline int mdp_ppp_load_blur(struct mdp_info *mdp) { return 0; }
+static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) { return 0; }
+static inline int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req) { return -EINVAL; }
+static inline int mdp_ppp_do_blit(struct mdp_info *mdp,
+ struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start,
+ unsigned long src_len, struct file *dst_file,
+ unsigned long dst_start, unsigned long dst_len) { return 0; }
+
+
+#endif /* CONFIG_FB_MSM_MDP_PPP */
+
+#endif /* _VIDEO_MSM_MDP_PPP_H_ */
diff --git a/drivers/video/msm/mdp_scale_tables.c b/drivers/video/msm/mdp_ppp22.c
similarity index 65%
rename from drivers/video/msm/mdp_scale_tables.c
rename to drivers/video/msm/mdp_ppp22.c
index 604783b..dc4cc27 100644
--- a/drivers/video/msm/mdp_scale_tables.c
+++ b/drivers/video/msm/mdp_ppp22.c
@@ -1,4 +1,4 @@
-/* drivers/video/msm_fb/mdp_scale_tables.c
+/* drivers/video/msm/mdp_ppp22.c
*
* Copyright (C) 2007 QUALCOMM Incorporated
* Copyright (C) 2007 Google Incorporated
@@ -13,10 +13,33 @@
* GNU General Public License for more details.
*/
-#include "mdp_scale_tables.h"
-#include "mdp_hw.h"
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/msm_mdp.h>
-struct mdp_table_entry mdp_upscale_table[] = {
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+
+struct mdp_table_entry {
+ uint32_t reg;
+ uint32_t val;
+};
+
+enum {
+ MDP_DOWNSCALE_PT2TOPT4,
+ MDP_DOWNSCALE_PT4TOPT6,
+ MDP_DOWNSCALE_PT6TOPT8,
+ MDP_DOWNSCALE_PT8TO1,
+ MDP_DOWNSCALE_MAX,
+
+ /* not technically in the downscale table list */
+ MDP_DOWNSCALE_BLUR,
+};
+
+static int downscale_x_table;
+static int downscale_y_table;
+
+static struct mdp_table_entry mdp_upscale_table[] = {
{ 0x5fffc, 0x0 },
{ 0x50200, 0x7fc00000 },
{ 0x5fffc, 0xff80000d },
@@ -764,3 +787,359 @@
{ 0x5fffc, 0x20000080 },
{ 0x5037c, 0x20000080 },
};
+
+static void load_table(const struct mdp_info *mdp,
+ struct mdp_table_entry *table, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ mdp_writel(mdp, table[i].val, table[i].reg);
+}
+
+enum {
+ IMG_LEFT,
+ IMG_RIGHT,
+ IMG_TOP,
+ IMG_BOTTOM,
+};
+
+static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
+ uint32_t *interp1, uint32_t *interp2,
+ uint32_t *repeat1, uint32_t *repeat2) {
+ if (src > 3 * dst) {
+ *interp1 = 0;
+ *interp2 = src - 1;
+ *repeat1 = 0;
+ *repeat2 = 0;
+ } else if (src == 3 * dst) {
+ *interp1 = 0;
+ *interp2 = src;
+ *repeat1 = 0;
+ *repeat2 = 1;
+ } else if (src > dst && src < 3 * dst) {
+ *interp1 = -1;
+ *interp2 = src;
+ *repeat1 = 1;
+ *repeat2 = 1;
+ } else if (src == dst) {
+ *interp1 = -1;
+ *interp2 = src + 1;
+ *repeat1 = 1;
+ *repeat2 = 2;
+ } else {
+ *interp1 = -2;
+ *interp2 = src + 1;
+ *repeat1 = 2;
+ *repeat2 = 2;
+ }
+ *interp1 += src_coord;
+ *interp2 += src_coord;
+}
+
+int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs)
+{
+ int32_t luma_interp[4];
+ int32_t luma_repeat[4];
+ int32_t chroma_interp[4];
+ int32_t chroma_bound[4];
+ int32_t chroma_repeat[4];
+ uint32_t dst_w, dst_h;
+
+ memset(&luma_interp, 0, sizeof(int32_t) * 4);
+ memset(&luma_repeat, 0, sizeof(int32_t) * 4);
+ memset(&chroma_interp, 0, sizeof(int32_t) * 4);
+ memset(&chroma_bound, 0, sizeof(int32_t) * 4);
+ memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
+ regs->edge = 0;
+
+ if (req->flags & MDP_ROT_90) {
+ dst_w = req->dst_rect.h;
+ dst_h = req->dst_rect.w;
+ } else {
+ dst_w = req->dst_rect.w;
+ dst_h = req->dst_rect.h;
+ }
+
+ if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
+ get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
+ &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
+ &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
+ get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
+ &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
+ &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
+ } else {
+ luma_interp[IMG_LEFT] = req->src_rect.x;
+ luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
+ luma_interp[IMG_TOP] = req->src_rect.y;
+ luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
+ luma_repeat[IMG_LEFT] = 0;
+ luma_repeat[IMG_TOP] = 0;
+ luma_repeat[IMG_RIGHT] = 0;
+ luma_repeat[IMG_BOTTOM] = 0;
+ }
+
+ chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
+ chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
+ chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
+ chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
+
+ chroma_bound[IMG_LEFT] = req->src_rect.x;
+ chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
+ chroma_bound[IMG_TOP] = req->src_rect.y;
+ chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
+
+ if (IS_YCRCB(req->src.format)) {
+ chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
+ chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
+
+ chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
+ chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
+ }
+
+ if (req->src.format == MDP_Y_CBCR_H2V2 ||
+ req->src.format == MDP_Y_CRCB_H2V2) {
+ chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
+ chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
+ >> 1;
+ chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
+ chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
+ }
+
+ chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
+ chroma_interp[IMG_LEFT];
+ chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
+ chroma_bound[IMG_RIGHT];
+ chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
+ chroma_interp[IMG_TOP];
+ chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
+ chroma_bound[IMG_BOTTOM];
+
+ if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
+ chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
+ chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
+ chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
+ luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
+ luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
+ luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
+ luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
+ return -1;
+
+ regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
+ regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
+ regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
+ regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
+ regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
+ regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
+ regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
+ regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
+ return 0;
+}
+
+#define ONE_HALF (1LL << 32)
+#define ONE (1LL << 33)
+#define TWO (2LL << 33)
+#define THREE (3LL << 33)
+#define FRAC_MASK (ONE - 1)
+#define INT_MASK (~FRAC_MASK)
+
+static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
+ uint32_t *phase_init, uint32_t *phase_step)
+{
+ /* to improve precicsion calculations are done in U31.33 and converted
+ * to U3.29 at the end */
+ int64_t k1, k2, k3, k4, tmp;
+ uint64_t n, d, os, os_p, od, od_p, oreq;
+ unsigned rpa = 0;
+ int64_t ip64, delta;
+
+ if (dim_out % 3 == 0)
+ rpa = !(dim_in % (dim_out / 3));
+
+ n = ((uint64_t)dim_out) << 34;
+ d = dim_in;
+ if (!d)
+ return -1;
+ do_div(n, d);
+ k3 = (n + 1) >> 1;
+ if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31))
+ return -1;
+
+ n = ((uint64_t)dim_in) << 34;
+ d = (uint64_t)dim_out;
+ if (!d)
+ return -1;
+ do_div(n, d);
+ k1 = (n + 1) >> 1;
+ k2 = (k1 - ONE) >> 1;
+
+ *phase_init = (int)(k2 >> 4);
+ k4 = (k3 - ONE) >> 1;
+
+ if (rpa) {
+ os = ((uint64_t)origin << 33) - ONE_HALF;
+ tmp = (dim_out * os) + ONE_HALF;
+ if (!dim_in)
+ return -1;
+ do_div(tmp, dim_in);
+ od = tmp - ONE_HALF;
+ } else {
+ os = ((uint64_t)origin << 1) - 1;
+ od = (((k3 * os) >> 1) + k4);
+ }
+
+ od_p = od & INT_MASK;
+ if (od_p != od)
+ od_p += ONE;
+
+ if (rpa) {
+ tmp = (dim_in * od_p) + ONE_HALF;
+ if (!dim_in)
+ return -1;
+ do_div(tmp, dim_in);
+ os_p = tmp - ONE_HALF;
+ } else {
+ os_p = ((k1 * (od_p >> 33)) + k2);
+ }
+
+ oreq = (os_p & INT_MASK) - ONE;
+
+ ip64 = os_p - oreq;
+ delta = ((int64_t)(origin) << 33) - oreq;
+ ip64 -= delta;
+ /* limit to valid range before the left shift */
+ delta = (ip64 & (1LL << 63)) ? 4 : -4;
+ delta <<= 33;
+ while (abs((int)(ip64 >> 33)) > 4)
+ ip64 += delta;
+ *phase_init = (int)(ip64 >> 4);
+ *phase_step = (uint32_t)(k1 >> 4);
+ return 0;
+}
+
+int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs,
+ struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+ uint32_t src_format, uint32_t dst_format)
+{
+ int downscale;
+ uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
+ uint32_t scale_factor_x, scale_factor_y;
+
+ if (scale_params(src_rect->w, dst_rect->w, 1, &phase_init_x,
+ &phase_step_x) ||
+ scale_params(src_rect->h, dst_rect->h, 1, &phase_init_y,
+ &phase_step_y))
+ return -1;
+
+ regs->phasex_init = phase_init_x;
+ regs->phasey_init = phase_init_y;
+ regs->phasex_step = phase_step_x;
+ regs->phasey_step = phase_step_y;
+
+ scale_factor_x = (dst_rect->w * 10) / src_rect->w;
+ scale_factor_y = (dst_rect->h * 10) / src_rect->h;
+
+ if (scale_factor_x > 8)
+ downscale = MDP_DOWNSCALE_PT8TO1;
+ else if (scale_factor_x > 6)
+ downscale = MDP_DOWNSCALE_PT6TOPT8;
+ else if (scale_factor_x > 4)
+ downscale = MDP_DOWNSCALE_PT4TOPT6;
+ else
+ downscale = MDP_DOWNSCALE_PT2TOPT4;
+
+ if (downscale != downscale_x_table) {
+ load_table(mdp, mdp_downscale_x_table[downscale], 64);
+ downscale_x_table = downscale;
+ }
+
+ if (scale_factor_y > 8)
+ downscale = MDP_DOWNSCALE_PT8TO1;
+ else if (scale_factor_y > 6)
+ downscale = MDP_DOWNSCALE_PT6TOPT8;
+ else if (scale_factor_y > 4)
+ downscale = MDP_DOWNSCALE_PT4TOPT6;
+ else
+ downscale = MDP_DOWNSCALE_PT2TOPT4;
+
+ if (downscale != downscale_y_table) {
+ load_table(mdp, mdp_downscale_y_table[downscale], 64);
+ downscale_y_table = downscale;
+ }
+
+ return 0;
+}
+
+
+int mdp_ppp_load_blur(struct mdp_info *mdp)
+{
+ if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
+ downscale_y_table == MDP_DOWNSCALE_BLUR)) {
+ load_table(mdp, mdp_gaussian_blur_table, 128);
+ downscale_x_table = MDP_DOWNSCALE_BLUR;
+ downscale_y_table = MDP_DOWNSCALE_BLUR;
+ }
+
+ return 0;
+}
+
+void mdp_ppp_init_scale(struct mdp_info *mdp)
+{
+ downscale_x_table = MDP_DOWNSCALE_MAX;
+ downscale_y_table = MDP_DOWNSCALE_MAX;
+
+ load_table(mdp, mdp_upscale_table, ARRAY_SIZE(mdp_upscale_table));
+}
+
+int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req)
+{
+ /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
+ if (unlikely(req->src_rect.h == 0 ||
+ req->src_rect.w == 0)) {
+ pr_info("mdp_ppp: src img of zero size!\n");
+ return -EINVAL;
+ }
+ if (unlikely(req->dst_rect.h == 0 ||
+ req->dst_rect.w == 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start,
+ unsigned long src_len, struct file *dst_file,
+ unsigned long dst_start, unsigned long dst_len)
+{
+ int ret;
+
+ if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
+ req->alpha != MDP_ALPHA_NOP ||
+ HAS_ALPHA(req->src.format)) &&
+ (req->flags & MDP_ROT_90 &&
+ req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
+ int i;
+ unsigned int tiles = req->dst_rect.h / 16;
+ unsigned int remainder = req->dst_rect.h % 16;
+ req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
+ req->dst_rect.h = 16;
+ for (i = 0; i < tiles; i++) {
+ ret = mdp_ppp_blit_and_wait(mdp, req,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ if (ret)
+ goto end;
+ req->dst_rect.y += 16;
+ req->src_rect.x += req->src_rect.w;
+ }
+ if (!remainder)
+ goto end;
+ req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
+ req->dst_rect.h = remainder;
+ }
+
+ ret = mdp_ppp_blit_and_wait(mdp, req,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+end:
+ return ret;
+}
diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c
new file mode 100644
index 0000000..da4bb02
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp31.c
@@ -0,0 +1,637 @@
+/* drivers/video/msm/mdp_ppp31.c
+ *
+ * Copyright (C) 2009 QUALCOMM Incorporated
+ * Copyright (C) 2009 Google Incorporated
+ *
+ * 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/kernel.h>
+#include <asm/io.h>
+#include <linux/msm_mdp.h>
+
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+
+#define NUM_COEFFS 32
+
+struct mdp_scale_coeffs {
+ uint16_t c[4][NUM_COEFFS];
+};
+
+struct mdp_scale_tbl_info {
+ uint16_t offset;
+ uint32_t set:2;
+ int use_pr;
+ struct mdp_scale_coeffs coeffs;
+};
+
+enum {
+ MDP_SCALE_PT2TOPT4,
+ MDP_SCALE_PT4TOPT6,
+ MDP_SCALE_PT6TOPT8,
+ MDP_SCALE_PT8TO8,
+ MDP_SCALE_MAX,
+};
+
+static struct mdp_scale_coeffs mdp_scale_pr_coeffs = {
+ .c = {
+ [0] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ [1] = {
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ [2] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ },
+ [3] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ },
+};
+
+static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = {
+ [ MDP_SCALE_PT2TOPT4 ] = {
+ .offset = 0,
+ .set = MDP_PPP_SCALE_COEFF_D0_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 131, 131, 130, 129, 128, 127, 127, 126,
+ 125, 125, 124, 123, 123, 121, 120, 119,
+ 119, 118, 117, 117, 116, 115, 115, 114,
+ 113, 112, 111, 110, 109, 109, 108, 107,
+ },
+ [1] = {
+ 141, 140, 140, 140, 140, 139, 138, 138,
+ 138, 137, 137, 137, 136, 137, 137, 137,
+ 136, 136, 136, 135, 135, 135, 134, 134,
+ 134, 134, 134, 133, 133, 132, 132, 132,
+ },
+ [2] = {
+ 132, 132, 132, 133, 133, 134, 134, 134,
+ 134, 134, 135, 135, 135, 136, 136, 136,
+ 137, 137, 137, 136, 137, 137, 137, 138,
+ 138, 138, 139, 140, 140, 140, 140, 141,
+ },
+ [3] = {
+ 107, 108, 109, 109, 110, 111, 112, 113,
+ 114, 115, 115, 116, 117, 117, 118, 119,
+ 119, 120, 121, 123, 123, 124, 125, 125,
+ 126, 127, 127, 128, 129, 130, 131, 131,
+ }
+ },
+ },
+ [ MDP_SCALE_PT4TOPT6 ] = {
+ .offset = 32,
+ .set = MDP_PPP_SCALE_COEFF_D1_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 136, 132, 128, 123, 119, 115, 111, 107,
+ 103, 98, 95, 91, 87, 84, 80, 76,
+ 73, 69, 66, 62, 59, 57, 54, 50,
+ 47, 44, 41, 39, 36, 33, 32, 29,
+ },
+ [1] = {
+ 206, 205, 204, 204, 201, 200, 199, 197,
+ 196, 194, 191, 191, 189, 185, 184, 182,
+ 180, 178, 176, 173, 170, 168, 165, 162,
+ 160, 157, 155, 152, 148, 146, 142, 140,
+ },
+ [2] = {
+ 140, 142, 146, 148, 152, 155, 157, 160,
+ 162, 165, 168, 170, 173, 176, 178, 180,
+ 182, 184, 185, 189, 191, 191, 194, 196,
+ 197, 199, 200, 201, 204, 204, 205, 206,
+ },
+ [3] = {
+ 29, 32, 33, 36, 39, 41, 44, 47,
+ 50, 54, 57, 59, 62, 66, 69, 73,
+ 76, 80, 84, 87, 91, 95, 98, 103,
+ 107, 111, 115, 119, 123, 128, 132, 136,
+ },
+ },
+ },
+ [ MDP_SCALE_PT6TOPT8 ] = {
+ .offset = 64,
+ .set = MDP_PPP_SCALE_COEFF_D2_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 104, 96, 89, 82, 75, 68, 61, 55,
+ 49, 43, 38, 33, 28, 24, 20, 16,
+ 12, 9, 6, 4, 2, 0, -2, -4,
+ -5, -6, -7, -7, -8, -8, -8, -8,
+ },
+ [1] = {
+ 303, 303, 302, 300, 298, 296, 293, 289,
+ 286, 281, 276, 270, 265, 258, 252, 245,
+ 238, 230, 223, 214, 206, 197, 189, 180,
+ 172, 163, 154, 145, 137, 128, 120, 112,
+ },
+ [2] = {
+ 112, 120, 128, 137, 145, 154, 163, 172,
+ 180, 189, 197, 206, 214, 223, 230, 238,
+ 245, 252, 258, 265, 270, 276, 281, 286,
+ 289, 293, 296, 298, 300, 302, 303, 303,
+ },
+ [3] = {
+ -8, -8, -8, -8, -7, -7, -6, -5,
+ -4, -2, 0, 2, 4, 6, 9, 12,
+ 16, 20, 24, 28, 33, 38, 43, 49,
+ 55, 61, 68, 75, 82, 89, 96, 104,
+ },
+ },
+ },
+ [ MDP_SCALE_PT8TO8 ] = {
+ .offset = 96,
+ .set = MDP_PPP_SCALE_COEFF_U1_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 0, -7, -13, -19, -24, -28, -32, -34,
+ -37, -39, -40, -41, -41, -41, -40, -40,
+ -38, -37, -35, -33, -31, -29, -26, -24,
+ -21, -18, -15, -13, -10, -7, -5, -2,
+ },
+ [1] = {
+ 511, 507, 501, 494, 485, 475, 463, 450,
+ 436, 422, 405, 388, 370, 352, 333, 314,
+ 293, 274, 253, 233, 213, 193, 172, 152,
+ 133, 113, 95, 77, 60, 43, 28, 13,
+ },
+ [2] = {
+ 0, 13, 28, 43, 60, 77, 95, 113,
+ 133, 152, 172, 193, 213, 233, 253, 274,
+ 294, 314, 333, 352, 370, 388, 405, 422,
+ 436, 450, 463, 475, 485, 494, 501, 507,
+ },
+ [3] = {
+ 0, -2, -5, -7, -10, -13, -15, -18,
+ -21, -24, -26, -29, -31, -33, -35, -37,
+ -38, -40, -40, -41, -41, -41, -40, -39,
+ -37, -34, -32, -28, -24, -19, -13, -7,
+ },
+ },
+ },
+};
+
+static void load_table(const struct mdp_info *mdp, int scale, int use_pr)
+{
+ int i;
+ uint32_t val;
+ struct mdp_scale_coeffs *coeffs;
+ struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale];
+
+ if (use_pr == tbl->use_pr)
+ return;
+
+ tbl->use_pr = use_pr;
+ if (!use_pr)
+ coeffs = &tbl->coeffs;
+ else
+ coeffs = &mdp_scale_pr_coeffs;
+
+ for (i = 0; i < NUM_COEFFS; ++i) {
+ val = ((coeffs->c[1][i] & 0x3ff) << 16) |
+ (coeffs->c[0][i] & 0x3ff);
+ mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i));
+
+ val = ((coeffs->c[3][i] & 0x3ff) << 16) |
+ (coeffs->c[2][i] & 0x3ff);
+ mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i));
+ }
+}
+
+#define SCALER_PHASE_BITS 29
+static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler,
+ uint32_t *phase_init, uint32_t *phase_step)
+{
+ uint64_t src = dim_in;
+ uint64_t dst = dim_out;
+ uint64_t numer;
+ uint64_t denom;
+
+ *phase_init = 0;
+
+ if (dst == 1) {
+ /* if destination is 1 pixel wide, the value of phase_step
+ * is unimportant. */
+ *phase_step = (uint32_t) (src << SCALER_PHASE_BITS);
+ if (scaler == MDP_PPP_SCALER_FIR)
+ *phase_init =
+ (uint32_t) ((src - 1) << SCALER_PHASE_BITS);
+ return;
+ }
+
+ if (scaler == MDP_PPP_SCALER_FIR) {
+ numer = (src - 1) << SCALER_PHASE_BITS;
+ denom = dst - 1;
+ /* we want to round up the result*/
+ numer += denom - 1;
+ } else {
+ numer = src << SCALER_PHASE_BITS;
+ denom = dst;
+ }
+
+ do_div(numer, denom);
+ *phase_step = (uint32_t) numer;
+}
+
+static int scale_idx(int factor)
+{
+ int idx;
+
+ if (factor > 80)
+ idx = MDP_SCALE_PT8TO8;
+ else if (factor > 60)
+ idx = MDP_SCALE_PT6TOPT8;
+ else if (factor > 40)
+ idx = MDP_SCALE_PT4TOPT6;
+ else
+ idx = MDP_SCALE_PT2TOPT4;
+
+ return idx;
+}
+
+int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs,
+ struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+ uint32_t src_format, uint32_t dst_format)
+{
+ uint32_t x_fac;
+ uint32_t y_fac;
+ uint32_t scaler_x = MDP_PPP_SCALER_FIR;
+ uint32_t scaler_y = MDP_PPP_SCALER_FIR;
+ // Don't use pixel repeat mode, it looks bad
+ int use_pr = 0;
+ int x_idx;
+ int y_idx;
+
+ if (unlikely(src_rect->w > 2048 || src_rect->h > 2048))
+ return -ENOTSUPP;
+
+ x_fac = (dst_rect->w * 100) / src_rect->w;
+ y_fac = (dst_rect->h * 100) / src_rect->h;
+
+ /* if down-scaling by a factor smaller than 1/4, use M/N */
+ scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+ scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+ scale_params(src_rect->w, dst_rect->w, scaler_x, ®s->phasex_init,
+ ®s->phasex_step);
+ scale_params(src_rect->h, dst_rect->h, scaler_y, ®s->phasey_init,
+ ®s->phasey_step);
+
+ x_idx = scale_idx(x_fac);
+ y_idx = scale_idx(y_fac);
+ load_table(mdp, x_idx, use_pr);
+ load_table(mdp, y_idx, use_pr);
+
+ regs->scale_cfg = 0;
+ // Enable SVI when source or destination is YUV
+ if (!IS_RGB(src_format) && !IS_RGB(dst_format))
+ regs->scale_cfg |= (1 << 6);
+ regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) |
+ (mdp_scale_tbl[x_idx].set << 4);
+ regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1);
+
+ return 0;
+}
+
+int mdp_ppp_load_blur(struct mdp_info *mdp)
+{
+ return -ENOTSUPP;
+}
+
+int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs)
+{
+ return 0;
+}
+
+void mdp_ppp_init_scale(struct mdp_info *mdp)
+{
+ int scale;
+ for (scale = 0; scale < MDP_SCALE_MAX; ++scale)
+ load_table(mdp, scale, 0);
+}
+
+/* Splits a blit into two horizontal stripes. Used to work around MDP bugs */
+static int blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start, unsigned long src_len,
+ struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
+{
+ int ret;
+ struct mdp_blit_req splitreq;
+ int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
+ int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
+
+ splitreq = *req;
+ /* break dest roi at height*/
+ d_x_0 = d_x_1 = req->dst_rect.x;
+ d_w_0 = d_w_1 = req->dst_rect.w;
+ d_y_0 = req->dst_rect.y;
+ if (req->dst_rect.h % 32 == 3)
+ d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
+ else
+ d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
+ d_h_0 = req->dst_rect.h - d_h_1;
+ d_y_1 = d_y_0 + d_h_0;
+ if (req->dst_rect.h == 3) {
+ d_h_1 = 2;
+ d_h_0 = 2;
+ d_y_1 = d_y_0 + 1;
+ }
+ /* break source roi */
+ if (splitreq.flags & MDP_ROT_90) {
+ s_y_0 = s_y_1 = req->src_rect.y;
+ s_h_0 = s_h_1 = req->src_rect.h;
+ s_x_0 = req->src_rect.x;
+ s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
+ s_w_0 = req->src_rect.w - s_w_1;
+ s_x_1 = s_x_0 + s_w_0;
+ if (d_h_1 >= 8 * s_w_1) {
+ s_w_1++;
+ s_x_1--;
+ }
+ } else {
+ s_x_0 = s_x_1 = req->src_rect.x;
+ s_w_0 = s_w_1 = req->src_rect.w;
+ s_y_0 = req->src_rect.y;
+ s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
+ s_h_0 = req->src_rect.h - s_h_1;
+ s_y_1 = s_y_0 + s_h_0;
+ if (d_h_1 >= 8 * s_h_1) {
+ s_h_1++;
+ s_y_1--;
+ }
+ }
+
+ /* blit first region */
+ if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
+ ((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ } else {
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ }
+ ret = mdp_ppp_blit_and_wait(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ if (ret)
+ return ret;
+
+ /* blit second region */
+ if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
+ ((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ } else {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ }
+ ret = mdp_ppp_blit_and_wait(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ return ret;
+}
+
+/* Splits a blit into two vertical stripes. Used to work around MDP bugs */
+static int blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start, unsigned long src_len,
+ struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
+{
+ int ret;
+ struct mdp_blit_req splitreq;
+ int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
+ int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
+ splitreq = *req;
+
+ /* break dest roi at width*/
+ d_y_0 = d_y_1 = req->dst_rect.y;
+ d_h_0 = d_h_1 = req->dst_rect.h;
+ d_x_0 = req->dst_rect.x;
+ if (req->dst_rect.w % 32 == 6)
+ d_w_1 = req->dst_rect.w / 2 - 1;
+ else if (req->dst_rect.w % 2 == 0)
+ d_w_1 = req->dst_rect.w / 2;
+ else if (req->dst_rect.w % 32 == 3)
+ d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
+ else
+ d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
+ d_w_0 = req->dst_rect.w - d_w_1;
+ d_x_1 = d_x_0 + d_w_0;
+ if (req->dst_rect.w == 3) {
+ d_w_1 = 2;
+ d_w_0 = 2;
+ d_x_1 = d_x_0 + 1;
+ }
+
+ /* break src roi at height or width*/
+ if (splitreq.flags & MDP_ROT_90) {
+ s_x_0 = s_x_1 = req->src_rect.x;
+ s_w_0 = s_w_1 = req->src_rect.w;
+ s_y_0 = req->src_rect.y;
+ s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w;
+ s_h_0 = req->src_rect.h - s_h_1;
+ s_y_1 = s_y_0 + s_h_0;
+ if (d_w_1 >= 8 * s_h_1) {
+ s_h_1++;
+ s_y_1--;
+ }
+ } else {
+ s_y_0 = s_y_1 = req->src_rect.y;
+ s_h_0 = s_h_1 = req->src_rect.h;
+ s_x_0 = req->src_rect.x;
+ s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w;
+ s_w_0 = req->src_rect.w - s_w_1;
+ s_x_1 = s_x_0 + s_w_0;
+ if (d_w_1 >= 8 * s_w_1) {
+ s_w_1++;
+ s_x_1--;
+ }
+ }
+
+ /* blit first region */
+ if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
+ ((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ } else {
+ splitreq.src_rect.h = s_h_0;
+ splitreq.src_rect.y = s_y_0;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_0;
+ splitreq.src_rect.w = s_w_0;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ }
+
+ if (unlikely((splitreq.dst_rect.h != 1) &&
+ ((splitreq.dst_rect.h % 32 == 3) ||
+ (splitreq.dst_rect.h % 32) == 1)))
+ ret = blit_split_height(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ else
+ ret = mdp_ppp_blit_and_wait(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ if (ret)
+ return ret;
+
+ /* blit second region */
+ if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
+ ((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_1;
+ splitreq.dst_rect.y = d_y_1;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_1;
+ splitreq.dst_rect.w = d_w_1;
+ } else {
+ splitreq.src_rect.h = s_h_1;
+ splitreq.src_rect.y = s_y_1;
+ splitreq.dst_rect.h = d_h_0;
+ splitreq.dst_rect.y = d_y_0;
+ splitreq.src_rect.x = s_x_1;
+ splitreq.src_rect.w = s_w_1;
+ splitreq.dst_rect.x = d_x_0;
+ splitreq.dst_rect.w = d_w_0;
+ }
+
+ if (unlikely((splitreq.dst_rect.h != 1) &&
+ ((splitreq.dst_rect.h % 32 == 3) ||
+ (splitreq.dst_rect.h % 32) == 1)))
+ ret = blit_split_height(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ else
+ ret = mdp_ppp_blit_and_wait(mdp, &splitreq,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ return ret;
+}
+
+
+int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req)
+{
+ if (req->flags & MDP_ROT_90) {
+ if (unlikely(((req->dst_rect.h == 1) &&
+ ((req->src_rect.w != 1) ||
+ (req->dst_rect.w != req->src_rect.h))) ||
+ ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
+ (req->dst_rect.h != req->src_rect.w))))) {
+ pr_err("mpd_ppp: error scaling when size is 1!\n");
+ return -EINVAL;
+ }
+ } else {
+ if (unlikely(((req->dst_rect.w == 1) &&
+ ((req->src_rect.w != 1) ||
+ (req->dst_rect.h != req->src_rect.h))) ||
+ ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
+ (req->dst_rect.h != req->src_rect.h))))) {
+ pr_err("mpd_ppp: error scaling when size is 1!\n");
+ return -EINVAL;
+ }
+ }
+
+ /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
+ if (unlikely(req->src_rect.h == 0 ||
+ req->src_rect.w == 0)) {
+ pr_info("mdp_ppp: src img of zero size!\n");
+ return -EINVAL;
+ }
+ if (unlikely(req->dst_rect.h == 0 ||
+ req->dst_rect.w == 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req,
+ struct file *src_file, unsigned long src_start,
+ unsigned long src_len, struct file *dst_file,
+ unsigned long dst_start, unsigned long dst_len)
+{
+ int ret;
+
+ /* Workarounds for MDP 3.1 hardware bugs */
+ if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) &&
+ (req->dst_rect.w != 1) &&
+ (((req->dst_rect.w % 8) == 6) ||
+ ((req->dst_rect.w % 32) == 3) ||
+ ((req->dst_rect.w % 32) == 1)))) {
+ ret = blit_split_width(mdp, req,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ goto end;
+ } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) &&
+ ((req->dst_rect.h % 32) == 3 ||
+ (req->dst_rect.h % 32) == 1))) {
+ ret = blit_split_height(mdp, req,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+ goto end;
+ }
+
+ ret = mdp_ppp_blit_and_wait(mdp, req,
+ src_file, src_start, src_len,
+ dst_file, dst_start, dst_len);
+end:
+ return ret;
+}
diff --git a/drivers/video/msm/mdp_scale_tables.h b/drivers/video/msm/mdp_scale_tables.h
deleted file mode 100644
index 34077b1..0000000
--- a/drivers/video/msm/mdp_scale_tables.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* drivers/video/msm_fb/mdp_scale_tables.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 _MDP_SCALE_TABLES_H_
-#define _MDP_SCALE_TABLES_H_
-
-#include <linux/types.h>
-struct mdp_table_entry {
- uint32_t reg;
- uint32_t val;
-};
-
-extern struct mdp_table_entry mdp_upscale_table[64];
-
-enum {
- MDP_DOWNSCALE_PT2TOPT4,
- MDP_DOWNSCALE_PT4TOPT6,
- MDP_DOWNSCALE_PT6TOPT8,
- MDP_DOWNSCALE_PT8TO1,
- MDP_DOWNSCALE_MAX,
-};
-
-extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry mdp_gaussian_blur_table[];
-
-#endif
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index debe593..a21a44a 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -22,6 +22,8 @@
#include <linux/freezer.h>
#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/earlysuspend.h>
#include <linux/msm_mdp.h>
#include <linux/io.h>
#include <linux/uaccess.h>
@@ -32,6 +34,12 @@
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
+#define MSMFB_DEBUG 1
+#ifdef CONFIG_FB_MSM_LOGO
+#define INIT_IMAGE_FILE "/logo.rle"
+extern int load_565rle_image(char *filename);
+#endif
+
#define PRINT_FPS 0
#define PRINT_BLIT_TIME 0
@@ -53,6 +61,9 @@
printk(KERN_INFO "msmfb: "fmt, ##args); \
} while (0)
+#define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel)
+#define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3)
+
static int msmfb_debug_mask;
module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
S_IRUGO | S_IWUSR | S_IWGRP);
@@ -78,6 +89,9 @@
} update_info;
char *black;
+ struct early_suspend earlier_suspend;
+ struct early_suspend early_suspend;
+ struct wake_lock idle_lock;
spinlock_t update_lock;
struct mutex panel_init_lock;
wait_queue_head_t frame_wq;
@@ -105,6 +119,12 @@
unsigned long irq_flags;
struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
dma_callback);
+#if PRINT_FPS
+ int64_t dt;
+ ktime_t now;
+ static int64_t frame_count;
+ static ktime_t last_sec;
+#endif
spin_lock_irqsave(&msmfb->update_lock, irq_flags);
msmfb->frame_done = msmfb->frame_requested;
@@ -113,6 +133,18 @@
DLOG(SUSPEND_RESUME, "full update completed\n");
queue_work(msmfb->resume_workqueue, &msmfb->resume_work);
}
+#if PRINT_FPS
+ now = ktime_get();
+ dt = ktime_to_ns(ktime_sub(now, last_sec));
+ frame_count++;
+ if (dt > NSEC_PER_SEC) {
+ int64_t fps = frame_count * NSEC_PER_SEC * 100;
+ frame_count = 0;
+ last_sec = ktime_get();
+ do_div(fps, dt);
+ DLOG(FPS, "fps * 100: %llu\n", fps);
+ }
+#endif
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
wake_up(&msmfb->frame_wq);
}
@@ -162,9 +194,10 @@
}
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- addr = ((msmfb->xres * (yoffset + y) + x) * 2);
+ addr = ((msmfb->xres * (yoffset + y) + x) * BYTES_PER_PIXEL(msmfb));
mdp->dma(mdp, addr + msmfb->fb->fix.smem_start,
- msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback,
+ msmfb->xres * BYTES_PER_PIXEL(msmfb), w, h, x, y,
+ &msmfb->dma_callback,
panel->interface_type);
return 0;
error:
@@ -181,6 +214,7 @@
{
struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
vsync_callback);
+ wake_unlock(&msmfb->idle_lock);
msmfb_start_dma(msmfb);
}
@@ -201,6 +235,12 @@
unsigned long irq_flags;
int sleeping;
int retry = 1;
+#if PRINT_FPS
+ ktime_t t1, t2;
+ static uint64_t pans;
+ static uint64_t dt;
+ t1 = ktime_get();
+#endif
DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
left, top, eright, ebottom, yoffset, pan_display);
@@ -220,8 +260,8 @@
sleeping = msmfb->sleeping;
/* on a full update, if the last frame has not completed, wait for it */
- if (pan_display && (msmfb->frame_requested != msmfb->frame_done ||
- sleeping == UPDATING)) {
+ if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
+ sleeping == UPDATING) {
int ret;
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
ret = wait_event_interruptible_timeout(msmfb->frame_wq,
@@ -231,6 +271,7 @@
msmfb->sleeping == UPDATING)) {
if (retry && panel->request_vsync &&
(sleeping == AWAKE)) {
+ wake_lock_timeout(&msmfb->idle_lock, HZ/4);
panel->request_vsync(panel,
&msmfb->vsync_callback);
retry = 0;
@@ -247,6 +288,21 @@
goto restart;
}
+#if PRINT_FPS
+ t2 = ktime_get();
+ if (pan_display) {
+ uint64_t temp = ktime_to_ns(ktime_sub(t2, t1));
+ do_div(temp, 1000);
+ dt += temp;
+ pans++;
+ if (pans > 1000) {
+ do_div(dt, pans);
+ DLOG(FPS, "ave_wait_time: %lld\n", dt);
+ dt = 0;
+ pans = 0;
+ }
+ }
+#endif
msmfb->frame_requested++;
/* if necessary, update the y offset, if this is the
@@ -282,6 +338,7 @@
* for 16 ms (long enough for the dma to panel) and then begin dma */
msmfb->vsync_request_time = ktime_get();
if (panel->request_vsync && (sleeping == AWAKE)) {
+ wake_lock_timeout(&msmfb->idle_lock, HZ/4);
panel->request_vsync(panel, &msmfb->vsync_callback);
} else {
if (!hrtimer_active(&msmfb->fake_vsync)) {
@@ -308,11 +365,13 @@
mutex_lock(&msmfb->panel_init_lock);
DLOG(SUSPEND_RESUME, "turning on panel\n");
if (msmfb->sleeping == UPDATING) {
+ wake_lock_timeout(&msmfb->idle_lock, HZ);
if (panel->unblank(panel)) {
printk(KERN_INFO "msmfb: panel unblank failed,"
"not starting drawing\n");
goto error;
}
+ wake_unlock(&msmfb->idle_lock);
spin_lock_irqsave(&msmfb->update_lock, irq_flags);
msmfb->sleeping = AWAKE;
wake_up(&msmfb->frame_wq);
@@ -322,17 +381,105 @@
mutex_unlock(&msmfb->panel_init_lock);
}
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* turn off the panel */
+static void msmfb_earlier_suspend(struct early_suspend *h)
+{
+ struct msmfb_info *msmfb = container_of(h, struct msmfb_info,
+ earlier_suspend);
+ struct msm_panel_data *panel = msmfb->panel;
+ unsigned long irq_flags;
+
+ mutex_lock(&msmfb->panel_init_lock);
+ msmfb->sleeping = SLEEPING;
+ wake_up(&msmfb->frame_wq);
+ spin_lock_irqsave(&msmfb->update_lock, irq_flags);
+ spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
+ wait_event_timeout(msmfb->frame_wq,
+ msmfb->frame_requested == msmfb->frame_done, HZ/10);
+
+ mdp->dma(mdp, virt_to_phys(msmfb->black), 0,
+ msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0,
+ NULL, panel->interface_type);
+ mdp->dma_wait(mdp, panel->interface_type);
+
+ /* turn off the panel */
+ panel->blank(panel);
+}
+
+static void msmfb_suspend(struct early_suspend *h)
+{
+ struct msmfb_info *msmfb = container_of(h, struct msmfb_info,
+ early_suspend);
+ struct msm_panel_data *panel = msmfb->panel;
+ /* suspend the panel */
+ panel->suspend(panel);
+ mutex_unlock(&msmfb->panel_init_lock);
+}
+
+static void msmfb_resume(struct early_suspend *h)
+{
+ struct msmfb_info *msmfb = container_of(h, struct msmfb_info,
+ early_suspend);
+ struct msm_panel_data *panel = msmfb->panel;
+ unsigned long irq_flags;
+
+ if (panel->resume(panel)) {
+ printk(KERN_INFO "msmfb: panel resume failed, not resuming "
+ "fb\n");
+ return;
+ }
+ spin_lock_irqsave(&msmfb->update_lock, irq_flags);
+ msmfb->frame_requested = msmfb->frame_done = msmfb->update_frame = 0;
+ msmfb->sleeping = WAKING;
+ DLOG(SUSPEND_RESUME, "ready, waiting for full update\n");
+ spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
+}
+#endif
static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
+ u32 size;
+
if ((var->xres != info->var.xres) ||
(var->yres != info->var.yres) ||
- (var->xres_virtual != info->var.xres_virtual) ||
- (var->yres_virtual != info->var.yres_virtual) ||
(var->xoffset != info->var.xoffset) ||
- (var->bits_per_pixel != info->var.bits_per_pixel) ||
+ (mdp->check_output_format(mdp, var->bits_per_pixel)) ||
(var->grayscale != info->var.grayscale))
return -EINVAL;
+
+ size = var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3);
+ if (size > info->fix.smem_len)
+ return -EINVAL;
+ return 0;
+}
+
+static int msmfb_set_par(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_fix_screeninfo *fix = &info->fix;
+
+ /* we only support RGB ordering for now */
+ if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) {
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ } else if (var->bits_per_pixel == 16) {
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ } else
+ return -1;
+ mdp->set_output_format(mdp, var->bits_per_pixel);
+ fix->line_length = var->xres * var->bits_per_pixel / 8;
+
return 0;
}
@@ -344,6 +491,13 @@
/* "UPDT" */
if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) &&
(var->reserved[0] == 0x54445055)) {
+#if 0
+ printk(KERN_INFO "pan frame %d-%d, rect %d %d %d %d\n",
+ msmfb->frame_requested, msmfb->frame_done,
+ var->reserved[1] & 0xffff,
+ var->reserved[1] >> 16, var->reserved[2] & 0xffff,
+ var->reserved[2] >> 16);
+#endif
msmfb_pan_update(info, var->reserved[1] & 0xffff,
var->reserved[1] >> 16,
var->reserved[2] & 0xffff,
@@ -407,13 +561,24 @@
{
void __user *argp = (void __user *)arg;
int ret;
+#if PRINT_BLIT_TIME
+ ktime_t t1, t2;
+#endif
switch (cmd) {
case MSMFB_GRP_DISP:
mdp->set_grp_disp(mdp, arg);
break;
case MSMFB_BLIT:
+#if PRINT_BLIT_TIME
+ t1 = ktime_get();
+#endif
ret = msmfb_blit(p, argp);
+#if PRINT_BLIT_TIME
+ t2 = ktime_get();
+ DLOG(BLIT_TIME, "total %lld\n",
+ ktime_to_ns(t2) - ktime_to_ns(t1));
+#endif
if (ret)
return ret;
break;
@@ -429,6 +594,7 @@
.fb_open = msmfb_open,
.fb_release = msmfb_release,
.fb_check_var = msmfb_check_var,
+ .fb_set_par = msmfb_set_par,
.fb_pan_display = msmfb_pan_display,
.fb_fillrect = msmfb_fillrect,
.fb_copyarea = msmfb_copyarea,
@@ -439,8 +605,44 @@
static unsigned PP[16];
+#if MSMFB_DEBUG
+static ssize_t debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
-#define BITS_PER_PIXEL 16
+
+static ssize_t debug_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct msmfb_info *msmfb = (struct msmfb_info *)file->private_data;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&msmfb->update_lock, irq_flags);
+ n = scnprintf(buffer, debug_bufmax, "yoffset %d\n", msmfb->yoffset);
+ n += scnprintf(buffer + n, debug_bufmax, "frame_requested %d\n",
+ msmfb->frame_requested);
+ n += scnprintf(buffer + n, debug_bufmax, "frame_done %d\n",
+ msmfb->frame_done);
+ n += scnprintf(buffer + n, debug_bufmax, "sleeping %d\n",
+ msmfb->sleeping);
+ n += scnprintf(buffer + n, debug_bufmax, "update_frame %d\n",
+ msmfb->update_frame);
+ spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
+ n++;
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static struct file_operations debug_fops = {
+ .read = debug_read,
+ .open = debug_open,
+};
+#endif
static void setup_fb_info(struct msmfb_info *msmfb)
{
@@ -457,19 +659,26 @@
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = msmfb->xres * 2;
-
fb_info->var.xres = msmfb->xres;
fb_info->var.yres = msmfb->yres;
fb_info->var.width = msmfb->panel->fb_data->width;
fb_info->var.height = msmfb->panel->fb_data->height;
fb_info->var.xres_virtual = msmfb->xres;
fb_info->var.yres_virtual = msmfb->yres * 2;
- fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
+ fb_info->var.bits_per_pixel = 16;
fb_info->var.accel_flags = 0;
fb_info->var.yoffset = 0;
if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) {
+ /* set the param in the fixed screen, so userspace can't
+ * change it. This will be used to check for the
+ * capability. */
+ fb_info->fix.reserved[0] = 0x5444;
+ fb_info->fix.reserved[1] = 0x5055;
+
+ /* This preloads the value so that if userspace doesn't
+ * change it, it will be a full update */
fb_info->var.reserved[0] = 0x54445055;
fb_info->var.reserved[1] = 0;
fb_info->var.reserved[2] = (uint16_t)msmfb->xres |
@@ -486,6 +695,8 @@
fb_info->var.blue.length = 5;
fb_info->var.blue.msb_right = 0;
+ mdp->set_output_format(mdp, fb_info->var.bits_per_pixel);
+
r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
fb_info->pseudo_palette = PP;
@@ -499,28 +710,30 @@
struct fb_info *fb = msmfb->fb;
struct resource *resource;
unsigned long size = msmfb->xres * msmfb->yres *
- (BITS_PER_PIXEL >> 3) * 2;
+ BYTES_PER_PIXEL(msmfb) * 2;
+ unsigned long resource_size;
unsigned char *fbram;
/* board file might have attached a resource describing an fb */
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!resource)
return -EINVAL;
+ resource_size = resource->end - resource->start + 1;
/* check the resource is large enough to fit the fb */
- if (resource->end - resource->start < size) {
- printk(KERN_ERR "allocated resource is too small for "
+ if (resource_size < size) {
+ printk(KERN_ERR "msmfb: allocated resource is too small for "
"fb\n");
return -ENOMEM;
}
fb->fix.smem_start = resource->start;
- fb->fix.smem_len = resource->end - resource->start;
- fbram = ioremap(resource->start,
- resource->end - resource->start);
+ fb->fix.smem_len = resource_size;
+ fbram = ioremap(resource->start, resource_size);
if (fbram == 0) {
printk(KERN_ERR "msmfb: cannot allocate fbram!\n");
return -ENOMEM;
}
+
fb->screen_base = fbram;
return 0;
}
@@ -569,6 +782,24 @@
msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres,
GFP_KERNEL);
+ wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock");
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ msmfb->early_suspend.suspend = msmfb_suspend;
+ msmfb->early_suspend.resume = msmfb_resume;
+ msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&msmfb->early_suspend);
+
+ msmfb->earlier_suspend.suspend = msmfb_earlier_suspend;
+ msmfb->earlier_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ register_early_suspend(&msmfb->earlier_suspend);
+#endif
+
+#if MSMFB_DEBUG
+ debugfs_create_file("msm_fb", S_IFREG | S_IRUGO, NULL,
+ (void *)fb->par, &debug_fops);
+#endif
+
printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n",
msmfb->xres, msmfb->yres);
@@ -586,9 +817,21 @@
msmfb->sleeping = WAKING;
+#ifdef CONFIG_FB_MSM_LOGO
+ if (!load_565rle_image(INIT_IMAGE_FILE)) {
+ /* Flip buffer */
+ msmfb->update_info.left = 0;
+ msmfb->update_info.top = 0;
+ msmfb->update_info.eright = info->var.xres;
+ msmfb->update_info.ebottom = info->var.yres;
+ msmfb_pan_update(info, 0, 0, fb->var.xres,
+ fb->var.yres, 0, 1);
+ }
+#endif
return 0;
error_register_framebuffer:
+ wake_lock_destroy(&msmfb->idle_lock);
destroy_workqueue(msmfb->resume_workqueue);
error_create_workqueue:
iounmap(fb->screen_base);
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c
index e5f7441..62a3702 100644
--- a/drivers/w1/masters/ds2482.c
+++ b/drivers/w1/masters/ds2482.c
@@ -84,7 +84,9 @@
static int ds2482_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int ds2482_remove(struct i2c_client *client);
-
+static int ds2482_suspend(struct i2c_client *client,
+ pm_message_t mesg);
+static int ds2482_resume(struct i2c_client *client);
/**
* Driver data (common to all clients)
@@ -101,6 +103,8 @@
},
.probe = ds2482_probe,
.remove = ds2482_remove,
+ .suspend = ds2482_suspend,
+ .resume = ds2482_resume,
.id_table = ds2482_id,
};
@@ -130,6 +134,52 @@
u8 reg_config;
};
+static int ds2482_write_byte(struct ds2482_data *pdev, u8 cmd)
+{
+ int ret;
+ int retry = 5;
+
+ do {
+ ret = i2c_smbus_write_byte(pdev->client, cmd);
+ if (ret >= 0)
+ break;
+ dev_warn(&pdev->client->dev,
+ "i2c write %x failed, %d, retries left %d\n",
+ cmd, ret, retry);
+ } while(retry--);
+ return ret;
+}
+
+static int ds2482_write_byte_data(struct ds2482_data *pdev, u8 cmd, u8 byte)
+{
+ int ret;
+ int retry = 5;
+
+ do {
+ ret = i2c_smbus_write_byte_data(pdev->client, cmd, byte);
+ if (ret >= 0)
+ break;
+ dev_warn(&pdev->client->dev,
+ "i2c write %x %x failed, %d, retries left %d\n",
+ cmd, byte, ret, retry);
+ } while(retry--);
+ return ret;
+}
+
+static int ds2482_read_byte(struct ds2482_data *pdev)
+{
+ int ret;
+ int retry = 5;
+
+ do {
+ ret = i2c_smbus_read_byte(pdev->client);
+ if (ret >= 0)
+ break;
+ dev_warn(&pdev->client->dev,
+ "i2c read failed, %d, retries left %d\n", ret, retry);
+ } while(retry--);
+ return ret;
+}
/**
* Sets the read pointer.
@@ -140,8 +190,7 @@
static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
{
if (pdev->read_prt != read_ptr) {
- if (i2c_smbus_write_byte_data(pdev->client,
- DS2482_CMD_SET_READ_PTR,
+ if (ds2482_write_byte_data(pdev, DS2482_CMD_SET_READ_PTR,
read_ptr) < 0)
return -1;
@@ -160,7 +209,7 @@
*/
static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
{
- if (i2c_smbus_write_byte(pdev->client, cmd) < 0)
+ if (ds2482_write_byte(pdev, cmd) < 0)
return -1;
pdev->read_prt = DS2482_PTR_CODE_STATUS;
@@ -180,7 +229,7 @@
static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
u8 cmd, u8 byte)
{
- if (i2c_smbus_write_byte_data(pdev->client, cmd, byte) < 0)
+ if (ds2482_write_byte_data(pdev, cmd, byte) < 0)
return -1;
/* all cmds leave in STATUS, except CONFIG */
@@ -231,13 +280,13 @@
*/
static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
{
- if (i2c_smbus_write_byte_data(pdev->client, DS2482_CMD_CHANNEL_SELECT,
+ if (ds2482_write_byte_data(pdev, DS2482_CMD_CHANNEL_SELECT,
ds2482_chan_wr[channel]) < 0)
return -1;
pdev->read_prt = DS2482_PTR_CODE_CHANNEL;
pdev->channel = -1;
- if (i2c_smbus_read_byte(pdev->client) == ds2482_chan_rd[channel]) {
+ if (ds2482_read_byte(pdev) == ds2482_chan_rd[channel]) {
pdev->channel = channel;
return 0;
}
@@ -361,7 +410,7 @@
ds2482_select_register(pdev, DS2482_PTR_CODE_DATA);
/* Read the data byte */
- result = i2c_smbus_read_byte(pdev->client);
+ result = ds2482_read_byte(pdev);
mutex_unlock(&pdev->access_lock);
@@ -408,6 +457,31 @@
}
+static int ds2482_suspend(struct i2c_client *client,
+ pm_message_t mesg)
+{
+ void (*set_slp_n)(int n) =
+ client->dev.platform_data;
+
+ if (set_slp_n)
+ set_slp_n(0);
+
+ return 0;
+}
+
+static int ds2482_resume(struct i2c_client *client)
+{
+ void (*set_slp_n)(int n) =
+ client->dev.platform_data;
+
+ if (set_slp_n) {
+ set_slp_n(1);
+ udelay(100);
+ }
+
+ return 0;
+}
+
static int ds2482_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -439,7 +513,7 @@
ndelay(525);
/* Read the status byte - only reset bit and line should be set */
- temp1 = i2c_smbus_read_byte(client);
+ temp1 = ds2482_read_byte(data);
if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) {
dev_warn(&client->dev, "DS2482 reset status "
"0x%02X - not a DS2482\n", temp1);
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 3ca1b92..f15ab4b 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -35,6 +35,7 @@
#define W1_THERM_DS18B20 0x28
#define W1_EEPROM_DS2431 0x2D
#define W1_FAMILY_DS2760 0x30
+#define W1_FAMILY_DS2784 0x32
#define MAXNAMELEN 32
diff --git a/include/linux/a1026.h b/include/linux/a1026.h
new file mode 100644
index 0000000..44cdf4d
--- /dev/null
+++ b/include/linux/a1026.h
@@ -0,0 +1,234 @@
+/* include/linux/a1026.h - a1026 voice processor driver
+ *
+ * Copyright (C) 2009 HTC 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 __LINUX_A1026_H
+#define __LINUX_A1026_H
+
+#include <linux/ioctl.h>
+
+#define A1026_MAX_FW_SIZE (32*1024)
+struct a1026img {
+ unsigned char *buf;
+ unsigned img_size;
+};
+
+enum A1026_PathID {
+ A1026_PATH_SUSPEND,
+ A1026_PATH_INCALL_RECEIVER,
+ A1026_PATH_INCALL_HEADSET,
+ A1026_PATH_INCALL_SPEAKER,
+ A1026_PATH_INCALL_BT,
+ A1026_PATH_VR_NO_NS_RECEIVER,
+ A1026_PATH_VR_NO_NS_HEADSET,
+ A1026_PATH_VR_NO_NS_SPEAKER,
+ A1026_PATH_VR_NO_NS_BT,
+ A1026_PATH_VR_NS_RECEIVER,
+ A1026_PATH_VR_NS_HEADSET,
+ A1026_PATH_VR_NS_SPEAKER,
+ A1026_PATH_VR_NS_BT,
+ A1026_PATH_RECORD_RECEIVER,
+ A1026_PATH_RECORD_HEADSET,
+ A1026_PATH_RECORD_SPEAKER,
+ A1026_PATH_RECORD_BT,
+ A1026_PATH_CAMCORDER,
+ A1026_PATH_INCALL_TTY
+};
+
+/* noise suppression states */
+enum A1026_NS_states {
+ A1026_NS_STATE_AUTO, /* leave mode as selected by driver */
+ A1026_NS_STATE_OFF, /* disable noise suppression */
+ A1026_NS_STATE_CT, /* force close talk mode */
+ A1026_NS_STATE_FT, /* force far talk mode */
+ A1026_NS_NUM_STATES
+};
+
+/* indicates if a1026_set_config() performs a full configuration or only
+ * a voice processing algorithm configuration */
+/* IOCTLs for Audience A1026 */
+#define A1026_IOCTL_MAGIC 'u'
+
+#define A1026_BOOTUP_INIT _IOW(A1026_IOCTL_MAGIC, 0x01, struct a1026img *)
+#define A1026_SET_CONFIG _IOW(A1026_IOCTL_MAGIC, 0x02, enum A1026_PathID)
+#define A1026_SET_NS_STATE _IOW(A1026_IOCTL_MAGIC, 0x03, enum A1026_NS_states)
+
+/* For Diag */
+#define A1026_SET_MIC_ONOFF _IOW(A1026_IOCTL_MAGIC, 0x50, unsigned)
+#define A1026_SET_MICSEL_ONOFF _IOW(A1026_IOCTL_MAGIC, 0x51, unsigned)
+#define A1026_READ_DATA _IOR(A1026_IOCTL_MAGIC, 0x52, unsigned)
+#define A1026_WRITE_MSG _IOW(A1026_IOCTL_MAGIC, 0x53, unsigned)
+#define A1026_SYNC_CMD _IO(A1026_IOCTL_MAGIC, 0x54)
+#define A1026_SET_CMD_FILE _IOW(A1026_IOCTL_MAGIC, 0x55, unsigned)
+
+#ifdef __KERNEL__
+
+/* A1026 Command codes */
+#define CtrlMode_LAL 0x0001 /* Level Active Low */
+#define CtrlMode_LAH 0x0002 /* Level Active High */
+#define CtrlMode_FE 0x0003 /* Falling Edge */
+#define CtrlMode_RE 0x0004 /* Rising Edge */
+#define A100_msg_Sync 0x80000000
+#define A100_msg_Sync_Ack 0x80000000
+
+#define A100_msg_Reset 0x8002
+#define RESET_IMMEDIATE 0x0000
+#define RESET_DELAYED 0x0001
+
+#define A100_msg_BootloadInitiate 0x8003
+#define A100_msg_GetDeviceParm 0x800B
+#define A100_msg_SetDeviceParmID 0x800C
+#define A100_msg_SetDeviceParm 0x800D
+
+/* Get/Set PCM Device Parameter ID List */
+/* PCM-0 */
+#define PCM0WordLength 0x0100
+#define PCM0DelFromFsTx 0x0101
+#define PCM0DelFromFsRx 0x0102
+#define PCM0LatchEdge 0x0103
+#define PCM0Endianness 0x0105
+#define PCM0TristateEnable 0x0107
+
+/* PCM-1 */
+#define PCM1WordLength 0x0200
+#define PCM1DelFromFsTx 0x0201
+#define PCM1DelFromFsRx 0x0202
+#define PCM1LatchEdge 0x0203
+#define PCM1Endianness 0x0205
+#define PCM1TristateEnable 0x0207
+
+/* Possible setting values for PCM I/F */
+#define PCMWordLength_16bit 0x10 /* Default */
+#define PCMWordLength_24bit 0x18
+#define PCMWordLength_32bit 0x20
+#define PCMLatchEdge_Tx_F_Rx_R 0x00 /* Tx/Rx on falling/rising edge */
+#define PCMLatchEdge_Tx_R_Rx_F 0x03 /* Tx/Rx on falling/rising edge */
+#define PCMEndianness_Little 0x00
+#define PCMEndianness_Big 0x01 /* Default */
+#define PCMTristate_Disable 0x00 /* Default */
+#define PCMTristate_Enable 0x01
+
+/* Get/Set ADC Device Parameter ID List */
+/* ADC-0 */
+#define ADC0Gain 0x0300
+#define ADC0Rate 0x0301
+#define ADC0CutoffFreq 0x0302
+
+/* ADC-1 */
+#define ADC1Gain 0x0400
+#define ADC1Rate 0x0401
+#define ADC1CutoffFreq 0x0402
+
+/* Possible setting values for ADC I/F */
+#define ADC_Gain_0db 0x00
+#define ADC_Gain_6db 0x01
+#define ADC_Gain_12db 0x02
+#define ADC_Gain_18db 0x03
+#define ADC_Gain_24db 0x04 /* Default */
+#define ADC_Gain_30db 0x05
+#define ADC_Rate_8kHz 0x00 /* Default */
+#define ADC_Rate_16kHz 0x01
+#define ADC_CutoffFreq_NO_DC_Filter 0x00
+#define ADC_CutoffFreq_59p68Hz 0x01 /* Default */
+#define ADC_CutoffFreq_7p46Hz 0x02
+#define ADC_CutoffFreq_3p73Hz 0x03
+
+/* Set Power State */
+#define A100_msg_Sleep 0x80100001
+
+/* Get/Set Algorithm Parameter command codes list */
+#define A100_msg_GetAlgorithmParm 0x8016
+#define A100_msg_SetAlgorithmParmID 0x8017
+#define A100_msg_SetAlgorithmParm 0x8018
+
+/* Get/Set Algorithm Parameter ID List (Transmit Feature) */
+#define AIS_Global_Supression_Level 0x0000
+#define Mic_Config 0x0002
+#define AEC_Mode 0x0003
+#define AEC_CNG 0x0023
+#define Output_AGC 0x0004
+#define Output_AGC_Target_Level 0x0005
+#define Output_AGC_Noise_Floor 0x0006
+#define Output_AGC_SNR_Improvement 0x0007
+#define Comfort_Noise 0x001A
+#define Comfort_Noise_Level 0x001B
+
+/* Get/Set Algorithm Parameter ID List (Receive Feature) */
+#define Speaker_Volume 0x0012
+#define VEQ_Mode 0x0009
+#define VEQ_Max_FarEnd_Limiter_Level 0x000D
+#define VEQ_Noise_Estimation_Adj 0x0025
+#define Receive_NS 0x000E
+#define Receive_NS_Level 0x000F
+#define SideTone 0x0015
+#define SideTone_Gain 0x0016
+
+/* Audio Path Commands */
+/* Get/Set Transmit Digital Input Gain */
+#define A100_msg_GetTxDigitalInputGain 0x801A
+#define A100_msg_SetTxDigitalInputGain 0x801B
+
+/* Get/Set Receive Digital Input Gain */
+#define A100_msg_GetRcvDigitalInputGain 0x8022
+#define A100_msg_SetRcvDigitalInputGain 0x8023
+
+/* Get/Set Transmit Digital Output Gain */
+#define A100_msg_GetTxDigitalOutputGain 0x801D
+#define A100_msg_SetTxDigitalOutputGain 0x8015
+
+/* Bypass */
+#define A100_msg_Bypass 0x801C /* 0ff = 0x0000; on = 0x0001 (Default) */
+#define A1026_msg_VP_ON 0x801C0001
+#define A1026_msg_VP_OFF 0x801C0000
+
+/* Diagnostic API Commands */
+#define A100_msg_GetMicRMS 0x8013
+#define A100_msg_GetMicPeak 0x8014
+#define DiagPath_Pri_Input_Mic 0x0000
+#define DiagPath_Sec_Input_Mic 0x0001
+#define DiagPath_Output_Mic 0x0002
+#define DiagPath_Far_End_Input 0x0003
+#define DiagPath_Far_End_Output 0x0004
+#define A100_msg_SwapInputCh 0x8019
+#define A100_msg_OutputKnownSig 0x801E
+
+#define A1026_msg_BOOT 0x0001
+#define A1026_msg_BOOT_ACK 0x01
+
+/* general definitions */
+#define TIMEOUT 20 /* ms */
+#define RETRY_CNT 5
+#define POLLING_RETRY_CNT 3
+#define A1026_ERROR_CODE 0xffff
+#define A1026_SLEEP 0
+#define A1026_ACTIVE 1
+#define A1026_CMD_FIFO_DEPTH 64
+#define ERROR 0xffffffff
+
+enum A1026_config_mode {
+ A1026_CONFIG_FULL,
+ A1026_CONFIG_VP
+};
+
+struct a1026_platform_data {
+ uint32_t gpio_a1026_micsel;
+ uint32_t gpio_a1026_wakeup;
+ uint32_t gpio_a1026_reset;
+ uint32_t gpio_a1026_int;
+ uint32_t gpio_a1026_clk;
+};
+
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_A1026_H */
diff --git a/include/linux/akm8973.h b/include/linux/akm8973.h
new file mode 100644
index 0000000..2afa3ba
--- /dev/null
+++ b/include/linux/akm8973.h
@@ -0,0 +1,61 @@
+/*
+ * Definitions for akm8973 compass chip.
+ */
+#ifndef AKM8973_H
+#define AKM8973_H
+
+#include <linux/ioctl.h>
+
+#define AKM8973_I2C_NAME "akm8973"
+
+/* Compass device dependent definition */
+#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */
+ /* or AKECS_MODE_MEASURE_SEQ instead of this. */
+#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */
+#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */
+
+#define RBUFF_SIZE 4 /* Rx buffer size */
+
+/* AK8973 register address */
+#define AKECS_REG_ST 0xC0
+#define AKECS_REG_TMPS 0xC1
+#define AKECS_REG_MS1 0xE0
+
+#define AKMIO 0xA1
+
+/* IOCTLs for AKM library */
+#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x01, char[5])
+#define ECS_IOCTL_READ _IOWR(AKMIO, 0x02, char[5])
+#define ECS_IOCTL_RESET _IO(AKMIO, 0x03)
+#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x04, short)
+#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x05, char[RBUFF_SIZE+1])
+#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x06, short[12])
+#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x07, int)
+#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x08, int)
+#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short)
+#define ECS_IOCTL_GET_PROJECT_NAME _IOR(AKMIO, 0x0D, char[64])
+#define ECS_IOCTL_GET_MATRIX _IOR(AKMIO, 0x0E, short [4][3][3])
+
+/* IOCTLs for APPs */
+#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short)
+#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short)
+#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short)
+#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short)
+#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short)
+#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short)
+#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short)
+#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17)
+#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short)
+#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY
+#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */
+#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */
+
+struct akm8973_platform_data {
+ short layouts[4][3][3];
+ char project_name[64];
+ int reset;
+ int intr;
+};
+
+#endif
+
diff --git a/include/linux/akm8976.h b/include/linux/akm8976.h
new file mode 100644
index 0000000..8f6a2bb
--- /dev/null
+++ b/include/linux/akm8976.h
@@ -0,0 +1,90 @@
+/*
+ * Definitions for akm8976 compass chip.
+ */
+#ifndef AKM8976_H
+#define AKM8976_H
+
+#include <linux/ioctl.h>
+
+/* Compass device dependent definition */
+#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */
+ /* or AKECS_MODE_MEASURE_SEQ instead of this. */
+#define AKECS_MODE_PFFD 0x01 /* Start pedometer and free fall detect. */
+#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */
+#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */
+
+#define AKECS_MODE_MEASURE_SNG 0x10 /* Starts single measurement */
+#define AKECS_MODE_MEASURE_SEQ 0x11 /* Starts sequential measurement */
+
+/* Default register settings */
+#define CSPEC_AINT 0x01 /* Amplification for acceleration sensor */
+#define CSPEC_SNG_NUM 0x01 /* Single measurement mode */
+#define CSPEC_SEQ_NUM 0x02 /* Sequential measurement mode */
+#define CSPEC_SFRQ_32 0x00 /* Measurement frequency: 32Hz */
+#define CSPEC_SFRQ_64 0x01 /* Measurement frequency: 64Hz */
+#define CSPEC_MCS 0x07 /* Clock frequency */
+#define CSPEC_MKS 0x01 /* Clock type: CMOS level */
+#define CSPEC_INTEN 0x01 /* Interruption pin enable: Enable */
+
+#define RBUFF_SIZE 31 /* Rx buffer size */
+#define MAX_CALI_SIZE 0x1000U /* calibration buffer size */
+
+/* AK8976A register address */
+#define AKECS_REG_ST 0xC0
+#define AKECS_REG_TMPS 0xC1
+#define AKECS_REG_MS1 0xE0
+#define AKECS_REG_MS2 0xE1
+#define AKECS_REG_MS3 0xE2
+
+#define AKMIO 0xA1
+
+/* IOCTLs for AKM library */
+#define ECS_IOCTL_INIT _IO(AKMIO, 0x01)
+#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x02, char[5])
+#define ECS_IOCTL_READ _IOWR(AKMIO, 0x03, char[5])
+#define ECS_IOCTL_RESET _IO(AKMIO, 0x04)
+#define ECS_IOCTL_INT_STATUS _IO(AKMIO, 0x05)
+#define ECS_IOCTL_FFD_STATUS _IO(AKMIO, 0x06)
+#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x07, short)
+#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE+1])
+#define ECS_IOCTL_GET_NUMFRQ _IOR(AKMIO, 0x09, char[2])
+#define ECS_IOCTL_SET_PERST _IO(AKMIO, 0x0A)
+#define ECS_IOCTL_SET_G0RST _IO(AKMIO, 0x0B)
+#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12])
+#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int)
+#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int)
+#define ECS_IOCTL_GET_CALI_DATA _IOR(AKMIO, 0x0F, char[MAX_CALI_SIZE])
+#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short)
+
+/* IOCTLs for APPs */
+#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short)
+#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short)
+#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short)
+#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short)
+#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short)
+#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short)
+#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short)
+#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17)
+#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short)
+#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY
+#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */
+#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */
+
+/* IOCTLs for pedometer */
+#define ECS_IOCTL_SET_STEP_CNT _IOW(AKMIO, 0x20, short)
+
+/* Default GPIO setting */
+#define ECS_RST 146 /*MISC4, bit2 */
+#define ECS_CLK_ON 155 /*MISC5, bit3 */
+#define ECS_INTR 161 /*INT2, bit1 */
+
+struct akm8976_platform_data {
+ int reset;
+ int clk_on;
+ int intr;
+};
+
+extern char *get_akm_cal_ram(void);
+
+#endif
+
diff --git a/include/linux/bma150.h b/include/linux/bma150.h
new file mode 100644
index 0000000..fe8ef22
--- /dev/null
+++ b/include/linux/bma150.h
@@ -0,0 +1,78 @@
+/*
+ * Definitions for BMA150 G-sensor chip.
+ */
+#ifndef BMA150_H
+#define BMA150_H
+
+#include <linux/ioctl.h>
+
+#define BMA150_I2C_NAME "bma150"
+#define BMA150_G_SENSOR_NAME "bma150"
+
+#define BMAIO 0xA1
+
+/* BMA150 register address */
+#define CHIP_ID_REG 0x00
+#define VERSION_REG 0x01
+#define X_AXIS_LSB_REG 0x02
+#define X_AXIS_MSB_REG 0x03
+#define Y_AXIS_LSB_REG 0x04
+#define Y_AXIS_MSB_REG 0x05
+#define Z_AXIS_LSB_REG 0x06
+#define Z_AXIS_MSB_REG 0x07
+#define TEMP_RD_REG 0x08
+#define SMB150_STATUS_REG 0x09
+#define SMB150_CTRL_REG 0x0a
+#define SMB150_CONF1_REG 0x0b
+#define LG_THRESHOLD_REG 0x0c
+#define LG_DURATION_REG 0x0d
+#define HG_THRESHOLD_REG 0x0e
+#define HG_DURATION_REG 0x0f
+#define MOTION_THRS_REG 0x10
+#define HYSTERESIS_REG 0x11
+#define CUSTOMER1_REG 0x12
+#define CUSTOMER2_REG 0x13
+#define RANGE_BWIDTH_REG 0x14
+#define SMB150_CONF2_REG 0x15
+
+#define OFFS_GAIN_X_REG 0x16
+#define OFFS_GAIN_Y_REG 0x17
+#define OFFS_GAIN_Z_REG 0x18
+#define OFFS_GAIN_T_REG 0x19
+#define OFFSET_X_REG 0x1a
+#define OFFSET_Y_REG 0x1b
+#define OFFSET_Z_REG 0x1c
+#define OFFSET_T_REG 0x1d
+
+
+/* IOCTLs*/
+#define BMA_IOCTL_INIT _IO(BMAIO, 0x31)
+#define BMA_IOCTL_WRITE _IOW(BMAIO, 0x32, char[5])
+#define BMA_IOCTL_READ _IOWR(BMAIO, 0x33, char[5])
+#define BMA_IOCTL_READ_ACCELERATION _IOWR(BMAIO, 0x34, short[7])
+#define BMA_IOCTL_SET_MODE _IOW(BMAIO, 0x35, short)
+#define BMA_IOCTL_GET_INT _IOR(BMAIO, 0x36, short)
+
+
+/* range and bandwidth */
+#define BMA_RANGE_2G 0
+#define BMA_RANGE_4G 1
+#define BMA_RANGE_8G 2
+
+#define BMA_BW_25HZ 0
+#define BMA_BW_50HZ 1
+#define BMA_BW_100HZ 2
+#define BMA_BW_190HZ 3
+#define BMA_BW_375HZ 4
+#define BMA_BW_750HZ 5
+#define BMA_BW_1500HZ 6
+
+/* mode settings */
+#define BMA_MODE_NORMAL 0
+#define BMA_MODE_SLEEP 1
+
+struct bma150_platform_data {
+ int intr;
+};
+
+#endif
diff --git a/include/linux/capella_cm3602.h b/include/linux/capella_cm3602.h
new file mode 100644
index 0000000..7f1de9b
--- /dev/null
+++ b/include/linux/capella_cm3602.h
@@ -0,0 +1,37 @@
+/* include/linux/capella_cm3602.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Iliyan Malchev <malchev@google.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_CAPELLA_CM3602_H
+#define __LINUX_CAPELLA_CM3602_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define CAPELLA_CM3602_IOCTL_MAGIC 'c'
+#define CAPELLA_CM3602_IOCTL_GET_ENABLED \
+ _IOR(CAPELLA_CM3602_IOCTL_MAGIC, 1, int *)
+#define CAPELLA_CM3602_IOCTL_ENABLE \
+ _IOW(CAPELLA_CM3602_IOCTL_MAGIC, 2, int *)
+
+#ifdef __KERNEL__
+#define CAPELLA_CM3602 "capella_cm3602"
+struct capella_cm3602_platform_data {
+ int (*power)(int); /* power to the chip */
+ int p_out; /* proximity-sensor outpuCAPELLA_CM3602_IOCTL_ENABLE,t */
+};
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/include/linux/cy8c_tmg_ts.h b/include/linux/cy8c_tmg_ts.h
new file mode 100644
index 0000000..f3cf17c
--- /dev/null
+++ b/include/linux/cy8c_tmg_ts.h
@@ -0,0 +1,37 @@
+/* include/linux/cy8c_tmg_ts.c
+ *
+ * Copyright (C) 2007-2008 HTC 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 CY8C_I2C_H
+#define CY8C_I2C_H
+
+#include <linux/types.h>
+
+#define CYPRESS_TMG_NAME "cy8c-tmg-ts"
+
+struct cy8c_i2c_platform_data {
+ uint16_t version;
+ int abs_x_min;
+ int abs_x_max;
+ int abs_y_min;
+ int abs_y_max;
+ int abs_pressure_min;
+ int abs_pressure_max;
+ int abs_width_min;
+ int abs_width_max;
+ int (*power)(int on);
+};
+
+#endif
+
diff --git a/include/linux/ds2784_battery.h b/include/linux/ds2784_battery.h
new file mode 100644
index 0000000..7ca10f6
--- /dev/null
+++ b/include/linux/ds2784_battery.h
@@ -0,0 +1,29 @@
+/* include/linux/ds2784_battery.h
+ *
+ * Copyright (C) 2009 HTC Corporation
+ * Copyright (C) 2009 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.
+ *
+ */
+
+#ifndef __LINUX_DS2784_BATTERY_H
+#define __LINUX_DS2784_BATTERY_H
+
+#ifdef __KERNEL__
+
+struct ds2784_platform_data {
+ int (*charge)(int on, int fast);
+ void *w1_slave;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* __LINUX_DS2784_BATTERY_H */
diff --git a/include/linux/elan_i2c.h b/include/linux/elan_i2c.h
new file mode 100644
index 0000000..41a9936
--- /dev/null
+++ b/include/linux/elan_i2c.h
@@ -0,0 +1,17 @@
+#ifndef ELAN_I2C_H
+#define ELAN_I2C_H
+
+#define ELAN_8232_I2C_NAME "elan-touch"
+
+struct elan_i2c_platform_data {
+ uint16_t version;
+ int abs_x_min;
+ int abs_x_max;
+ int abs_y_min;
+ int abs_y_max;
+ int intr_gpio;
+ int (*power)(int on);
+};
+
+#endif
+
diff --git a/include/linux/lightsensor.h b/include/linux/lightsensor.h
new file mode 100644
index 0000000..501074f
--- /dev/null
+++ b/include/linux/lightsensor.h
@@ -0,0 +1,28 @@
+/* include/linux/lightsensor.h
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Iliyan Malchev <malchev@google.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_LIGHTSENSOR_H
+#define __LINUX_LIGHTSENSOR_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define LIGHTSENSOR_IOCTL_MAGIC 'l'
+
+#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *)
+#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *)
+
+#endif
diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h
new file mode 100644
index 0000000..4e0ffdd
--- /dev/null
+++ b/include/linux/mfd/pm8058.h
@@ -0,0 +1,186 @@
+/* include/linux/mfd/pm8058.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Dima Zavin <dima@android.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_MFD_PM8058_CORE_H
+#define __LINUX_MFD_PM8058_CORE_H
+
+#include <mach/irqs.h>
+
+#define PM8058_NUM_GPIO_IRQS 40
+#define PM8058_NUM_MPP_IRQS 12
+#define PM8058_NUM_KEYPAD_IRQS 2
+#define PM8058_NUM_CHARGER_IRQS 7
+#define PM8058_NUM_IRQS (PM8058_NUM_GPIO_IRQS + \
+ PM8058_NUM_MPP_IRQS + \
+ PM8058_NUM_KEYPAD_IRQS + \
+ PM8058_NUM_CHARGER_IRQS)
+
+/* be careful if you change this since this is used to map irq <-> gpio */
+#define PM8058_FIRST_GPIO_IRQ 0
+#define PM8058_FIRST_MPP_IRQ (PM8058_FIRST_GPIO_IRQ + \
+ PM8058_NUM_GPIO_IRQS)
+#define PM8058_FIRST_KEYPAD_IRQ (PM8058_FIRST_MPP_IRQ + \
+ PM8058_NUM_MPP_IRQS)
+#define PM8058_FIRST_CHARGER_IRQ (PM8058_FIRST_KEYPAD_IRQ + \
+ PM8058_NUM_KEYPAD_IRQS)
+
+#define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0)
+#define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1)
+
+#define PM8058_CHGVAL_IRQ (PM8058_FIRST_CHARGER_IRQ + 0)
+#define PM8058_CHGEND_IRQ (PM8058_FIRST_CHARGER_IRQ + 1)
+#define PM8058_FASTCHG_IRQ (PM8058_FIRST_CHARGER_IRQ + 2)
+#define PM8058_CHGFAIL_IRQ (PM8058_FIRST_CHARGER_IRQ + 5)
+#define PM8058_CHGDONE_IRQ (PM8058_FIRST_CHARGER_IRQ + 6)
+
+#define PM8058_GPIO_TO_IRQ(base,gpio) (PM8058_FIRST_GPIO_IRQ + \
+ (base) + (gpio))
+
+/* these need to match the irq counts/offsets above above */
+#define PM8058_FIRST_GPIO PM8058_FIRST_GPIO_IRQ
+#define PM8058_NUM_GPIOS PM8058_NUM_GPIO_IRQS
+#define PM8058_FIRST_MPP PM8058_FIRST_MPP_IRQ
+#define PM8058_NUM_MPP PM8058_NUM_MPP_IRQS
+
+#define PM8058_GPIO(base,gpio) ((base) + (gpio) + PM8058_FIRST_GPIO)
+/*#define PM8058_MPP(base,mpp) ((base) + (mpp) + PM8058_FIRST_MPP)*/
+
+struct pm8058_keypad_platform_data {
+ const char *name;
+ int num_drv;
+ int num_sns;
+ /* delay in ms = 1 << scan_delay_shift, 0-7 */
+ int scan_delay_shift;
+ /* # of 32kHz clock cycles, 1-4 */
+ int drv_hold_clks;
+ /* in increments of 5ms, max 20ms */
+ int debounce_ms;
+
+ /* size must be num_drv * num_sns
+ * index is (drv * num_sns + sns) */
+ const unsigned short *keymap;
+
+ int (*init)(struct device *dev);
+};
+
+struct pm8058_charger_platform_data {
+ /* function to call on vbus detect */
+ void (*vbus_present)(bool present);
+
+ int (*charge)(u32 max_current, bool is_ac);
+
+ char **supplied_to;
+ int num_supplicants;
+};
+
+struct pm8058_platform_data {
+ unsigned int irq_base;
+ unsigned int gpio_base;
+ int (*init)(struct device *dev);
+
+ /* child devices */
+ struct pm8058_keypad_platform_data *keypad_pdata;
+ struct pm8058_charger_platform_data *charger_pdata;
+};
+
+#define PM8058_GPIO_VIN_SRC_VPH_PWR 0x0 /* VDD_L6_L7 */
+#define PM8058_GPIO_VIN_SRC_VREG_BB 0x1 /* VDD_L3_L4_L5 */
+#define PM8058_GPIO_VIN_SRC_VREG_S3 0x2 /* VDD_L0_L1_LVS, 1.8V */
+#define PM8058_GPIO_VIN_SRC_VREG_L3 0x3 /* 1.8V or 2.85 */
+#define PM8058_GPIO_VIN_SRC_VREG_L7 0x4 /* 1.8V */
+#define PM8058_GPIO_VIN_SRC_VREG_L6 0x5 /* 3.3V */
+#define PM8058_GPIO_VIN_SRC_VREG_L5 0x6 /* 2.85V */
+#define PM8058_GPIO_VIN_SRC_VREG_L2 0x7 /* 2.6V */
+
+#define PM8058_GPIO_INPUT 0x01
+#define PM8058_GPIO_OUTPUT 0x02
+#define PM8058_GPIO_OUTPUT_HIGH 0x04
+
+#define PM8058_GPIO_STRENGTH_OFF 0x0
+#define PM8058_GPIO_STRENGTH_HIGH 0x1
+#define PM8058_GPIO_STRENGTH_MED 0x2
+#define PM8058_GPIO_STRENGTH_LOW 0x3
+
+#define PM8058_GPIO_PULL_UP_30 0x0
+#define PM8058_GPIO_PULL_UP_1P5 0x1
+#define PM8058_GPIO_PULL_UP_31P5 0x2
+#define PM8058_GPIO_PULL_UP_1P5_30 0x3
+#define PM8058_GPIO_PULL_DOWN 0x4
+#define PM8058_GPIO_PULL_NONE 0x5
+
+#define PM8058_GPIO_FUNC_NORMAL 0x0
+#define PM8058_GPIO_FUNC_PAIRED 0x1
+#define PM8058_GPIO_FUNC_1 0x2
+#define PM8058_GPIO_FUNC_2 0x3
+
+/* gpio pin flags */
+#define PM8058_GPIO_OPEN_DRAIN 0x10
+#define PM8058_GPIO_HIGH_Z 0x20
+#define PM8058_GPIO_INV_IRQ_POL 0x40
+#define PM8058_GPIO_CONFIGURED 0x80 /* FOR INTERNAL USE ONLY */
+
+struct pm8058_pin_config {
+ u8 vin_src;
+ u8 dir;
+ u8 pull_up;
+ u8 strength;
+ u8 func;
+ u8 flags;
+};
+
+#define PM8058_GPIO_PIN_CONFIG(v,d,p,s,fn,fl) \
+ { \
+ .vin_src = (v), \
+ .dir = (d), \
+ .pull_up = (p), \
+ .strength = (s), \
+ .func = (fn), \
+ .flags = (fl), \
+ }
+
+#ifdef CONFIG_PM8058
+int pm8058_readb(struct device *dev, u16 addr, u8 *val);
+int pm8058_writeb(struct device *dev, u16 addr, u8 val);
+int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, int cnt);
+int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt);
+int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio,
+ struct pm8058_pin_config *cfg);
+int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg);
+int pm8058_irq_get_status(struct device *dev, unsigned int irq);
+#else
+static inline int pm8058_readb(struct device *dev, u16 addr, u8 *val)
+{ return 0; }
+static inline int pm8058_writeb(struct device *dev, u16 addr, u8 val)
+{ return 0; }
+static inline int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf,
+ int cnt) { return 0; }
+static inline int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf,
+ int cnt) { return 0; }
+static inline int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio,
+ struct pm8058_pin_config *cfg) { return 0; }
+static inline int pm8058_gpio_mux(unsigned int gpio,
+ struct pm8058_pin_config *cfg) { return 0; }
+static inline int pm8058_irq_get_status(struct device *dev, unsigned int irq)
+{ return 0; }
+#endif
+
+#ifdef CONFIG_CHARGER_PM8058
+void pm8058_notify_charger_connected(int status);
+#else
+static inline void pm8058_notify_charger_connected(int status) {}
+#endif
+
+#endif
diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h
new file mode 100644
index 0000000..12f219e
--- /dev/null
+++ b/include/linux/msm_adsp.h
@@ -0,0 +1,84 @@
+/* include/linux/msm_adsp.h
+ *
+ * Copyright (c) QUALCOMM Incorporated
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.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_MSM_ADSP_H
+#define __LINUX_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define ADSP_IOCTL_MAGIC 'q'
+
+/* ADSP_IOCTL_WRITE_COMMAND */
+struct adsp_command_t {
+ uint16_t queue;
+ uint32_t len; /* bytes */
+ uint8_t *data;
+};
+
+/* ADSP_IOCTL_GET_EVENT */
+struct adsp_event_t {
+ uint16_t type; /* 1 == event (RPC), 0 == message (adsp) */
+ uint32_t timeout_ms; /* -1 for infinite, 0 for immediate return */
+ uint16_t msg_id;
+ uint16_t flags; /* 1 == 16--bit event, 0 == 32-bit event */
+ uint32_t len; /* size in, number of bytes out */
+ uint8_t *data;
+};
+
+#define ADSP_IOCTL_ENABLE \
+ _IOR(ADSP_IOCTL_MAGIC, 1, unsigned)
+
+#define ADSP_IOCTL_DISABLE \
+ _IOR(ADSP_IOCTL_MAGIC, 2, unsigned)
+
+#define ADSP_IOCTL_DISABLE_ACK \
+ _IOR(ADSP_IOCTL_MAGIC, 3, unsigned)
+
+#define ADSP_IOCTL_WRITE_COMMAND \
+ _IOR(ADSP_IOCTL_MAGIC, 4, struct adsp_command_t *)
+
+#define ADSP_IOCTL_GET_EVENT \
+ _IOWR(ADSP_IOCTL_MAGIC, 5, struct adsp_event_data_t *)
+
+#define ADSP_IOCTL_SET_CLKRATE \
+ _IOR(ADSP_IOCTL_MAGIC, 6, unsigned)
+
+#define ADSP_IOCTL_DISABLE_EVENT_RSP \
+ _IOR(ADSP_IOCTL_MAGIC, 10, unsigned)
+
+struct adsp_pmem_info {
+ int fd;
+ void *vaddr;
+};
+
+#define ADSP_IOCTL_REGISTER_PMEM \
+ _IOW(ADSP_IOCTL_MAGIC, 13, unsigned)
+
+#define ADSP_IOCTL_UNREGISTER_PMEM \
+ _IOW(ADSP_IOCTL_MAGIC, 14, unsigned)
+
+/* Cause any further GET_EVENT ioctls to fail (-ENODEV)
+ * until the device is closed and reopened. Useful for
+ * terminating event dispatch threads
+ */
+#define ADSP_IOCTL_ABORT_EVENT_READ \
+ _IOW(ADSP_IOCTL_MAGIC, 15, unsigned)
+
+#define ADSP_IOCTL_LINK_TASK \
+ _IOW(ADSP_IOCTL_MAGIC, 16, unsigned)
+
+#endif
diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h
new file mode 100644
index 0000000..5875c99
--- /dev/null
+++ b/include/linux/msm_audio.h
@@ -0,0 +1,120 @@
+/* include/linux/msm_audio.h
+ *
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#ifndef __LINUX_MSM_AUDIO_H
+#define __LINUX_MSM_AUDIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/sizes.h>
+
+/* PCM Audio */
+
+#define AUDIO_IOCTL_MAGIC 'a'
+
+#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned)
+#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
+#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
+#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
+#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
+#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned)
+#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned)
+#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned)
+#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned)
+#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned)
+#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned)
+#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned)
+#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned)
+#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned)
+#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned)
+#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned)
+#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned)
+#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned)
+#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned)
+#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned)
+#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned)
+#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned)
+#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned)
+#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned)
+
+#define AUDIO_MAX_COMMON_IOCTL_NUM 100
+
+#define AUDIO_MAX_COMMON_IOCTL_NUM 100
+
+struct msm_audio_config {
+ uint32_t buffer_size;
+ uint32_t buffer_count;
+ uint32_t channel_count;
+ uint32_t sample_rate;
+ uint32_t type;
+ uint32_t unused[3];
+};
+
+struct msm_audio_stats {
+ uint32_t byte_count;
+ uint32_t sample_count;
+ uint32_t unused[2];
+};
+
+/* Audio routing */
+
+#define SND_IOCTL_MAGIC 's'
+
+#define SND_MUTE_UNMUTED 0
+#define SND_MUTE_MUTED 1
+
+struct msm_snd_device_config {
+ uint32_t device;
+ uint32_t ear_mute;
+ uint32_t mic_mute;
+};
+
+#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *)
+
+#define SND_METHOD_VOICE 0
+
+struct msm_snd_volume_config {
+ uint32_t device;
+ uint32_t method;
+ uint32_t volume;
+};
+
+#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *)
+
+/* Returns the number of SND endpoints supported. */
+
+#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned *)
+
+struct msm_snd_endpoint {
+ int id; /* input and output */
+ char name[64]; /* output only */
+};
+
+/* Takes an index between 0 and one less than the number returned by
+ * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a
+ * SND endpoint. On input, the .id field contains the number of the
+ * endpoint, and on exit it contains the SND index, while .name contains
+ * the description of the endpoint.
+ */
+
+#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *)
+
+struct msm_audio_pcm_config {
+ uint32_t pcm_feedback; /* 0 - disable > 0 - enable */
+ uint32_t buffer_count; /* Number of buffers to allocate */
+ uint32_t buffer_size; /* Size of buffer for capturing of
+ PCM samples */
+};
+#endif
diff --git a/include/linux/msm_hw3d.h b/include/linux/msm_hw3d.h
new file mode 100644
index 0000000..9178526
--- /dev/null
+++ b/include/linux/msm_hw3d.h
@@ -0,0 +1,61 @@
+/* include/linux/msm_hw3d.h
+ *
+ * Copyright (C) 2007 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.
+ *
+ */
+
+
+#ifndef _MSM_HW3D_H_
+#define _MSM_HW3D_H_
+
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+
+struct hw3d_region;
+
+#define HW3D_IOCTL_MAGIC 'h'
+#define HW3D_WAIT_FOR_REVOKE _IO(HW3D_IOCTL_MAGIC, 0x80)
+#define HW3D_WAIT_FOR_INTERRUPT _IO(HW3D_IOCTL_MAGIC, 0x81)
+#define HW3D_GET_REGIONS \
+ _IOR(HW3D_IOCTL_MAGIC, 0x82, struct hw3d_region *)
+
+#define HW3D_REGION_OFFSET(id) ((((uint32_t)(id)) & 0xf) << 28)
+#define HW3D_REGION_ID(addr) (((uint32_t)(addr) >> 28) & 0xf)
+#define HW3D_OFFSET_IN_REGION(addr) ((uint32_t)(addr) & ~(0xfUL << 28))
+
+enum {
+ HW3D_EBI = 0,
+ HW3D_SMI = 1,
+ HW3D_REGS = 2,
+
+ HW3D_NUM_REGIONS = HW3D_REGS + 1,
+};
+
+struct hw3d_region {
+ unsigned long phys;
+ unsigned long map_offset;
+ unsigned long len;
+};
+
+#ifdef CONFIG_MSM_HW3D
+int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase,
+ unsigned long *len, struct file **filp);
+void put_msm_hw3d_file(struct file *file);
+bool is_msm_hw3d_file(struct file *file);
+#else
+int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase,
+ unsigned long *len, struct file **filp) { return -1; }
+void put_msm_hw3d_file(struct file *file) {}
+bool is_msm_hw3d_file(struct file *file) { return false; }
+#endif
+
+#endif /* _MSM_HW3D_H_ */
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
new file mode 100644
index 0000000..28a1e1e8
--- /dev/null
+++ b/include/linux/msm_kgsl.h
@@ -0,0 +1,286 @@
+/*
+ * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * All source code in this file is licensed under the following license
+ *
+ * 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, you can find it at http://www.fsf.org
+ */
+#ifndef _MSM_KGSL_H
+#define _MSM_KGSL_H
+
+/*context flags */
+#define KGSL_CONTEXT_SAVE_GMEM 1
+#define KGSL_CONTEXT_NO_GMEM_ALLOC 2
+
+/* generic flag values */
+#define KGSL_FLAGS_NORMALMODE 0x00000000
+#define KGSL_FLAGS_SAFEMODE 0x00000001
+#define KGSL_FLAGS_INITIALIZED0 0x00000002
+#define KGSL_FLAGS_INITIALIZED 0x00000004
+#define KGSL_FLAGS_STARTED 0x00000008
+#define KGSL_FLAGS_ACTIVE 0x00000010
+#define KGSL_FLAGS_RESERVED0 0x00000020
+#define KGSL_FLAGS_RESERVED1 0x00000040
+#define KGSL_FLAGS_RESERVED2 0x00000080
+
+/* device id */
+enum kgsl_deviceid {
+ KGSL_DEVICE_ANY = 0x00000000,
+ KGSL_DEVICE_YAMATO = 0x00000001,
+ KGSL_DEVICE_G12 = 0x00000002,
+ KGSL_DEVICE_MAX = 0x00000002
+};
+
+struct kgsl_devinfo {
+
+ unsigned int device_id;
+ /* chip revision id
+ * coreid:8 majorrev:8 minorrev:8 patch:8
+ */
+ unsigned int chip_id;
+ unsigned int mmu_enabled;
+ unsigned int gmem_gpubaseaddr;
+ /* if gmem_hostbaseaddr is NULL, we would know its not mapped into
+ * mmio space */
+ unsigned int gmem_hostbaseaddr;
+ unsigned int gmem_sizebytes;
+};
+
+/* this structure defines the region of memory that can be mmap()ed from this
+ driver. The timestamp fields are volatile because they are written by the
+ GPU
+*/
+struct kgsl_devmemstore {
+ volatile unsigned int soptimestamp;
+ unsigned int sbz;
+ volatile unsigned int eoptimestamp;
+ unsigned int sbz2;
+ volatile unsigned int ts_cmp_enable;
+ unsigned int sbz3;
+ volatile unsigned int ref_wait_ts;
+ unsigned int sbz4;
+};
+
+#define KGSL_DEVICE_MEMSTORE_OFFSET(field) \
+ offsetof(struct kgsl_devmemstore, field)
+
+
+/* timestamp id*/
+enum kgsl_timestamp_type {
+ KGSL_TIMESTAMP_CONSUMED = 0x00000001, /* start-of-pipeline timestamp */
+ KGSL_TIMESTAMP_RETIRED = 0x00000002, /* end-of-pipeline timestamp*/
+ KGSL_TIMESTAMP_MAX = 0x00000002,
+};
+
+/* property types - used with kgsl_device_getproperty */
+enum kgsl_property_type {
+ KGSL_PROP_DEVICE_INFO = 0x00000001,
+ KGSL_PROP_DEVICE_SHADOW = 0x00000002,
+ KGSL_PROP_DEVICE_POWER = 0x00000003,
+ KGSL_PROP_SHMEM = 0x00000004,
+ KGSL_PROP_SHMEM_APERTURES = 0x00000005,
+ KGSL_PROP_MMU_ENABLE = 0x00000006,
+ KGSL_PROP_INTERRUPT_WAITS = 0x00000007,
+};
+
+struct kgsl_shadowprop {
+ unsigned int gpuaddr;
+ unsigned int size;
+ unsigned int flags; /* contains KGSL_FLAGS_ values */
+};
+
+/* ioctls */
+#define KGSL_IOC_TYPE 0x09
+
+/* get misc info about the GPU
+ type should be a value from enum kgsl_property_type
+ value points to a structure that varies based on type
+ sizebytes is sizeof() that structure
+ for KGSL_PROP_DEVICE_INFO, use struct kgsl_devinfo
+ this structure contaings hardware versioning info.
+ for KGSL_PROP_DEVICE_SHADOW, use struct kgsl_shadowprop
+ this is used to find mmap() offset and sizes for mapping
+ struct kgsl_memstore into userspace.
+*/
+struct kgsl_device_getproperty {
+ unsigned int type;
+ void *value;
+ unsigned int sizebytes;
+};
+
+#define IOCTL_KGSL_DEVICE_GETPROPERTY \
+ _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty)
+
+
+/* read a GPU register.
+ offsetwords it the 32 bit word offset from the beginning of the
+ GPU register space.
+ */
+struct kgsl_device_regread {
+ unsigned int offsetwords;
+ unsigned int value; /* output param */
+};
+
+#define IOCTL_KGSL_DEVICE_REGREAD \
+ _IOWR(KGSL_IOC_TYPE, 0x3, struct kgsl_device_regread)
+
+
+/* block until the GPU has executed past a given timestamp
+ * timeout is in milliseconds.
+ */
+struct kgsl_device_waittimestamp {
+ unsigned int timestamp;
+ unsigned int timeout;
+};
+
+#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP \
+ _IOW(KGSL_IOC_TYPE, 0x6, struct kgsl_device_waittimestamp)
+
+
+/* issue indirect commands to the GPU.
+ * drawctxt_id must have been created with IOCTL_KGSL_DRAWCTXT_CREATE
+ * ibaddr and sizedwords must specify a subset of a buffer created
+ * with IOCTL_KGSL_SHAREDMEM_FROM_PMEM
+ * flags may be a mask of KGSL_CONTEXT_ values
+ * timestamp is a returned counter value which can be passed to
+ * other ioctls to determine when the commands have been executed by
+ * the GPU.
+ */
+struct kgsl_ringbuffer_issueibcmds {
+ unsigned int drawctxt_id;
+ unsigned int ibaddr;
+ unsigned int sizedwords;
+ unsigned int timestamp; /*output param */
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS \
+ _IOWR(KGSL_IOC_TYPE, 0x10, struct kgsl_ringbuffer_issueibcmds)
+
+/* read the most recently executed timestamp value
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_readtimestamp {
+ unsigned int type;
+ unsigned int timestamp; /*output param */
+};
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \
+ _IOR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp)
+
+/* free memory when the GPU reaches a given timestamp.
+ * gpuaddr specify a memory region created by a
+ * IOCTL_KGSL_SHAREDMEM_FROM_PMEM call
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_freememontimestamp {
+ unsigned int gpuaddr;
+ unsigned int type;
+ unsigned int timestamp;
+};
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP \
+ _IOR(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp)
+
+/* create a draw context, which is used to preserve GPU state.
+ * The flags field may contain a mask KGSL_CONTEXT_* values
+ */
+struct kgsl_drawctxt_create {
+ unsigned int flags;
+ unsigned int drawctxt_id; /*output param */
+};
+
+#define IOCTL_KGSL_DRAWCTXT_CREATE \
+ _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
+
+/* destroy a draw context */
+struct kgsl_drawctxt_destroy {
+ unsigned int drawctxt_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_DESTROY \
+ _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
+
+/* add a block of pmem or fb into the GPU address space */
+struct kgsl_sharedmem_from_pmem {
+ int pmem_fd;
+ unsigned int gpuaddr; /*output param */
+ unsigned int len;
+ unsigned int offset;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_PMEM \
+ _IOWR(KGSL_IOC_TYPE, 0x20, struct kgsl_sharedmem_from_pmem)
+
+/* remove memory from the GPU's address space */
+struct kgsl_sharedmem_free {
+ unsigned int gpuaddr;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FREE \
+ _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free)
+
+struct kgsl_gmem_desc {
+ unsigned int x;
+ unsigned int y;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pitch;
+};
+
+struct kgsl_buffer_desc {
+ void *hostptr;
+ unsigned int gpuaddr;
+ int size;
+ unsigned int format;
+ unsigned int pitch;
+ unsigned int enabled;
+};
+
+struct kgsl_bind_gmem_shadow {
+ unsigned int drawctxt_id;
+ struct kgsl_gmem_desc gmem_desc;
+ unsigned int shadow_x;
+ unsigned int shadow_y;
+ struct kgsl_buffer_desc shadow_buffer;
+ unsigned int buffer_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_BIND_GMEM_SHADOW \
+ _IOW(KGSL_IOC_TYPE, 0x22, struct kgsl_bind_gmem_shadow)
+
+/* add a block of memory into the GPU address space */
+struct kgsl_sharedmem_from_vmalloc {
+ unsigned int gpuaddr; /*output param */
+ unsigned int hostptr;
+ /* If set from user space then will attempt to
+ * allocate even if low watermark is crossed */
+ int force_no_low_watermark;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \
+ _IOWR(KGSL_IOC_TYPE, 0x23, struct kgsl_sharedmem_from_vmalloc)
+
+#define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \
+ _IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free)
+
+struct kgsl_drawctxt_set_bin_base_offset {
+ unsigned int drawctxt_id;
+ unsigned int offset;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET \
+ _IOW(KGSL_IOC_TYPE, 0x25, struct kgsl_drawctxt_set_bin_base_offset)
+
+#endif /* _MSM_KGSL_H */
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index d11fe0f..03d8fde 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -32,6 +32,7 @@
MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */
MDP_RGBA_8888, /* ARGB 888 */
MDP_BGRA_8888, /* ABGR 888 */
+ MDP_RGBX_8888, /* RGBX 888 */
MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */
};
@@ -47,8 +48,10 @@
#define MDP_ROT_90 0x4
#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR)
#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR)
+#define MDP_ROT_MASK 0x7
#define MDP_DITHER 0x8
#define MDP_BLUR 0x10
+#define MDP_BLEND_FG_PREMULT 0x20000
#define MDP_TRANSP_NOP 0xffffffff
#define MDP_ALPHA_NOP 0xff
diff --git a/include/linux/msm_q6vdec.h b/include/linux/msm_q6vdec.h
new file mode 100644
index 0000000..1dca803
--- /dev/null
+++ b/include/linux/msm_q6vdec.h
@@ -0,0 +1,230 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MSM_VDEC_H_
+#define _MSM_VDEC_H_
+
+#include <linux/types.h>
+
+#define VDEC_IOCTL_MAGIC 'v'
+
+#define VDEC_IOCTL_INITIALIZE _IOWR(VDEC_IOCTL_MAGIC, 1, struct vdec_init)
+#define VDEC_IOCTL_SETBUFFERS _IOW(VDEC_IOCTL_MAGIC, 2, struct vdec_buffer)
+#define VDEC_IOCTL_QUEUE _IOWR(VDEC_IOCTL_MAGIC, 3, \
+ struct vdec_input_buf)
+#define VDEC_IOCTL_REUSEFRAMEBUFFER _IOW(VDEC_IOCTL_MAGIC, 4, unsigned int)
+#define VDEC_IOCTL_FLUSH _IOW(VDEC_IOCTL_MAGIC, 5, unsigned int)
+#define VDEC_IOCTL_EOS _IO(VDEC_IOCTL_MAGIC, 6)
+#define VDEC_IOCTL_GETMSG _IOR(VDEC_IOCTL_MAGIC, 7, struct vdec_msg)
+#define VDEC_IOCTL_CLOSE _IO(VDEC_IOCTL_MAGIC, 8)
+#define VDEC_IOCTL_FREEBUFFERS _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_buf_info)
+#define VDEC_IOCTL_GETDECATTRIBUTES _IOR(VDEC_IOCTL_MAGIC, 10, \
+ struct vdec_dec_attributes)
+
+enum {
+ VDEC_FRAME_DECODE_OK,
+ VDEC_FRAME_DECODE_ERR,
+ VDEC_FATAL_ERR,
+ VDEC_FLUSH_FINISH,
+ VDEC_EOS,
+ VDEC_FRAME_FLUSH,
+ VDEC_STREAM_SWITCH,
+ VDEC_SUSPEND_FINISH,
+ VDEC_BUFFER_CONSUMED
+};
+
+enum {
+ VDEC_FLUSH_INPUT,
+ VDEC_FLUSH_OUTPUT,
+ VDEC_FLUSH_ALL
+};
+
+enum {
+ VDEC_BUFFER_TYPE_INPUT,
+ VDEC_BUFFER_TYPE_OUTPUT,
+ VDEC_BUFFER_TYPE_INTERNAL1,
+ VDEC_BUFFER_TYPE_INTERNAL2,
+};
+
+enum {
+ VDEC_QUEUE_SUCCESS,
+ VDEC_QUEUE_FAILED,
+ VDEC_QUEUE_BADSTATE,
+};
+
+struct vdec_input_buf_info {
+ u32 offset;
+ u32 data;
+ u32 size;
+ int timestamp_lo;
+ int timestamp_hi;
+ int avsync_state;
+ u32 flags;
+};
+
+struct vdec_buf_desc {
+ u32 bufsize;
+ u32 num_min_buffers;
+ u32 num_max_buffers;
+};
+
+struct vdec_buf_req {
+ u32 max_input_queue_size;
+ struct vdec_buf_desc input;
+ struct vdec_buf_desc output;
+ struct vdec_buf_desc dec_req1;
+ struct vdec_buf_desc dec_req2;
+};
+
+struct vdec_region_info {
+ u32 src_id;
+ u32 offset;
+ u32 size;
+};
+
+struct vdec_config {
+ u32 fourcc; /* video format */
+ u32 width; /* source width */
+ u32 height; /* source height */
+ u32 order; /* render decoder order */
+ u32 notify_enable; /* enable notify input buffer done event */
+ u32 vc1_rowbase;
+ u32 h264_startcode_detect;
+ u32 h264_nal_len_size;
+ u32 postproc_flag;
+ u32 fruc_enable;
+ u32 reserved;
+};
+
+struct vdec_vc1_panscan_regions {
+ int num;
+ int width[4];
+ int height[4];
+ int xoffset[4];
+ int yoffset[4];
+};
+
+struct vdec_cropping_window {
+ u32 x1;
+ u32 y1;
+ u32 x2;
+ u32 y2;
+};
+
+struct vdec_frame_info {
+ u32 status; /* video decode status */
+ u32 offset; /* buffer offset */
+ u32 data1; /* user data field 1 */
+ u32 data2; /* user data field 2 */
+ int timestamp_lo; /* lower 32 bits timestamp, in msec */
+ int timestamp_hi; /* higher 32 bits timestamp, in msec */
+ int cal_timestamp_lo; /* lower 32 bits cal timestamp, in msec */
+ int cal_timestamp_hi; /* higher 32 bits cal timestamp, in msec */
+ u32 dec_width; /* frame roi width */
+ u32 dec_height; /* frame roi height */
+ struct vdec_cropping_window cwin; /* The frame cropping window */
+ u32 picture_type[2]; /* picture coding type */
+ u32 picture_format; /* picture coding format */
+ u32 vc1_rangeY; /* luma range mapping */
+ u32 vc1_rangeUV; /* chroma range mapping */
+ u32 picture_resolution; /* scaling factor */
+ u32 frame_disp_repeat; /* how often repeated by disp */
+ u32 repeat_first_field; /* repeat 1st field after 2nd */
+ u32 top_field_first; /* top field displayed first */
+ u32 interframe_interp; /* not for inter-frame interp */
+ struct vdec_vc1_panscan_regions panscan; /* pan region */
+ u32 concealed_macblk_num; /* number of concealed macro blk */
+ u32 flags; /* input flags */
+ u32 performance_stats; /* performance statistics returned by decoder */
+ u32 data3; /* user data field 3 */
+};
+
+struct vdec_buf_info {
+ u32 buf_type;
+ struct vdec_region_info region;
+ u32 num_buf;
+ u32 islast;
+};
+
+struct vdec_buffer {
+ u32 pmem_id;
+ struct vdec_buf_info buf;
+};
+
+struct vdec_sequence {
+ u8 *header;
+ u32 len;
+};
+
+struct vdec_config_sps {
+ struct vdec_config cfg;
+ struct vdec_sequence seq;
+};
+
+#define VDEC_MSG_REUSEINPUTBUFFER 1
+#define VDEC_MSG_FRAMEDONE 2
+
+struct vdec_msg {
+ u32 id;
+
+ union {
+ /* id = VDEC_MSG_REUSEINPUTBUFFER */
+ u32 buf_id;
+ /* id = VDEC_MSG_FRAMEDONE */
+ struct vdec_frame_info vfr_info;
+ };
+};
+
+struct vdec_init {
+ struct vdec_config_sps sps_cfg;
+ struct vdec_buf_req *buf_req;
+};
+
+struct vdec_input_buf {
+ u32 pmem_id;
+ struct vdec_input_buf_info buffer;
+ struct vdec_queue_status *queue_status;
+};
+
+struct vdec_queue_status {
+ u32 status;
+};
+
+struct vdec_dec_attributes {
+ u32 fourcc;
+ u32 profile;
+ u32 level;
+ u32 dec_pic_width;
+ u32 dec_pic_height;
+ struct vdec_buf_desc input;
+ struct vdec_buf_desc output;
+ struct vdec_buf_desc dec_req1;
+ struct vdec_buf_desc dec_req2;
+};
+
+#endif /* _MSM_VDEC_H_ */
diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h
new file mode 100755
index 0000000..db31332
--- /dev/null
+++ b/include/linux/msm_q6venc.h
@@ -0,0 +1,321 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MSM_VENC_H_
+#define _MSM_VENC_H_
+
+#include <linux/types.h>
+
+#define VENC_MAX_RECON_BUFFERS 2
+
+#define VENC_FLAG_EOS 0x00000001
+#define VENC_FLAG_END_OF_FRAME 0x00000010
+#define VENC_FLAG_SYNC_FRAME 0x00000020
+#define VENC_FLAG_EXTRA_DATA 0x00000040
+#define VENC_FLAG_CODEC_CONFIG 0x00000080
+
+enum venc_flush_type {
+ VENC_FLUSH_INPUT,
+ VENC_FLUSH_OUTPUT,
+ VENC_FLUSH_ALL
+};
+
+enum venc_state_type {
+ VENC_STATE_PAUSE = 0x1,
+ VENC_STATE_START = 0x2,
+ VENC_STATE_STOP = 0x4
+};
+
+enum venc_event_type_enum {
+ VENC_EVENT_START_STATUS,
+ VENC_EVENT_STOP_STATUS,
+ VENC_EVENT_SUSPEND_STATUS,
+ VENC_EVENT_RESUME_STATUS,
+ VENC_EVENT_FLUSH_STATUS,
+ VENC_EVENT_RELEASE_INPUT,
+ VENC_EVENT_DELIVER_OUTPUT,
+ VENC_EVENT_UNKNOWN_STATUS
+};
+
+enum venc_status_code {
+ VENC_STATUS_SUCCESS,
+ VENC_STATUS_ERROR,
+ VENC_STATUS_INVALID_STATE,
+ VENC_STATUS_FLUSHING,
+ VENC_STATUS_INVALID_PARAM,
+ VENC_STATUS_CMD_QUEUE_FULL,
+ VENC_STATUS_CRITICAL,
+ VENC_STATUS_INSUFFICIENT_RESOURCES,
+ VENC_STATUS_TIMEOUT
+};
+
+enum venc_msg_code {
+ VENC_MSG_INDICATION,
+ VENC_MSG_INPUT_BUFFER_DONE,
+ VENC_MSG_OUTPUT_BUFFER_DONE,
+ VENC_MSG_NEED_OUTPUT_BUFFER,
+ VENC_MSG_FLUSH,
+ VENC_MSG_START,
+ VENC_MSG_STOP,
+ VENC_MSG_PAUSE,
+ VENC_MSG_RESUME,
+ VENC_MSG_STOP_READING_MSG
+};
+
+enum venc_error_code {
+ VENC_S_SUCCESS,
+ VENC_S_EFAIL,
+ VENC_S_EFATAL,
+ VENC_S_EBADPARAM,
+ VENC_S_EINVALSTATE,
+ VENC_S_ENOSWRES,
+ VENC_S_ENOHWRES,
+ VENC_S_EBUFFREQ,
+ VENC_S_EINVALCMD,
+ VENC_S_ETIMEOUT,
+ VENC_S_ENOREATMPT,
+ VENC_S_ENOPREREQ,
+ VENC_S_ECMDQFULL,
+ VENC_S_ENOTSUPP,
+ VENC_S_ENOTIMPL,
+ VENC_S_ENOTPMEM,
+ VENC_S_EFLUSHED,
+ VENC_S_EINSUFBUF,
+ VENC_S_ESAMESTATE,
+ VENC_S_EINVALTRANS
+};
+
+enum venc_mem_region_enum {
+ VENC_PMEM_EBI1,
+ VENC_PMEM_SMI
+};
+
+struct venc_buf_type {
+ unsigned int region;
+ unsigned int phys;
+ unsigned int size;
+ int offset;
+};
+
+struct venc_qp_range {
+ unsigned int min_qp;
+ unsigned int max_qp;
+};
+
+struct venc_frame_rate {
+ unsigned int frame_rate_num;
+ unsigned int frame_rate_den;
+};
+
+struct venc_slice_info {
+ unsigned int slice_mode;
+ unsigned int units_per_slice;
+};
+
+struct venc_extra_data {
+ unsigned int slice_extra_data_flag;
+ unsigned int slice_client_data1;
+ unsigned int slice_client_data2;
+ unsigned int slice_client_data3;
+ unsigned int none_extra_data_flag;
+ unsigned int none_client_data1;
+ unsigned int none_client_data2;
+ unsigned int none_client_data3;
+};
+
+struct venc_common_config {
+ unsigned int standard;
+ unsigned int input_frame_height;
+ unsigned int input_frame_width;
+ unsigned int output_frame_height;
+ unsigned int output_frame_width;
+ unsigned int rotation_angle;
+ unsigned int intra_period;
+ unsigned int rate_control;
+ struct venc_frame_rate frame_rate;
+ unsigned int bitrate;
+ struct venc_qp_range qp_range;
+ unsigned int iframe_qp;
+ unsigned int pframe_qp;
+ struct venc_slice_info slice_config;
+ struct venc_extra_data extra_data;
+};
+
+struct venc_nonio_buf_config {
+ struct venc_buf_type recon_buf1;
+ struct venc_buf_type recon_buf2;
+ struct venc_buf_type wb_buf;
+ struct venc_buf_type cmd_buf;
+ struct venc_buf_type vlc_buf;
+};
+
+struct venc_mpeg4_config {
+ unsigned int profile;
+ unsigned int level;
+ unsigned int time_resolution;
+ unsigned int ac_prediction;
+ unsigned int hec_interval;
+ unsigned int data_partition;
+ unsigned int short_header;
+ unsigned int rvlc_enable;
+};
+
+struct venc_h263_config {
+ unsigned int profile;
+ unsigned int level;
+};
+
+struct venc_h264_config {
+ unsigned int profile;
+ unsigned int level;
+ unsigned int max_nal;
+ unsigned int idr_period;
+};
+
+struct venc_pmem {
+ int src;
+ int fd;
+ unsigned int offset;
+ void *virt;
+ void *phys;
+ unsigned int size;
+};
+
+struct venc_buffer {
+ unsigned char *ptr_buffer;
+ unsigned int size;
+ unsigned int len;
+ unsigned int offset;
+ long long time_stamp;
+ unsigned int flags;
+ unsigned int client_data;
+};
+
+struct venc_buffers {
+ struct venc_pmem recon_buf[VENC_MAX_RECON_BUFFERS];
+ struct venc_pmem wb_buf;
+ struct venc_pmem cmd_buf;
+ struct venc_pmem vlc_buf;
+};
+
+struct venc_buffer_flush {
+ unsigned int flush_mode;
+};
+
+union venc_msg_data {
+ struct venc_buffer buf;
+ struct venc_buffer_flush flush_ret;
+};
+
+struct venc_msg {
+ unsigned int status_code;
+ unsigned int msg_code;
+ union venc_msg_data msg_data;
+ unsigned int msg_data_size;
+};
+
+union venc_codec_config {
+ struct venc_mpeg4_config mpeg4_params;
+ struct venc_h263_config h263_params;
+ struct venc_h264_config h264_params;
+};
+
+struct venc_q6_config {
+ struct venc_common_config config_params;
+ union venc_codec_config codec_params;
+ struct venc_nonio_buf_config buf_params;
+ void *callback_event;
+};
+
+struct venc_hdr_config {
+ struct venc_common_config config_params;
+ union venc_codec_config codec_params;
+};
+
+struct venc_init_config {
+ struct venc_q6_config q6_config;
+ struct venc_buffers q6_bufs;
+};
+
+struct venc_seq_config {
+ int size;
+ struct venc_pmem buf;
+ struct venc_q6_config q6_config;
+};
+
+#define VENC_IOCTL_MAGIC 'V'
+
+#define VENC_IOCTL_CMD_READ_NEXT_MSG \
+ _IOWR(VENC_IOCTL_MAGIC, 1, struct venc_msg)
+
+#define VENC_IOCTL_CMD_STOP_READ_MSG _IO(VENC_IOCTL_MAGIC, 2)
+
+#define VENC_IOCTL_SET_INPUT_BUFFER \
+ _IOW(VENC_IOCTL_MAGIC, 3, struct venc_pmem)
+
+#define VENC_IOCTL_SET_OUTPUT_BUFFER \
+ _IOW(VENC_IOCTL_MAGIC, 4, struct venc_pmem)
+
+#define VENC_IOCTL_CMD_START _IOW(VENC_IOCTL_MAGIC, 5, struct venc_init_config)
+
+#define VENC_IOCTL_CMD_ENCODE_FRAME \
+ _IOW(VENC_IOCTL_MAGIC, 6, struct venc_buffer)
+
+#define VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER \
+ _IOW(VENC_IOCTL_MAGIC, 7, struct venc_buffer)
+
+#define VENC_IOCTL_CMD_FLUSH \
+ _IOW(VENC_IOCTL_MAGIC, 8, struct venc_buffer_flush)
+
+#define VENC_IOCTL_CMD_PAUSE _IO(VENC_IOCTL_MAGIC, 9)
+
+#define VENC_IOCTL_CMD_RESUME _IO(VENC_IOCTL_MAGIC, 10)
+
+#define VENC_IOCTL_CMD_STOP _IO(VENC_IOCTL_MAGIC, 11)
+
+#define VENC_IOCTL_SET_INTRA_PERIOD \
+ _IOW(VENC_IOCTL_MAGIC, 12, int)
+
+#define VENC_IOCTL_CMD_REQUEST_IFRAME _IO(VENC_IOCTL_MAGIC, 13)
+
+#define VENC_IOCTL_GET_SEQUENCE_HDR \
+ _IOWR(VENC_IOCTL_MAGIC, 14, struct venc_seq_config)
+
+#define VENC_IOCTL_SET_INTRA_REFRESH \
+ _IOW(VENC_IOCTL_MAGIC, 15, int)
+
+#define VENC_IOCTL_SET_FRAME_RATE \
+ _IOW(VENC_IOCTL_MAGIC, 16, struct venc_frame_rate)
+
+#define VENC_IOCTL_SET_TARGET_BITRATE \
+ _IOW(VENC_IOCTL_MAGIC, 17, int)
+
+#define VENC_IOCTL_SET_QP_RANGE \
+ _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range)
+
+#endif
diff --git a/include/linux/msm_rpcrouter.h b/include/linux/msm_rpcrouter.h
new file mode 100644
index 0000000..62141a0
--- /dev/null
+++ b/include/linux/msm_rpcrouter.h
@@ -0,0 +1,47 @@
+/* include/linux/msm_rpcrouter.h
+ *
+ * Copyright (c) QUALCOMM Incorporated
+ * Copyright (C) 2007 Google, Inc.
+ * Author: San Mehat <san@android.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_MSM_RPCROUTER_H
+#define __LINUX_MSM_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define RPC_ROUTER_VERSION_V1 0x00010000
+
+struct rpcrouter_ioctl_server_args {
+ uint32_t prog;
+ uint32_t vers;
+};
+
+#define RPC_ROUTER_IOCTL_MAGIC (0xC1)
+
+#define RPC_ROUTER_IOCTL_GET_VERSION \
+ _IOR(RPC_ROUTER_IOCTL_MAGIC, 0, unsigned int)
+
+#define RPC_ROUTER_IOCTL_GET_MTU \
+ _IOR(RPC_ROUTER_IOCTL_MAGIC, 1, unsigned int)
+
+#define RPC_ROUTER_IOCTL_REGISTER_SERVER \
+ _IOWR(RPC_ROUTER_IOCTL_MAGIC, 2, unsigned int)
+
+#define RPC_ROUTER_IOCTL_UNREGISTER_SERVER \
+ _IOWR(RPC_ROUTER_IOCTL_MAGIC, 3, unsigned int)
+
+#define RPC_ROUTER_IOCTL_GET_MINOR_VERSION \
+ _IOW(RPC_ROUTER_IOCTL_MAGIC, 4, unsigned int)
+
+#endif
diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h
new file mode 100644
index 0000000..5d6233a
--- /dev/null
+++ b/include/linux/msm_vidc_dec.h
@@ -0,0 +1,526 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _MSM_VIDC_DEC_H_
+#define _MSM_VIDC_DEC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* STATUS CODES */
+/* Base value for status codes */
+#define VDEC_S_BASE 0x40000000
+/* Success */
+#define VDEC_S_SUCCESS (VDEC_S_BASE)
+/* General failure */
+#define VDEC_S_EFAIL (VDEC_S_BASE + 1)
+/* Fatal irrecoverable failure. Need to tear down session. */
+#define VDEC_S_EFATAL (VDEC_S_BASE + 2)
+/* Error detected in the passed parameters */
+#define VDEC_S_EBADPARAM (VDEC_S_BASE + 3)
+/* Command called in invalid state. */
+#define VDEC_S_EINVALSTATE (VDEC_S_BASE + 4)
+ /* Insufficient OS resources - thread, memory etc. */
+#define VDEC_S_ENOSWRES (VDEC_S_BASE + 5)
+ /* Insufficient HW resources - core capacity maxed out. */
+#define VDEC_S_ENOHWRES (VDEC_S_BASE + 6)
+/* Invalid command called */
+#define VDEC_S_EINVALCMD (VDEC_S_BASE + 7)
+/* Command timeout. */
+#define VDEC_S_ETIMEOUT (VDEC_S_BASE + 8)
+/* Pre-requirement is not met for API. */
+#define VDEC_S_ENOPREREQ (VDEC_S_BASE + 9)
+/* Command queue is full. */
+#define VDEC_S_ECMDQFULL (VDEC_S_BASE + 10)
+/* Command is not supported by this driver */
+#define VDEC_S_ENOTSUPP (VDEC_S_BASE + 11)
+/* Command is not implemented by thedriver. */
+#define VDEC_S_ENOTIMPL (VDEC_S_BASE + 12)
+/* Command is not implemented by the driver. */
+#define VDEC_S_BUSY (VDEC_S_BASE + 13)
+
+#define VDEC_INTF_VER 1
+#define VDEC_MSG_BASE 0x0000000
+/* Codes to identify asynchronous message responses and events that driver
+ wants to communicate to the app.*/
+#define VDEC_MSG_INVALID (VDEC_MSG_BASE + 0)
+#define VDEC_MSG_RESP_INPUT_BUFFER_DONE (VDEC_MSG_BASE + 1)
+#define VDEC_MSG_RESP_OUTPUT_BUFFER_DONE (VDEC_MSG_BASE + 2)
+#define VDEC_MSG_RESP_INPUT_FLUSHED (VDEC_MSG_BASE + 3)
+#define VDEC_MSG_RESP_OUTPUT_FLUSHED (VDEC_MSG_BASE + 4)
+#define VDEC_MSG_RESP_FLUSH_INPUT_DONE (VDEC_MSG_BASE + 5)
+#define VDEC_MSG_RESP_FLUSH_OUTPUT_DONE (VDEC_MSG_BASE + 6)
+#define VDEC_MSG_RESP_START_DONE (VDEC_MSG_BASE + 7)
+#define VDEC_MSG_RESP_STOP_DONE (VDEC_MSG_BASE + 8)
+#define VDEC_MSG_RESP_PAUSE_DONE (VDEC_MSG_BASE + 9)
+#define VDEC_MSG_RESP_RESUME_DONE (VDEC_MSG_BASE + 10)
+#define VDEC_MSG_RESP_RESOURCE_LOADED (VDEC_MSG_BASE + 11)
+#define VDEC_EVT_RESOURCES_LOST (VDEC_MSG_BASE + 12)
+#define VDEC_MSG_EVT_CONFIG_CHANGED (VDEC_MSG_BASE + 13)
+#define VDEC_MSG_EVT_HW_ERROR (VDEC_MSG_BASE + 14)
+
+/*Buffer flags bits masks.*/
+#define VDEC_BUFFERFLAG_EOS 0x00000001
+#define VDEC_BUFFERFLAG_DECODEONLY 0x00000004
+#define VDEC_BUFFERFLAG_DATACORRUPT 0x00000008
+#define VDEC_BUFFERFLAG_ENDOFFRAME 0x00000010
+#define VDEC_BUFFERFLAG_SYNCFRAME 0x00000020
+#define VDEC_BUFFERFLAG_EXTRADATA 0x00000040
+#define VDEC_BUFFERFLAG_CODECCONFIG 0x00000080
+
+/*Post processing flags bit masks*/
+#define VDEC_EXTRADATA_QP 0x00000001
+#define VDEC_EXTRADATA_SEI 0x00000002
+#define VDEC_EXTRADATA_VUI 0x00000004
+#define VDEC_EXTRADATA_MB_ERROR_MAP 0x00000008
+
+#define VDEC_CMDBASE 0x800
+#define VDEC_CMD_SET_INTF_VERSION (VDEC_CMDBASE)
+
+#define VDEC_IOCTL_MAGIC 'v'
+
+struct vdec_ioctl_msg {
+ void __user *in;
+ void __user *out;
+};
+
+/* CMD params: InputParam:enum vdec_codec
+ OutputParam: struct vdec_profile_level*/
+#define VDEC_IOCTL_GET_PROFILE_LEVEL_SUPPORTED \
+ _IOWR(VDEC_IOCTL_MAGIC, 0, struct vdec_ioctl_msg)
+
+/*CMD params:InputParam: NULL
+ OutputParam: uint32_t(bitmask)*/
+#define VDEC_IOCTL_GET_INTERLACE_FORMAT \
+ _IOR(VDEC_IOCTL_MAGIC, 1, struct vdec_ioctl_msg)
+
+/* CMD params: InputParam: enum vdec_codec
+ OutputParam: struct vdec_profile_level*/
+#define VDEC_IOCTL_GET_CURRENT_PROFILE_LEVEL \
+ _IOWR(VDEC_IOCTL_MAGIC, 2, struct vdec_ioctl_msg)
+
+/*CMD params: SET: InputParam: enum vdec_output_fromat OutputParam: NULL
+ GET: InputParam: NULL OutputParam: enum vdec_output_fromat*/
+#define VDEC_IOCTL_SET_OUTPUT_FORMAT \
+ _IOWR(VDEC_IOCTL_MAGIC, 3, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_OUTPUT_FORMAT \
+ _IOWR(VDEC_IOCTL_MAGIC, 4, struct vdec_ioctl_msg)
+
+/*CMD params: SET: InputParam: enum vdec_codec OutputParam: NULL
+ GET: InputParam: NULL OutputParam: enum vdec_codec*/
+#define VDEC_IOCTL_SET_CODEC \
+ _IOW(VDEC_IOCTL_MAGIC, 5, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_CODEC \
+ _IOR(VDEC_IOCTL_MAGIC, 6, struct vdec_ioctl_msg)
+
+/*CMD params: SET: InputParam: struct vdec_picsize outputparam: NULL
+ GET: InputParam: NULL outputparam: struct vdec_picsize*/
+#define VDEC_IOCTL_SET_PICRES \
+ _IOW(VDEC_IOCTL_MAGIC, 7, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_PICRES \
+ _IOR(VDEC_IOCTL_MAGIC, 8, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_EXTRADATA \
+ _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_EXTRADATA \
+ _IOR(VDEC_IOCTL_MAGIC, 10, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_SEQUENCE_HEADER \
+ _IOW(VDEC_IOCTL_MAGIC, 11, struct vdec_ioctl_msg)
+
+/* CMD params: SET: InputParam - vdec_allocatorproperty, OutputParam - NULL
+ GET: InputParam - NULL, OutputParam - vdec_allocatorproperty*/
+#define VDEC_IOCTL_SET_BUFFER_REQ \
+ _IOW(VDEC_IOCTL_MAGIC, 12, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_BUFFER_REQ \
+ _IOR(VDEC_IOCTL_MAGIC, 13, struct vdec_ioctl_msg)
+/* CMD params: InputParam - vdec_buffer, OutputParam - uint8_t** */
+#define VDEC_IOCTL_ALLOCATE_BUFFER \
+ _IOWR(VDEC_IOCTL_MAGIC, 14, struct vdec_ioctl_msg)
+/* CMD params: InputParam - uint8_t *, OutputParam - NULL.*/
+#define VDEC_IOCTL_FREE_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 15, struct vdec_ioctl_msg)
+
+/*CMD params: CMD: InputParam - struct vdec_setbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_SET_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 16, struct vdec_ioctl_msg)
+
+/* CMD params: InputParam - struct vdec_fillbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_FILL_OUTPUT_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 17, struct vdec_ioctl_msg)
+
+/*CMD params: InputParam - struct vdec_frameinfo , OutputParam - NULL*/
+#define VDEC_IOCTL_DECODE_FRAME \
+ _IOW(VDEC_IOCTL_MAGIC, 18, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_LOAD_RESOURCES _IO(VDEC_IOCTL_MAGIC, 19)
+#define VDEC_IOCTL_CMD_START _IO(VDEC_IOCTL_MAGIC, 20)
+#define VDEC_IOCTL_CMD_STOP _IO(VDEC_IOCTL_MAGIC, 21)
+#define VDEC_IOCTL_CMD_PAUSE _IO(VDEC_IOCTL_MAGIC, 22)
+#define VDEC_IOCTL_CMD_RESUME _IO(VDEC_IOCTL_MAGIC, 23)
+
+/*CMD params: InputParam - enum vdec_bufferflush , OutputParam - NULL */
+#define VDEC_IOCTL_CMD_FLUSH _IOW(VDEC_IOCTL_MAGIC, 24, struct vdec_ioctl_msg)
+
+/* ========================================================
+ * IOCTL for getting asynchronous notification from driver
+ * ========================================================*/
+
+/*IOCTL params: InputParam - NULL, OutputParam - struct vdec_msginfo*/
+#define VDEC_IOCTL_GET_NEXT_MSG \
+ _IOR(VDEC_IOCTL_MAGIC, 25, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_STOP_NEXT_MSG _IO(VDEC_IOCTL_MAGIC, 26)
+
+#define VDEC_IOCTL_GET_NUMBER_INSTANCES \
+ _IOR(VDEC_IOCTL_MAGIC, 27, struct vdec_ioctl_msg)
+
+enum vdec_picture {
+ PICTURE_TYPE_I,
+ PICTURE_TYPE_P,
+ PICTURE_TYPE_B,
+ PICTURE_TYPE_BI,
+ PICTURE_TYPE_SKIP,
+ PICTURE_TYPE_UNKNOWN
+};
+
+enum vdec_buffer {
+ VDEC_BUFFER_TYPE_INPUT,
+ VDEC_BUFFER_TYPE_OUTPUT
+};
+
+struct vdec_allocatorproperty {
+ enum vdec_buffer buffer_type;
+ uint32_t mincount;
+ uint32_t maxcount;
+ uint32_t actualcount;
+ uint32_t buffer_size;
+ uint32_t alignment;
+ uint32_t buf_poolid;
+};
+
+struct vdec_bufferpayload {
+ void __user *addr;
+ size_t sz;
+ int pmem_fd;
+ size_t offset;
+ size_t mmaped_sz;
+};
+
+struct vdec_setbuffer_cmd {
+ enum vdec_buffer buffer_type;
+ struct vdec_bufferpayload buffer;
+};
+
+struct vdec_fillbuffer_cmd {
+ struct vdec_bufferpayload buffer;
+ void *client_data;
+};
+
+enum vdec_bufferflush {
+ VDEC_FLUSH_TYPE_INPUT,
+ VDEC_FLUSH_TYPE_OUTPUT,
+ VDEC_FLUSH_TYPE_ALL
+};
+
+enum vdec_codec {
+ VDEC_CODECTYPE_H264 = 0x1,
+ VDEC_CODECTYPE_H263 = 0x2,
+ VDEC_CODECTYPE_MPEG4 = 0x3,
+ VDEC_CODECTYPE_DIVX_3 = 0x4,
+ VDEC_CODECTYPE_DIVX_4 = 0x5,
+ VDEC_CODECTYPE_DIVX_5 = 0x6,
+ VDEC_CODECTYPE_DIVX_6 = 0x7,
+ VDEC_CODECTYPE_XVID = 0x8,
+ VDEC_CODECTYPE_MPEG1 = 0x9,
+ VDEC_CODECTYPE_MPEG2 = 0xa,
+ VDEC_CODECTYPE_VC1 = 0xb,
+ VDEC_CODECTYPE_VC1_RCV = 0xc
+};
+
+enum vdec_mpeg2_profile {
+ VDEC_MPEG2ProfileSimple = 0x1,
+ VDEC_MPEG2ProfileMain = 0x2,
+ VDEC_MPEG2Profile422 = 0x4,
+ VDEC_MPEG2ProfileSNR = 0x8,
+ VDEC_MPEG2ProfileSpatial = 0x10,
+ VDEC_MPEG2ProfileHigh = 0x20,
+ VDEC_MPEG2ProfileKhronosExtensions = 0x6F000000,
+ VDEC_MPEG2ProfileVendorStartUnused = 0x7F000000,
+ VDEC_MPEG2ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg2_level {
+
+ VDEC_MPEG2LevelLL = 0x1,
+ VDEC_MPEG2LevelML = 0x2,
+ VDEC_MPEG2LevelH14 = 0x4,
+ VDEC_MPEG2LevelHL = 0x8,
+ VDEC_MPEG2LevelKhronosExtensions = 0x6F000000,
+ VDEC_MPEG2LevelVendorStartUnused = 0x7F000000,
+ VDEC_MPEG2LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_profile {
+ VDEC_MPEG4ProfileSimple = 0x01,
+ VDEC_MPEG4ProfileSimpleScalable = 0x02,
+ VDEC_MPEG4ProfileCore = 0x04,
+ VDEC_MPEG4ProfileMain = 0x08,
+ VDEC_MPEG4ProfileNbit = 0x10,
+ VDEC_MPEG4ProfileScalableTexture = 0x20,
+ VDEC_MPEG4ProfileSimpleFace = 0x40,
+ VDEC_MPEG4ProfileSimpleFBA = 0x80,
+ VDEC_MPEG4ProfileBasicAnimated = 0x100,
+ VDEC_MPEG4ProfileHybrid = 0x200,
+ VDEC_MPEG4ProfileAdvancedRealTime = 0x400,
+ VDEC_MPEG4ProfileCoreScalable = 0x800,
+ VDEC_MPEG4ProfileAdvancedCoding = 0x1000,
+ VDEC_MPEG4ProfileAdvancedCore = 0x2000,
+ VDEC_MPEG4ProfileAdvancedScalable = 0x4000,
+ VDEC_MPEG4ProfileAdvancedSimple = 0x8000,
+ VDEC_MPEG4ProfileKhronosExtensions = 0x6F000000,
+ VDEC_MPEG4ProfileVendorStartUnused = 0x7F000000,
+ VDEC_MPEG4ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_level {
+ VDEC_MPEG4Level0 = 0x01,
+ VDEC_MPEG4Level0b = 0x02,
+ VDEC_MPEG4Level1 = 0x04,
+ VDEC_MPEG4Level2 = 0x08,
+ VDEC_MPEG4Level3 = 0x10,
+ VDEC_MPEG4Level4 = 0x20,
+ VDEC_MPEG4Level4a = 0x40,
+ VDEC_MPEG4Level5 = 0x80,
+ VDEC_MPEG4LevelKhronosExtensions = 0x6F000000,
+ VDEC_MPEG4LevelVendorStartUnused = 0x7F000000,
+ VDEC_MPEG4LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_profile {
+ VDEC_AVCProfileBaseline = 0x01,
+ VDEC_AVCProfileMain = 0x02,
+ VDEC_AVCProfileExtended = 0x04,
+ VDEC_AVCProfileHigh = 0x08,
+ VDEC_AVCProfileHigh10 = 0x10,
+ VDEC_AVCProfileHigh422 = 0x20,
+ VDEC_AVCProfileHigh444 = 0x40,
+ VDEC_AVCProfileKhronosExtensions = 0x6F000000,
+ VDEC_AVCProfileVendorStartUnused = 0x7F000000,
+ VDEC_AVCProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_level {
+ VDEC_AVCLevel1 = 0x01,
+ VDEC_AVCLevel1b = 0x02,
+ VDEC_AVCLevel11 = 0x04,
+ VDEC_AVCLevel12 = 0x08,
+ VDEC_AVCLevel13 = 0x10,
+ VDEC_AVCLevel2 = 0x20,
+ VDEC_AVCLevel21 = 0x40,
+ VDEC_AVCLevel22 = 0x80,
+ VDEC_AVCLevel3 = 0x100,
+ VDEC_AVCLevel31 = 0x200,
+ VDEC_AVCLevel32 = 0x400,
+ VDEC_AVCLevel4 = 0x800,
+ VDEC_AVCLevel41 = 0x1000,
+ VDEC_AVCLevel42 = 0x2000,
+ VDEC_AVCLevel5 = 0x4000,
+ VDEC_AVCLevel51 = 0x8000,
+ VDEC_AVCLevelKhronosExtensions = 0x6F000000,
+ VDEC_AVCLevelVendorStartUnused = 0x7F000000,
+ VDEC_AVCLevelMax = 0x7FFFFFFF
+};
+
+enum vdec_divx_profile {
+ VDEC_DIVXProfile_qMobile = 0x01,
+ VDEC_DIVXProfile_Mobile = 0x02,
+ VDEC_DIVXProfile_HD = 0x04,
+ VDEC_DIVXProfile_Handheld = 0x08,
+ VDEC_DIVXProfile_Portable = 0x10,
+ VDEC_DIVXProfile_HomeTheater = 0x20
+};
+
+enum vdec_xvid_profile {
+ VDEC_XVIDProfile_Simple = 0x1,
+ VDEC_XVIDProfile_Advanced_Realtime_Simple = 0x2,
+ VDEC_XVIDProfile_Advanced_Simple = 0x4
+};
+
+enum vdec_xvid_level {
+ VDEC_XVID_LEVEL_S_L0 = 0x1,
+ VDEC_XVID_LEVEL_S_L1 = 0x2,
+ VDEC_XVID_LEVEL_S_L2 = 0x4,
+ VDEC_XVID_LEVEL_S_L3 = 0x8,
+ VDEC_XVID_LEVEL_ARTS_L1 = 0x10,
+ VDEC_XVID_LEVEL_ARTS_L2 = 0x20,
+ VDEC_XVID_LEVEL_ARTS_L3 = 0x40,
+ VDEC_XVID_LEVEL_ARTS_L4 = 0x80,
+ VDEC_XVID_LEVEL_AS_L0 = 0x100,
+ VDEC_XVID_LEVEL_AS_L1 = 0x200,
+ VDEC_XVID_LEVEL_AS_L2 = 0x400,
+ VDEC_XVID_LEVEL_AS_L3 = 0x800,
+ VDEC_XVID_LEVEL_AS_L4 = 0x1000
+};
+
+enum vdec_h263profile {
+ VDEC_H263ProfileBaseline = 0x01,
+ VDEC_H263ProfileH320Coding = 0x02,
+ VDEC_H263ProfileBackwardCompatible = 0x04,
+ VDEC_H263ProfileISWV2 = 0x08,
+ VDEC_H263ProfileISWV3 = 0x10,
+ VDEC_H263ProfileHighCompression = 0x20,
+ VDEC_H263ProfileInternet = 0x40,
+ VDEC_H263ProfileInterlace = 0x80,
+ VDEC_H263ProfileHighLatency = 0x100,
+ VDEC_H263ProfileKhronosExtensions = 0x6F000000,
+ VDEC_H263ProfileVendorStartUnused = 0x7F000000,
+ VDEC_H263ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_h263level {
+ VDEC_H263Level10 = 0x01,
+ VDEC_H263Level20 = 0x02,
+ VDEC_H263Level30 = 0x04,
+ VDEC_H263Level40 = 0x08,
+ VDEC_H263Level45 = 0x10,
+ VDEC_H263Level50 = 0x20,
+ VDEC_H263Level60 = 0x40,
+ VDEC_H263Level70 = 0x80,
+ VDEC_H263LevelKhronosExtensions = 0x6F000000,
+ VDEC_H263LevelVendorStartUnused = 0x7F000000,
+ VDEC_H263LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_wmv_format {
+ VDEC_WMVFormatUnused = 0x01,
+ VDEC_WMVFormat7 = 0x02,
+ VDEC_WMVFormat8 = 0x04,
+ VDEC_WMVFormat9 = 0x08,
+ VDEC_WMFFormatKhronosExtensions = 0x6F000000,
+ VDEC_WMFFormatVendorStartUnused = 0x7F000000,
+ VDEC_WMVFormatMax = 0x7FFFFFFF
+};
+
+enum vdec_vc1_profile {
+ VDEC_VC1ProfileSimple = 0x1,
+ VDEC_VC1ProfileMain = 0x2,
+ VDEC_VC1ProfileAdvanced = 0x4
+};
+
+enum vdec_vc1_level {
+ VDEC_VC1_LEVEL_S_Low = 0x1,
+ VDEC_VC1_LEVEL_S_Medium = 0x2,
+ VDEC_VC1_LEVEL_M_Low = 0x4,
+ VDEC_VC1_LEVEL_M_Medium = 0x8,
+ VDEC_VC1_LEVEL_M_High = 0x10,
+ VDEC_VC1_LEVEL_A_L0 = 0x20,
+ VDEC_VC1_LEVEL_A_L1 = 0x40,
+ VDEC_VC1_LEVEL_A_L2 = 0x80,
+ VDEC_VC1_LEVEL_A_L3 = 0x100,
+ VDEC_VC1_LEVEL_A_L4 = 0x200
+};
+
+struct vdec_profile_level {
+ uint32_t profiles;
+ uint32_t levels;
+};
+
+enum vdec_interlaced_format {
+ VDEC_InterlaceFrameProgressive = 0x1,
+ VDEC_InterlaceInterleaveFrameTopFieldFirst = 0x2,
+ VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4
+};
+
+enum vdec_output_format {
+ VDEC_YUV_FORMAT_NV12 = 0x1,
+ VDEC_YUV_FORMAT_TILE_4x2 = 0x2
+};
+
+struct vdec_picsize {
+ uint32_t frame_width;
+ uint32_t frame_height;
+ uint32_t stride;
+ uint32_t scan_lines;
+};
+
+struct vdec_seqheader {
+ void *addr;
+ size_t sz;
+ int pmem_fd;
+ size_t pmem_offset;
+};
+
+struct vdec_mberror {
+ uint8_t *ptr_errormap;
+ uint32_t err_mapsize;
+};
+
+struct vdec_input_frameinfo {
+ void __user *user_addr;
+ size_t offset;
+ size_t data_len;
+ uint32_t flags;
+ int64_t timestamp;
+ void *client_data;
+ int pmem_fd;
+ size_t pmem_offset;
+};
+
+struct vdec_framesize {
+ uint32_t left;
+ uint32_t top;
+ uint32_t right;
+ uint32_t bottom;
+};
+
+struct vdec_output_frameinfo {
+ phys_addr_t phys_addr;
+ void __user *user_addr;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t flags;
+ int64_t time_stamp;
+ void *client_data;
+ void *input_frame_clientdata;
+ struct vdec_framesize framesize;
+};
+
+union vdec_msgdata {
+ struct vdec_output_frameinfo output_frame;
+ void *input_frame_clientdata;
+};
+
+struct vdec_msginfo {
+ uint32_t status_code;
+ uint32_t msgcode;
+ union vdec_msgdata msgdata;
+ uint32_t msgdatasize;
+};
+#endif /* end of macro _VDECDECODER_H_ */
diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h
new file mode 100644
index 0000000..f7f398c
--- /dev/null
+++ b/include/linux/msm_vidc_enc.h
@@ -0,0 +1,592 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _MSM_VIDC_ENC_H_
+#define _MSM_VIDC_ENC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/** STATUS CODES*/
+/* Base value for status codes */
+#define VEN_S_BASE 0x00000000
+#define VEN_S_SUCCESS (VEN_S_BASE)/* Success */
+#define VEN_S_EFAIL (VEN_S_BASE+1)/* General failure */
+#define VEN_S_EFATAL (VEN_S_BASE+2)/* Fatal irrecoverable failure*/
+#define VEN_S_EBADPARAM (VEN_S_BASE+3)/* Error passed parameters*/
+/*Command called in invalid state*/
+#define VEN_S_EINVALSTATE (VEN_S_BASE+4)
+#define VEN_S_ENOSWRES (VEN_S_BASE+5)/* Insufficient OS resources*/
+#define VEN_S_ENOHWRES (VEN_S_BASE+6)/*Insufficient HW resources */
+#define VEN_S_EBUFFREQ (VEN_S_BASE+7)/* Buffer requirements were not met*/
+#define VEN_S_EINVALCMD (VEN_S_BASE+8)/* Invalid command called */
+#define VEN_S_ETIMEOUT (VEN_S_BASE+9)/* Command timeout. */
+/*Re-attempt was made when multiple invocation not supported for API.*/
+#define VEN_S_ENOREATMPT (VEN_S_BASE+10)
+#define VEN_S_ENOPREREQ (VEN_S_BASE+11)/*Pre-requirement is not met for API*/
+#define VEN_S_ECMDQFULL (VEN_S_BASE+12)/*Command queue is full*/
+#define VEN_S_ENOTSUPP (VEN_S_BASE+13)/*Command not supported*/
+#define VEN_S_ENOTIMPL (VEN_S_BASE+14)/*Command not implemented.*/
+#define VEN_S_ENOTPMEM (VEN_S_BASE+15)/*Buffer is not from PMEM*/
+#define VEN_S_EFLUSHED (VEN_S_BASE+16)/*returned buffer was flushed*/
+#define VEN_S_EINSUFBUF (VEN_S_BASE+17)/*provided buffer size insufficient*/
+#define VEN_S_ESAMESTATE (VEN_S_BASE+18)
+#define VEN_S_EINVALTRANS (VEN_S_BASE+19)
+
+#define VEN_INTF_VER 1
+
+/*Asynchronous messages from driver*/
+#define VEN_MSG_INDICATION 0
+#define VEN_MSG_INPUT_BUFFER_DONE 1
+#define VEN_MSG_OUTPUT_BUFFER_DONE 2
+#define VEN_MSG_NEED_OUTPUT_BUFFER 3
+#define VEN_MSG_FLUSH_INPUT_DONE 4
+#define VEN_MSG_FLUSH_OUPUT_DONE 5
+#define VEN_MSG_START 6
+#define VEN_MSG_STOP 7
+#define VEN_MSG_PAUSE 8
+#define VEN_MSG_RESUME 9
+#define VEN_MSG_STOP_READING_MSG 10
+
+/*Buffer flags bits masks*/
+#define VEN_BUFFLAG_EOS 0x00000001
+#define VEN_BUFFLAG_ENDOFFRAME 0x00000010
+#define VEN_BUFFLAG_SYNCFRAME 0x00000020
+#define VEN_BUFFLAG_EXTRADATA 0x00000040
+#define VEN_BUFFLAG_CODECCONFIG 0x00000080
+
+/*ENCODER CONFIGURATION CONSTANTS*/
+
+/*Encoded video frame types*/
+#define VEN_FRAME_TYPE_I 1/* I frame type */
+#define VEN_FRAME_TYPE_P 2/* P frame type */
+#define VEN_FRAME_TYPE_B 3/* B frame type */
+
+/*Video codec types*/
+#define VEN_CODEC_MPEG4 1/* MPEG4 Codec */
+#define VEN_CODEC_H264 2/* H.264 Codec */
+#define VEN_CODEC_H263 3/* H.263 Codec */
+
+/*Video codec profile types.*/
+#define VEN_PROFILE_MPEG4_SP 1/* 1 - MPEG4 SP profile */
+#define VEN_PROFILE_MPEG4_ASP 2/* 2 - MPEG4 ASP profile */
+#define VEN_PROFILE_H264_BASELINE 3/* 3 - H264 Baseline profile */
+#define VEN_PROFILE_H264_MAIN 4/* 4 - H264 Main profile*/
+#define VEN_PROFILE_H264_HIGH 5/* 5 - H264 High profile*/
+#define VEN_PROFILE_H263_BASELINE 6/* 6 - H263 Baseline profile */
+
+/*Video codec profile level types.*/
+#define VEN_LEVEL_MPEG4_0 0x1/* MPEG4 Level 0 */
+#define VEN_LEVEL_MPEG4_1 0x2/* MPEG4 Level 1 */
+#define VEN_LEVEL_MPEG4_2 0x3/* MPEG4 Level 2 */
+#define VEN_LEVEL_MPEG4_3 0x4/* MPEG4 Level 3 */
+#define VEN_LEVEL_MPEG4_4 0x5/* MPEG4 Level 4 */
+#define VEN_LEVEL_MPEG4_5 0x6/* MPEG4 Level 5 */
+#define VEN_LEVEL_MPEG4_3b 0x7/* MPEG4 Level 3b */
+#define VEN_LEVEL_MPEG4_6 0x8/* MPEG4 Level 6 */
+
+#define VEN_LEVEL_H264_1 0x9/* H.264 Level 1 */
+#define VEN_LEVEL_H264_1b 0xA/* H.264 Level 1b */
+#define VEN_LEVEL_H264_1p1 0xB/* H.264 Level 1.1 */
+#define VEN_LEVEL_H264_1p2 0xC/* H.264 Level 1.2 */
+#define VEN_LEVEL_H264_1p3 0xD/* H.264 Level 1.3 */
+#define VEN_LEVEL_H264_2 0xE/* H.264 Level 2 */
+#define VEN_LEVEL_H264_2p1 0xF/* H.264 Level 2.1 */
+#define VEN_LEVEL_H264_2p2 0x10/* H.264 Level 2.2 */
+#define VEN_LEVEL_H264_3 0x11/* H.264 Level 3 */
+#define VEN_LEVEL_H264_3p1 0x12/* H.264 Level 3.1 */
+
+#define VEN_LEVEL_H263_10 0x13/* H.263 Level 10 */
+#define VEN_LEVEL_H263_20 0x14/* H.263 Level 20 */
+#define VEN_LEVEL_H263_30 0x15/* H.263 Level 30 */
+#define VEN_LEVEL_H263_40 0x16/* H.263 Level 40 */
+#define VEN_LEVEL_H263_45 0x17/* H.263 Level 45 */
+#define VEN_LEVEL_H263_50 0x18/* H.263 Level 50 */
+#define VEN_LEVEL_H263_60 0x19/* H.263 Level 60 */
+#define VEN_LEVEL_H263_70 0x1A/* H.263 Level 70 */
+
+/*Entropy coding model selection for H.264 encoder.*/
+#define VEN_ENTROPY_MODEL_CAVLC 1
+#define VEN_ENTROPY_MODEL_CABAC 2
+/*Cabac model number (0,1,2) for encoder.*/
+#define VEN_CABAC_MODEL_0 1/* CABAC Model 0. */
+#define VEN_CABAC_MODEL_1 2/* CABAC Model 1. */
+#define VEN_CABAC_MODEL_2 3/* CABAC Model 2. */
+
+/*Deblocking filter control type for encoder.*/
+#define VEN_DB_DISABLE 1/* 1 - Disable deblocking filter*/
+#define VEN_DB_ALL_BLKG_BNDRY 2/* 2 - All blocking boundary filtering*/
+#define VEN_DB_SKIP_SLICE_BNDRY 3/* 3 - Filtering except sliceboundary*/
+
+/*Different methods of Multi slice selection.*/
+#define VEN_MSLICE_OFF 1
+#define VEN_MSLICE_CNT_MB 2 /*number of MBscount per slice*/
+#define VEN_MSLICE_CNT_BYTE 3 /*number of bytes count per slice.*/
+#define VEN_MSLICE_GOB 4 /*Multi slice by GOB for H.263 only.*/
+
+/*Different modes for Rate Control.*/
+#define VEN_RC_OFF 1
+#define VEN_RC_VBR_VFR 2
+#define VEN_RC_VBR_CFR 3
+#define VEN_RC_CBR_VFR 4
+
+/*Different modes for flushing buffers*/
+#define VEN_FLUSH_INPUT 1
+#define VEN_FLUSH_OUTPUT 2
+#define VEN_FLUSH_ALL 3
+
+/*Different input formats for YUV data.*/
+#define VEN_INPUTFMT_NV12 1/* NV12 Linear */
+#define VEN_INPUTFMT_NV21 2/* NV21 Linear */
+
+/*Different allowed rotation modes.*/
+#define VEN_ROTATION_0 1/* 0 degrees */
+#define VEN_ROTATION_90 2/* 90 degrees */
+#define VEN_ROTATION_180 3/* 180 degrees */
+#define VEN_ROTATION_270 4/* 270 degrees */
+
+/*IOCTL timeout values*/
+#define VEN_TIMEOUT_INFINITE 0xffffffff
+
+/*Different allowed intra refresh modes.*/
+#define VEN_IR_OFF 1
+#define VEN_IR_CYCLIC 2
+#define VEN_IR_RANDOM 3
+
+/*IOCTL BASE CODES Not to be used directly by the client.*/
+/* Base value for ioctls that are not related to encoder configuration.*/
+#define VEN_IOCTLBASE_NENC 0x800
+/* Base value for encoder configuration ioctls*/
+#define VEN_IOCTLBASE_ENC 0x850
+
+struct venc_ioctl_msg {
+ void __user *in;
+ void __user *out;
+};
+
+/*NON ENCODER CONFIGURATION IOCTLs*/
+
+/*IOCTL params:SET: InputData - unsigned long, OutputData - NULL*/
+#define VEN_IOCTL_SET_INTF_VERSION \
+ _IOW(VEN_IOCTLBASE_NENC, 0, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_timeout, OutputData - venc_msg*/
+#define VEN_IOCTL_CMD_READ_NEXT_MSG \
+ _IOWR(VEN_IOCTLBASE_NENC, 1, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - NULL, OutputData - NULL*/
+#define VEN_IOCTL_CMD_STOP_READ_MSG _IO(VEN_IOCTLBASE_NENC, 2)
+
+/*IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_allocatorproperty*/
+#define VEN_IOCTL_SET_INPUT_BUFFER_REQ \
+ _IOW(VEN_IOCTLBASE_NENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INPUT_BUFFER_REQ \
+ _IOR(VEN_IOCTLBASE_NENC, 4, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 5, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 6, struct venc_ioctl_msg)
+
+/*IOCTL params: CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_FREE_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 7, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_allocatorproperty*/
+#define VEN_IOCTL_SET_OUTPUT_BUFFER_REQ \
+ _IOW(VEN_IOCTLBASE_NENC, 8, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \
+ _IOR(VEN_IOCTLBASE_NENC, 9, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 10, struct venc_ioctl_msg)
+
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 11, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL.*/
+#define VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 12, struct venc_ioctl_msg)
+
+
+/* Asynchronous respone message code:* VEN_MSG_START*/
+#define VEN_IOCTL_CMD_START _IO(VEN_IOCTLBASE_NENC, 13)
+
+
+/*IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ Asynchronous respone message code:VEN_MSG_INPUT_BUFFER_DONE*/
+#define VEN_IOCTL_CMD_ENCODE_FRAME \
+ _IOW(VEN_IOCTLBASE_NENC, 14, struct venc_ioctl_msg)
+
+
+/*IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ Asynchronous response message code:VEN_MSG_OUTPUT_BUFFER_DONE*/
+#define VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 15, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferflush, OutputData - NULL
+ * Asynchronous response message code:VEN_MSG_INPUT_BUFFER_DONE*/
+#define VEN_IOCTL_CMD_FLUSH \
+ _IOW(VEN_IOCTLBASE_NENC, 16, struct venc_ioctl_msg)
+
+
+/*Asynchronous respone message code:VEN_MSG_PAUSE*/
+#define VEN_IOCTL_CMD_PAUSE _IO(VEN_IOCTLBASE_NENC, 17)
+
+/*Asynchronous respone message code:VEN_MSG_RESUME*/
+#define VEN_IOCTL_CMD_RESUME _IO(VEN_IOCTLBASE_NENC, 18)
+
+/* Asynchronous respone message code:VEN_MSG_STOP*/
+#define VEN_IOCTL_CMD_STOP _IO(VEN_IOCTLBASE_NENC, 19)
+
+
+/*ENCODER PROPERTY CONFIGURATION & CAPABILITY IOCTLs*/
+
+/*IOCTL params:SET: InputData - venc_basecfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_basecfg*/
+#define VEN_IOCTL_SET_BASE_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 1, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_BASE_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 2, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_switch*/
+#define VEN_IOCTL_SET_LIVE_MODE \
+ _IOW(VEN_IOCTLBASE_ENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_LIVE_MODE \
+ _IOR(VEN_IOCTLBASE_ENC, 4, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_profile, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_profile*/
+#define VEN_IOCTL_SET_CODEC_PROFILE \
+ _IOW(VEN_IOCTLBASE_ENC, 5, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_CODEC_PROFILE \
+ _IOR(VEN_IOCTLBASE_ENC, 6, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - ven_profilelevel, OutputData - NULL
+ GET: InputData - NULL, OutputData - ven_profilelevel*/
+#define VEN_IOCTL_SET_PROFILE_LEVEL \
+ _IOW(VEN_IOCTLBASE_ENC, 7, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_PROFILE_LEVEL \
+ _IOR(VEN_IOCTLBASE_ENC, 8, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_switch*/
+#define VEN_IOCTL_SET_SHORT_HDR \
+ _IOW(VEN_IOCTLBASE_ENC, 9, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SHORT_HDR \
+ _IOR(VEN_IOCTLBASE_ENC, 10, struct venc_ioctl_msg)
+
+
+/*IOCTL params: SET: InputData - venc_sessionqp, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_sessionqp*/
+#define VEN_IOCTL_SET_SESSION_QP \
+ _IOW(VEN_IOCTLBASE_ENC, 11, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SESSION_QP \
+ _IOR(VEN_IOCTLBASE_ENC, 12, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_intraperiod, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_intraperiod*/
+#define VEN_IOCTL_SET_INTRA_PERIOD \
+ _IOW(VEN_IOCTLBASE_ENC, 13, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_PERIOD \
+ _IOR(VEN_IOCTLBASE_ENC, 14, struct venc_ioctl_msg)
+
+
+/* Request an Iframe*/
+#define VEN_IOCTL_CMD_REQUEST_IFRAME _IO(VEN_IOCTLBASE_ENC, 15)
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_capability*/
+#define VEN_IOCTL_GET_CAPABILITY \
+ _IOR(VEN_IOCTLBASE_ENC, 16, struct venc_ioctl_msg)
+
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_seqheader*/
+#define VEN_IOCTL_GET_SEQUENCE_HDR \
+ _IOR(VEN_IOCTLBASE_ENC, 17, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_entropycfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_entropycfg*/
+#define VEN_IOCTL_SET_ENTROPY_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 18, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ENTROPY_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 19, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_dbcfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_dbcfg*/
+#define VEN_IOCTL_SET_DEBLOCKING_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 20, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DEBLOCKING_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 21, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_intrarefresh, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_intrarefresh*/
+#define VEN_IOCTL_SET_INTRA_REFRESH \
+ _IOW(VEN_IOCTLBASE_ENC, 22, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_REFRESH \
+ _IOR(VEN_IOCTLBASE_ENC, 23, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_multiclicecfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_multiclicecfg*/
+#define VEN_IOCTL_SET_MULTI_SLICE_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 24, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_MULTI_SLICE_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 25, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ratectrlcfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_ratectrlcfg*/
+#define VEN_IOCTL_SET_RATE_CTRL_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 26, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RATE_CTRL_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 27, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_voptimingcfg, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_voptimingcfg*/
+#define VEN_IOCTL_SET_VOP_TIMING_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 28, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_VOP_TIMING_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 29, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_framerate, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_framerate*/
+#define VEN_IOCTL_SET_FRAME_RATE \
+ _IOW(VEN_IOCTLBASE_ENC, 30, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_FRAME_RATE \
+ _IOR(VEN_IOCTLBASE_ENC, 31, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_targetbitrate, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_targetbitrate*/
+#define VEN_IOCTL_SET_TARGET_BITRATE \
+ _IOW(VEN_IOCTLBASE_ENC, 32, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_TARGET_BITRATE \
+ _IOR(VEN_IOCTLBASE_ENC, 33, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_rotation, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_rotation*/
+#define VEN_IOCTL_SET_ROTATION \
+ _IOW(VEN_IOCTLBASE_ENC, 34, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ROTATION \
+ _IOR(VEN_IOCTLBASE_ENC, 35, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_headerextension, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_headerextension*/
+#define VEN_IOCTL_SET_HEC \
+ _IOW(VEN_IOCTLBASE_ENC, 36, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_HEC \
+ _IOR(VEN_IOCTLBASE_ENC, 37, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_switch*/
+#define VEN_IOCTL_SET_DATA_PARTITION \
+ _IOW(VEN_IOCTLBASE_ENC, 38, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DATA_PARTITION \
+ _IOR(VEN_IOCTLBASE_ENC, 39, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_switch*/
+#define VEN_IOCTL_SET_RVLC \
+ _IOW(VEN_IOCTLBASE_ENC, 40, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RVLC \
+ _IOR(VEN_IOCTLBASE_ENC, 41, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_switch*/
+#define VEN_IOCTL_SET_AC_PREDICTION \
+ _IOW(VEN_IOCTLBASE_ENC, 42, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_AC_PREDICTION \
+ _IOR(VEN_IOCTLBASE_ENC, 43, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - venc_qprange, OutputData - NULL
+ GET: InputData - NULL, OutputData - venc_qprange*/
+#define VEN_IOCTL_SET_QP_RANGE \
+ _IOW(VEN_IOCTLBASE_ENC, 44, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_QP_RANGE \
+ _IOR(VEN_IOCTLBASE_ENC, 45, struct venc_ioctl_msg)
+
+struct venc_switch {
+ unsigned char status;
+};
+
+struct venc_allocatorproperty {
+ u32 mincount;
+ u32 maxcount;
+ u32 actualcount;
+ u32 datasize;
+ u32 suffixsize;
+ u32 alignment;
+ u32 bufpoolid;
+};
+
+struct venc_bufferpayload {
+ void __user *buffer;
+ size_t sz;
+ int fd;
+ size_t offset;
+ unsigned int maped_size;
+ unsigned long filled_len;
+};
+
+struct venc_buffer {
+ void __user *addr;
+ size_t sz;
+ size_t len;
+ size_t offset;
+ long long timestamp;
+ u32 flags;
+ void *clientdata;
+};
+
+struct venc_basecfg {
+ u32 input_width;
+ u32 input_height;
+ u32 dvs_width;
+ u32 dvs_height;
+ u32 codectype;
+ u32 fps_num;
+ u32 fps_den;
+ u32 targetbitrate;
+ u32 inputformat;
+};
+
+struct venc_profile {
+ unsigned long profile;
+};
+struct ven_profilelevel {
+ unsigned long level;
+};
+
+struct venc_sessionqp {
+ unsigned long iframeqp;
+ unsigned long pframqp;
+};
+
+struct venc_qprange {
+ u32 maxqp;
+ u32 minqp;
+};
+struct venc_intraperiod {
+ unsigned long num_pframes;
+};
+struct venc_seqheader {
+ void *buf;
+ size_t buf_sz;
+ size_t hdr_len;
+};
+
+struct venc_capability {
+ unsigned long codec_types;
+ unsigned long maxframe_width;
+ unsigned long maxframe_height;
+ unsigned long maxtarget_bitrate;
+ unsigned long maxframe_rate;
+ unsigned long input_formats;
+ unsigned char dvs;
+};
+
+struct venc_entropycfg {
+ unsigned longentropysel;
+ unsigned long cabacmodel;
+};
+
+struct venc_dbcfg {
+ u32 db_mode;
+ u32 slicealpha_offset;
+ u32 slicebeta_offset;
+};
+
+struct venc_intrarefresh {
+ unsigned long irmode;
+ unsigned long mbcount;
+};
+
+struct venc_multiclicecfg {
+ unsigned long mslice_mode;
+ unsigned long mslice_size;
+};
+
+struct venc_bufferflush {
+ unsigned long flush_mode;
+};
+
+struct venc_ratectrlcfg {
+ unsigned long rcmode;
+};
+
+struct venc_voptimingcfg {
+ u32 voptime_resolution;
+};
+struct venc_framerate {
+ u32 fps_denominator;
+ u32 fps_numerator;
+};
+
+//TODO remove these stupid structs
+struct venc_targetbitrate{
+ u32 target_bitrate;
+};
+
+struct venc_rotation {
+ u32 rotation;
+};
+
+struct venc_timeout {
+ u32 millisec;
+};
+
+struct venc_headerextension {
+ unsigned long header_extension;
+};
+
+struct venc_msg {
+ unsigned long statuscode;
+ unsigned long msgcode;
+ struct venc_buffer buf;
+ size_t msgdata_size;
+};
+#endif /* _MSM_VIDC_ENC_H_ */
diff --git a/include/linux/mt9t013.h b/include/linux/mt9t013.h
new file mode 100644
index 0000000..543923a
--- /dev/null
+++ b/include/linux/mt9t013.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007-2008 HTC 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 CAMERA_MT9T013_H
+#define CAMERA_MT9T013_H
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/sizes.h>
+
+/*************************************************************
+* IOCTL define
+*************************************************************/
+
+#define MT9T013_I2C_IOCTL_MAGIC 'm'
+
+#define MT9T013_I2C_IOCTL_W \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 0, unsigned)
+
+#define MT9T013_I2C_IOCTL_R \
+ _IOR(MT9T013_I2C_IOCTL_MAGIC, 1, unsigned)
+
+#define MT9T013_I2C_IOCTL_AF_W \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 2, unsigned)
+
+#define MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 3)
+
+#define MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET_2 \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 4)
+
+#define CAMERA_CONFIGURE_GPIOS \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 7)
+
+#define CAMERA_UNCONFIGURE_GPIOS \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 8)
+
+#define CAMERA_LENS_POWER_ON \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 9)
+
+#define CAMERA_LENS_POWER_OFF \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 10)
+
+#define MT9T013_I2C_IOCTL_CAMIF_APPS_RESET \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 11)
+
+/* Replacement ioctls() for the clkrgm_sec RPCs. */
+
+#define CAMIO_VFE_MDC_CLK 1 /* enable, disable */
+#define CAMIO_MDC_CLK 2 /* enable, disable */
+#define CAMIO_VFE_CLK 3 /* clk_select, freq_prog */
+
+#define MT9T013_I2C_IOCTL_CLK_ENABLE \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 12, unsigned)
+
+#define MT9T013_I2C_IOCTL_CLK_DISABLE \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 13, unsigned)
+
+#define MT9T013_I2C_IOCTL_CLK_SELECT \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 14, unsigned)
+
+#define MT9T013_I2C_IOCTL_CLK_FREQ_PROG \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 15, unsigned)
+
+#define CAMSENSOR_REG_INIT 0<<0
+#define CAMSENSOR_REG_UPDATE_PERIODIC 1<<0
+#define CAMSENSOR_TYPE_PREVIEW 0<<1
+#define CAMSENSOR_TYPE_SNAPSHOT 1<<1
+
+#define MT9T013_I2C_IOCTL_SENSOR_SETTING \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 16, uint32_t)
+
+struct mt9t013_reg_struct
+{
+ uint16_t vt_pix_clk_div; /* 0x0300 */
+ uint16_t vt_sys_clk_div; /* 0x0302 */
+ uint16_t pre_pll_clk_div; /* 0x0304 */
+ uint16_t pll_multiplier; /* 0x0306 */
+ uint16_t op_pix_clk_div; /* 0x0308 */
+ uint16_t op_sys_clk_div; /* 0x030A */
+ uint16_t scale_m; /* 0x0404 */
+ uint16_t row_speed; /* 0x3016 */
+ uint16_t x_addr_start; /* 0x3004 */
+ uint16_t x_addr_end; /* 0x3008 */
+ uint16_t y_addr_start; /* 0x3002 */
+ uint16_t y_addr_end; /* 0x3006 */
+ uint16_t read_mode; /* 0x3040 */
+ uint16_t x_output_size ; /* 0x034C */
+ uint16_t y_output_size; /* 0x034E */
+ uint16_t line_length_pck; /* 0x300C */
+ uint16_t frame_length_lines; /* 0x300A */
+ uint16_t coarse_integration_time; /* 0x3012 */
+ uint16_t fine_integration_time; /* 0x3014 */
+};
+
+struct mt9t013_reg_pat {
+ struct mt9t013_reg_struct reg[2];
+};
+
+#define MT9T013_I2C_IOCTL_GET_REGISTERS \
+ _IOR(MT9T013_I2C_IOCTL_MAGIC, 17, struct mt9t013_reg_pat *)
+
+struct mt9t013_exposure_gain {
+ uint16_t gain;
+ uint16_t line;
+ uint32_t mode;
+};
+
+#define MT9T013_I2C_IOCTL_EXPOSURE_GAIN \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 18, struct exposure_gain *)
+
+#define MT9T013_I2C_IOCTL_MOVE_FOCUS \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 19, uint32_t)
+
+#define MT9T013_I2C_IOCTL_SET_DEFAULT_FOCUS \
+ _IOW(MT9T013_I2C_IOCTL_MAGIC, 20, uint32_t)
+
+#define MT9T013_I2C_IOCTL_POWER_DOWN \
+ _IO(MT9T013_I2C_IOCTL_MAGIC, 21)
+
+struct mt9t013_init {
+ int preview; /* in: 1 for preview, 0 for capture */
+ uint16_t chipid; /* out: chip id */
+};
+
+#define MT9T013_I2C_IOCTL_INIT \
+ _IOWR(MT9T013_I2C_IOCTL_MAGIC, 22, struct mt9t013_init *)
+
+#endif
+
diff --git a/include/linux/tpa2018d1.h b/include/linux/tpa2018d1.h
new file mode 100644
index 0000000..26f608b
--- /dev/null
+++ b/include/linux/tpa2018d1.h
@@ -0,0 +1,36 @@
+/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver
+ *
+ * Copyright (C) 2009 HTC 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 _LINUX_TPA2018D1_H
+#define _LINUX_TPA2018D1_H
+
+#include <linux/ioctl.h>
+
+enum tpa2018d1_mode {
+ TPA2018_MODE_OFF,
+ TPA2018_MODE_PLAYBACK,
+ TPA2018_MODE_RINGTONE,
+ TPA2018_MODE_VOICE_CALL,
+ TPA2018_NUM_MODES,
+};
+
+#define TPA2018_IOCTL_MAGIC 'a'
+#define TPA2018_SET_CONFIG _IOW(TPA2018_IOCTL_MAGIC, 1, unsigned)
+#define TPA2018_READ_CONFIG _IOR(TPA2018_IOCTL_MAGIC, 2, unsigned)
+#define TPA2018_SET_PARAM _IOW(TPA2018_IOCTL_MAGIC, 3, unsigned)
+#define TPA2018_SET_MODE _IOW(TPA2018_IOCTL_MAGIC, 4, unsigned)
+
+#endif
+
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
new file mode 100644
index 0000000..c016540
--- /dev/null
+++ b/include/media/msm_camera.h
@@ -0,0 +1,465 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 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.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __LINUX_MSM_CAMERA_H
+#define __LINUX_MSM_CAMERA_H
+
+#include <linux/types.h>
+#include <asm/sizes.h>
+#include <linux/ioctl.h>
+
+#define MSM_CAM_IOCTL_MAGIC 'm'
+
+#define MSM_CAM_IOCTL_GET_SENSOR_INFO \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 1, struct msm_camsensor_info *)
+
+#define MSM_CAM_IOCTL_REGISTER_PMEM \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 2, struct msm_pmem_info *)
+
+#define MSM_CAM_IOCTL_UNREGISTER_PMEM \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 3, unsigned)
+
+#define MSM_CAM_IOCTL_CTRL_COMMAND \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 4, struct msm_ctrl_cmd *)
+
+#define MSM_CAM_IOCTL_CONFIG_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 5, struct msm_camera_vfe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_GET_STATS \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 6, struct msm_camera_stats_event_ctrl *)
+
+#define MSM_CAM_IOCTL_GETFRAME \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 7, struct msm_camera_get_frame *)
+
+#define MSM_CAM_IOCTL_ENABLE_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 8, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_CTRL_CMD_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 9, struct camera_cmd *)
+
+#define MSM_CAM_IOCTL_CONFIG_CMD \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 10, struct camera_cmd *)
+
+#define MSM_CAM_IOCTL_DISABLE_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 11, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_PAD_REG_RESET2 \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 12, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_VFE_APPS_RESET \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 13, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 14, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_RELEASE_STATS_BUFFER \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 15, struct msm_stats_buf *)
+
+#define MSM_CAM_IOCTL_AXI_CONFIG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 16, struct msm_camera_vfe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_GET_PICTURE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 17, struct msm_camera_ctrl_cmd *)
+
+#define MSM_CAM_IOCTL_SET_CROP \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 18, struct crop_info *)
+
+#define MSM_CAM_IOCTL_PP \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 19, uint8_t *)
+
+#define MSM_CAM_IOCTL_PP_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 20, struct msm_snapshot_pp_status *)
+
+#define MSM_CAM_IOCTL_SENSOR_IO_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 21, struct sensor_cfg_data *)
+
+#define MSM_CAMERA_LED_OFF 0
+#define MSM_CAMERA_LED_LOW 1
+#define MSM_CAMERA_LED_HIGH 2
+
+#define MSM_CAM_IOCTL_FLASH_LED_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 22, unsigned *)
+
+#define MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME \
+ _IO(MSM_CAM_IOCTL_MAGIC, 23)
+
+#define MSM_CAM_IOCTL_CTRL_COMMAND_2 \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *)
+
+#define MSM_CAM_IOCTL_ENABLE_OUTPUT_IND \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 25, uint32_t *)
+
+#define MSM_CAM_IOCTL_AF_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *)
+#define MSM_CAM_IOCTL_AF_CTRL_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_ctrl_cmt_t *)
+
+#define MAX_SENSOR_NUM 3
+#define MAX_SENSOR_NAME 32
+
+#define PP_SNAP 1
+#define PP_RAW_SNAP (1<<1)
+#define PP_PREV (1<<2)
+#define PP_MASK (PP_SNAP|PP_RAW_SNAP|PP_PREV)
+
+#define MSM_CAM_CTRL_CMD_DONE 0
+#define MSM_CAM_SENSOR_VFE_CMD 1
+
+/*****************************************************
+ * structure
+ *****************************************************/
+
+/* define five type of structures for userspace <==> kernel
+ * space communication:
+ * command 1 - 2 are from userspace ==> kernel
+ * command 3 - 4 are from kernel ==> userspace
+ *
+ * 1. control command: control command(from control thread),
+ * control status (from config thread);
+ */
+struct msm_ctrl_cmd {
+ uint16_t type;
+ uint16_t length;
+ void *value;
+ uint16_t status;
+ uint32_t timeout_ms;
+ int resp_fd; /* FIXME: to be used by the kernel, pass-through for now */
+};
+
+struct msm_vfe_evt_msg {
+ unsigned short type; /* 1 == event (RPC), 0 == message (adsp) */
+ unsigned short msg_id;
+ unsigned int len; /* size in, number of bytes out */
+ void *data;
+};
+
+#define MSM_CAM_RESP_CTRL 0
+#define MSM_CAM_RESP_STAT_EVT_MSG 1
+#define MSM_CAM_RESP_V4L2 2
+#define MSM_CAM_RESP_MAX 3
+
+/* this one is used to send ctrl/status up to config thread */
+struct msm_stats_event_ctrl {
+ /* 0 - ctrl_cmd from control thread,
+ * 1 - stats/event kernel,
+ * 2 - V4L control or read request */
+ int resptype;
+ int timeout_ms;
+ struct msm_ctrl_cmd ctrl_cmd;
+ /* struct vfe_event_t stats_event; */
+ struct msm_vfe_evt_msg stats_event;
+};
+
+/* 2. config command: config command(from config thread); */
+struct msm_camera_cfg_cmd {
+ /* what to config:
+ * 1 - sensor config, 2 - vfe config */
+ uint16_t cfg_type;
+
+ /* sensor config type */
+ uint16_t cmd_type;
+ uint16_t queue;
+ uint16_t length;
+ void *value;
+};
+
+#define CMD_GENERAL 0
+#define CMD_AXI_CFG_OUT1 1
+#define CMD_AXI_CFG_SNAP_O1_AND_O2 2
+#define CMD_AXI_CFG_OUT2 3
+#define CMD_PICT_T_AXI_CFG 4
+#define CMD_PICT_M_AXI_CFG 5
+#define CMD_RAW_PICT_AXI_CFG 6
+#define CMD_STATS_AXI_CFG 7
+#define CMD_STATS_AF_AXI_CFG 8
+#define CMD_FRAME_BUF_RELEASE 9
+#define CMD_PREV_BUF_CFG 10
+#define CMD_SNAP_BUF_RELEASE 11
+#define CMD_SNAP_BUF_CFG 12
+#define CMD_STATS_DISABLE 13
+#define CMD_STATS_AEC_AWB_ENABLE 14
+#define CMD_STATS_AF_ENABLE 15
+#define CMD_STATS_BUF_RELEASE 16
+#define CMD_STATS_AF_BUF_RELEASE 17
+#define CMD_STATS_ENABLE 18
+#define UPDATE_STATS_INVALID 19
+
+#define CMD_STATS_AEC_ENABLE 20
+#define CMD_STATS_AWB_ENABLE 21
+#define CMD_STATS_AEC_AXI_CFG 22
+#define CMD_STATS_AWB_AXI_CFG 23
+#define CMD_STATS_RS_AXI_CFG 24
+#define CMD_STATS_CS_AXI_CFG 25
+#define CMD_STATS_IHIST_AXI_CFG 26
+#define CMD_STATS_SKIN_AXI_CFG 27
+#define CMD_STATS_AEC_BUF_RELEASE 28
+#define CMD_STATS_AWB_BUF_RELEASE 29
+#define CMD_STATS_RS_BUF_RELEASE 30
+#define CMD_STATS_CS_BUF_RELEASE 31
+#define CMD_STATS_IHIST_BUF_RELEASE 32
+#define CMD_STATS_SKIN_BUF_RELEASE 33
+
+#define CMD_AXI_CFG_SNAP_GEMINI 34
+#define CMD_AXI_CFG_SNAP 35
+#define CMD_AXI_CFG_PREVIEW 36
+#define CMD_AXI_CFG_VIDEO 37
+
+#define CMD_STATS_IHIST_ENABLE 38
+#define CMD_STATS_RS_ENABLE 39
+#define CMD_STATS_CS_ENABLE 40
+#define CMD_AXI_CFG_O1_AND_O2 41 /* output1 and output2 */
+
+/* vfe config command: config command(from config thread)*/
+struct msm_vfe_cfg_cmd {
+ int cmd_type;
+ uint16_t length;
+ void *value;
+};
+
+#define MAX_CAMERA_ENABLE_NAME_LEN 32
+struct camera_enable_cmd {
+ char name[MAX_CAMERA_ENABLE_NAME_LEN];
+};
+
+#define MSM_PMEM_OUTPUT1 0
+#define MSM_PMEM_OUTPUT2 1
+#define MSM_PMEM_OUTPUT1_OUTPUT2 2
+#define MSM_PMEM_THUMBNAIL 3
+#define MSM_PMEM_MAINIMG 4
+#define MSM_PMEM_RAW_MAINIMG 5
+#define MSM_PMEM_AEC_AWB 6
+#define MSM_PMEM_AF 7
+#define MSM_PMEM_AEC 8
+#define MSM_PMEM_AWB 9
+#define MSM_PMEM_RS 10
+#define MSM_PMEM_CS 11
+#define MSM_PMEM_IHIST 12
+#define MSM_PMEM_SKIN 13
+#define MSM_PMEM_VIDEO 14
+#define MSM_PMEM_PREVIEW 15
+#define MSM_PMEM_MAX 16
+
+#define FRAME_PREVIEW_OUTPUT1 0
+#define FRAME_PREVIEW_OUTPUT2 1
+#define FRAME_SNAPSHOT 2
+#define FRAME_THUMBNAIL 3
+#define FRAME_RAW_SNAPSHOT 4
+#define FRAME_MAX 5
+
+struct msm_pmem_info {
+ int type;
+ int fd;
+ void *vaddr;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t y_off; /* relative to offset */
+ uint32_t cbcr_off; /* relative to offset */
+ uint8_t vfe_can_write;
+};
+
+struct outputCfg {
+ uint32_t height;
+ uint32_t width;
+
+ uint32_t window_height_firstline;
+ uint32_t window_height_lastline;
+};
+
+#define OUTPUT_1 0
+#define OUTPUT_2 1
+#define OUTPUT_1_AND_2 2
+#define CAMIF_TO_AXI_VIA_OUTPUT_2 3
+#define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 4
+#define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 5
+#define OUTPUT_1_AND_3 6
+#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_1_AND_3 7 /* video */
+
+
+#define MSM_FRAME_PREV_1 0
+#define MSM_FRAME_PREV_2 1
+#define MSM_FRAME_ENC 2
+
+#define OUTPUT_TYPE_P 1
+#define OUTPUT_TYPE_T 2
+#define OUTPUT_TYPE_S 3
+#define OUTPUT_TYPE_V 4
+
+struct msm_frame {
+ int path;
+ unsigned long buffer;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+ int fd;
+
+ void *cropinfo;
+ int croplen;
+};
+
+#define STAT_AEAW 0
+#define STAT_AF 1
+#define STAT_AEC 2
+#define STAT_AWB 3
+#define STAT_RS 4
+#define STAT_CS 5
+#define STAT_IHIST 6
+#define STAT_SKIN 7
+#define STAT_MAX 8
+
+struct msm_stats_buf {
+ int type;
+ unsigned long buffer;
+ int fd;
+};
+
+#define MSM_V4L2_VID_CAP_TYPE 0
+#define MSM_V4L2_STREAM_ON 1
+#define MSM_V4L2_STREAM_OFF 2
+#define MSM_V4L2_SNAPSHOT 3
+#define MSM_V4L2_QUERY_CTRL 4
+#define MSM_V4L2_GET_CTRL 5
+#define MSM_V4L2_SET_CTRL 6
+#define MSM_V4L2_QUERY 7
+#define MSM_V4L2_MAX 8
+
+struct crop_info {
+ void *info;
+ int len;
+};
+
+struct msm_postproc {
+ int ftnum;
+ struct msm_frame fthumnail;
+ int fmnum;
+ struct msm_frame fmain;
+};
+
+struct msm_snapshot_pp_status {
+ void *status;
+};
+
+#define CFG_SET_MODE 0
+#define CFG_SET_EFFECT 1
+#define CFG_START 2
+#define CFG_PWR_UP 3
+#define CFG_PWR_DOWN 4
+#define CFG_WRITE_EXPOSURE_GAIN 5
+#define CFG_SET_DEFAULT_FOCUS 6
+#define CFG_MOVE_FOCUS 7
+#define CFG_REGISTER_TO_REAL_GAIN 8
+#define CFG_REAL_TO_REGISTER_GAIN 9
+#define CFG_SET_FPS 10
+#define CFG_SET_PICT_FPS 11
+#define CFG_SET_BRIGHTNESS 12
+#define CFG_SET_CONTRAST 13
+#define CFG_SET_ZOOM 14
+#define CFG_SET_EXPOSURE_MODE 15
+#define CFG_SET_WB 16
+#define CFG_SET_ANTIBANDING 17
+#define CFG_SET_EXP_GAIN 18
+#define CFG_SET_PICT_EXP_GAIN 19
+#define CFG_SET_LENS_SHADING 20
+#define CFG_GET_PICT_FPS 21
+#define CFG_GET_PREV_L_PF 22
+#define CFG_GET_PREV_P_PL 23
+#define CFG_GET_PICT_L_PF 24
+#define CFG_GET_PICT_P_PL 25
+#define CFG_GET_AF_MAX_STEPS 26
+#define CFG_GET_PICT_MAX_EXP_LC 27
+#define CFG_MAX 28
+
+#define MOVE_NEAR 0
+#define MOVE_FAR 1
+
+#define SENSOR_PREVIEW_MODE 0
+#define SENSOR_SNAPSHOT_MODE 1
+#define SENSOR_RAW_SNAPSHOT_MODE 2
+
+#define SENSOR_QTR_SIZE 0
+#define SENSOR_FULL_SIZE 1
+#define SENSOR_INVALID_SIZE 2
+
+#define CAMERA_EFFECT_OFF 0
+#define CAMERA_EFFECT_MONO 1
+#define CAMERA_EFFECT_NEGATIVE 2
+#define CAMERA_EFFECT_SOLARIZE 3
+#define CAMERA_EFFECT_PASTEL 4
+#define CAMERA_EFFECT_MOSAIC 5
+#define CAMERA_EFFECT_RESIZE 6
+#define CAMERA_EFFECT_SEPIA 7
+#define CAMERA_EFFECT_POSTERIZE 8
+#define CAMERA_EFFECT_WHITEBOARD 9
+#define CAMERA_EFFECT_BLACKBOARD 10
+#define CAMERA_EFFECT_AQUA 11
+#define CAMERA_EFFECT_MAX 12
+
+struct sensor_pict_fps {
+ uint16_t prevfps;
+ uint16_t pictfps;
+};
+
+struct exp_gain_cfg {
+ uint16_t gain;
+ uint32_t line;
+};
+
+struct focus_cfg {
+ int32_t steps;
+ int dir;
+};
+
+struct fps_cfg {
+ uint16_t f_mult;
+ uint16_t fps_div;
+ uint32_t pict_fps_div;
+};
+
+struct sensor_cfg_data {
+ int cfgtype;
+ int mode;
+ int rs;
+ uint8_t max_steps;
+
+ union {
+ int8_t effect;
+ uint8_t lens_shading;
+ uint16_t prevl_pf;
+ uint16_t prevp_pl;
+ uint16_t pictl_pf;
+ uint16_t pictp_pl;
+ uint32_t pict_max_exp_lc;
+ uint16_t p_fps;
+ struct sensor_pict_fps gfps;
+ struct exp_gain_cfg exp_gain;
+ struct focus_cfg focus;
+ struct fps_cfg fps;
+ } cfg;
+};
+
+#define GET_NAME 0
+#define GET_PREVIEW_LINE_PER_FRAME 1
+#define GET_PREVIEW_PIXELS_PER_LINE 2
+#define GET_SNAPSHOT_LINE_PER_FRAME 3
+#define GET_SNAPSHOT_PIXELS_PER_LINE 4
+#define GET_SNAPSHOT_FPS 5
+#define GET_SNAPSHOT_MAX_EP_LINE_CNT 6
+
+struct msm_camsensor_info {
+ char name[MAX_SENSOR_NAME];
+ uint8_t flash_enabled;
+};
+#endif /* __LINUX_MSM_CAMERA_H */