diff options
118 files changed, 21212 insertions, 1287 deletions
diff --git a/arch/arm/configs/gta01_defconfig b/arch/arm/configs/gta01_defconfig new file mode 100644 index 00000000000..415ca4959e2 --- /dev/null +++ b/arch/arm/configs/gta01_defconfig @@ -0,0 +1,2948 @@ +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_9P_FS is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_ACENIC is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_ADM8211 is not set +# CONFIG_AEABI is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_RXRPC is not set +CONFIG_AIO=y +# CONFIG_AIRO is not set +# CONFIG_AIRO_CS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_ALIM7101_WDT is not set +# CONFIG_ALTERA_PCIE_CHDMA is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ANDROID is not set +CONFIG_ANON_INODES=y +# CONFIG_APM_EMULATION is not set +# CONFIG_APPLICOM is not set +# CONFIG_AR8216_PHY is not set +# CONFIG_AR9170_USB is not set +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BAST is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_H1940 is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_REALVIEW is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +# CONFIG_ARCH_RPC is not set +CONFIG_ARCH_S3C2410=y +# CONFIG_ARCH_S3C2440 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_SMDK2410 is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCNET is not set +CONFIG_ARM=y +CONFIG_ARM_THUMB=y +# CONFIG_ARM_UNWIND is not set +# CONFIG_ARPD is not set +# CONFIG_ARTHUR is not set +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_ASUS_OLED is not set +# CONFIG_AT24 is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_ATA is not set +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_ACPI is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_ATA_SFF=y +# CONFIG_ATH5K is not set +# CONFIG_ATH9K is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1C is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL2 is not set +# CONFIG_ATM is not set +# CONFIG_ATMEL is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_ATM_BR2684_IPFILTER=y +# CONFIG_ATM_CLIP is not set +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_MPOA is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_SOLOS is not set +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_AUDIT is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_AX25 is not set +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_B3DFG is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_B44 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_PWM=y +# CONFIG_BACKTRACE_SELF_TEST is not set +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +# CONFIG_BASLER_EXCITE is not set +# CONFIG_BATTERY_BQ27000_HDQ is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +# CONFIG_BCM43XX is not set +CONFIG_BCM43XX_DEBUG=y +CONFIG_BCM43XX_DMA=y +CONFIG_BCM43XX_DMA_AND_PIO_MODE=y +# CONFIG_BCM43XX_DMA_MODE is not set +CONFIG_BCM43XX_PIO=y +# CONFIG_BCM43XX_PIO_MODE is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_BINARY_PRINTF is not set +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_BITREVERSE=y +# CONFIG_BLINK is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_CPQ_DA is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_4DRIVES is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI14XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CS5536 is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_DELKIN is not set +# CONFIG_BLK_DEV_DTC2278 is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD_ONLY is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_HT6560B is not set +# CONFIG_BLK_DEV_IDEACPI is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_IDEPNP is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_IT8172 is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_JMICRON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_BLK_DEV_QD65XX is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_SD is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_UMC8672 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_XIP is not set +CONFIG_BLOCK=y +# CONFIG_BNX2 is not set +# CONFIG_BONDING is not set +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_BPQETHER is not set +CONFIG_BRIDGE=y +# CONFIG_BRIDGE_EBT_802_3 is not set +# CONFIG_BRIDGE_EBT_AMONG is not set +# CONFIG_BRIDGE_EBT_ARP is not set +# CONFIG_BRIDGE_EBT_ARPREPLY is not set +# CONFIG_BRIDGE_EBT_BROUTE is not set +# CONFIG_BRIDGE_EBT_DNAT is not set +# CONFIG_BRIDGE_EBT_IP is not set +# CONFIG_BRIDGE_EBT_IP6 is not set +# CONFIG_BRIDGE_EBT_LIMIT is not set +# CONFIG_BRIDGE_EBT_LOG is not set +# CONFIG_BRIDGE_EBT_MARK is not set +# CONFIG_BRIDGE_EBT_MARK_T is not set +# CONFIG_BRIDGE_EBT_NFLOG is not set +# CONFIG_BRIDGE_EBT_PKTTYPE is not set +# CONFIG_BRIDGE_EBT_REDIRECT is not set +# CONFIG_BRIDGE_EBT_SNAT is not set +# CONFIG_BRIDGE_EBT_STP is not set +# CONFIG_BRIDGE_EBT_T_FILTER is not set +# CONFIG_BRIDGE_EBT_T_NAT is not set +# CONFIG_BRIDGE_EBT_ULOG is not set +# CONFIG_BRIDGE_EBT_VLAN is not set +CONFIG_BRIDGE_NETFILTER=y +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_BROADCOM_PHY is not set +CONFIG_BROKEN_ON_SMP=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_BT is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_BT_BNEP is not set +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_CMTP is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIBLUECARD is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIBTUART is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIDTL1 is not set +# CONFIG_BT_HCIUART is not set +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUSB is not set +CONFIG_BT_HCIUSB_SCO=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_HIDP is not set +# CONFIG_BT_L2CAP is not set +# CONFIG_BT_RFCOMM is not set +CONFIG_BT_RFCOMM_TTY=y +# CONFIG_BT_SCO is not set +CONFIG_BUG=y +# CONFIG_C2PORT is not set +# CONFIG_CAN is not set +# CONFIG_CAPI_AVM is not set +# CONFIG_CAPI_EICON is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_CASSINI is not set +# CONFIG_CB710_CORE is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CFG80211 is not set +# CONFIG_CGROUPS is not set +CONFIG_CHARGER_PCF50606=y +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_CIFS is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_STATS=y +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +CONFIG_CLASSIC_RCU=y +CONFIG_CLS_U32_MARK=y +CONFIG_CLS_U32_PERF=y +CONFIG_CMDLINE="unused -- bootloader passes ATAG list" +# CONFIG_CNIC is not set +# CONFIG_CODA_FS is not set +# CONFIG_COMEDI is not set +CONFIG_COMPAT_BRK=y +CONFIG_COMPAT_NET_DEV_OPS=y +# CONFIG_CONFIGFS_FS is not set +# CONFIG_CONNECTOR is not set +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONSTRUCTORS=y +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_CPU_32=y +CONFIG_CPU_32v4T=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_ARM920T=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_LLSERIAL_S3C2410=y +CONFIG_CPU_LLSERIAL_S3C2410_ONLY=y +CONFIG_CPU_PABRT_NOIFAR=y +CONFIG_CPU_S3C2410=y +CONFIG_CPU_S3C2410_DMA=y +CONFIG_CPU_TLB_V4WBI=y +# CONFIG_CRAMFS is not set +CONFIG_CRC16=y +CONFIG_CRC32=y +CONFIG_CRC7=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC_T10DIF=y +CONFIG_CROSSCOMPILE=y +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_AEAD is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_AES_586 is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_BLKCIPHER is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_FIPS is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCBC is not set +CONFIG_CRYPTO_PCOMP=y +# CONFIG_CRYPTO_PRNG is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_RNG is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_586 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SEQIV is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set +# CONFIG_CRYPTO_TWOFISH_COMMON is not set +CONFIG_CRYPTO_UNLZMA=y +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_XTS is not set +CONFIG_CRYPTO_ZLIB=y +# CONFIG_CUSE is not set +# CONFIG_DAB is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_DCB is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_GPIO is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_OBJECTS is not set +CONFIG_DEBUG_PREEMPT=y +# CONFIG_DEBUG_RT_MUTEXES is not set +CONFIG_DEBUG_S3C_UART=2 +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DECNET is not set +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_LZMA=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_BIC is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_CUBIC is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_HTCP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="westwood" +# CONFIG_DEFAULT_VEGAS is not set +CONFIG_DEFAULT_WESTWOOD=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_DEVKMEM is not set +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_DGRS is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_DL2K is not set +# CONFIG_DLM is not set +# CONFIG_DMADEVICES is not set +# CONFIG_DMASCC is not set +# CONFIG_DMATEST is not set +# CONFIG_DMA_ENGINE is not set +# CONFIG_DNET is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DRAGONRISE_FF is not set +# CONFIG_DRM is not set +# CONFIG_DS1682 is not set +# CONFIG_DST is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_DVB is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_E100 is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_ECHO is not set +# CONFIG_ECONET is not set +# CONFIG_EEPRO100 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_EFS_FS is not set +CONFIG_ELF_CORE=y +CONFIG_EMBEDDED=y +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +# CONFIG_ET131X is not set +# CONFIG_ETHOC is not set +CONFIG_EVENTFD=y +# CONFIG_EVENT_TRACER is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_EXPORTFS is not set +CONFIG_EXT2_FS=m +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_EXT4_FS is not set +CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_TARGETS="" +# CONFIG_EZX_PCAP is not set +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_FAT_FS=y +# CONFIG_FAULT_INJECTION is not set +CONFIG_FB=y +# CONFIG_FB_3DFX is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_CARMINE is not set +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_EARLYSUSPEND is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_GEODE is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_OF is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_PS3 is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_S3 is not set +CONFIG_FB_S3C2410=y +# CONFIG_FB_S3C2410_DEBUG is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FCOE is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +CONFIG_FIB_RULES=y +CONFIG_FILE_LOCKING=y +# CONFIG_FIREWIRE is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +# CONFIG_FIXED_PHY is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_FONTS=y +# CONFIG_FONT_10x18 is not set +CONFIG_FONT_6x11=y +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FRAME_POINTER=y +CONFIG_FRAME_WARN=1024 +CONFIG_FREEZER=y +# CONFIG_FSCACHE is not set +# CONFIG_FSNOTIFY is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_FTL is not set +# CONFIG_FTRACE is not set +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FW_LOADER=y +CONFIG_GACT_PROB=y +# CONFIG_GAMEPORT is not set +# CONFIG_GCOV_KERNEL is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_TIME=y +# CONFIG_GFS2_FS is not set +CONFIG_GPIOLIB=y +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_DEVICE is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +CONFIG_GPIO_SYSFS=y +# CONFIG_GPIO_XILINX is not set +# CONFIG_GREENASIA_FF is not set +# CONFIG_GROUP_SCHED is not set +# CONFIG_HAMACHI is not set +# CONFIG_HAMRADIO is not set +# CONFIG_HAPPYMEAL is not set +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAVE_AOUT=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PWM=y +# CONFIG_HDLC is not set +# CONFIG_HDLC_CISCO is not set +# CONFIG_HDLC_FR is not set +# CONFIG_HDLC_PPP is not set +# CONFIG_HDLC_RAW is not set +# CONFIG_HDLC_RAW_ETH is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_HECI is not set +# CONFIG_HERMES is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_HFS_FS is not set +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +# CONFIG_HID_BRIGHT is not set +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +# CONFIG_HID_COMPAT is not set +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DEBUG is not set +# CONFIG_HID_DELL is not set +CONFIG_HID_DRAGONRISE=y +CONFIG_HID_EZKEY=y +# CONFIG_HID_FF is not set +CONFIG_HID_GREENASIA=y +CONFIG_HID_GYRATION=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_KYE=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +CONFIG_HID_PETALYNX=y +# CONFIG_HID_PID is not set +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_SUPPORT=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_ZEROPLUS=y +# CONFIG_HIGHMEM is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HIPPI is not set +# CONFIG_HOSTAP is not set +# CONFIG_HOSTAP_CS is not set +# CONFIG_HOSTAP_PCI is not set +# CONFIG_HOSTAP_PLX is not set +CONFIG_HOTPLUG=y +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HP100 is not set +# CONFIG_HPFS_FS is not set +# CONFIG_HP_ILO is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HVC_UDBG is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set +CONFIG_HW_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HZ=200 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_IBM_IIC is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_MPC is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +CONFIG_I2C_S3C2410=y +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2O is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_ICS932S401 is not set +# CONFIG_IDE is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_IDE_GD is not set +CONFIG_IDE_MAX_HWIFS=4 +# CONFIG_IDE_PHISON is not set +CONFIG_IDE_PROC_FS=y +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IEEE1394 is not set +# CONFIG_IEEE1394_DV1394 is not set +# CONFIG_IEEE1394_ETH1394 is not set +# CONFIG_IEEE1394_OHCI1394 is not set +# CONFIG_IEEE1394_PCILYNX is not set +# CONFIG_IEEE1394_RAWIO is not set +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +# CONFIG_IEEE1394_VIDEO1394 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_IEEE80211_CRYPT_CCMP is not set +# CONFIG_IEEE80211_CRYPT_TKIP is not set +# CONFIG_IEEE80211_CRYPT_WEP is not set +# CONFIG_IEEE80211_DEBUG is not set +# CONFIG_IEEE80211_SOFTMAC is not set +# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set +# CONFIG_IEEE802154 is not set +# CONFIG_IFB is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IKCONFIG is not set +# CONFIG_IKCONFIG_PROC is not set +# CONFIG_IMAGE_CMDLINE_HACK is not set +# CONFIG_IMQ is not set +# CONFIG_IMQ_BEHAVIOR_AA is not set +# CONFIG_IMQ_BEHAVIOR_AB is not set +# CONFIG_IMQ_BEHAVIOR_BA is not set +# CONFIG_IMQ_BEHAVIOR_BB is not set +CONFIG_IMQ_NUM_DEVS=2 +CONFIG_INET=y +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET_AH is not set +CONFIG_INET_DIAG=y +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set +# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set +# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set +CONFIG_INITRAMFS_COMPRESSION_NONE=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_INOTIFY=y +# CONFIG_INOTIFY_USER is not set +CONFIG_INPUT=y +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_KEYBOARD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_MIMIO is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=480 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=640 +CONFIG_INPUT_PCF50606_PMU=y +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INSTRUMENTATION is not set +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_NOOP=y +# CONFIG_IP1000 is not set +# CONFIG_IP175C_PHY is not set +# CONFIG_IP6_NF_FILTER is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP6_NF_MANGLE is not set +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_LIMIT is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_OWNER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_RAW is not set +# CONFIG_IP6_NF_TARGET_HL is not set +# CONFIG_IP6_NF_TARGET_IMQ is not set +# CONFIG_IP6_NF_TARGET_LOG is not set +# CONFIG_IP6_NF_TARGET_REJECT is not set +# CONFIG_IP6_NF_TARGET_ROUTE is not set +# CONFIG_IPC_NS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_IPSEC_NAT_TRAVERSAL=y +# CONFIG_IPV6 is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_ROUTE_INFO is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +# CONFIG_IPW2200 is not set +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_IPX is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_MROUTE=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_NF_AMANDA is not set +# CONFIG_IP_NF_ARPFILTER is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_ARP_MANGLE is not set +CONFIG_IP_NF_CONNTRACK=y +# CONFIG_IP_NF_CONNTRACK_EVENTS is not set +CONFIG_IP_NF_CONNTRACK_MARK=y +CONFIG_IP_NF_CT_ACCT=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +# CONFIG_IP_NF_FILTER is not set +# CONFIG_IP_NF_FTP is not set +# CONFIG_IP_NF_H323 is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_IRC is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_HASHLIMIT is not set +# CONFIG_IP_NF_MATCH_IPP2P is not set +# CONFIG_IP_NF_MATCH_OWNER is not set +# CONFIG_IP_NF_MATCH_RECENT is not set +# CONFIG_IP_NF_MATCH_SET is not set +# CONFIG_IP_NF_MATCH_TIME is not set +# CONFIG_IP_NF_MATCH_TOS is not set +# CONFIG_IP_NF_MATCH_TTL is not set +CONFIG_IP_NF_NAT=y +# CONFIG_IP_NF_NAT_AMANDA is not set +# CONFIG_IP_NF_NAT_FTP is not set +# CONFIG_IP_NF_NAT_H323 is not set +# CONFIG_IP_NF_NAT_IRC is not set +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_NAT_PPTP is not set +# CONFIG_IP_NF_NAT_SIP is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +# CONFIG_IP_NF_NAT_TFTP is not set +# CONFIG_IP_NF_NETBIOS_NS is not set +# CONFIG_IP_NF_PPTP is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_SET is not set +CONFIG_IP_NF_SET_HASHSIZE=1024 +# CONFIG_IP_NF_SET_IPHASH is not set +# CONFIG_IP_NF_SET_IPMAP is not set +# CONFIG_IP_NF_SET_IPPORTHASH is not set +# CONFIG_IP_NF_SET_IPTREE is not set +# CONFIG_IP_NF_SET_IPTREEMAP is not set +# CONFIG_IP_NF_SET_MACIPMAP is not set +CONFIG_IP_NF_SET_MAX=256 +# CONFIG_IP_NF_SET_NETHASH is not set +# CONFIG_IP_NF_SET_PORTMAP is not set +# CONFIG_IP_NF_SIP is not set +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_IMQ is not set +# CONFIG_IP_NF_TARGET_LOG is not set +# CONFIG_IP_NF_TARGET_MASQUERADE is not set +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_TARGET_REJECT is not set +# CONFIG_IP_NF_TARGET_ROUTE is not set +# CONFIG_IP_NF_TARGET_SAME is not set +# CONFIG_IP_NF_TARGET_SET is not set +# CONFIG_IP_NF_TARGET_TOS is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +# CONFIG_IP_NF_TFTP is not set +# CONFIG_IP_PIMSM_V1 is not set +# CONFIG_IP_PIMSM_V2 is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_RARP is not set +CONFIG_IP_ROUTE_FWMARK=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +CONFIG_IP_ROUTE_MULTIPATH_CACHED=y +# CONFIG_IP_ROUTE_MULTIPATH_DRR is not set +# CONFIG_IP_ROUTE_MULTIPATH_RANDOM is not set +# CONFIG_IP_ROUTE_MULTIPATH_RR is not set +# CONFIG_IP_ROUTE_MULTIPATH_WRANDOM is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_SCTP is not set +# CONFIG_IP_VS is not set +# CONFIG_IRDA is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISDN is not set +# CONFIG_ISDN_CAPI is not set +# CONFIG_ISDN_CAPI_CAPI20 is not set +# CONFIG_ISDN_CAPI_CAPIFS is not set +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +# CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON is not set +# CONFIG_ISDN_I4L is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_IWL3945 is not set +# CONFIG_IWLAGN is not set +# CONFIG_IWLCORE is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IWLWIFI_LEDS is not set +# CONFIG_IXGB is not set +CONFIG_JBD=y +# CONFIG_JBD2_DEBUG is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_JME is not set +CONFIG_JOLIET=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_KALLSYMS_EXTRA_PASS=y +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KEXEC is not set +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYS is not set +# CONFIG_KGDB is not set +# CONFIG_KMEMCHECK is not set +# CONFIG_KMEMTRACE is not set +CONFIG_KMOD=y +# CONFIG_KPROBES is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_LANMEDIA is not set +# CONFIG_LAPB is not set +# CONFIG_LASAT is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_LBD is not set +# CONFIG_LBDAF is not set +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_ILI9320 is not set +CONFIG_LCD_JBT6K74=y +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_PLATFORM is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_LEDS_ALIX is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_DAC124S085 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +CONFIG_LEDS_PWM=y +# CONFIG_LEDS_S3C24XX is not set +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_IDE_DISK is not set +# CONFIG_LEDS_TRIGGER_MORSE is not set +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LIB80211 is not set +# CONFIG_LIB80211_CRYPT_CCMP is not set +# CONFIG_LIB80211_CRYPT_TKIP is not set +# CONFIG_LIB80211_CRYPT_WEP is not set +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_LIBERTAS_USB is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_LIBIPW_DEBUG is not set +# CONFIG_LINE6_USB is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_LOCKD is not set +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +CONFIG_LOCK_KERNEL=y +# CONFIG_LOCK_STAT is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGITECH_FF is not set +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_CLUT224 is not set +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOG_BUF_SHIFT=18 +# CONFIG_LSF is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_MAC80211 is not set +CONFIG_MAC80211_DEFAULT_PS=y +CONFIG_MAC80211_DEFAULT_PS_VALUE=0 +# CONFIG_MACH_AML_M5900 is not set +# CONFIG_MACH_ANUBIS is not set +# CONFIG_MACH_AT2440EVB is not set +# CONFIG_MACH_JIVE is not set +# CONFIG_MACH_MINI2440 is not set +# CONFIG_MACH_N30 is not set +CONFIG_MACH_NEO1973_GTA01=y +# CONFIG_MACH_NEO1973_GTA02 is not set +# CONFIG_MACH_NEXCODER_2440 is not set +# CONFIG_MACH_OSIRIS is not set +# CONFIG_MACH_OTOM is not set +# CONFIG_MACH_QT2410 is not set +# CONFIG_MACH_RX3715 is not set +# CONFIG_MACH_SMDK2412 is not set +# CONFIG_MACH_SMDK2413 is not set +# CONFIG_MACH_SMDK2443 is not set +# CONFIG_MACH_TCT_HAMMER is not set +# CONFIG_MACH_VR1000 is not set +# CONFIG_MACH_VSTMS is not set +# CONFIG_MACVLAN is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_MARKEINS is not set +# CONFIG_MARKERS is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MD is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_ME4000 is not set +# CONFIG_MEDIA_ATTACH is not set +# CONFIG_MEDIA_SUPPORT is not set +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MEILHAUS is not set +# CONFIG_MEMSTICK is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_CORE is not set +CONFIG_MFD_PCF50606=y +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MG_DISK is not set +# CONFIG_MIGRATION is not set +CONFIG_MII=y +# CONFIG_MINIX_FS is not set +# CONFIG_MINIX_SUBPARTITION is not set +CONFIG_MINI_FO=y +# CONFIG_MISC_DEVICES is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_MISDN is not set +# CONFIG_MISDN_HFCPCI is not set +# CONFIG_MISDN_HFCUSB is not set +# CONFIG_MKISS is not set +CONFIG_MMC=y +# CONFIG_MMC_ARMMMCI is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_S3C=y +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDRICOH_CS is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_UNSAFE_RESUME is not set +# CONFIG_MMC_VIA_SDMMC is not set +CONFIG_MMU=y +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MSDOS_FS is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_MTD=y +CONFIG_MTD_ABSENT=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_AR7_PARTS is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_CONCAT=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_JEDECPROBE is not set +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_M25P80 is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_CAFE is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +CONFIG_MTD_NAND_S3C2410=y +# CONFIG_MTD_NAND_S3C2410_CLKSTOP is not set +# CONFIG_MTD_NAND_S3C2410_DEBUG is not set +CONFIG_MTD_NAND_S3C2410_HWECC=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_OTP is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_PHYSMAP=y +# CONFIG_MTD_PHYSMAP_COMPAT is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +CONFIG_MTD_ROM=y +CONFIG_MTD_ROOTFS_ROOT_DEV=y +CONFIG_MTD_ROOTFS_SPLIT=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_UBI is not set +# CONFIG_MVSWITCH_PHY is not set +# CONFIG_MWAVE is not set +# CONFIG_MWL8K is not set +# CONFIG_MYRI10GE is not set +CONFIG_NAMESPACES=y +# CONFIG_NATIONAL_PHY is not set +# CONFIG_NCP_FS is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NEC_MARKEINS is not set +CONFIG_NET=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETDEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_NETDEVICES_MULTIQUEUE=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_TPROXY is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_LAYER7 is not set +# CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_STATE is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_IMQ is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_SIMP is not set +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_BASIC=y +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_IND=y +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_U32 is not set +# CONFIG_NET_DSA is not set +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_TEXT is not set +# CONFIG_NET_EMATCH_U32 is not set +CONFIG_NET_ESTIMATOR=y +# CONFIG_NET_ETHERNET is not set +# CONFIG_NET_FC is not set +# CONFIG_NET_IPGRE is not set +CONFIG_NET_IPGRE_BROADCAST=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_KEY is not set +# CONFIG_NET_KEY_MIGRATE is not set +# CONFIG_NET_NS is not set +CONFIG_NET_PCI=y +# CONFIG_NET_PCMCIA is not set +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_NET_RADIO=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +# CONFIG_NET_SCH_ATM is not set +# CONFIG_NET_SCH_CBQ is not set +# CONFIG_NET_SCH_CLK_CPU is not set +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_ESFQ is not set +CONFIG_NET_SCH_ESFQ_NFCT=y +CONFIG_NET_SCH_FIFO=y +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_RR is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_TULIP is not set +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_NET_WIRELESS=y +CONFIG_NET_WIRELESS_RTNETLINK=y +# CONFIG_NEW_GPIO is not set +CONFIG_NEW_LEDS=y +# CONFIG_NFSD is not set +CONFIG_NFSD_TCP=y +# CONFIG_NFSD_V2_ACL is not set +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +CONFIG_NFSD_V4=y +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFS_FS is not set +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFTL is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +CONFIG_NF_CONNTRACK_ENABLED=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_FTP is not set +# CONFIG_NF_CONNTRACK_H323 is not set +# CONFIG_NF_CONNTRACK_IPV4 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_NF_CONNTRACK_IRC is not set +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_NF_CONNTRACK_RTSP is not set +# CONFIG_NF_CONNTRACK_SANE is not set +# CONFIG_NF_CONNTRACK_SIP is not set +CONFIG_NF_CONNTRACK_SUPPORT=y +# CONFIG_NF_CONNTRACK_TFTP is not set +# CONFIG_NF_CT_ACCT is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_GRE is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_NF_NAT is not set +# CONFIG_NF_NAT_AMANDA is not set +# CONFIG_NF_NAT_FTP is not set +# CONFIG_NF_NAT_H323 is not set +# CONFIG_NF_NAT_IRC is not set +CONFIG_NF_NAT_NEEDED=y +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_PROTO_GRE is not set +# CONFIG_NF_NAT_RTSP is not set +# CONFIG_NF_NAT_SIP is not set +# CONFIG_NF_NAT_SNMP_BASIC is not set +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NILFS2_FS is not set +CONFIG_NL80211=y +CONFIG_NLATTR=y +CONFIG_NLS=y +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_NORTEL_HERMES is not set +# CONFIG_NOZOMI is not set +# CONFIG_NO_HZ is not set +CONFIG_NO_IOPORT=y +# CONFIG_NS83820 is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OCF_BENCH is not set +# CONFIG_OCF_EP80579 is not set +# CONFIG_OCF_HIFN is not set +# CONFIG_OCF_HIFNHIPP is not set +# CONFIG_OCF_IXP4XX is not set +# CONFIG_OCF_OCF is not set +# CONFIG_OCF_OCFNULL is not set +# CONFIG_OCF_SAFE is not set +# CONFIG_OCF_TALITOS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_P54_COMMON is not set +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PAGE_POISONING is not set +# CONFIG_PAGE_SIZE_16KB is not set +# CONFIG_PAGE_SIZE_32KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PANTHERLORD_FF is not set +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +CONFIG_PCF50606_ADC=y +# CONFIG_PCF50606_GPO is not set +CONFIG_PCF50606_WATCHDOG=y +# CONFIG_PCF8575 is not set +# CONFIG_PCI is not set +# CONFIG_PCI200SYN is not set +# CONFIG_PCIEASPM is not set +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_PCI_ATMEL is not set +# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set +# CONFIG_PCI_HERMES is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_LEGACY is not set +# CONFIG_PCI_MSI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_ATMEL is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_HERMES is not set +# CONFIG_PCMCIA_IOCTL is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_SPECTRUM is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_WAVELAN is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PCNET32 is not set +# CONFIG_PD6729 is not set +# CONFIG_PDA_POWER is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONE is not set +# CONFIG_PHONET is not set +# CONFIG_PHYLIB is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +# CONFIG_PID_NS is not set +# CONFIG_PLAN9AUTH is not set +CONFIG_PLAT_S3C=y +CONFIG_PLAT_S3C24XX=y +CONFIG_PLIST=y +# CONFIG_PLX_HERMES is not set +CONFIG_PM=y +# CONFIG_PMIC_DA903X is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +# CONFIG_POHMELFS is not set +# CONFIG_POSIX_MQUEUE is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +CONFIG_PPC4xx_GPIO=y +# CONFIG_PPC_16K_PAGES is not set +# CONFIG_PPC_256K_PAGES is not set +CONFIG_PPC_4K_PAGES=y +# CONFIG_PPC_64K_PAGES is not set +# CONFIG_PPC_EMULATED_STATS is not set +# CONFIG_PPP is not set +# CONFIG_PPPOATM is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_FILTER=y +# CONFIG_PPP_MPPE is not set +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPS is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_PREEMPT_RCU_TRACE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_PRINTK=y +# CONFIG_PRINTK_TIME is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_PRISM54 is not set +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROFILING is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_QEMU is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_QUOTA is not set +# CONFIG_R3964 is not set +# CONFIG_R6040 is not set +# CONFIG_R8169 is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_GEMTEK_PCI is not set +# CONFIG_RADIO_MAESTRO is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID_ATTRS is not set +CONFIG_RAMFS=y +# CONFIG_RAW_DRIVER is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RDC_17F3101X is not set +CONFIG_RD_BZIP2=y +CONFIG_RD_GZIP=y +CONFIG_RD_LZMA=y +# CONFIG_REALTEK_PHY is not set +# CONFIG_REDWOOD is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_MAX1586 is not set +CONFIG_REGULATOR_PCF50606=y +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_RFD_FTL is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_RFKILL_LEDS=y +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_RT2860 is not set +# CONFIG_RT2870 is not set +# CONFIG_RT2X00 is not set +# CONFIG_RT3070 is not set +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DEBUG is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +CONFIG_RTC_DRV_PCF50606=y +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_RTC7301 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_S3C is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_X1205 is not set +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_LIB=y +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_RTL8187SE is not set +# CONFIG_RTL8192SU is not set +# CONFIG_RTL8306_PHY is not set +CONFIG_RT_MUTEXES=y +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_S2IO is not set +CONFIG_S3C2410_CLOCK=y +CONFIG_S3C2410_DMA=y +# CONFIG_S3C2410_DMA_DEBUG is not set +CONFIG_S3C2410_GPIO=y +CONFIG_S3C2410_PM=y +# CONFIG_S3C2410_PM_CHECK is not set +# CONFIG_S3C2410_PM_DEBUG is not set +CONFIG_S3C2410_WATCHDOG=y +CONFIG_S3C24XX_ADC=y +CONFIG_S3C24XX_GPIO_EXTRA=0 +CONFIG_S3C24XX_PWM=y +CONFIG_S3C_BOOT_ERROR_RESET=y +CONFIG_S3C_BOOT_UART_FORCE_FIFO=y +# CONFIG_S3C_BOOT_WATCHDOG is not set +CONFIG_S3C_DEV_USB_HOST=y +CONFIG_S3C_DMA=y +CONFIG_S3C_GPIO_SPACE=0 +CONFIG_S3C_LOWLEVEL_UART_PORT=0 +# CONFIG_SAMPLES is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PMP is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SC92031 is not set +# CONFIG_SCC is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_BFS is not set +CONFIG_SCHED_CFS=y +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHED_TRACER is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BNX2_ISCSI is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_ESP_CORE is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_LPFC is not set +# CONFIG_SCSI_MPT2SAS is not set +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_SCSI_PAS16 is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +CONFIG_SCSI_WAIT_SCAN=m +# CONFIG_SDIO_UART is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_M41T00 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL is not set +CONFIG_SERIAL_S3C2410=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_SAMSUNG_UARTS=3 +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIO=y +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SHAPER is not set +CONFIG_SHMEM=y +CONFIG_SIGNALFD=y +# CONFIG_SIMPLE_GPIO is not set +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SK98LIN is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_SKY2_DEBUG is not set +CONFIG_SLAB=y +CONFIG_SLABINFO=y +# CONFIG_SLHC is not set +# CONFIG_SLICOSS is not set +# CONFIG_SLIP is not set +# CONFIG_SLOB is not set +# CONFIG_SLOW_WORK is not set +# CONFIG_SLUB is not set +# CONFIG_SLUB_DEBUG is not set +# CONFIG_SMARTJOYPLUS_FF is not set +# CONFIG_SMB_FS is not set +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_SMC911X is not set +# CONFIG_SMC91X is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +# CONFIG_SMSC_PHY is not set +CONFIG_SND=y +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_DT019X is not set +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_ES968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HIFIER is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_HWDEP is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_ISA is not set +CONFIG_SND_JACK=y +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIPS is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_PCI is not set +CONFIG_SND_PCM=y +# CONFIG_SND_PCMCIA is not set +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +# CONFIG_SND_RAWMIDI is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_RTCTIMER is not set +CONFIG_SND_S3C24XX_SOC=y +CONFIG_SND_S3C24XX_SOC_I2S=y +# CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650 is not set +CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753=y +# CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SGALAXY is not set +CONFIG_SND_SOC=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +CONFIG_SND_SOC_WM8753=y +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_TIMER=y +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_USB is not set +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_VERBOSE_PROCFS is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_SONYPI is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +# CONFIG_SOUND_PRIME is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPI=y +# CONFIG_SPI_AT25 is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_GPIO=y +# CONFIG_SPI_GPIO_OLD is not set +CONFIG_SPI_MASTER=y +# CONFIG_SPI_ORION is not set +# CONFIG_SPI_S3C24XX is not set +# CONFIG_SPI_S3C24XX_GPIO is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_SQUASHFS is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +CONFIG_SQUASHFS_SUPPORT_LZMA=y +CONFIG_SQUASHFS_SUPPORT_ZLIB=y +# CONFIG_SQUASHFS_VMALLOC is not set +# CONFIG_SSB is not set +# CONFIG_SSB_BLOCKIO is not set +# CONFIG_SSB_DEBUG is not set +# CONFIG_SSB_PCMCIAHOST is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB_SILENT is not set +# CONFIG_SSFDC is not set +CONFIG_STACKTRACE_SUPPORT=y +# CONFIG_STACK_TRACER is not set +# CONFIG_STAGING is not set +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_STANDALONE=y +CONFIG_STDBINUTILS=y +# CONFIG_STE10XP is not set +CONFIG_STP=y +# CONFIG_STRIP is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_SUNRPC is not set +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SUNRPC_REGISTER_V4 is not set +# CONFIG_SUN_PARTITION is not set +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SWAP=y +# CONFIG_SWCONFIG is not set +# CONFIG_SYNCLINK_CS is not set +CONFIG_SYN_COOKIES=y +CONFIG_SYSCTL=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_SYSFS=y +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_TASKSTATS is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TCIC is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_CUBIC is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_VENO is not set +CONFIG_TCP_CONG_WESTWOOD=y +# CONFIG_TCP_CONG_YEAH is not set +CONFIG_TCP_MD5SIG=y +CONFIG_TEXTSEARCH=y +# CONFIG_TEXTSEARCH_BM is not set +# CONFIG_TEXTSEARCH_FSM is not set +# CONFIG_TEXTSEARCH_KMP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_HWMON is not set +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +CONFIG_TIMERFD=y +# CONFIG_TIMER_STATS is not set +# CONFIG_TINY_SHMEM is not set +# CONFIG_TIPC is not set +# CONFIG_TLAN is not set +# CONFIG_TMD_HERMES is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_FILTER is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_S3C2410 is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TR is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_TRANZPORT is not set +# CONFIG_TREE_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_TUN is not set +# CONFIG_TUNER_3036 is not set +# CONFIG_TUNER_TEA5761 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_UDF_FS is not set +CONFIG_UDF_NLS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_UFS_FS is not set +CONFIG_UID16=y +# CONFIG_UIO is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_UNEVICTABLE_LRU is not set +# CONFIG_UNION_MOUNT is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_USB=y +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIPTEK is not set +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_APPLETOUCH is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +# CONFIG_USB_ATM is not set +# CONFIG_USB_ATMEL is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_BANDWIDTH is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_CPC is not set +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EHCI_HCD_PPC_OF is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ET61X251 is not set +CONFIG_USB_ETH=y +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_EZUSB=y +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_FTDI_ELAN is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_MUSB_HDRC is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_PXA27X is not set +CONFIG_USB_GADGET_S3C2410=y +# CONFIG_USB_GADGET_S3C_HSOTG is not set +CONFIG_USB_GADGET_SELECTED=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_GSPCA is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +CONFIG_USB_GSPCA_SN9C20X_EVDEV=y +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_SERIAL is not set +CONFIG_USB_HID=y +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_HIDINPUT=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_IP_COMMON is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LIBUSUAL is not set +# CONFIG_USB_M5602 is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_HCD_PCI is not set +# CONFIG_USB_OHCI_HCD_PPC_OF is not set +# CONFIG_USB_OHCI_HCD_PPC_SOC is not set +# CONFIG_USB_OHCI_HCD_SSB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_PWC_INPUT_EVDEV is not set +# CONFIG_USB_QUICKCAM_MESSENGER is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_S2255 is not set +CONFIG_USB_S3C2410=y +# CONFIG_USB_S3C2410_DEBUG is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_GARMIN is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_USB_SERIAL_SAFE is not set +CONFIG_USB_SERIAL_SAFE_PADDED=y +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_SPEEDTOUCH is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_STORAGE is not set +CONFIG_USB_STORAGE_ALAUDA=y +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +CONFIG_USB_STORAGE_DATAFAB=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_FREECOM=y +# CONFIG_USB_STORAGE_ISD200 is not set +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_KARMA=y +# CONFIG_USB_STORAGE_ONETOUCH is not set +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +# CONFIG_USB_STORAGE_SIERRA is not set +CONFIG_USB_STORAGE_USBAT=y +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_STV680 is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_SUSPEND=y +# CONFIG_USB_TEST is not set +# CONFIG_USB_TMC is not set +# CONFIG_USB_TOUCHSCREEN is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_UEAGLEATM is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USBNET_MII is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_VST is not set +# CONFIG_USB_W9968CF is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_WHCI_HCD is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_XUSBATM is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_ZC0301 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USER_NS is not set +# CONFIG_USE_GENERIC_SMP_HELPERS is not set +# CONFIG_UTS_NS is not set +# CONFIG_UWB is not set +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_VETH is not set +CONFIG_VFAT_FS=y +# CONFIG_VGASTATE is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_ALLOW_V4L1 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CAPTURE_DRIVERS is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX231XX is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_DPC is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_GO7007 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_MEDIA is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MXB is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_SAA6588 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA7111 is not set +# CONFIG_VIDEO_SAA7114 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_STRADIS is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_VIDEO_V4L1 is not set +CONFIG_VIDEO_V4L1_COMPAT=y +# CONFIG_VIDEO_V4L2 is not set +# CONFIG_VIDEO_V4L2_COMMON is not set +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_VP27SMPX is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIRQ_DEBUG is not set +# CONFIG_VIRTUALIZATION is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_VITESSE_PHY is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VMSPLIT_1G is not set +# CONFIG_VMSPLIT_2G is not set +CONFIG_VMSPLIT_3G=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +# CONFIG_VT6655 is not set +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_VXFS_FS is not set +# CONFIG_W1 is not set +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83697HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WANXL is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_WDTPCI is not set +# CONFIG_WIMAX is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_WIRELESS_OLD_REGULATORY=y +# CONFIG_WLAN_80211 is not set +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_WR_PPMC is not set +# CONFIG_X25 is not set +CONFIG_XFRM=y +# CONFIG_XFRM_IPCOMP is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_USER is not set +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SECURITY is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_YAFFS_FS is not set +# CONFIG_YAM is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_YENTA is not set +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ZBOOT_ROM_TEXT=0x0 +# CONFIG_ZD1211RW is not set +# CONFIG_ZD1211RW_DEBUG is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_ZISOFS=y +# CONFIG_ZISOFS_FS is not set +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h index 2242ce22ec6..7ade2b8445d 100644 --- a/arch/arm/include/asm/fiq.h +++ b/arch/arm/include/asm/fiq.h @@ -29,8 +29,9 @@ struct fiq_handler { extern int claim_fiq(struct fiq_handler *f); extern void release_fiq(struct fiq_handler *f); extern void set_fiq_handler(void *start, unsigned int length); -extern void set_fiq_regs(struct pt_regs *regs); -extern void get_fiq_regs(struct pt_regs *regs); +extern void set_fiq_c_handler(void (*handler)(void)); +extern void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs); +extern void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs); extern void enable_fiq(int fiq); extern void disable_fiq(int fiq); diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index 6ff7919613d..c07691ef4c0 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -8,6 +8,8 @@ * * FIQ support re-written by Russell King to be more generic * + * FIQ handler in C supoprt written by Andy Green <andy@openmoko.com> + * * We now properly support a method by which the FIQ handlers can * be stacked onto the vector. We still do not support sharing * the FIQ vector itself. @@ -124,6 +126,83 @@ void __naked get_fiq_regs(struct pt_regs *regs) : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE)); } +/* -------- FIQ handler in C --------- + * + * Major Caveats for using this + * --------------------------- + * * + * * 1) it CANNOT touch any vmalloc()'d memory, only memory + * that was kmalloc()'d. Static allocations in the monolithic kernel + * are kmalloc()'d so they are okay. You can touch memory-mapped IO, but + * the pointer for it has to have been stored in kmalloc'd memory. The + * reason for this is simple: every now and then Linux turns off interrupts + * and reorders the paging tables. If a FIQ happens during this time, the + * virtual memory space can be partly or entirely disordered or missing. + * + * 2) Because vmalloc() is used when a module is inserted, THIS FIQ + * ISR HAS TO BE IN THE MONOLITHIC KERNEL, not a module. But the way + * it is set up, you can all to enable and disable it from your module + * and intercommunicate with it through struct fiq_ipc + * fiq_ipc which you can define in + * asm/archfiq_ipc_type.h. The reason is the same as above, a + * FIQ could happen while even the ISR is not present in virtual memory + * space due to pagetables being changed at the time. + * + * 3) You can't call any Linux API code except simple macros + * - understand that FIQ can come in at any time, no matter what + * state of undress the kernel may privately be in, thinking it + * locked the door by turning off interrupts... FIQ is an + * unstoppable monster force (which is its value) + * - they are not vmalloc()'d memory safe + * - they might do crazy stuff like sleep: FIQ pisses fire and + * is not interested in 'sleep' that the weak seem to need + * - calling APIs from FIQ can re-enter un-renterable things + * - summary: you cannot interoperate with linux APIs directly in the FIQ ISR + * + * If you follow these rules, it is fantastic, an extremely powerful, solid, + * genuine hard realtime feature. + */ + +static void (*current_fiq_c_isr)(void); +#define FIQ_C_ISR_STACK_SIZE 256 + +static void __attribute__((naked)) __jump_to_isr(void) +{ + asm __volatile__ ("mov pc, r8"); +} + + +static void __attribute__((naked)) __actual_isr(void) +{ + asm __volatile__ ( + "stmdb sp!, {r0-r12, lr};" + "mov fp, sp;" + ); + + current_fiq_c_isr(); + + asm __volatile__ ( + "ldmia sp!, {r0-r12, lr};" + "subs pc, lr, #4;" + ); +} + +void set_fiq_c_handler(void (*isr)(void)) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + regs.ARM_r8 = (unsigned long) __actual_isr; + regs.ARM_sp = 0xffff001c + FIQ_C_ISR_STACK_SIZE; + + set_fiq_handler(__jump_to_isr, 4); + + current_fiq_c_isr = isr; + + set_fiq_regs(®s); +} +/* -------- FIQ handler in C ---------*/ + int claim_fiq(struct fiq_handler *f) { int ret = 0; diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 41bb65d5b91..d336ac42289 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -139,4 +139,23 @@ config MACH_QT2410 help Say Y here if you are using the Armzone QT2410 +config MACH_NEO1973_GTA01 + bool "FIC Neo1973 GSM Phone (GTA01 Hardware)" + select CPU_S3C2410 + select MACH_NEO1973 + select S3C_DEV_USB_HOST + select MFD_PCF50606 + select INPUT_PCF50606_PMU + select PCF50606_ADC + select PCF50606_GPIO + select RTC_DRV_PCF50606 + select REGULATOR_PCF50606 + select CHARGER_PCF50606 + select PCF50606_WATCHDOG + select POWER_SUPPLY + select BATTERY_GTA01 + select S3C24XX_ADC + help + Say Y here if you are using the FIC Neo1973 GSM Phone + endmenu diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index fca02f82711..ed3939d2cf7 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_SIMTEC_NOR) += nor-simtec.o # machine additions obj-$(CONFIG_MACH_BAST_IDE) += bast-ide.o +obj-$(CONFIG_MACH_NEO1973_GTA01) += mach-gta01.o diff --git a/arch/arm/mach-s3c2410/include/mach/gta01.h b/arch/arm/mach-s3c2410/include/mach/gta01.h new file mode 100644 index 00000000000..673a1161105 --- /dev/null +++ b/arch/arm/mach-s3c2410/include/mach/gta01.h @@ -0,0 +1,67 @@ +#ifndef _GTA01_H +#define _GTA01_H + +#include <mach/regs-gpio.h> +#include <mach/irqs.h> + +/* Different hardware revisions, passed in ATAG_REVISION by u-boot */ +#define GTA01v3_SYSTEM_REV 0x00000130 +#define GTA01v4_SYSTEM_REV 0x00000140 +#define GTA01Bv2_SYSTEM_REV 0x00000220 +#define GTA01Bv3_SYSTEM_REV 0x00000230 +#define GTA01Bv4_SYSTEM_REV 0x00000240 + +/* !!!!!!!!!!! */ +#define S3C_SYSTEM_REV_ATAG GTA01Bv2_SYSTEM_REV + +/* Backlight */ + +/* Definitions common to all revisions */ +#define GTA01_GPIO_BACKLIGHT S3C2410_GPB(0) +#define GTA01_GPIO_GPS_PWRON S3C2410_GPB(1) +#define GTA01_GPIO_MODEM_RST S3C2410_GPB(6) +#define GTA01_GPIO_MODEM_ON S3C2410_GPB(7) +#define GTA01_GPIO_LCD_RESET S3C2410_GPC(6) +#define GTA01_GPIO_PMU_IRQ S3C2410_GPG(8) +#define GTA01_GPIO_JACK_INSERT S3C2410_GPF(4) +#define GTA01_GPIO_nSD_DETECT S3C2410_GPF(5) +#define GTA01_GPIO_AUX_KEY S3C2410_GPF(6) +#define GTA01_GPIO_HOLD_KEY S3C2410_GPF(7) + +#define GTA01_IRQ_MODEM IRQ_EINT1 +#define GTA01_IRQ_JACK_INSERT IRQ_EINT4 +#define GTA01_IRQ_nSD_DETECT IRQ_EINT5 +#define GTA01_IRQ_AUX_KEY IRQ_EINT6 + +/* GTA01v3 */ +#define GTA01v3_GPIO_nGSM_EN S3C2410_GPG(9) + +/* GTA01v4 */ +#define GTA01_GPIO_MODEM_DNLOAD S3C2410_GPG(0) + +/* GTA01Bv2 */ +#define GTA01Bv2_GPIO_nGSM_EN S3C2410_GPF(2) +#define GTA01Bv2_GPIO_VIBRATOR_ON S3C2410_GPB(10) +#define GTA01Bv2_IRQ_PCF50606 IRQ_EINT16 + +/* GTA01Bv3 */ +#define GTA01_GPIO_GPS_EN_3V3 S3C2410_GPG(9) + +#define GTA01_GPIO_SDMMC_ON S3C2410_GPB(2) +#define GTA01_GPIO_BT_EN S3C2410_GPB(5) +#define GTA01_GPIO_USB_PULLUP S3C2410_GPB(9) + +#define GTA01_GPIO_GPS_EN_2V8 S3C2410_GPG(9) +#define GTA01_GPIO_GPS_EN_3V S3C2410_GPG(10) +#define GTA01_GPIO_GPS_RESET S3C2410_GPC(0) + +/* GTA01Bv4 */ +#define GTA01Bv4_GPIO_nNAND_WP S3C2410_GPA(16) +#define GTA01Bv4_GPIO_VIBRATOR_ON S3C2410_GPB(3) +#define GTA01Bv4_GPIO_PMU_IRQ S3C2410_GPG(1) + +#define GTA01Bv4_IRQ_PCF50606 IRQ_EINT9 + +extern struct pcf50606 *gta01_pcf; + +#endif /* _GTA01_H */ diff --git a/arch/arm/mach-s3c2410/include/mach/irqs.h b/arch/arm/mach-s3c2410/include/mach/irqs.h index 2a2384ffa7b..b6d2c2adbcf 100644 --- a/arch/arm/mach-s3c2410/include/mach/irqs.h +++ b/arch/arm/mach-s3c2410/include/mach/irqs.h @@ -153,9 +153,9 @@ #define IRQ_S3C2443_AC97 S3C2410_IRQSUB(28) #ifdef CONFIG_CPU_S3C2443 -#define NR_IRQS (IRQ_S3C2443_AC97+1) +#define S3C2410_NR_INTERNAL_IRQS (IRQ_S3C2443_AC97+1) #else -#define NR_IRQS (IRQ_S3C2440_AC97+1) +#define S3C2410_NR_INTERNAL_IRQS (IRQ_S3C2440_AC97+1) #endif /* compatibility define. */ @@ -167,4 +167,22 @@ /* Our FIQs are routable from IRQ_EINT0 to IRQ_ADCPARENT */ #define FIQ_START IRQ_EINT0 +/* Board specific IRQs + * If your board needs a extra set of IRQ numbers add it to the list here. + * Make sure that the numbers are kept in descending, so if multiple boards are + * selected the maximum will be used and there are enough IRQ numbers available + * for each board. + */ + +#if defined(CONFIG_MACH_NEO1973_GTA02) +#define S3C2410_NR_BOARD_IRQS 9 +#else +#define S3C2410_NR_BOARD_IRQS 0 +#endif + +#define S3C2410_BOARD_IRQ_START S3C2410_NR_INTERNAL_IRQS +#define S3C2410_BOARD_IRQ_END (S3C2410_BOARD_IRQ_START + S3C2410_NR_BOARD_IRQS) + +#define NR_IRQS S3C2410_BOARD_IRQ_END + #endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s3c2410/include/mach/ts.h b/arch/arm/mach-s3c2410/include/mach/ts.h new file mode 100644 index 00000000000..ac0c727d155 --- /dev/null +++ b/arch/arm/mach-s3c2410/include/mach/ts.h @@ -0,0 +1,35 @@ +/* arch/arm/mach-s3c2410/include/mach/ts.h + * + * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * + * Changelog: + * 24-Mar-2005 RTP Created file + * 03-Aug-2005 RTP Renamed to ts.h + */ + +#ifndef __ASM_ARM_TS_H +#define __ASM_ARM_TS_H + +#include <linux/input/touchscreen/ts_filter.h> + +struct s3c2410_ts_mach_info { + /* Touchscreen delay. */ + int delay; + /* Prescaler value. */ + int presc; + /* + * Null-terminated array of pointers to filter APIs and configurations + * we want to use. In the same order they will be applied. + */ + const struct ts_filter_chain_configuration *filter_config; +}; + +void set_s3c2410ts_info(const struct s3c2410_ts_mach_info *hard_s3c2410ts_info); + +#endif /* __ASM_ARM_TS_H */ diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c new file mode 100644 index 00000000000..c559a28d442 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-gta01.c @@ -0,0 +1,976 @@ +/* + * linux/arch/arm/mach-s3c2410/mach-gta01.c + * + * S3C2410 Machine Support for the FIC Neo1973 GTA01 + * + * Copyright (C) 2006-2007 by Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de> + * 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 as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/serial_core.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> + +#include <linux/mmc/host.h> + +#include <linux/mfd/pcf50606/core.h> +#include <linux/mfd/pcf50606/pmic.h> +#include <linux/mfd/pcf50606/mbc.h> +#include <linux/mfd/pcf50606/adc.h> + +#include <linux/regulator/machine.h> +#include <linux/regulator/consumer.h> + + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <mach/hardware.h> +#include <mach/io.h> +#include <asm/mach-types.h> + +#include <mach/regs-gpio.h> +#include <mach/gpio-fns.h> +#include <mach/fb.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_gpio.h> + +#include <mach/gta01.h> + +#include <plat/regs-serial.h> +#include <plat/nand.h> +#include <plat/devs.h> +#include <plat/cpu.h> +#include <plat/pm.h> +#include <plat/udc.h> +#include <plat/iic.h> +#include <plat/mci.h> +#include <plat/usb-control.h> +/*#include <mach/neo1973-pm-gsm.h>*/ + +#include <linux/jbt6k74.h> + +#include <linux/input.h> +#include <linux/gpio_keys.h> + +#include <linux/pwm_backlight.h> +#include <linux/leds_pwm.h> + +#include <linux/gpio.h> + +/* +#include <../drivers/input/touchscreen/ts_filter_chain.h> +#ifdef CONFIG_TOUCHSCREEN_FILTER +#include <../drivers/input/touchscreen/ts_filter_linear.h> +#include <../drivers/input/touchscreen/ts_filter_mean.h> +#include <../drivers/input/touchscreen/ts_filter_median.h> +#include <../drivers/input/touchscreen/ts_filter_group.h> +#endif +*/ + +static void gta01_pmu_attach_child_devices(struct pcf50606 *pcf); + +static struct map_desc gta01_iodesc[] __initdata = { + { + .virtual = 0xe0000000, + .pfn = __phys_to_pfn(S3C2410_CS3+0x01000000), + .length = SZ_1M, + .type = MT_DEVICE + }, +}; + +#define UCON S3C2410_UCON_DEFAULT +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE +/* UFCON for the gta01 sets the FIFO trigger level at 4, not 8 */ +#define UFCON_GTA01_PORT0 S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg gta01_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON_GTA01_PORT0, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, +}; + +/* TODO */ +static void gta01_pmu_event_callback(struct pcf50606 *pcf, int irq) +{ + /*TODO : Handle ACD here */ +} +#if 0 +/* FIXME : Goes away when ACD is handled above */ +static int pmu_callback(struct device *dev, unsigned int feature, + enum pmu_event event) +{ + switch (feature) { + case PCF50606_FEAT_ACD: + switch (event) { + case PMU_EVT_INSERT: + pcf50606_charge_fast(pcf50606_global, 1); + break; + case PMU_EVT_REMOVE: + pcf50606_charge_fast(pcf50606_global, 0); + break; + default: + break; + } + break; + default: + break; + } + + return 0; +} +#endif +struct pcf50606 *gta01_pcf; + +static struct platform_device gta01_pm_gsm_dev = { + .name = "neo1973-pm-gsm", +}; + +static struct platform_device gta01_pm_bt_dev = { + .name = "neo1973-pm-bt", +}; +static struct platform_device gta01_pm_gps_dev = { + .name = "neo1973-pm-gps", +}; + +static struct regulator_consumer_supply ioreg_consumers[] = { + { + .dev = >a01_pm_gps_dev.dev, + .supply = "GPS_2V8", + }, +}; + +static struct regulator_consumer_supply d1reg_consumers[] = { + { + .dev = >a01_pm_gps_dev.dev, + .supply = "GPS_3V", + }, + { + .dev = >a01_pm_bt_dev.dev, + .supply = "BT_3V1", + }, +}; + +static struct regulator_consumer_supply dcd_consumers[] = { + { + .dev = >a01_pm_gps_dev.dev, + .supply = "GPS_3V3", + }, + { + .dev = >a01_pm_gps_dev.dev, + .supply = "GPS_1V5", + }, +}; + +static struct regulator_consumer_supply d2reg_consumers[] = { + { + .dev = >a01_pm_gps_dev.dev, + .supply = "GPS_2V5", + }, + { + .dev = &s3c_device_sdi.dev, + .supply = "SD_3V3", + }, +}; + +#if 0 +/* This will come with 2.6.32. Don't forget to uncomment it then. */ +static struct regulator_consumer_supply lpreg_consumers[] = { + REGULATOR_SUPPLY("VDC", "jbt6k74"), + REGULATOR_SUPPLY("VDDIO", "jbt6k74"), +}; +#else +static struct regulator_consumer_supply lpreg_consumers[] = { + { .supply = "VDC", }, + { .supply = "VDDIO", }, +}; +#endif + +#if 0 + +static int gta01_bat_get_charging_status(void) +{ + struct pcf50606 *pcf = gta01_pcf; + u8 mbcc1, chgmod; + + mbcc1 = pcf50606_reg_read(pcf, PCF50606_REG_MBCC1); + chgmod = mbcc1 & PCF50606_MBCC1_CHGMOD_MASK; + + if (chgmod == PCF50606_MBCC1_CHGMOD_IDLE) + return 0; + else + return 1; +} + +static int gta01_bat_get_voltage(void) +{ + struct pcf50606 *pcf = gta01_pcf; + u16 adc, mv = 0; + + adc = pcf50606_adc_sync_read(pcf, PCF50606_ADCMUX_BATVOLT_RES); + mv = (adc * 6000) / 1024; + + return mv * 1000; +} + +static int gta01_bat_get_current(void) +{ + struct pcf50606 *pcf = gta01_pcf; + u16 adc_battvolt, adc_adcin1; + s32 res; + + adc_battvolt = pcf50606_adc_sync_read(pcf, PCF50606_ADCMUX_BATVOLT_SUBTR); + adc_adcin1 = pcf50606_adc_sync_read(pcf, PCF50606_ADCMUX_ADCIN1_SUBTR); + res = (adc_battvolt - adc_adcin1) * 2400; + + /*rsense is 220 milli */ + return (res * 1000) / (220 * 1024) * 1000; +} + +static struct gta01_bat_platform_data gta01_bat_pdata = { + .get_charging_status = gta01_bat_get_charging_status, + .get_voltage = gta01_bat_get_voltage, + .get_current = gta01_bat_get_current, +}; + +static struct platform_device gta01_bat = { + .name = "gta01_battery", + .id = -1, + .dev = { + .platform_data = >a01_bat_pdata, + } +}; +#endif + +static void __devinit gta01_pcf_probe_done(struct pcf50606 *pcf) +{ + gta01_pcf = pcf; + + gta01_pmu_attach_child_devices(pcf); +} + +static int gps_registered_regulators = 0; + +static void gta01_pmu_regulator_registered(struct pcf50606 *pcf, int id) +{ + switch(id) { + case PCF50606_REGULATOR_D1REG: + platform_device_register(>a01_pm_bt_dev); + gps_registered_regulators++; + break; + case PCF50606_REGULATOR_D2REG: + gps_registered_regulators++; + break; + case PCF50606_REGULATOR_IOREG: + case PCF50606_REGULATOR_DCD: + gps_registered_regulators++; + break; + } + + /* All GPS related regulators registered ? */ + if (gps_registered_regulators == 4) + platform_device_register(>a01_pm_gps_dev); +} + +static struct pcf50606_platform_data gta01_pcf_pdata = { + .resumers = { + [0] = PCF50606_INT1_ALARM | + PCF50606_INT1_ONKEYF | + PCF50606_INT1_EXTONR, + [1] = PCF50606_INT2_CHGWD10S | + PCF50606_INT2_CHGPROT | + PCF50606_INT2_CHGERR, + [2] = PCF50606_INT3_LOWBAT | + PCF50606_INT3_HIGHTMP | + PCF50606_INT3_ACDINS, + }, + .mbc_event_callback = gta01_pmu_event_callback, + .reg_init_data = { + /* BT, GPS */ + [PCF50606_REGULATOR_D1REG] = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3150000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(d1reg_consumers), + .consumer_supplies = d1reg_consumers, + }, + /* GPS */ + [PCF50606_REGULATOR_D2REG] = { + .constraints = { + .min_uV = 1650000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(d2reg_consumers), + .consumer_supplies = d2reg_consumers, + + }, + /* RTC/Standby */ + [PCF50606_REGULATOR_D3REG] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 2100000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .always_on = 1, + .state_mem = { + .enabled = 1, + }, + }, + .num_consumer_supplies = 0, + }, + /* GPS */ + [PCF50606_REGULATOR_DCD] = { + .constraints = { + .min_uV = 1500000, + .max_uV = 1500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(dcd_consumers), + .consumer_supplies = dcd_consumers, + }, + + /* S3C2410 Memory and IO, Vibrator, RAM, NAND, AMP, SD Card */ + [PCF50606_REGULATOR_DCDE] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .enabled = 1, + }, + }, + .num_consumer_supplies = 0, + }, + /* SoC */ + [PCF50606_REGULATOR_DCUD] = { + .constraints = { + .min_uV = 2100000, + .max_uV = 2100000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .enabled = 1, + }, + }, + .num_consumer_supplies = 0, + }, + /* Codec, GPS */ + [PCF50606_REGULATOR_IOREG] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(ioreg_consumers), + .consumer_supplies = ioreg_consumers, + + }, + /* LCM */ + [PCF50606_REGULATOR_LPREG] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(lpreg_consumers), + .consumer_supplies = lpreg_consumers, + }, + }, + .probe_done = gta01_pcf_probe_done, + .regulator_registered = gta01_pmu_regulator_registered, +}; + +static void mangle_pmu_pdata_by_system_rev(void) +{ + struct regulator_init_data *reg_init_data; + + reg_init_data = gta01_pcf_pdata.reg_init_data; + + switch (S3C_SYSTEM_REV_ATAG) { + case GTA01Bv4_SYSTEM_REV: + /* FIXME : gta01_pcf_pdata.used_features |= PCF50606_FEAT_ACD; */ + break; + case GTA01Bv3_SYSTEM_REV: + case GTA01Bv2_SYSTEM_REV: + reg_init_data[PCF50606_REGULATOR_D3REG].constraints.state_mem.enabled = 1; + break; + default: + break; + } +} + +static void gta01_power_off(void) +{ + pcf50606_reg_write(gta01_pcf, PCF50606_REG_OOCC1, + PCF50606_OOCC1_GOSTDBY); +} + +/* LCD driver info */ + +/* Configuration for 480x640 toppoly TD028TTEC1. + * Do not mark this as __initdata or it will break! */ +static struct s3c2410fb_display gta01_displays[] = { + { + .type = S3C2410_LCDCON1_TFT, + .width = 43, + .height = 58, + .xres = 480, + .yres = 640, + .bpp = 16, + + .pixclock = 40000, /* HCLK/4 */ + .left_margin = 104, + .right_margin = 8, + .hsync_len = 8, + .upper_margin = 2, + .lower_margin = 16, + .vsync_len = 2, + .lcdcon5 = S3C2410_LCDCON5_FRM565 | + S3C2410_LCDCON5_INVVCLK | + S3C2410_LCDCON5_INVVLINE | + S3C2410_LCDCON5_INVVFRAME | + S3C2410_LCDCON5_PWREN | + S3C2410_LCDCON5_HWSWP, + }, + { + .type = S3C2410_LCDCON1_TFT, + .width = 43, + .height = 58, + .xres = 480, + .yres = 640, + .bpp = 32, + + .pixclock = 40000, /* HCLK/4 */ + .left_margin = 104, + .right_margin = 8, + .hsync_len = 8, + .upper_margin = 2, + .lower_margin = 16, + .vsync_len = 2, + .lcdcon5 = S3C2410_LCDCON5_FRM565 | + S3C2410_LCDCON5_INVVCLK | + S3C2410_LCDCON5_INVVLINE | + S3C2410_LCDCON5_INVVFRAME | + S3C2410_LCDCON5_PWREN | + S3C2410_LCDCON5_HWSWP, + }, + { + .type = S3C2410_LCDCON1_TFT, + .width = 43, + .height = 58, + .xres = 240, + .yres = 320, + .bpp = 16, + + .pixclock = 40000, /* HCLK/4 */ + .left_margin = 104, + .right_margin = 8, + .hsync_len = 8, + .upper_margin = 2, + .lower_margin = 16, + .vsync_len = 2, + .lcdcon5 = S3C2410_LCDCON5_FRM565 | + S3C2410_LCDCON5_INVVCLK | + S3C2410_LCDCON5_INVVLINE | + S3C2410_LCDCON5_INVVFRAME | + S3C2410_LCDCON5_PWREN | + S3C2410_LCDCON5_HWSWP, + }, +}; + +static struct s3c2410fb_mach_info gta01_lcd_cfg __initdata = { + .displays = gta01_displays, + .num_displays = ARRAY_SIZE(gta01_displays), + .default_display = 0, + + .lpcsel = ((0xCE6) & ~7) | 1<<4, +}; + +static struct s3c2410_nand_set gta01_nand_sets[] = { + [0] = { + .name = "neo1973-nand", + .nr_chips = 1, +/* .flags = S3C2410_NAND_BBT,*/ + }, +}; + +static struct s3c2410_platform_nand gta01_nand_info = { + .tacls = 20, + .twrph0 = 60, + .twrph1 = 20, + .nr_sets = ARRAY_SIZE(gta01_nand_sets), + .sets = gta01_nand_sets, +}; + +/* MMC */ + +static void gta01_mmc_set_power(unsigned char power_mode, unsigned short vdd) +{ + printk(KERN_DEBUG "mmc_set_power(power_mode=%u, vdd=%u)\n", + power_mode, vdd); + + switch (power_mode) { + case MMC_POWER_OFF: + gpio_set_value(GTA01_GPIO_SDMMC_ON, 1); + break; + case MMC_POWER_UP: + gpio_set_value(GTA01_GPIO_SDMMC_ON, 0); + break; + } +} + +static struct s3c24xx_mci_pdata gta01_mmc_cfg = { + .gpio_detect = GTA01_GPIO_nSD_DETECT, + .set_power = >a01_mmc_set_power, + .ocr_avail = MMC_VDD_32_33, +}; + +/* UDC */ + +static void gta01_udc_command(enum s3c2410_udc_cmd_e cmd) +{ + printk("udc command: %d\n", cmd); + switch (cmd) { + case S3C2410_UDC_P_ENABLE: + gpio_set_value(GTA01_GPIO_USB_PULLUP, 1); + break; + case S3C2410_UDC_P_DISABLE: + gpio_set_value(GTA01_GPIO_USB_PULLUP, 0); + break; + default: + break; + } +} + +/* use a work queue, since I2C API inherently schedules + * and we get called in hardirq context from UDC driver */ + +struct vbus_draw { + struct work_struct work; + int ma; +}; +static struct vbus_draw gta01_udc_vbus_drawer; + +static void __gta01_udc_vbus_draw(struct work_struct *work) +{ + /* + * This is a fix to work around boot-time ordering problems if the + * s3c2410_udc is initialized before the pcf50606 driver has defined + * pcf50606_global + */ + if (!gta01_pcf) + return; +#if 0 + if (gta01_udc_vbus_drawer.ma >= 500) { + /* enable fast charge */ + printk(KERN_DEBUG "udc: enabling fast charge\n"); + pcf50606_charge_fast(gta01_pcf, 1); + } else { + /* disable fast charge */ + printk(KERN_DEBUG "udc: disabling fast charge\n"); + pcf50606_charge_fast(gta01_pcf, 0); + } +#endif +} + +static void gta01_udc_vbus_draw(unsigned int ma) +{ + gta01_udc_vbus_drawer.ma = ma; + schedule_work(>a01_udc_vbus_drawer.work); +} + +static struct s3c2410_udc_mach_info gta01_udc_cfg = { + .vbus_draw = gta01_udc_vbus_draw, + .udc_command = gta01_udc_command, +}; + +/* Touchscreen configuration. */ + +#if 0 + +#ifdef CONFIG_TOUCHSCREEN_FILTER +const static struct ts_filter_group_configuration gta01_ts_group = { + .length = 12, + .close_enough = 10, + .threshold = 6, /* At least half of the points in a group. */ + .attempts = 10, +}; + +const static struct ts_filter_median_configuration gta01_ts_median = { + .extent = 20, + .decimation_below = 3, + .decimation_threshold = 8 * 3, + .decimation_above = 4, +}; + +const static struct ts_filter_mean_configuration gta01_ts_mean = { + .length = 4, +}; + +const static struct ts_filter_linear_configuration gta01_ts_linear = { + .constants = {1, 0, 0, 0, 1, 0, 1}, /* Don't modify coords. */ + .coord0 = 0, + .coord1 = 1, +}; +#endif + +const static struct ts_filter_chain_configuration gta01_filter_configuration[] = +{ +#ifdef CONFIG_TOUCHSCREEN_FILTER + {&ts_filter_group_api, >a01_ts_group.config}, + {&ts_filter_median_api, >a01_ts_median.config}, + {&ts_filter_mean_api, >a01_ts_mean.config}, + {&ts_filter_linear_api, >a01_ts_linear.config}, +#endif + {NULL, NULL}, +}; + +const static struct s3c2410_ts_mach_info gta01_ts_cfg = { + .delay = 10000, + .presc = 0xff, /* slow as we can go */ + .filter_config = gta01_filter_configuration, +}; +#endif + +/* SPI / Display */ + +const struct jbt6k74_platform_data gta01_jbt6k74_pdata = { + .gpio_reset = GTA01_GPIO_LCD_RESET, +}; + +static struct spi_board_info gta01_spi_board_info[] = { + { + .modalias = "jbt6k74", + .platform_data = >a01_jbt6k74_pdata, + .controller_data = (void*)S3C2410_GPG(3), + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 1, + }, +}; + +static struct spi_gpio_platform_data spigpio_platform_data = { + .sck = S3C2410_GPG(7), + .mosi = S3C2410_GPG(6), + .miso = S3C2410_GPG(5), + .num_chipselect = 1, +}; + +static struct platform_device gta01_lcm_spigpio_device = { + .name = "spi_gpio", + .id = 1, + .dev = { + .platform_data = &spigpio_platform_data, + }, +}; + +/* Backlight */ + +static struct platform_pwm_backlight_data gta01_bl_pdata = { + .max_brightness = 0xff, + .dft_brightness = 0xff, + .pwm_period_ns = 5000000, /* TODO: Tweak this */ + .pwm_id = 0, +}; + +static struct platform_device gta01_bl_device = { + .name = "pwm-backlight", + .id = -1, + .dev = { + .platform_data = >a01_bl_pdata, + .parent = &s3c_device_timer[0].dev, + }, +}; + +/* Vibrator */ +static struct led_pwm gta01_vibrator = { + .name = "gta01::vibrator", + .max_brightness = 0xff, + .pwm_period_ns = 500000, /* TODO: Tweak this */ + .pwm_id = 3, +}; + +static struct led_pwm_platform_data gta01_vibrator_pdata = { + .num_leds = 1, + .leds = >a01_vibrator, +}; + +static struct platform_device gta01_vibrator_device_bv4 = { + .name = "leds_pwm", + .id = -1, + .dev = { + .platform_data = >a01_vibrator_pdata, + .parent = &s3c_device_timer[3].dev, + } +}; + +static struct gpio_led gta01_vibrator_bv2 = { + .name = "gta01::vibrator", + .gpio = GTA01Bv2_GPIO_VIBRATOR_ON, + .default_state = LEDS_GPIO_DEFSTATE_OFF, +}; + +static struct gpio_led_platform_data gta01_vibrator_pdata_bv2 = { + .num_leds = 1, + .leds = >a01_vibrator_bv2, +}; + +static struct platform_device gta01_vibrator_device_bv2 = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = >a01_vibrator_pdata_bv2, + } +}; + +static struct platform_device *gta01_vibrator_device = >a01_vibrator_device_bv4; +/* Buttons */ + +static struct gpio_keys_button gta01_buttons[] = { + { + .gpio = GTA01_GPIO_AUX_KEY, + .code = KEY_PHONE, + .desc = "Aux", + .type = EV_KEY, + .debounce_interval = 100, + }, + { + .gpio = GTA01_GPIO_HOLD_KEY, + .code = KEY_PAUSE, + .desc = "Hold", + .type = EV_KEY, + .debounce_interval = 100, + }, +}; + +static struct gpio_keys_platform_data gta01_buttons_pdata = { + .buttons = gta01_buttons, + .nbuttons = ARRAY_SIZE(gta01_buttons), +}; + +static struct platform_device gta01_buttons_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = >a01_buttons_pdata, + }, +}; + +/* USB */ + +static struct s3c2410_hcd_info gta01_usb_info = { + .port[0] = { + .flags = S3C_HCDFLG_USED, + }, + .port[1] = { + .flags = 0, + }, +}; + +static struct platform_device *gta01_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c0, + &s3c_device_iis, + &s3c_device_sdi, + &s3c_device_usbgadget, + &s3c_device_nand, + &s3c_device_adc, +/* &s3c_device_ts,*/ + &s3c_device_timer[0], + &s3c_device_timer[3], + >a01_bl_device, + >a01_buttons_device, + >a01_pm_gsm_dev, +}; + +static struct platform_device *gta01_pmu_child_devices[] __devinitdata = { + >a01_lcm_spigpio_device, +}; + +static void __devinit gta01_pmu_attach_child_devices(struct pcf50606 *pcf) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(gta01_pmu_child_devices); ++i) + gta01_pmu_child_devices[i]->dev.parent = pcf->dev; + + platform_add_devices(gta01_pmu_child_devices, + ARRAY_SIZE(gta01_pmu_child_devices)); +} +static void __init gta01_map_io(void) +{ + s3c24xx_init_io(gta01_iodesc, ARRAY_SIZE(gta01_iodesc)); + s3c24xx_init_clocks(12*1000*1000); + s3c24xx_init_uarts(gta01_uartcfgs, ARRAY_SIZE(gta01_uartcfgs)); +} + +static irqreturn_t gta01_modem_irq(int irq, void *param) +{ + printk(KERN_DEBUG "GSM wakeup interrupt (IRQ %d)\n", irq); +/* gta_gsm_interrupts++;*/ + return IRQ_HANDLED; +} + +static struct i2c_board_info gta01_i2c_devs[] __initdata = { + { + I2C_BOARD_INFO("pcf50606", 0x08), + .irq = GTA01Bv4_IRQ_PCF50606, + .platform_data = >a01_pcf_pdata, + }, + { + I2C_BOARD_INFO("lm4857", 0x7c), + }, + { + I2C_BOARD_INFO("wm8753", 0x1a), + }, +}; + +static int __init gta01_init_gpio(void) +{ + int ret; + + ret = gpio_request(GTA01_GPIO_USB_PULLUP, "udc pullup"); + + ret = gpio_direction_output(GTA01_GPIO_USB_PULLUP, 0); + + ret = gpio_request(GTA01_GPIO_SDMMC_ON, "SD/MMC power"); + + ret = gpio_direction_output(GTA01_GPIO_SDMMC_ON, 1); + + s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPB0_TOUT0); + if (S3C_SYSTEM_REV_ATAG == GTA01Bv4_SYSTEM_REV) + s3c2410_gpio_cfgpin(GTA01Bv4_GPIO_VIBRATOR_ON, S3C2410_GPB3_TOUT3); + + return ret; +} + +static void __init gta01_machine_init(void) +{ + int rc; + + gta01_init_gpio(); + + if (S3C_SYSTEM_REV_ATAG != GTA01Bv4_SYSTEM_REV) { + gta01_vibrator_device = >a01_vibrator_device_bv2; + gta01_i2c_devs[0].irq = GTA01Bv2_IRQ_PCF50606; + } + + s3c_device_usb.dev.platform_data = >a01_usb_info; + s3c_device_nand.dev.platform_data = >a01_nand_info; + s3c_device_sdi.dev.platform_data = >a01_mmc_cfg; + + s3c24xx_fb_set_platdata(>a01_lcd_cfg); + + INIT_WORK(>a01_udc_vbus_drawer.work, __gta01_udc_vbus_draw); + s3c24xx_udc_set_platdata(>a01_udc_cfg); + s3c_i2c0_set_platdata(NULL); + +/* set_s3c2410ts_info(>a01_ts_cfg);*/ + +#if 0 + switch (S3C_SYSTEM_REV_ATAG) { + case GTA01v3_SYSTEM_REV: + case GTA01v4_SYSTEM_REV: + /* just use the default (GTA01_IRQ_PCF50606) */ + break; + case GTA01Bv2_SYSTEM_REV: + case GTA01Bv3_SYSTEM_REV: + /* just use the default (GTA01_IRQ_PCF50606) */ + gta01_led_resources[0].start = + gta01_led_resources[0].end = GTA01Bv2_GPIO_VIBRATOR_ON; + break; + case GTA01Bv4_SYSTEM_REV: + gta01_i2c_devs[0].irq = GTA01Bv4_IRQ_PCF50606; + gta01_led_resources[0].start = + gta01_led_resources[0].end = GTA01Bv4_GPIO_VIBRATOR_ON; + break; + } +#endif + mangle_pmu_pdata_by_system_rev(); + + i2c_register_board_info(0, gta01_i2c_devs, ARRAY_SIZE(gta01_i2c_devs)); + spi_register_board_info(gta01_spi_board_info, + ARRAY_SIZE(gta01_spi_board_info)); + + platform_add_devices(gta01_devices, ARRAY_SIZE(gta01_devices)); +/* platform_add_devices(gta01_vibrator_device, 1);*/ + + s3c_pm_init(); + + set_irq_type(GTA01_IRQ_MODEM, IRQ_TYPE_EDGE_RISING); + rc = request_irq(GTA01_IRQ_MODEM, gta01_modem_irq, IRQF_DISABLED, + "modem", NULL); + enable_irq_wake(GTA01_IRQ_MODEM); + + printk(KERN_DEBUG "Enabled GSM wakeup IRQ %d (rc=%d)\n", + GTA01_IRQ_MODEM, rc); + + pm_power_off = >a01_power_off; +} + +MACHINE_START(NEO1973_GTA01, "GTA01") + .phys_io = S3C2410_PA_UART, + .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, + .boot_params = S3C2410_SDRAM_PA + 0x100, + .map_io = gta01_map_io, + .init_irq = s3c24xx_init_irq, + .init_machine = gta01_machine_init, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 935c7558469..bae4b955edf 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -166,6 +166,11 @@ config S3C_DMA help Internal configuration for S3C DMA core +config S3C_PWM + bool + help + PWM timer code for the S3C2410, and similar processors + # device definitions to compile in config S3C_DEV_HSMMC diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 0761766b183..4d8e4a35d34 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += dev-audio.o obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o +obj-$(CONFIG_S3C_PWM) += pwm.o diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h index 2e170827e0b..41773808040 100644 --- a/arch/arm/plat-s3c/include/plat/devs.h +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -51,6 +51,8 @@ extern struct platform_device s3c_device_nand; extern struct platform_device s3c_device_usbgadget; extern struct platform_device s3c_device_usb_hsotg; +extern struct platform_device s3c_device_ts; + /* s3c2440 specific devices */ #ifdef CONFIG_CPU_S3C2440 diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 18f958801e6..723ab032059 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -47,6 +47,7 @@ struct s3c2410_platform_nand { int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ unsigned int ignore_unset_ecc:1; + unsigned int software_ecc:1; /* force software ecc at runtime */ int nr_sets; struct s3c2410_nand_set *sets; diff --git a/arch/arm/plat-s3c/include/plat/pwm.h b/arch/arm/plat-s3c/include/plat/pwm.h new file mode 100644 index 00000000000..6a41b0ad840 --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/pwm.h @@ -0,0 +1,45 @@ +#ifndef __S3C2410_PWM_H +#define __S3C2410_PWM_H + +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <mach/io.h> +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <plat/regs-timer.h> + +enum pwm_timer { + PWM0, + PWM1, + PWM2, + PWM3, + PWM4 +}; + +struct s3c2410_pwm { + enum pwm_timer timerid; + struct clk *pclk; + unsigned long pclk_rate; + unsigned long prescaler; + unsigned long divider; + unsigned long counter; + unsigned long comparer; +}; + +struct s3c24xx_pwm_platform_data{ + /* callback to attach platform children (to enforce suspend / resume + * ordering */ + void (*attach_child_devices)(struct device *parent_device); +}; + +int s3c2410_pwm_init(struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_enable(struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_disable(struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_start(struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_stop(struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *s3c2410_pwm); +int s3c2410_pwm_dumpregs(void); + +#endif /* __S3C2410_PWM_H */ diff --git a/arch/arm/plat-s3c/pwm.c b/arch/arm/plat-s3c/pwm.c new file mode 100644 index 00000000000..250bd2bc9b8 --- /dev/null +++ b/arch/arm/plat-s3c/pwm.c @@ -0,0 +1,288 @@ +/* + * arch/arm/plat-s3c/pwm.c + * + * Copyright (c) by Javi Roman <javiroman@kernel-labs.org> + * for the Openmoko Project. + * + * S3C2410A SoC PWM support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <mach/hardware.h> +#include <plat/regs-timer.h> +#include <plat/pwm.h> +#include <asm/io.h> + +#ifdef CONFIG_PM + static unsigned long standby_reg_tcon; + static unsigned long standby_reg_tcfg0; + static unsigned long standby_reg_tcfg1; +#endif + +int s3c2410_pwm_disable(struct s3c2410_pwm *pwm) +{ + unsigned long tcon; + + /* stop timer */ + tcon = __raw_readl(S3C2410_TCON); + tcon &= 0xffffff00; + __raw_writel(tcon, S3C2410_TCON); + + clk_disable(pwm->pclk); + clk_put(pwm->pclk); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_disable); + +int s3c2410_pwm_init(struct s3c2410_pwm *pwm) +{ + pwm->pclk = clk_get(NULL, "timers"); + if (IS_ERR(pwm->pclk)) + return PTR_ERR(pwm->pclk); + + clk_enable(pwm->pclk); + pwm->pclk_rate = clk_get_rate(pwm->pclk); + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_init); + +int s3c2410_pwm_enable(struct s3c2410_pwm *pwm) +{ + unsigned long tcfg0, tcfg1, tcnt, tcmp; + + /* control registers bits */ + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + /* divider & scaler slection */ + switch (pwm->timerid) { + case PWM0: + tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + break; + case PWM1: + tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + break; + case PWM2: + tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + break; + case PWM3: + tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + break; + case PWM4: + /* timer four is not capable of doing PWM */ + break; + default: + clk_disable(pwm->pclk); + clk_put(pwm->pclk); + return -1; + } + + /* divider & scaler values */ + tcfg1 |= pwm->divider; + __raw_writel(tcfg1, S3C2410_TCFG1); + + switch (pwm->timerid) { + case PWM0: + case PWM1: + tcfg0 |= pwm->prescaler; + __raw_writel(tcfg0, S3C2410_TCFG0); + break; + default: + if ((tcfg0 | pwm->prescaler) != tcfg0) { + printk(KERN_WARNING "not changing prescaler of PWM %u," + " since it's shared with timer4 (clock tick)\n", + pwm->timerid); + } + break; + } + + /* timer count and compare buffer initial values */ + tcnt = pwm->counter; + tcmp = pwm->comparer; + + __raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid)); + __raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid)); + + /* ensure timer is stopped */ + s3c2410_pwm_stop(pwm); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_enable); + +int s3c2410_pwm_start(struct s3c2410_pwm *pwm) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (pwm->timerid) { + case PWM0: + tcon |= S3C2410_TCON_T0START; + tcon &= ~S3C2410_TCON_T0MANUALUPD; + break; + case PWM1: + tcon |= S3C2410_TCON_T1START; + tcon &= ~S3C2410_TCON_T1MANUALUPD; + break; + case PWM2: + tcon |= S3C2410_TCON_T2START; + tcon &= ~S3C2410_TCON_T2MANUALUPD; + break; + case PWM3: + tcon |= S3C2410_TCON_T3START; + tcon &= ~S3C2410_TCON_T3MANUALUPD; + break; + case PWM4: + /* timer four is not capable of doing PWM */ + default: + return -ENODEV; + } + + __raw_writel(tcon, S3C2410_TCON); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_start); + +int s3c2410_pwm_stop(struct s3c2410_pwm *pwm) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (pwm->timerid) { + case PWM0: + tcon &= ~0x00000000; + tcon |= S3C2410_TCON_T0RELOAD; + tcon |= S3C2410_TCON_T0MANUALUPD; + break; + case PWM1: + tcon &= ~0x00000080; + tcon |= S3C2410_TCON_T1RELOAD; + tcon |= S3C2410_TCON_T1MANUALUPD; + break; + case PWM2: + tcon &= ~0x00000800; + tcon |= S3C2410_TCON_T2RELOAD; + tcon |= S3C2410_TCON_T2MANUALUPD; + break; + case PWM3: + tcon &= ~0x00008000; + tcon |= S3C2410_TCON_T3RELOAD; + tcon |= S3C2410_TCON_T3MANUALUPD; + break; + case PWM4: + /* timer four is not capable of doing PWM */ + default: + return -ENODEV; + } + + __raw_writel(tcon, S3C2410_TCON); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_stop); + +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm) +{ + __raw_writel(reg_value, S3C2410_TCMPB(pwm->timerid)); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle); + +int s3c2410_pwm_dumpregs(void) +{ + printk(KERN_INFO "TCON: %08lx, TCFG0: %08lx, TCFG1: %08lx\n", + (unsigned long) __raw_readl(S3C2410_TCON), + (unsigned long) __raw_readl(S3C2410_TCFG0), + (unsigned long) __raw_readl(S3C2410_TCFG1)); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs); + +static int __init s3c24xx_pwm_probe(struct platform_device *pdev) +{ + struct s3c24xx_pwm_platform_data *pdata = pdev->dev.platform_data; + + dev_info(&pdev->dev, "s3c24xx_pwm is registered \n"); + + /* if platform was interested, give him a chance to register + * platform devices that switch power with us as the parent + * at registration time -- ensures suspend / resume ordering + */ + if (pdata) + if (pdata->attach_child_devices) + (pdata->attach_child_devices)(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c24xx_pwm_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* PWM config should be kept in suspending */ + standby_reg_tcon = __raw_readl(S3C2410_TCON); + standby_reg_tcfg0 = __raw_readl(S3C2410_TCFG0); + standby_reg_tcfg1 = __raw_readl(S3C2410_TCFG1); + + return 0; +} + +static int s3c24xx_pwm_resume(struct platform_device *pdev) +{ + __raw_writel(standby_reg_tcon, S3C2410_TCON); + __raw_writel(standby_reg_tcfg0, S3C2410_TCFG0); + __raw_writel(standby_reg_tcfg1, S3C2410_TCFG1); + + return 0; +} +#else +#define s3c24xx_pwm_suspend NULL +#define s3c24xx_pwm_resume NULL +#endif + +static struct platform_driver s3c24xx_pwm_driver = { + .driver = { + .name = "s3c24xx_pwm", + .owner = THIS_MODULE, + }, + .probe = s3c24xx_pwm_probe, + .suspend = s3c24xx_pwm_suspend, + .resume = s3c24xx_pwm_resume, +}; + +static int __init s3c24xx_pwm_init(void) +{ + return platform_driver_register(&s3c24xx_pwm_driver); +} + +static void __exit s3c24xx_pwm_exit(void) +{ +} + +MODULE_AUTHOR("Javi Roman <javiroman@kernel-labs.org>"); +MODULE_LICENSE("GPL"); + +module_init(s3c24xx_pwm_init); +module_exit(s3c24xx_pwm_exit); diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c index ee1baf11ad9..9cff2320e11 100644 --- a/arch/arm/plat-s3c24xx/adc.c +++ b/arch/arm/plat-s3c24xx/adc.c @@ -43,6 +43,7 @@ struct s3c_adc_client { unsigned int nr_samples; unsigned char is_ts; unsigned char channel; + unsigned int selected; void (*select_cb)(unsigned selected); void (*convert_cb)(unsigned val1, unsigned val2, @@ -58,20 +59,31 @@ struct adc_device { void __iomem *regs; unsigned int prescale; + unsigned int delay; int irq; }; static struct adc_device *adc_dev; +static struct work_struct resume_work; + static LIST_HEAD(adc_pending); #define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) +#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_AUTO_PST | \ + S3C2410_ADCTSC_XY_PST(0)) + static inline void s3c_adc_convert(struct adc_device *adc) { unsigned con = readl(adc->regs + S3C2410_ADCCON); + if (adc->cur->is_ts) + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + adc->regs + S3C2410_ADCTSC); + con |= S3C2410_ADCCON_ENABLE_START; writel(con, adc->regs + S3C2410_ADCCON); } @@ -81,7 +93,10 @@ static inline void s3c_adc_select(struct adc_device *adc, { unsigned con = readl(adc->regs + S3C2410_ADCCON); - client->select_cb(1); + if (!client->selected) { + client->selected = 1; + client->select_cb(1); + } con &= ~S3C2410_ADCCON_MUXMASK; con &= ~S3C2410_ADCCON_STDBM; @@ -105,12 +120,9 @@ static void s3c_adc_try(struct adc_device *adc) { struct s3c_adc_client *next = adc->ts_pend; - if (!next && !list_empty(&adc_pending)) { + if (!next && !list_empty(&adc_pending)) next = list_first_entry(&adc_pending, struct s3c_adc_client, pend); - list_del(&next->pend); - } else - adc->ts_pend = NULL; if (next) { adc_dbg(adc, "new client is %p\n", next); @@ -234,14 +246,20 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) if (client->nr_samples > 0) { /* fire another conversion for this */ - + client->selected = 1; client->select_cb(1); s3c_adc_convert(adc); } else { local_irq_save(flags); - (client->select_cb)(0); + client->selected = 0; + if (!adc->cur->is_ts) + list_del(&adc->cur->pend); + else + adc->ts_pend = NULL; adc->cur = NULL; + (client->select_cb)(0); + s3c_adc_try(adc); local_irq_restore(flags); } @@ -264,6 +282,7 @@ static int s3c_adc_probe(struct platform_device *pdev) adc->pdev = pdev; adc->prescale = S3C2410_ADCCON_PRSCVL(49); + adc->delay = 0x2710; adc->irq = platform_get_irq(pdev, 1); if (adc->irq <= 0) { @@ -303,6 +322,7 @@ static int s3c_adc_probe(struct platform_device *pdev) writel(adc->prescale | S3C2410_ADCCON_PRSCEN, adc->regs + S3C2410_ADCCON); + writel(adc->delay, adc->regs + S3C2410_ADCDLY); dev_info(dev, "attached adc driver\n"); @@ -346,18 +366,42 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) writel(con, adc->regs + S3C2410_ADCCON); clk_disable(adc->clk); + disable_irq(IRQ_ADC); + if (!list_empty(&adc_pending) || adc->ts_pend) + dev_info(&pdev->dev, "We still have adc clients pending\n"); return 0; } +/* It seems this is not needed. This is under upstream review now. */ +static void adc_resume_work(struct work_struct *work) +{ + if (!adc_dev) /* Have no ADC here */ + return; + + if (!list_empty(&adc_pending) || adc_dev->ts_pend) + /* We still have adc clients pending */ + s3c_adc_try(adc_dev); +} + static int s3c_adc_resume(struct platform_device *pdev) { struct adc_device *adc = platform_get_drvdata(pdev); + enable_irq(IRQ_ADC); clk_enable(adc->clk); writel(adc->prescale | S3C2410_ADCCON_PRSCEN, adc->regs + S3C2410_ADCCON); + writel(adc->delay, adc->regs + S3C2410_ADCDLY); + + /* Schedule task if there are clients pending. */ + if (!list_empty(&adc_pending) || adc_dev->ts_pend) { + INIT_WORK(&resume_work, adc_resume_work); + if (!schedule_work(&resume_work)) + dev_err(&pdev->dev, + "Failed to schedule adc_resume work!\n"); + } return 0; } diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c index 1932b7e0da1..ed4c19fa196 100644 --- a/arch/arm/plat-s3c24xx/cpu.c +++ b/arch/arm/plat-s3c24xx/cpu.c @@ -61,6 +61,7 @@ static const char name_s3c2410[] = "S3C2410"; static const char name_s3c2412[] = "S3C2412"; static const char name_s3c2440[] = "S3C2440"; static const char name_s3c2442[] = "S3C2442"; +static const char name_s3c2442b[] = "S3C2442B"; static const char name_s3c2443[] = "S3C2443"; static const char name_s3c2410a[] = "S3C2410A"; static const char name_s3c2440a[] = "S3C2440A"; @@ -112,6 +113,15 @@ static struct cpu_table cpu_ids[] __initdata = { .name = name_s3c2442 }, { + .idcode = 0x32440aab, + .idmask = 0xffffffff, + .map_io = s3c244x_map_io, + .init_clocks = s3c244x_init_clocks, + .init_uarts = s3c244x_init_uarts, + .init = s3c2442_init, + .name = name_s3c2442b + }, + { .idcode = 0x32412001, .idmask = 0xffffffff, .map_io = s3c2412_map_io, diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 4eb378c89a3..a89286b4a12 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -28,6 +28,8 @@ #include <mach/hardware.h> #include <asm/irq.h> +#include <mach/ts.h> + #include <plat/regs-serial.h> #include <plat/udc.h> @@ -199,6 +201,24 @@ struct platform_device s3c_device_nand = { EXPORT_SYMBOL(s3c_device_nand); +/* Touchscreen */ + +struct platform_device s3c_device_ts = { + .name = "s3c2410-ts", + .id = -1, + .dev.parent = &s3c_device_adc.dev, +}; + +static struct s3c2410_ts_mach_info s3c2410ts_info; + +void set_s3c2410ts_info(const struct s3c2410_ts_mach_info *hard_s3c2410ts_info) +{ + memcpy(&s3c2410ts_info, hard_s3c2410ts_info, + sizeof(struct s3c2410_ts_mach_info)); + s3c_device_ts.dev.platform_data = &s3c2410ts_info; +} +EXPORT_SYMBOL(set_s3c2410ts_info); + /* USB Device (Gadget)*/ static struct resource s3c_usbgadget_resource[] = { diff --git a/arch/arm/plat-s3c24xx/include/plat/irq.h b/arch/arm/plat-s3c24xx/include/plat/irq.h index 69e1be8bec3..11a866466d8 100644 --- a/arch/arm/plat-s3c24xx/include/plat/irq.h +++ b/arch/arm/plat-s3c24xx/include/plat/irq.h @@ -12,6 +12,7 @@ #include <linux/io.h> +#include <mach/irqs.h> #include <mach/hardware.h> #include <mach/regs-irq.h> #include <mach/regs-gpio.h> @@ -31,8 +32,15 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, { unsigned long mask; unsigned long submask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif submask = __raw_readl(S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); @@ -45,6 +53,9 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, /* write back masks */ __raw_writel(submask, S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } @@ -53,8 +64,15 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) { unsigned long mask; unsigned long submask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif submask = __raw_readl(S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0)); @@ -63,6 +81,9 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) /* write back masks */ __raw_writel(submask, S3C2410_INTSUBMSK); __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c index 958737775ad..4b21ac9d693 100644 --- a/arch/arm/plat-s3c24xx/irq.c +++ b/arch/arm/plat-s3c24xx/irq.c @@ -28,6 +28,8 @@ #include <asm/mach/irq.h> #include <plat/regs-irqtype.h> +#include <mach/regs-irq.h> +#include <mach/regs-gpio.h> #include <plat/cpu.h> #include <plat/pm.h> @@ -37,12 +39,20 @@ static void s3c_irq_mask(unsigned int irqno) { unsigned long mask; - +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif irqno -= IRQ_EINT0; - +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); mask |= 1UL << irqno; __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } static inline void @@ -59,9 +69,19 @@ s3c_irq_maskack(unsigned int irqno) { unsigned long bitval = 1UL << (irqno - IRQ_EINT0); unsigned long mask; - +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif + +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); __raw_writel(mask|bitval, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif __raw_writel(bitval, S3C2410_SRCPND); __raw_writel(bitval, S3C2410_INTPND); @@ -72,15 +92,25 @@ static void s3c_irq_unmask(unsigned int irqno) { unsigned long mask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) irqdbf2("s3c_irq_unmask %d\n", irqno); irqno -= IRQ_EINT0; +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); mask &= ~(1UL << irqno); __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } struct irq_chip s3c_irq_level_chip = { @@ -523,26 +553,26 @@ void __init s3c24xx_init_irq(void) last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_INTPND); + pend = __raw_readl(S3C2410_SUBSRCPND); if (pend == 0 || pend == last) break; - __raw_writel(pend, S3C2410_SRCPND); - __raw_writel(pend, S3C2410_INTPND); - printk("irq: clearing pending status %08x\n", (int)pend); + printk("irq: clearing subpending status %08x\n", (int)pend); + __raw_writel(pend, S3C2410_SUBSRCPND); last = pend; } last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_SUBSRCPND); + pend = __raw_readl(S3C2410_INTPND); if (pend == 0 || pend == last) break; - printk("irq: clearing subpending status %08x\n", (int)pend); - __raw_writel(pend, S3C2410_SUBSRCPND); + __raw_writel(pend, S3C2410_SRCPND); + __raw_writel(pend, S3C2410_INTPND); + printk("irq: clearing pending status %08x\n", (int)pend); last = pend; } diff --git a/arch/arm/plat-s3c24xx/pwm-clock.c b/arch/arm/plat-s3c24xx/pwm-clock.c new file mode 100644 index 00000000000..d41cccd6a25 --- /dev/null +++ b/arch/arm/plat-s3c24xx/pwm-clock.c @@ -0,0 +1,437 @@ +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c + * + * Copyright (c) 2007 Simtec Electronics + * Copyright (c) 2007, 2008 Ben Dooks + * Ben Dooks <ben-linux@fluff.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <asm/irq.h> + +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> + +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> + +#include <asm/plat-s3c/regs-timer.h> + +/* Each of the timers 0 through 5 go through the following + * clock tree, with the inputs depending on the timers. + * + * pclk ---- [ prescaler 0 ] -+---> timer 0 + * +---> timer 1 + * + * pclk ---- [ prescaler 1 ] -+---> timer 2 + * +---> timer 3 + * \---> timer 4 + * + * Which are fed into the timers as so: + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 0 + * tclk 0 ------------------------------/ + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 1 + * tclk 0 ------------------------------/ + * + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 2 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 3 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8, 16 ] --\ + * [mux] -> timer 4 + * tclk 1 ------------------------------/ + * + * Since the mux and the divider are tied together in the + * same register space, it is impossible to set the parent + * and the rate at the same time. To avoid this, we add an + * intermediate 'prescaled-and-divided' clock to select + * as the parent for the timer input clock called tdiv. + * + * prescaled clk --> pwm-tdiv ---\ + * [ mux ] --> timer X + * tclk -------------------------/ +*/ + +static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +{ + unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk->id == 1) { + tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; + } + + return clk_get_rate(clk->parent) / (tcfg0 + 1); +} + +/* TODO - add set rate calls. */ + +static struct clk clk_timer_scaler[] = { + [0] = { + .name = "pwm-scaler0", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, + [1] = { + .name = "pwm-scaler1", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, +}; + +static struct clk clk_timer_tclk[] = { + [0] = { + .name = "pwm-tclk0", + .id = -1, + }, + [1] = { + .name = "pwm-tclk1", + .id = -1, + }, +}; + +struct pwm_tdiv_clk { + struct clk clk; + unsigned int divisor; +}; + +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) +{ + return container_of(clk, struct pwm_tdiv_clk, clk); +} + +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) +{ + return 1 << (1 + tcfg1); +} + +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + divisor = to_tdiv(clk)->divisor; + else + divisor = tcfg_to_divisor(tcfg1); + + return clk_get_rate(clk->parent) / divisor; +} + +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate; + unsigned long divisor; + + parent_rate = clk_get_rate(clk->parent); + divisor = parent_rate / rate; + + if (divisor <= 2) + divisor = 2; + else if (divisor <= 4) + divisor = 4; + else if (divisor <= 8) + divisor = 8; + else + divisor = 16; + + return parent_rate / divisor; +} + +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) +{ + unsigned long bits; + + switch (divclk->divisor) { + case 2: + bits = S3C2410_TCFG1_MUX_DIV2; + break; + case 4: + bits = S3C2410_TCFG1_MUX_DIV4; + break; + case 8: + bits = S3C2410_TCFG1_MUX_DIV8; + break; + case 16: + default: + bits = S3C2410_TCFG1_MUX_DIV16; + break; + } + + return bits; +} + +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long bits = clk_pwm_tdiv_bits(divclk); + unsigned long flags; + unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + tcfg1 |= bits << shift; + __raw_writel(tcfg1, S3C2410_TCFG1); + + local_irq_restore(flags); +} + +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) +{ + struct pwm_tdiv_clk *divclk = to_tdiv(clk); + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + rate = clk_round_rate(clk, rate); + divisor = parent_rate / rate; + + if (divisor > 16) + return -EINVAL; + + divclk->divisor = divisor; + + /* Update the current MUX settings if we are currently + * selected as the clock source for this clock. */ + + if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) + clk_pwm_tdiv_update(divclk); + + return 0; +} + +static struct pwm_tdiv_clk clk_timer_tdiv[] = { + [0] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [1] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + } + }, + [2] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [3] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [4] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, +}; + +static int __init clk_pwm_tdiv_register(unsigned int id) +{ + struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + divclk->clk.id = id; + divclk->divisor = tcfg_to_divisor(tcfg1); + + return s3c24xx_register_clock(&divclk->clk); +} + +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) +{ + return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; +} + +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) +{ + return &clk_timer_tdiv[id].clk; +} + +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned int id = clk->id; + unsigned long tcfg1; + unsigned long flags; + unsigned long bits; + unsigned long shift = S3C2410_TCFG1_SHIFT(id); + + if (parent == s3c24xx_pwmclk_tclk(id)) + bits = S3C2410_TCFG1_MUX_TCLK << shift; + else if (parent == s3c24xx_pwmclk_tdiv(id)) + bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; + else + return -EINVAL; + + clk->parent = parent; + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + __raw_writel(tcfg1 | bits, S3C2410_TCFG1); + + local_irq_restore(flags); + + return 0; +} + +static struct clk clk_tin[] = { + [0] = { + .name = "pwm-tin", + .id = 0, + .set_parent = clk_pwm_tin_set_parent, + }, + [1] = { + .name = "pwm-tin", + .id = 1, + .set_parent = clk_pwm_tin_set_parent, + }, + [2] = { + .name = "pwm-tin", + .id = 2, + .set_parent = clk_pwm_tin_set_parent, + }, + [3] = { + .name = "pwm-tin", + .id = 3, + .set_parent = clk_pwm_tin_set_parent, + }, + [4] = { + .name = "pwm-tin", + .id = 4, + .set_parent = clk_pwm_tin_set_parent, + }, +}; + +static __init int clk_pwm_tin_register(struct clk *pwm) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int id = pwm->id; + + struct clk *parent; + int ret; + + ret = s3c24xx_register_clock(pwm); + if (ret < 0) + return ret; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + parent = s3c24xx_pwmclk_tclk(id); + else + parent = s3c24xx_pwmclk_tdiv(id); + + return clk_set_parent(pwm, parent); +} + +static __init int s3c24xx_pwmclk_init(void) +{ + struct clk *clk_timers; + unsigned int clk; + int ret; + + clk_timers = clk_get(NULL, "timers"); + if (IS_ERR(clk_timers)) { + printk(KERN_ERR "%s: no parent clock\n", __func__); + return -EINVAL; + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { + clk_timer_scaler[clk].parent = clk_timers; + ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { + ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pww tclk%d\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { + ret = clk_pwm_tdiv_register(clk); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { + ret = clk_pwm_tin_register(&clk_tin[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tin clock\n", clk); + goto err; + } + } + + return 0; + + err: + return ret; +} + +arch_initcall(s3c24xx_pwmclk_init); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 72e2712c7e2..f8c4dd2226b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -11,6 +11,68 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN +menuconfig TOUCHSCREEN_FILTER + boolean "Touchscreen Filtering" + depends on INPUT_TOUCHSCREEN + select TOUCHSCREEN_FILTER_GROUP + select TOUCHSCREEN_FILTER_MEDIAN + select TOUCHSCREEN_FILTER_MEAN + select TOUCHSCREEN_FILTER_LINEAR + help + Select this to include kernel touchscreen filter support. The filters + can be combined in any order in your machine init and the parameters + for them can also be set there. + +config TOUCHSCREEN_FILTER_GROUP + bool "Group Touchscreen Filter" + depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER + default Y + help + Say Y here if you want to use the Group touchscreen filter, it + avoids using atypical samples. + +config TOUCHSCREEN_FILTER_MEDIAN + bool "Median Average Touchscreen Filter" + depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER + default Y + help + Say Y here if you want to use the Median touchscreen filter, it's + highly effective if you data is noisy with occasional excursions. + +config TOUCHSCREEN_FILTER_MEAN + bool "Mean Average Touchscreen Filter" + depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER + default Y + help + Say Y here if you want to use the Mean touchscreen filter, it + can further improve decent quality data by removing jitter + +config TOUCHSCREEN_FILTER_LINEAR + bool "Linear Touchscreen Filter" + depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER + default Y + help + Say Y here if you want to use the Linear touchscreen filter, it + enables the use of calibration data for the touchscreen. + +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410 touchscreen input driver" + depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN + select SERIO + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + +config TOUCHSCREEN_S3C2410_DEBUG + boolean "Samsung S3C2410 touchscreen debug messages" + depends on TOUCHSCREEN_S3C2410 + help + Select this if you want debug messages + config TOUCHSCREEN_ADS7846 tristate "ADS7846/TSC2046 and ADS7843 based touchscreens" depends on SPI_MASTER diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3e1c5e0b952..dafec0b5e3f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -40,3 +40,9 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_FILTER) += ts_filter_chain.o +obj-$(CONFIG_TOUCHSCREEN_FILTER_GROUP) += ts_filter_group.o +obj-$(CONFIG_TOUCHSCREEN_FILTER_LINEAR) += ts_filter_linear.o +obj-$(CONFIG_TOUCHSCREEN_FILTER_MEDIAN) += ts_filter_median.o +obj-$(CONFIG_TOUCHSCREEN_FILTER_MEAN) += ts_filter_mean.o +obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c new file mode 100644 index 00000000000..db083a16284 --- /dev/null +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -0,0 +1,552 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org> + * iPAQ H1940 touchscreen support + * + * ChangeLog + * + * 2004-09-05: Herbert Pötzl <herbert@13thfloor.at> + * - Added clock (de-)allocation code + * + * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org> + * - h1940_ -> s3c2410 (this driver is now also used on the n30 + * machines :P) + * - Debug messages are now enabled with the config option + * TOUCHSCREEN_S3C2410_DEBUG + * - Changed the way the value are read + * - Input subsystem should now work + * - Use ioremap and readl/writel + * + * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org> + * - Make use of some undocumented features of the touchscreen + * controller + * + * 2007-05-23: Harald Welte <laforge@openmoko.org> + * - Add proper support for S32440 + * + * 2008-06-23: Andy Green <andy@warmcat.com> + * - Removed averaging system + * - Added generic Touchscreen filter stuff + * + * 2008-11-27: Nelson Castillo <arhuaco@freaks-unidos.net> + * - Improve interrupt handling + * + * 2009-04-09: Nelson Castillo <arhuaco@freaks-unidos.net> + * - Use s3c-adc API (Vasily Khoruzhick <anarsoul@gmail.com> provided + * a working example for a simpler version of this driver). + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/timer.h> +#include <linux/kfifo.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <asm/irq.h> + +#include <mach/regs-gpio.h> +#include <mach/gpio-nrs.h> +#include <mach/gpio-fns.h> +#include <mach/ts.h> +#include <mach/hardware.h> +#include <plat/regs-adc.h> +#include <plat/adc.h> + +#include <linux/input/touchscreen/ts_filter_chain.h> + +/* For ts.dev.id.version */ +#define S3C2410TSVERSION 0x0101 + +#define WAIT4INT(x) (((x)<<8) | \ + S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_XY_PST(3)) + +#define TSPRINTK(fmt, args...) \ + printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG +# define DPRINTK TSPRINTK +#else +# define DPRINTK(fmt, args...) +#endif + + +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); +MODULE_DESCRIPTION("s3c2410 touchscreen driver"); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +static char *s3c2410ts_name = "s3c2410 TouchScreen"; + +#define TS_RELEASE_TIMEOUT (HZ >> 7 ? HZ >> 7 : 1) /* 8ms (5ms if HZ is 200) */ +#define TS_EVENT_FIFO_SIZE (2 << 6) /* Must be a power of 2. */ + +/* + * Per-touchscreen data. + */ + +enum ts_state {TS_STATE_STANDBY, TS_STATE_PRESSED, TS_STATE_RELEASE_PENDING, + TS_STATE_RELEASE}; + +struct s3c2410ts { + struct input_dev *dev; + struct ts_filter_chain *chain; + enum ts_state state; + int is_down; + struct kfifo *event_fifo; + struct s3c_adc_client *adc_client; + unsigned adc_selected; +}; + +static struct s3c2410ts ts; + +static void __iomem *base_addr; + +/* + * A few low level functions. + */ + +static inline void s3c2410_ts_connect(void) +{ + s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); + s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); +} + +/* + * Code that starts ADC conversions. + */ + +static void ts_adc_timer_f(unsigned long data); +static struct timer_list ts_adc_timer = TIMER_INITIALIZER(ts_adc_timer_f, 0, 0); + +static void ts_adc_timer_f(unsigned long data) +{ + if (s3c_adc_start(ts.adc_client, 0, 1)) + mod_timer(&ts_adc_timer, jiffies + 1); +} + +static void s3c2410_ts_start_adc_conversion(void) +{ + if (ts.adc_selected) + mod_timer(&ts_adc_timer, jiffies + 1); + else + ts_adc_timer_f(0); +} + +/* Callback for the s3c-adc API. */ +void adc_selected_f(unsigned selected) +{ + ts.adc_selected = selected; +} + +/* + * Just send the input events. + */ + +enum ts_input_event {IE_DOWN = 0, IE_UP}; + +static void ts_input_report(int event, int coords[]) +{ +#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG + static char *s[] = {"down", "up"}; + struct timeval tv; + + do_gettimeofday(&tv); +#endif + + if (event == IE_DOWN) { + input_report_abs(ts.dev, ABS_X, coords[0]); + input_report_abs(ts.dev, ABS_Y, coords[1]); + input_report_key(ts.dev, BTN_TOUCH, 1); + input_report_abs(ts.dev, ABS_PRESSURE, 1); + + DPRINTK("T:%06d %6s (X:%03d, Y:%03d)\n", + (int)tv.tv_usec, s[event], coords[0], coords[1]); + } else { + input_report_key(ts.dev, BTN_TOUCH, 0); + input_report_abs(ts.dev, ABS_PRESSURE, 0); + + DPRINTK("T:%06d %6s\n", + (int)tv.tv_usec, s[event]); + } + + input_sync(ts.dev); +} + +/* + * Manage the state of the touchscreen. + */ + +static void event_send_timer_f(unsigned long data); + +static struct timer_list event_send_timer = + TIMER_INITIALIZER(event_send_timer_f, 0, 0); + +static void event_send_timer_f(unsigned long data) +{ + static int noop_counter; + int event_type; + + while (__kfifo_get(ts.event_fifo, (unsigned char *)&event_type, + sizeof(int))) { + int buf[2]; + + switch (event_type) { + case 'D': + if (ts.state == TS_STATE_RELEASE_PENDING) + /* Ignore short UP event. */ + ts.state = TS_STATE_PRESSED; + break; + + case 'U': + ts.state = TS_STATE_RELEASE_PENDING; + break; + + case 'P': + if (ts.is_down) /* Stylus_action needs a conversion. */ + s3c2410_ts_start_adc_conversion(); + + if (unlikely(__kfifo_get(ts.event_fifo, + (unsigned char *)buf, + sizeof(int) * 2) + != sizeof(int) * 2)) { + /* This will only happen if we have a bug. */ + TSPRINTK("Invalid packet\n"); + return; + } + + ts_input_report(IE_DOWN, buf); + ts.state = TS_STATE_PRESSED; + break; + } + + noop_counter = 0; + } + + if (noop_counter++ >= 1) { + noop_counter = 0; + if (ts.state == TS_STATE_RELEASE_PENDING) { + /* + * We delay the UP event for a while to avoid jitter. + * If we get a DOWN event we do not send it. + */ + ts_input_report(IE_UP, NULL); + ts.state = TS_STATE_STANDBY; + + ts_filter_chain_clear(ts.chain); + } + } else { + mod_timer(&event_send_timer, jiffies + TS_RELEASE_TIMEOUT); + } +} + +/* + * Manage interrupts. + */ + +static irqreturn_t stylus_updown(int irq, void *dev_id) +{ + unsigned long data0; + unsigned long data1; + int event_type; + + data0 = readl(base_addr + S3C2410_ADCDAT0); + data1 = readl(base_addr + S3C2410_ADCDAT1); + + ts.is_down = !(data0 & S3C2410_ADCDAT0_UPDOWN) && + !(data1 & S3C2410_ADCDAT0_UPDOWN); + + event_type = ts.is_down ? 'D' : 'U'; + + if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)&event_type, + sizeof(int)) != sizeof(int))) + /* Only happens if we have a bug. */ + TSPRINTK("FIFO full\n"); + + if (ts.is_down) + s3c2410_ts_start_adc_conversion(); + else + writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC); + + mod_timer(&event_send_timer, jiffies + 1); + + return IRQ_HANDLED; +} + +static void stylus_adc_action(unsigned p0, unsigned p1, unsigned *conv_left) +{ + int buf[3]; + + /* TODO: Do we really need this? */ + if (p0 & S3C2410_ADCDAT0_AUTO_PST || + p1 & S3C2410_ADCDAT1_AUTO_PST) { + *conv_left = 1; + return; + } + + buf[1] = p0; + buf[2] = p1; + + switch (ts_filter_chain_feed(ts.chain, &buf[1])) { + case 0: + /* The filter wants more points. */ + *conv_left = 1; + return; + case 1: + /* We have a point from the filters or no filtering enabled. */ + buf[0] = 'P'; + break; + default: + TSPRINTK("invalid return value\n"); + case -1: + /* Too much noise. Ignore the event. */ + ts_filter_chain_clear(ts.chain); + writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); + return; + }; + + if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf, + sizeof(int) * 3) != sizeof(int) * 3)) + /* This will only happen if we have a bug. */ + TSPRINTK("FIFO full\n"); + + writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC); + mod_timer(&event_send_timer, jiffies + 1); + + return; +} + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init s3c2410ts_probe(struct platform_device *pdev) +{ + int rc; + struct s3c2410_ts_mach_info *info; + struct input_dev *input_dev; + int ret = 0; + + dev_info(&pdev->dev, "Starting\n"); + + info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data; + + if (!info) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + base_addr = ioremap(S3C2410_PA_ADC, 0x20); + if (base_addr == NULL) { + dev_err(&pdev->dev, "Failed to remap register block\n"); + ret = -ENOMEM; + goto bail0; + } + + /* If we acutally are a S3C2410: Configure GPIOs */ + if (!strcmp(pdev->name, "s3c2410-ts")) + s3c2410_ts_connect(); + + writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC); + + /* Initialise input stuff */ + memset(&ts, 0, sizeof(struct s3c2410ts)); + + ts.adc_client = + s3c_adc_register(pdev, adc_selected_f, stylus_adc_action, 1); + if (!ts.adc_client) { + dev_err(&pdev->dev, + "Unable to register s3c2410_ts as s3_adc client\n"); + iounmap(base_addr); + ret = -EIO; + goto bail0; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "Unable to allocate the input device\n"); + ret = -ENOMEM; + goto bail1; + } + + ts.dev = input_dev; + ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | + BIT_MASK(EV_ABS); + ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0); + + ts.dev->name = s3c2410ts_name; + ts.dev->id.bustype = BUS_RS232; + ts.dev->id.vendor = 0xDEAD; + ts.dev->id.product = 0xBEEF; + ts.dev->id.version = S3C2410TSVERSION; + ts.state = TS_STATE_STANDBY; + ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL); + if (IS_ERR(ts.event_fifo)) { + ret = -EIO; + goto bail2; + } + + /* Create the filter chain set up for the 2 coordinates we produce. */ + ts.chain = ts_filter_chain_create(pdev, info->filter_config, 2); + + if (IS_ERR(ts.chain)) + goto bail2; + + ts_filter_chain_clear(ts.chain); + + /* Get IRQ. */ + if (request_irq(IRQ_TC, stylus_updown, 0, "s3c2410_action", ts.dev)) { + dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n"); + iounmap(base_addr); + ret = -EIO; + goto bail3; + } + + dev_info(&pdev->dev, "Successfully loaded\n"); + + /* All went ok. Register to the input system. */ + rc = input_register_device(ts.dev); + if (rc) { + dev_info(&pdev->dev, "Could not register input device\n"); + ret = -EIO; + goto bail4; + } + + return 0; + +bail4: + free_irq(IRQ_TC, ts.dev); + iounmap(base_addr); + disable_irq(IRQ_TC); +bail3: + ts_filter_chain_destroy(ts.chain); + kfifo_free(ts.event_fifo); +bail2: + input_unregister_device(ts.dev); +bail1: + iounmap(base_addr); +bail0: + + return ret; +} + +static int s3c2410ts_remove(struct platform_device *pdev) +{ + disable_irq(IRQ_TC); + free_irq(IRQ_TC, ts.dev); + + s3c_adc_release(ts.adc_client); + input_unregister_device(ts.dev); + iounmap(base_addr); + + ts_filter_chain_destroy(ts.chain); + + kfifo_free(ts.event_fifo); + + return 0; +} + +#ifdef CONFIG_PM + +#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | \ + S3C2410_ADCTSC_XY_PST(0)) + +static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + writel(TSC_SLEEP, base_addr + S3C2410_ADCTSC); + writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_STDBM, + base_addr + S3C2410_ADCCON); + disable_irq(IRQ_TC); + + return 0; +} + +static int s3c2410ts_resume(struct platform_device *pdev) +{ + ts_filter_chain_clear(ts.chain); + enable_irq(IRQ_TC); + writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC); + + return 0; +} + +#else +#define s3c2410ts_suspend NULL +#define s3c2410ts_resume NULL +#endif + +static struct platform_driver s3c2410ts_driver = { + .driver = { + .name = "s3c2410-ts", + .owner = THIS_MODULE, + }, + .probe = s3c2410ts_probe, + .remove = s3c2410ts_remove, + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, +}; + +static struct platform_driver s3c2440ts_driver = { + .driver = { + .name = "s3c2440-ts", + .owner = THIS_MODULE, + }, + .probe = s3c2410ts_probe, + .remove = s3c2410ts_remove, + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, +}; + +static int __init s3c2410ts_init(void) +{ + int rc; + + rc = platform_driver_register(&s3c2410ts_driver); + if (rc < 0) + return rc; + + rc = platform_driver_register(&s3c2440ts_driver); + if (rc < 0) + platform_driver_unregister(&s3c2410ts_driver); + + return rc; +} + +static void __exit s3c2410ts_exit(void) +{ + platform_driver_unregister(&s3c2440ts_driver); + platform_driver_unregister(&s3c2410ts_driver); +} + +module_init(s3c2410ts_init); +module_exit(s3c2410ts_exit); + diff --git a/drivers/input/touchscreen/ts_filter_chain.c b/drivers/input/touchscreen/ts_filter_chain.c new file mode 100644 index 00000000000..8b5e2145b27 --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_chain.c @@ -0,0 +1,183 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2008,2009 Andy Green <andy@warmcat.com> + */ + +#include <linux/kernel.h> +#include <linux/device.h> + +#include <linux/input/touchscreen/ts_filter_chain.h> +#include <linux/input/touchscreen/ts_filter.h> + +/* + * Tux, would you like the following function in /lib? + * It helps us avoid silly code. + */ + +/** + * sptrlen - Count how many non-null pointers are in a pointer array + * @arr: The array of pointers + */ +static int sptrlen(const void *arr) +{ + /* All pointers have the same size. */ + const int **p = (const int **)arr; + int len = 0; + + while (*(p++)) + len++; + + return len; +} + + +struct ts_filter_chain { + /* All of the filters. */ + struct ts_filter **arr; + /* Filters that can propagate values in the chain. */ + struct ts_filter **pchain; + /* Length of the pchain array. */ + int pchain_len; + /* FIXME: Add a spinlock and use it. */ +}; + +struct ts_filter_chain *ts_filter_chain_create( + struct platform_device *pdev, + const struct ts_filter_chain_configuration conf[], + int count_coords) +{ + struct ts_filter_chain *c; + int count = 0; + int len; + + BUG_ON((count_coords < 1)); + BUG_ON(count_coords > MAX_TS_FILTER_COORDS); + + c = kzalloc(sizeof(struct ts_filter_chain), GFP_KERNEL); + if (!c) + goto create_err_1; + + len = (sptrlen(conf) + 1); + /* Memory for two null-terminated arrays of filters. */ + c->arr = kzalloc(2 * sizeof(struct ts_filter *) * len, GFP_KERNEL); + if (!c->arr) + goto create_err_1; + c->pchain = c->arr + len; + + while (conf->api) { + /* TODO: Can we get away with only sending pdev->dev? */ + struct ts_filter *f = + (conf->api->create)(pdev, conf->config, count_coords); + if (!f) { + dev_info(&pdev->dev, "Filter %d creation failed\n", + count); + goto create_err_2; + } + + f->api = conf->api; + c->arr[count++] = f; + + if (f->api->haspoint && f->api->getpoint && f->api->process) + c->pchain[c->pchain_len++] = f; + + conf++; + } + + dev_info(&pdev->dev, "%d filter(s) initialized\n", count); + + return c; + +create_err_2: + ts_filter_chain_destroy(c); /* Also frees c. */ +create_err_1: + dev_info(&pdev->dev, "Error in filter chain initialization\n"); + /* + * FIXME: Individual filters have to return errors this way. + * We only have to forward the errors we find. + */ + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL_GPL(ts_filter_chain_create); + +void ts_filter_chain_destroy(struct ts_filter_chain *c) +{ + if (c->arr) { + struct ts_filter **a = c->arr; + while (*a) { + ((*a)->api->destroy)(*a); + a++; + } + kfree(c->arr); + } + kfree(c); +} +EXPORT_SYMBOL_GPL(ts_filter_chain_destroy); + +void ts_filter_chain_clear(struct ts_filter_chain *c) +{ + struct ts_filter **a = c->arr; + + while (*a) { + if ((*a)->api->clear) + ((*a)->api->clear)(*a); + a++; + } +} +EXPORT_SYMBOL_GPL(ts_filter_chain_clear); + +static void ts_filter_chain_scale(struct ts_filter_chain *c, int *coords) +{ + struct ts_filter **a = c->arr; + while (*a) { + if ((*a)->api->scale) + ((*a)->api->scale)(*a, coords); + a++; + } +} + +int ts_filter_chain_feed(struct ts_filter_chain *c, int *coords) +{ + int len = c->pchain_len; + int i = len - 1; + + if (!c->pchain[0]) + return 1; /* Nothing to do. */ + + BUG_ON(c->pchain[0]->api->haspoint(c->pchain[0])); + + if (c->pchain[0]->api->process(c->pchain[0], coords)) + return -1; + + while (i >= 0 && i < len) { + if (c->pchain[i]->api->haspoint(c->pchain[i])) { + c->pchain[i]->api->getpoint(c->pchain[i], coords); + if (++i < len && + c->pchain[i]->api->process(c->pchain[i], coords)) + return -1; /* Error. */ + } else { + i--; + } + } + + if (i >= 0) { /* Same as i == len. */ + ts_filter_chain_scale(c, coords); + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ts_filter_chain_feed); + diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c new file mode 100644 index 00000000000..9e344c5f19f --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_group.c @@ -0,0 +1,330 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2008,2009 by Openmoko, Inc. + * Author: Nelson Castillo <arhuaco@freaks-unidos.net> + * All rights reserved. + * + * + * This filter is useful to reject samples that are not reliable. We consider + * that a sample is not reliable if it deviates form the Majority. + * This filter mixes information from all the available dimensions. It means + * that for two dimensions we draw a rectangle where the thought-to-be good + * points can be found. + * + * The implementation would be more efficient with a double-linked list but + * let's keep it simple for now. + * + * 1) We collect S samples and keep it in sorted sets. + * - Points that are "close enough" are considered to be in the same set. + * We don't actually keep the sets but ranges of points. + * + * 2) For each dimension: + * - We choose the range with more elements. If more than "threshold" + * points are in this range we use the minimum and the maximum point + * of the range to define the valid range for this dimension [min, max], + * otherwise we discard all the points and the ranges and go to step 1. + * + * 3) We consider the unsorted S samples and try to feed them to the next + * filter in the chain. If one of the points of each sample + * is not in the allowed range for its dimension we discard the sample. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input/touchscreen/ts_filter_group.h> + +struct coord_range { + int min; /* Minimum value of the range. */ + int max; /* Maximum value of the range */ + int N; /* Number of points in the range. */ +}; + +struct ts_filter_group { + /* Private filter configuration. */ + struct ts_filter_group_configuration *config; + /* Filter API. */ + struct ts_filter tsf; + + int N; /* How many samples we have. */ + int *samples[MAX_TS_FILTER_COORDS]; /* The samples: our input. */ + + /* Temporal values that help us compute range_min and range_max. */ + struct coord_range *ranges[MAX_TS_FILTER_COORDS]; /* Ranges. */ + int n_ranges[MAX_TS_FILTER_COORDS]; /* Number of ranges */ + + /* Computed ranges that help us filter the points. */ + int range_max[MAX_TS_FILTER_COORDS]; /* Max. computed ranges. */ + int range_min[MAX_TS_FILTER_COORDS]; /* Min. computed ranges. */ + + int tries_left; /* We finish if we can't get enough samples. */ + int ready; /* If we are ready to deliver samples. */ + int result; /* Index of the point being returned. */ +}; + +#define ts_filter_to_filter_group(f) \ + container_of(f, struct ts_filter_group, tsf) + + +static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg, + int attempts) +{ + int n; + tsfg->N = 0; + tsfg->tries_left = attempts; + tsfg->ready = 0; + tsfg->result = 0; + for (n = 0; n < tsfg->tsf.count_coords; n++) + tsfg->n_ranges[n] = 0; +} + +static void ts_filter_group_clear(struct ts_filter *tsf) +{ + struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf); + + ts_filter_group_clear_internal(tsfg, tsfg->config->attempts); +} + +static struct ts_filter *ts_filter_group_create( + struct platform_device *pdev, + const struct ts_filter_configuration *conf, + int count_coords) +{ + struct ts_filter_group *tsfg; + int i; + + tsfg = kzalloc(sizeof(struct ts_filter_group), GFP_KERNEL); + if (!tsfg) + return NULL; + + tsfg->config = container_of(conf, + struct ts_filter_group_configuration, + config); + tsfg->tsf.count_coords = count_coords; + + BUG_ON(tsfg->config->attempts <= 0); + BUG_ON(tsfg->config->length < tsfg->config->threshold); + + tsfg->samples[0] = kmalloc(count_coords * sizeof(int) * + tsfg->config->length, GFP_KERNEL); + if (!tsfg->samples[0]) { + kfree(tsfg); + return NULL; + } + for (i = 1; i < count_coords; ++i) + tsfg->samples[i] = tsfg->samples[0] + i * tsfg->config->length; + + tsfg->ranges[0] = kmalloc(count_coords * sizeof(struct coord_range) * + tsfg->config->length, GFP_KERNEL); + if (!tsfg->ranges[0]) { + kfree(tsfg->samples[0]); + kfree(tsfg); + return NULL; + } + for (i = 1; i < count_coords; ++i) + tsfg->ranges[i] = tsfg->ranges[0] + i * tsfg->config->length; + + ts_filter_group_clear_internal(tsfg, tsfg->config->attempts); + + dev_info(&pdev->dev, "Created Group filter len:%d coords:%d close:%d " + "thresh:%d\n", tsfg->config->length, count_coords, + tsfg->config->close_enough, tsfg->config->threshold); + + return &tsfg->tsf; +} + +static void ts_filter_group_destroy(struct ts_filter *tsf) +{ + struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf); + + kfree(tsfg->samples[0]); + kfree(tsfg->ranges[0]); + kfree(tsf); +} + +static void ts_filter_group_prepare_next(struct ts_filter *tsf); + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define IN_RANGE(c, r) ((c) >= (r).min - tsfg->config->close_enough && \ + (c) <= (r).max + tsfg->config->close_enough) + +static void delete_spot(struct coord_range *v, int n, int size) +{ + int i; + for (i = n; i < size - 1; ++i) + v[i] = v[i + 1]; +} + +static int ts_filter_group_process(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf); + int n; + int j; + + BUG_ON(tsfg->N >= tsfg->config->length); + BUG_ON(tsfg->ready); + + for (n = 0; n < tsfg->tsf.count_coords; n++) { + int i; + struct coord_range *range = tsfg->ranges[n]; + int *n_ranges = &tsfg->n_ranges[n]; + int found = 0; + + tsfg->samples[n][tsfg->N] = coords[n]; + + for (i = 0; i < *n_ranges; ++i) { + if (IN_RANGE(coords[n], range[i])) { + range[i].min = MIN(range[i].min, coords[n]); + range[i].max = MAX(range[i].max, coords[n]); + range[i].N++; + found = 1; + break; + } else if (coords[n] <= range[i].min) + break; /* We need to insert a range. */ + } + if (found) { /* We might need to melt ranges. */ + if (i && range[i - 1].max + tsfg->config->close_enough + >= range[i].min) { + BUG_ON(range[i - 1].max >= range[i].max); + range[i - 1].max = range[i].max; + range[i - 1].N += range[i].N; + delete_spot(range, i, *n_ranges); + (*n_ranges)--; + i--; + } + if (i < *n_ranges - 1 && range[i + 1].min - + tsfg->config->close_enough <= range[i].max) { + range[i].max = range[i + 1].max; + range[i].N += range[i + 1].N; + delete_spot(range, i + 1, *n_ranges); + (*n_ranges)--; + } + } else { + BUG_ON((*n_ranges) >= tsfg->config->length); + (*n_ranges)++; + for (j = *n_ranges - 1; j > i; --j) + range[j] = range[j - 1]; + range[i].N = 1; + range[i].min = coords[n]; + range[i].max = coords[n]; + } + } + + if (++tsfg->N < tsfg->config->length) + return 0; + + for (n = 0; n < tsfg->tsf.count_coords; ++n) { + int best = 0; + for (j = 1; j < tsfg->n_ranges[n]; ++j) + if (tsfg->ranges[n][best].N < tsfg->ranges[n][j].N) + best = j; + if (tsfg->ranges[n][best].N < tsfg->config->threshold) { + /* This set of points is not good enough for us. */ + if (--tsfg->tries_left) { + ts_filter_group_clear_internal + (tsfg, tsfg->tries_left); + /* No errors but we need more samples. */ + return 0; + } + return 1; /* We give up: error. */ + } + tsfg->range_min[n] = tsfg->ranges[n][best].min; + tsfg->range_max[n] = tsfg->ranges[n][best].max; + } + + ts_filter_group_prepare_next(tsf); + + return 0; +} + +/* + * This private function prepares a point that will be returned + * in ts_filter_group_getpoint if it is available. It updates + * the priv->ready state also. + */ +static void ts_filter_group_prepare_next(struct ts_filter *tsf) +{ + struct ts_filter_group *priv = ts_filter_to_filter_group(tsf); + int n; + + while (priv->result < priv->N) { + for (n = 0; n < priv->tsf.count_coords; ++n) { + if (priv->samples[n][priv->result] < + priv->range_min[n] || + priv->samples[n][priv->result] > priv->range_max[n]) + break; + } + + if (n == priv->tsf.count_coords) /* Sample is OK. */ + break; + + priv->result++; + } + + if (unlikely(priv->result >= priv->N)) { /* No sample to deliver. */ + ts_filter_group_clear_internal(priv, priv->config->attempts); + priv->ready = 0; + } else { + priv->ready = 1; + } +} + +static int ts_filter_group_haspoint(struct ts_filter *tsf) +{ + struct ts_filter_group *priv = ts_filter_to_filter_group(tsf); + + return priv->ready; +} + +static void ts_filter_group_getpoint(struct ts_filter *tsf, int *point) +{ + struct ts_filter_group *priv = ts_filter_to_filter_group(tsf); + int n; + + BUG_ON(!priv->ready); + + for (n = 0; n < priv->tsf.count_coords; n++) + point[n] = priv->samples[n][priv->result]; + + priv->result++; + + /* This call will update priv->ready. */ + ts_filter_group_prepare_next(tsf); +} + +/* + * Get ready to process the next batch of points, forget + * points we could have delivered. + */ +static void ts_filter_group_scale(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_group *priv = ts_filter_to_filter_group(tsf); + + ts_filter_group_clear_internal(priv, priv->config->attempts); +} + +const struct ts_filter_api ts_filter_group_api = { + .create = ts_filter_group_create, + .destroy = ts_filter_group_destroy, + .clear = ts_filter_group_clear, + .process = ts_filter_group_process, + .haspoint = ts_filter_group_haspoint, + .getpoint = ts_filter_group_getpoint, + .scale = ts_filter_group_scale, +}; +EXPORT_SYMBOL_GPL(ts_filter_group_api); + diff --git a/drivers/input/touchscreen/ts_filter_linear.c b/drivers/input/touchscreen/ts_filter_linear.c new file mode 100644 index 00000000000..8b496591eec --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_linear.c @@ -0,0 +1,212 @@ +/* + * 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, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2008,2009 by Openmoko, Inc. + * Author: Nelson Castillo <arhuaco@freaks-unidos.net> + * All rights reserved. + * + * Linearly scale touchscreen values. + * + * Expose the TS_FILTER_LINEAR_NCONSTANTS for the linear transformation + * using sysfs. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <linux/input/touchscreen/ts_filter_linear.h> + +struct ts_filter_linear; + +/* Sysfs code. */ + +struct const_obj { + /* The actual private object. */ + struct ts_filter_linear *tsfl; + /* Our kobject. */ + struct kobject kobj; +}; + +#define to_const_obj(x) container_of(x, struct const_obj, kobj) + +struct const_attribute { + struct attribute attr; + ssize_t (*show)(struct const_obj *const, struct const_attribute *attr, + char *buf); + ssize_t (*store)(struct const_obj *const, struct const_attribute *attr, + const char *buf, size_t count); +}; + +#define to_const_attr(x) container_of(x, struct const_attribute, attr) + + +/* Private linear filter structure. */ + +struct ts_filter_linear { + /* Private configuration for this filter. */ + struct ts_filter_linear_configuration *config; + + /* Generic filter API. */ + struct ts_filter tsf; + + /* Linear constants for the transformation. */ + int constants[TS_FILTER_LINEAR_NCONSTANTS]; + + /* Sysfs. */ + + /* Our const_object. */ + struct const_obj c_obj; + /* Our type. We will stick operations to it. */ + struct kobj_type const_ktype; + /* Attrs. of the virtual files. */ + struct const_attribute kattrs[TS_FILTER_LINEAR_NCONSTANTS]; + /* Default Attrs. Always NULL for us. */ + struct attribute *attrs[TS_FILTER_LINEAR_NCONSTANTS + 1]; + /* Storage for the name of the virtual files. */ + char attr_names[TS_FILTER_LINEAR_NCONSTANTS][2]; +}; + +#define ts_filter_to_filter_linear(f) \ + container_of(f, struct ts_filter_linear, tsf) + +/* Sysfs functions. */ + +static ssize_t const_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct const_attribute *a = to_const_attr(attr); + + return a->show(to_const_obj(kobj), a, buf); +} + +static ssize_t const_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct const_attribute *a = to_const_attr(attr); + + return a->store(to_const_obj(kobj), a, buf, len); +} + +static struct sysfs_ops const_sysfs_ops = { + .show = const_attr_show, + .store = const_attr_store, +}; + +static void const_release(struct kobject *kobj) +{ + kfree(to_const_obj(kobj)->tsfl); +} + +static ssize_t const_show(struct const_obj *obj, struct const_attribute *attr, + char *buf) +{ + int who; + + sscanf(attr->attr.name, "%d", &who); + return sprintf(buf, "%d\n", obj->tsfl->constants[who]); +} + +static ssize_t const_store(struct const_obj *obj, struct const_attribute *attr, + const char *buf, size_t count) +{ + int who; + + sscanf(attr->attr.name, "%d", &who); + sscanf(buf, "%d", &obj->tsfl->constants[who]); + return count; +} + +/* Filter functions. */ + +static struct ts_filter *ts_filter_linear_create( + struct platform_device *pdev, + const struct ts_filter_configuration *conf, + int count_coords) +{ + struct ts_filter_linear *tsfl; + int i; + int ret; + + tsfl = kzalloc(sizeof(struct ts_filter_linear), GFP_KERNEL); + if (!tsfl) + return NULL; + + tsfl->config = container_of(conf, + struct ts_filter_linear_configuration, + config); + + tsfl->tsf.count_coords = count_coords; + + for (i = 0; i < TS_FILTER_LINEAR_NCONSTANTS; ++i) { + tsfl->constants[i] = tsfl->config->constants[i]; + + /* sysfs */ + sprintf(tsfl->attr_names[i], "%d", i); + tsfl->kattrs[i].attr.name = tsfl->attr_names[i]; + tsfl->kattrs[i].attr.mode = 0666; + tsfl->kattrs[i].show = const_show; + tsfl->kattrs[i].store = const_store; + tsfl->attrs[i] = &tsfl->kattrs[i].attr; + } + tsfl->attrs[i] = NULL; + + tsfl->const_ktype.sysfs_ops = &const_sysfs_ops; + tsfl->const_ktype.release = const_release; + tsfl->const_ktype.default_attrs = tsfl->attrs; + tsfl->c_obj.tsfl = tsfl; /* kernel frees tsfl in const_release */ + + ret = kobject_init_and_add(&tsfl->c_obj.kobj, &tsfl->const_ktype, + &pdev->dev.kobj, "calibration"); + if (ret) { + kobject_put(&tsfl->c_obj.kobj); + return NULL; + } + + dev_info(&pdev->dev, "Created Linear filter coords:%d\n", count_coords); + + return &tsfl->tsf; +} + +static void ts_filter_linear_destroy(struct ts_filter *tsf) +{ + struct ts_filter_linear *tsfl = ts_filter_to_filter_linear(tsf); + + /* Kernel frees tsfl in const_release. */ + kobject_put(&tsfl->c_obj.kobj); +} + +static void ts_filter_linear_scale(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_linear *tsfl = ts_filter_to_filter_linear(tsf); + + int *k = tsfl->constants; + int c0 = coords[tsfl->config->coord0]; + int c1 = coords[tsfl->config->coord1]; + + coords[tsfl->config->coord0] = (k[2] + k[0] * c0 + k[1] * c1) / k[6]; + coords[tsfl->config->coord1] = (k[5] + k[3] * c0 + k[4] * c1) / k[6]; +} + +const struct ts_filter_api ts_filter_linear_api = { + .create = ts_filter_linear_create, + .destroy = ts_filter_linear_destroy, + .scale = ts_filter_linear_scale, +}; +EXPORT_SYMBOL_GPL(ts_filter_linear_api); + diff --git a/drivers/input/touchscreen/ts_filter_mean.c b/drivers/input/touchscreen/ts_filter_mean.c new file mode 100644 index 00000000000..ad4e9c1207c --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_mean.c @@ -0,0 +1,174 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2008,2009 + * Andy Green <andy@warmcat.com> + * Nelson Castillo <arhuaco@freaks-unidos.net> + * + * Simple mean filter. + * + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/input/touchscreen/ts_filter_mean.h> + +struct ts_filter_mean { + /* Copy of the private filter configuration. */ + struct ts_filter_mean_configuration *config; + /* Filter API. */ + struct ts_filter tsf; + + /* Index on a circular buffer. */ + int curr; + /* Useful to tell if the circular buffer is full(read:ready). */ + int count; + /* Sumation used to compute the mean. */ + int sum[MAX_TS_FILTER_COORDS]; + /* Keep point values and decrement them from the sum on time. */ + int *fifo[MAX_TS_FILTER_COORDS]; + /* Store the output of this filter. */ + int ready; +}; + +#define ts_filter_to_filter_mean(f) container_of(f, struct ts_filter_mean, tsf) + + +static void ts_filter_mean_clear(struct ts_filter *tsf); + +static struct ts_filter *ts_filter_mean_create( + struct platform_device *pdev, + const struct ts_filter_configuration *conf, + int count_coords) +{ + struct ts_filter_mean *priv; + int *v; + int n; + + priv = kzalloc(sizeof(struct ts_filter_mean), GFP_KERNEL); + if (!priv) + return NULL; + + priv->tsf.count_coords = count_coords; + priv->config = container_of(conf, + struct ts_filter_mean_configuration, + config); + + BUG_ON(priv->config->length <= 0); + + v = kmalloc(priv->config->length * sizeof(int) * count_coords, + GFP_KERNEL); + if (!v) + return NULL; + + for (n = 0; n < count_coords; n++) { + priv->fifo[n] = v; + v += priv->config->length; + } + + ts_filter_mean_clear(&priv->tsf); + + dev_info(&pdev->dev, "Created Mean filter len:%d coords:%d\n", + priv->config->length, count_coords); + + return &priv->tsf; +} + +static void ts_filter_mean_destroy(struct ts_filter *tsf) +{ + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + + kfree(priv->fifo[0]); /* first guy has pointer from kmalloc */ + kfree(tsf); +} + +static void ts_filter_mean_clear(struct ts_filter *tsf) +{ + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + + priv->count = 0; + priv->curr = 0; + priv->ready = 0; + memset(priv->sum, 0, tsf->count_coords * sizeof(int)); +} + +static int ts_filter_mean_process(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + int n; + + BUG_ON(priv->ready); + + for (n = 0; n < tsf->count_coords; n++) { + priv->sum[n] += coords[n]; + priv->fifo[n][priv->curr] = coords[n]; + } + + if (priv->count + 1 == priv->config->length) + priv->ready = 1; + else + priv->count++; + + priv->curr = (priv->curr + 1) % priv->config->length; + + return 0; /* No error. */ +} + +static int ts_filter_mean_haspoint(struct ts_filter *tsf) +{ + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + + return priv->ready; +} + +static void ts_filter_mean_getpoint(struct ts_filter *tsf, int *point) +{ + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + int n; + + BUG_ON(!priv->ready); + + for (n = 0; n < tsf->count_coords; n++) { + point[n] = priv->sum[n]; + priv->sum[n] -= priv->fifo[n][priv->curr]; + } + + priv->ready = 0; +} + +static void ts_filter_mean_scale(struct ts_filter *tsf, int *coords) +{ + int n; + struct ts_filter_mean *priv = ts_filter_to_filter_mean(tsf); + + for (n = 0; n < tsf->count_coords; n++) { + coords[n] += priv->config->length >> 1; /* Rounding. */ + coords[n] /= priv->config->length; + } +} + +const struct ts_filter_api ts_filter_mean_api = { + .create = ts_filter_mean_create, + .destroy = ts_filter_mean_destroy, + .clear = ts_filter_mean_clear, + .process = ts_filter_mean_process, + .scale = ts_filter_mean_scale, + .haspoint = ts_filter_mean_haspoint, + .getpoint = ts_filter_mean_getpoint, +}; +EXPORT_SYMBOL_GPL(ts_filter_mean_api); + diff --git a/drivers/input/touchscreen/ts_filter_median.c b/drivers/input/touchscreen/ts_filter_median.c new file mode 100644 index 00000000000..c608f7a9794 --- /dev/null +++ b/drivers/input/touchscreen/ts_filter_median.c @@ -0,0 +1,261 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2008 Andy Green <andy@warmcat.com> + * + * + * Median averaging stuff. We sort incoming raw samples into an array of + * MEDIAN_SIZE length, discarding the oldest sample each time once we are full. + * We then return the sum of the middle three samples for X and Y. It means + * the final result must be divided by (3 * scaling factor) to correct for + * avoiding the repeated /3. + * + * This strongly rejects brief excursions away from a central point that is + * sticky in time compared to the excursion duration. + * + * Thanks to Dale Schumacher (who wrote some example code) and Carl-Daniel + * Halifinger who pointed out this would be a good method. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input/touchscreen/ts_filter_median.h> + +struct ts_filter_median { + /* Private configuration. */ + struct ts_filter_median_configuration *config; + /* Generic Filter API. */ + struct ts_filter tsf; + + /* Count raw samples we get. */ + int samples_count; + /* + * Remember the last coordinates we got in order to know if + * we are moving slow or fast. + */ + int last_issued[MAX_TS_FILTER_COORDS]; + /* How many samples in the sort buffer are valid. */ + int valid; + /* Samples taken for median in sorted form. */ + int *sort[MAX_TS_FILTER_COORDS]; + /* Samples taken for median. */ + int *fifo[MAX_TS_FILTER_COORDS]; + /* Where we are in the fifo sample memory. */ + int pos; + /* Do we have a sample to deliver? */ + int ready; +}; + +#define ts_filter_to_filter_median(f) \ + container_of(f, struct ts_filter_median, tsf) + + +static void ts_filter_median_insert(int *p, int sample, int count) +{ + int n; + + /* Search through what we got so far to find where to put sample. */ + for (n = 0; n < count; n++) + if (sample < p[n]) { /* We met somebody bigger than us? */ + /* Starting from the end, push bigger guys down one. */ + for (count--; count >= n; count--) + p[count + 1] = p[count]; + p[n] = sample; /* Put us in place of first bigger. */ + return; + } + + p[count] = sample; /* Nobody was bigger than us, add us on the end. */ +} + +static void ts_filter_median_del(int *p, int value, int count) +{ + int index; + + for (index = 0; index < count; index++) + if (p[index] == value) { + for (; index < count; index++) + p[index] = p[index + 1]; + return; + } +} + + +static void ts_filter_median_clear(struct ts_filter *tsf) +{ + struct ts_filter_median *tsfm = ts_filter_to_filter_median(tsf); + + tsfm->pos = 0; + tsfm->valid = 0; + tsfm->ready = 0; + memset(&tsfm->last_issued[0], 1, tsf->count_coords * sizeof(int)); +} + +static struct ts_filter *ts_filter_median_create( + struct platform_device *pdev, + const struct ts_filter_configuration *conf, + int count_coords) +{ + int *p; + int n; + struct ts_filter_median *tsfm = kzalloc(sizeof(struct ts_filter_median), + GFP_KERNEL); + + if (!tsfm) + return NULL; + + tsfm->config = container_of(conf, + struct ts_filter_median_configuration, + config); + + tsfm->tsf.count_coords = count_coords; + + tsfm->config->midpoint = (tsfm->config->extent >> 1) + 1; + + p = kmalloc(2 * count_coords * sizeof(int) * (tsfm->config->extent + 1), + GFP_KERNEL); + if (!p) { + kfree(tsfm); + return NULL; + } + + for (n = 0; n < count_coords; n++) { + tsfm->sort[n] = p; + p += tsfm->config->extent + 1; + tsfm->fifo[n] = p; + p += tsfm->config->extent + 1; + } + + ts_filter_median_clear(&tsfm->tsf); + + dev_info(&pdev->dev, + "Created Median filter len:%d coords:%d dec_threshold:%d\n", + tsfm->config->extent, count_coords, + tsfm->config->decimation_threshold); + + return &tsfm->tsf; +} + +static void ts_filter_median_destroy(struct ts_filter *tsf) +{ + struct ts_filter_median *tsfm = ts_filter_to_filter_median(tsf); + + kfree(tsfm->sort[0]); /* First guy has pointer from kmalloc. */ + kfree(tsf); +} + +static void ts_filter_median_scale(struct ts_filter *tsf, int *coords) +{ + int n; + + for (n = 0; n < tsf->count_coords; n++) + coords[n] = (coords[n] + 2) / 3; +} + +/* + * Give us the raw sample data coords, and if we return 1 then you can + * get a filtered coordinate from coords. If we return 0 you didn't + * fill all the filters with samples yet. + */ + +static int ts_filter_median_process(struct ts_filter *tsf, int *coords) +{ + struct ts_filter_median *tsfm = ts_filter_to_filter_median(tsf); + int n; + int movement = 1; + + for (n = 0; n < tsf->count_coords; n++) { + /* Grab copy in insertion order to remove when oldest. */ + tsfm->fifo[n][tsfm->pos] = coords[n]; + /* Insert these samples in sorted order in the median arrays. */ + ts_filter_median_insert(tsfm->sort[n], coords[n], tsfm->valid); + } + /* Move us on in the fifo. */ + if (++tsfm->pos == (tsfm->config->extent + 1)) + tsfm->pos = 0; + + /* Have we finished a median sampling? */ + if (++tsfm->valid < tsfm->config->extent) + goto process_exit; /* No valid sample to use. */ + + BUG_ON(tsfm->valid != tsfm->config->extent); + + tsfm->valid--; + + /* + * Sum the middle 3 in the median sorted arrays. We don't divide back + * down which increases the sum resolution by a factor of 3 until the + * scale API function is called. + */ + for (n = 0; n < tsf->count_coords; n++) + /* Perform the deletion of the oldest sample. */ + ts_filter_median_del(tsfm->sort[n], tsfm->fifo[n][tsfm->pos], + tsfm->valid); + + tsfm->samples_count--; + if (tsfm->samples_count >= 0) + goto process_exit; + + for (n = 0; n < tsf->count_coords; n++) { + /* Give the coordinate result from summing median 3. */ + coords[n] = tsfm->sort[n][tsfm->config->midpoint - 1] + + tsfm->sort[n][tsfm->config->midpoint] + + tsfm->sort[n][tsfm->config->midpoint + 1]; + + movement += abs(tsfm->last_issued[n] - coords[n]); + } + + if (movement > tsfm->config->decimation_threshold) /* Moving fast. */ + tsfm->samples_count = tsfm->config->decimation_above; + else + tsfm->samples_count = tsfm->config->decimation_below; + + memcpy(&tsfm->last_issued[0], coords, tsf->count_coords * sizeof(int)); + + tsfm->ready = 1; + +process_exit: + return 0; +} + +static int ts_filter_median_haspoint(struct ts_filter *tsf) +{ + struct ts_filter_median *priv = ts_filter_to_filter_median(tsf); + + return priv->ready; +} + +static void ts_filter_median_getpoint(struct ts_filter *tsf, int *point) +{ + struct ts_filter_median *priv = ts_filter_to_filter_median(tsf); + + BUG_ON(!priv->ready); + + memcpy(point, &priv->last_issued[0], tsf->count_coords * sizeof(int)); + + priv->ready = 0; +} + +const struct ts_filter_api ts_filter_median_api = { + .create = ts_filter_median_create, + .destroy = ts_filter_median_destroy, + .clear = ts_filter_median_clear, + .process = ts_filter_median_process, + .scale = ts_filter_median_scale, + .haspoint = ts_filter_median_haspoint, + .getpoint = ts_filter_median_getpoint, +}; +EXPORT_SYMBOL_GPL(ts_filter_median_api); + diff --git a/drivers/mfd/pcf50606-adc.c b/drivers/mfd/pcf50606-adc.c index 47210a9cce8..f6ffd0b146d 100644 --- a/drivers/mfd/pcf50606-adc.c +++ b/drivers/mfd/pcf50606-adc.c @@ -37,6 +37,7 @@ struct pcf50606_adc_request { }; +/* code expects this to be a power of two */ #define PCF50606_MAX_ADC_FIFO_DEPTH 8 struct pcf50606_adc { @@ -44,12 +45,12 @@ struct pcf50606_adc { /* Private stuff */ struct pcf50606_adc_request *queue[PCF50606_MAX_ADC_FIFO_DEPTH]; - int queue_head; - int queue_tail; + unsigned int queue_head; + unsigned int queue_tail; struct mutex queue_mutex; }; -static inline struct pcf50606_adc *__to_adc(struct pcf50606 *pcf) +static inline struct pcf50606_adc *pcf50606_to_adc(struct pcf50606 *pcf) { return platform_get_drvdata(pcf->adc_pdev); } @@ -66,13 +67,12 @@ static void adc_setup(struct pcf50606 *pcf, int channel) static void trigger_next_adc_job_if_any(struct pcf50606 *pcf) { - struct pcf50606_adc *adc = __to_adc(pcf); - int head, tail; + struct pcf50606_adc *adc = pcf50606_to_adc(pcf); + unsigned int head; mutex_lock(&adc->queue_mutex); head = adc->queue_head; - tail = adc->queue_tail; if (!adc->queue[head]) goto out; @@ -85,11 +85,10 @@ out: static int adc_enqueue_request(struct pcf50606 *pcf, struct pcf50606_adc_request *req) { - struct pcf50606_adc *adc = __to_adc(pcf); - int head, tail; + struct pcf50606_adc *adc = pcf50606_to_adc(pcf); + unsigned int tail; mutex_lock(&adc->queue_mutex); - head = adc->queue_head; tail = adc->queue_tail; if (adc->queue[tail]) { @@ -172,7 +171,7 @@ EXPORT_SYMBOL_GPL(pcf50606_adc_async_read); static int adc_result(struct pcf50606 *pcf) { - u16 ret = (pcf50606_reg_read(pcf, PCF50606_REG_ADCS1) << 2) | + int ret = (pcf50606_reg_read(pcf, PCF50606_REG_ADCS1) << 2) | (pcf50606_reg_read(pcf, PCF50606_REG_ADCS2) & 0x03); dev_dbg(pcf->dev, "adc result = %d\n", ret); @@ -185,7 +184,7 @@ static void pcf50606_adc_irq(int irq, void *data) struct pcf50606_adc *adc = data; struct pcf50606 *pcf = adc->pcf; struct pcf50606_adc_request *req; - int head; + unsigned int head; mutex_lock(&adc->queue_mutex); head = adc->queue_head; @@ -198,8 +197,7 @@ static void pcf50606_adc_irq(int irq, void *data) } adc->queue[head] = NULL; - adc->queue_head = (head + 1) & - (PCF50606_MAX_ADC_FIFO_DEPTH - 1); + adc->queue_head = (head + 1) & (PCF50606_MAX_ADC_FIFO_DEPTH - 1); mutex_unlock(&adc->queue_mutex); @@ -231,7 +229,8 @@ static int __devinit pcf50606_adc_probe(struct platform_device *pdev) static int __devexit pcf50606_adc_remove(struct platform_device *pdev) { struct pcf50606_adc *adc = platform_get_drvdata(pdev); - int i, head; + unsigned int i; + unsigned int head; pcf50606_free_irq(adc->pcf, PCF50606_IRQ_ADCRDY); @@ -254,6 +253,7 @@ static int __devexit pcf50606_adc_remove(struct platform_device *pdev) struct platform_driver pcf50606_adc_driver = { .driver = { .name = "pcf50606-adc", + .owner = THIS_MODULE, }, .probe = pcf50606_adc_probe, .remove = __devexit_p(pcf50606_adc_remove), diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 11dc7e69c4f..7dc854c3fd8 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -816,7 +816,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->mtd.owner = THIS_MODULE; nmtd->set = set; - if (hardware_ecc) { + if (!(info->platform && info->platform->software_ecc) && hardware_ecc) { chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; diff --git a/drivers/power/pcf50606-charger.c b/drivers/power/pcf50606-charger.c index ee270714a74..a4b77950788 100644 --- a/drivers/power/pcf50606-charger.c +++ b/drivers/power/pcf50606-charger.c @@ -203,7 +203,7 @@ static int __devinit pcf50606_mbc_probe(struct platform_device *pdev) pcf50606_register_irq(mbc->pcf, mbc_irq_handlers[i], pcf50606_mbc_irq_handler, mbc); - ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_charge_mode) + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_charge_mode.attr); if (ret) dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index c8851a0db63..7d0616495bf 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -1263,6 +1263,13 @@ module_exit(s3c24xx_serial_modexit); #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE static struct uart_port *cons_uart; +static int cons_silenced; + +void s3c24xx_serial_console_set_silence(int silenced) +{ + cons_silenced = silenced; +} +EXPORT_SYMBOL(s3c24xx_serial_console_set_silence); static int s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) @@ -1287,9 +1294,21 @@ static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int umcon = rd_regl(cons_uart, S3C2410_UMCON); + + if (cons_silenced) + return; + + /* If auto HW flow control enabled, temporarily turn it off */ + if (umcon & S3C2410_UMCOM_AFC) + wr_regl(port, S3C2410_UMCON, (umcon & !S3C2410_UMCOM_AFC)); + while (!s3c24xx_serial_console_txrdy(port, ufcon)) barrier(); wr_regb(cons_uart, S3C2410_UTXH, ch); + + if (umcon & S3C2410_UMCOM_AFC) + wr_regl(port, S3C2410_UMCON, umcon); } static void diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index f9d19be0554..0eb77333c83 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -93,6 +93,20 @@ config LCD_HP700 If you have an HP Jornada 700 series handheld (710/720/728) say Y to enable LCD control driver. +config LCD_JBT6K74 + tristate "TPO JBT6K74-AS TFT display ASIC control interface" + depends on SPI_MASTER && SYSFS && LCD_CLASS_DEVICE + help + SPI driver for the control interface of TFT panels containing + the TPO JBT6K74-AS controller ASIC, such as the TPO TD028TTEC1 + TFT diplay module used in the Openmoko Freerunner GSM phone. + + The control interface is required for display operation, as it + controls power management, display timing and gamma calibration. + + This driver can also be build as a module, if so it will be called + jbt6k74. + # # Backlight # diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 4eb178c1d68..13db30f415f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o +obj-$(CONFIG_LCD_JBT6K74) += jbt6k74.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c new file mode 100644 index 00000000000..b1aacb7ebcd --- /dev/null +++ b/drivers/video/backlight/jbt6k74.c @@ -0,0 +1,885 @@ +/* Linux kernel driver for the tpo JBT6K74-AS LCM ASIC + * + * Copyright (C) 2006-2007 by Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org>, + * Stefan Schmidt <stefan@openmoko.org> + * Copyright (C) 2008 by Harald Welte <laforge@openmoko.org> + * Copyright (C) 2009 by Lars-Peter Clausen <lars@metafoo.de> + * 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 as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/jbt6k74.h> +#include <linux/fb.h> +#include <linux/lcd.h> +#include <linux/time.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> + +enum jbt_register { + JBT_REG_SLEEP_IN = 0x10, + JBT_REG_SLEEP_OUT = 0x11, + + JBT_REG_DISPLAY_OFF = 0x28, + JBT_REG_DISPLAY_ON = 0x29, + + JBT_REG_RGB_FORMAT = 0x3a, + JBT_REG_QUAD_RATE = 0x3b, + + JBT_REG_POWER_ON_OFF = 0xb0, + JBT_REG_BOOSTER_OP = 0xb1, + JBT_REG_BOOSTER_MODE = 0xb2, + JBT_REG_BOOSTER_FREQ = 0xb3, + JBT_REG_OPAMP_SYSCLK = 0xb4, + JBT_REG_VSC_VOLTAGE = 0xb5, + JBT_REG_VCOM_VOLTAGE = 0xb6, + JBT_REG_EXT_DISPL = 0xb7, + JBT_REG_OUTPUT_CONTROL = 0xb8, + JBT_REG_DCCLK_DCEV = 0xb9, + JBT_REG_DISPLAY_MODE1 = 0xba, + JBT_REG_DISPLAY_MODE2 = 0xbb, + JBT_REG_DISPLAY_MODE = 0xbc, + JBT_REG_ASW_SLEW = 0xbd, + JBT_REG_DUMMY_DISPLAY = 0xbe, + JBT_REG_DRIVE_SYSTEM = 0xbf, + + JBT_REG_SLEEP_OUT_FR_A = 0xc0, + JBT_REG_SLEEP_OUT_FR_B = 0xc1, + JBT_REG_SLEEP_OUT_FR_C = 0xc2, + JBT_REG_SLEEP_IN_LCCNT_D = 0xc3, + JBT_REG_SLEEP_IN_LCCNT_E = 0xc4, + JBT_REG_SLEEP_IN_LCCNT_F = 0xc5, + JBT_REG_SLEEP_IN_LCCNT_G = 0xc6, + + JBT_REG_GAMMA1_FINE_1 = 0xc7, + JBT_REG_GAMMA1_FINE_2 = 0xc8, + JBT_REG_GAMMA1_INCLINATION = 0xc9, + JBT_REG_GAMMA1_BLUE_OFFSET = 0xca, + + /* VGA */ + JBT_REG_BLANK_CONTROL = 0xcf, + JBT_REG_BLANK_TH_TV = 0xd0, + JBT_REG_CKV_ON_OFF = 0xd1, + JBT_REG_CKV_1_2 = 0xd2, + JBT_REG_OEV_TIMING = 0xd3, + JBT_REG_ASW_TIMING_1 = 0xd4, + JBT_REG_ASW_TIMING_2 = 0xd5, + + /* QVGA */ + JBT_REG_BLANK_CONTROL_QVGA = 0xd6, + JBT_REG_BLANK_TH_TV_QVGA = 0xd7, + JBT_REG_CKV_ON_OFF_QVGA = 0xd8, + JBT_REG_CKV_1_2_QVGA = 0xd9, + JBT_REG_OEV_TIMING_QVGA = 0xde, + JBT_REG_ASW_TIMING_1_QVGA = 0xdf, + JBT_REG_ASW_TIMING_2_QVGA = 0xe0, + + + JBT_REG_HCLOCK_VGA = 0xec, + JBT_REG_HCLOCK_QVGA = 0xed, +}; + +enum jbt_resolution { + JBT_RESOLUTION_VGA, + JBT_RESOLUTION_QVGA, +}; + +enum jbt_power_mode { + JBT_POWER_MODE_DEEP_STANDBY, + JBT_POWER_MODE_SLEEP, + JBT_POWER_MODE_NORMAL, +}; + +static const char *jbt_power_mode_names[] = { + [JBT_POWER_MODE_DEEP_STANDBY] = "deep-standby", + [JBT_POWER_MODE_SLEEP] = "sleep", + [JBT_POWER_MODE_NORMAL] = "normal", +}; + +static const char *jbt_resolution_names[] = { + [JBT_RESOLUTION_VGA] = "vga", + [JBT_RESOLUTION_QVGA] = "qvga", +}; + +struct jbt_info { + struct mutex lock; /* protects this structure */ + enum jbt_resolution resolution; + enum jbt_power_mode power_mode; + enum jbt_power_mode suspend_mode; + int suspended; + + struct spi_device *spi; + struct lcd_device *lcd_dev; + + unsigned long next_sleep; + struct delayed_work blank_work; + int blank_mode; + struct regulator_bulk_data supplies[2]; + uint16_t tx_buf[3]; + uint16_t reg_cache[0xEE]; +}; + +#define JBT_COMMAND 0x000 +#define JBT_DATA 0x100 + +static int jbt_reg_write_nodata(struct jbt_info *jbt, uint8_t reg) +{ + int ret; + + jbt->tx_buf[0] = JBT_COMMAND | reg; + ret = spi_write(jbt->spi, (uint8_t *)jbt->tx_buf, + sizeof(uint16_t)); + if (ret == 0) + jbt->reg_cache[reg] = 0; + else + dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + + return ret; +} + + +static int jbt_reg_write(struct jbt_info *jbt, uint8_t reg, uint8_t data) +{ + int ret; + + jbt->tx_buf[0] = JBT_COMMAND | reg; + jbt->tx_buf[1] = JBT_DATA | data; + ret = spi_write(jbt->spi, (uint8_t *)jbt->tx_buf, + 2*sizeof(uint16_t)); + if (ret == 0) + jbt->reg_cache[reg] = data; + else + dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + + return ret; +} + +static int jbt_reg_write16(struct jbt_info *jbt, uint8_t reg, uint16_t data) +{ + int ret; + + jbt->tx_buf[0] = JBT_COMMAND | reg; + jbt->tx_buf[1] = JBT_DATA | (data >> 8); + jbt->tx_buf[2] = JBT_DATA | (data & 0xff); + + ret = spi_write(jbt->spi, (uint8_t *)jbt->tx_buf, + 3*sizeof(uint16_t)); + if (ret == 0) + jbt->reg_cache[reg] = data; + else + dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + + return ret; +} + +static int jbt_init_regs(struct jbt_info *jbt) +{ + int ret; + + dev_dbg(&jbt->spi->dev, "entering %cVGA mode\n", + jbt->resolution == JBT_RESOLUTION_QVGA ? 'Q' : ' '); + + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE1, 0x01); + ret |= jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE2, 0x00); + ret |= jbt_reg_write(jbt, JBT_REG_RGB_FORMAT, 0x60); + ret |= jbt_reg_write(jbt, JBT_REG_DRIVE_SYSTEM, 0x10); + ret |= jbt_reg_write(jbt, JBT_REG_BOOSTER_OP, 0x56); + ret |= jbt_reg_write(jbt, JBT_REG_BOOSTER_MODE, 0x33); + ret |= jbt_reg_write(jbt, JBT_REG_BOOSTER_FREQ, 0x11); + ret |= jbt_reg_write(jbt, JBT_REG_OPAMP_SYSCLK, 0x02); + ret |= jbt_reg_write(jbt, JBT_REG_VSC_VOLTAGE, 0x2b); + ret |= jbt_reg_write(jbt, JBT_REG_VCOM_VOLTAGE, 0x40); + ret |= jbt_reg_write(jbt, JBT_REG_EXT_DISPL, 0x03); + ret |= jbt_reg_write(jbt, JBT_REG_DCCLK_DCEV, 0x04); + /* + * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement + * to avoid red / blue flicker + */ + ret |= jbt_reg_write(jbt, JBT_REG_ASW_SLEW, 0x00 | (1 << 5)); + ret |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0x00); + + ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_A, 0x11); + ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_B, 0x11); + ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_C, 0x11); + ret |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040); + ret |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0); + ret |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020); + ret |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0); + + ret |= jbt_reg_write16(jbt, JBT_REG_GAMMA1_FINE_1, 0x5533); + ret |= jbt_reg_write(jbt, JBT_REG_GAMMA1_FINE_2, 0x00); + ret |= jbt_reg_write(jbt, JBT_REG_GAMMA1_INCLINATION, 0x00); + ret |= jbt_reg_write(jbt, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00); + + if (jbt->resolution != JBT_RESOLUTION_QVGA) { + ret |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_VGA, 0x1f0); + ret |= jbt_reg_write(jbt, JBT_REG_BLANK_CONTROL, 0x02); + ret |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV, 0x0804); + + ret |= jbt_reg_write(jbt, JBT_REG_CKV_ON_OFF, 0x01); + ret |= jbt_reg_write16(jbt, JBT_REG_CKV_1_2, 0x0000); + + ret |= jbt_reg_write16(jbt, JBT_REG_OEV_TIMING, 0x0d0e); + ret |= jbt_reg_write16(jbt, JBT_REG_ASW_TIMING_1, 0x11a4); + ret |= jbt_reg_write(jbt, JBT_REG_ASW_TIMING_2, 0x0e); + } else { + ret |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_QVGA, 0x00ff); + ret |= jbt_reg_write(jbt, JBT_REG_BLANK_CONTROL_QVGA, 0x02); + ret |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV_QVGA, 0x0804); + + ret |= jbt_reg_write(jbt, JBT_REG_CKV_ON_OFF_QVGA, 0x01); + ret |= jbt_reg_write16(jbt, JBT_REG_CKV_1_2_QVGA, 0x0008); + + ret |= jbt_reg_write16(jbt, JBT_REG_OEV_TIMING_QVGA, 0x050a); + ret |= jbt_reg_write16(jbt, JBT_REG_ASW_TIMING_1_QVGA, 0x0a19); + ret |= jbt_reg_write(jbt, JBT_REG_ASW_TIMING_2_QVGA, 0x0a); + } + + return ret ? -EIO : 0; +} + +static int jbt_standby_to_sleep(struct jbt_info *jbt) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(jbt->supplies), jbt->supplies); + + /* three times command zero */ + ret |= jbt_reg_write_nodata(jbt, 0x00); + mdelay(1); + ret |= jbt_reg_write_nodata(jbt, 0x00); + mdelay(1); + ret |= jbt_reg_write_nodata(jbt, 0x00); + mdelay(1); + + /* deep standby out */ + ret |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x11); + mdelay(1); + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28); + + /* (re)initialize register set */ + ret |= jbt_init_regs(jbt); + + return ret ? -EIO : 0; +} + +static int jbt_sleep_to_normal(struct jbt_info *jbt) +{ + int ret; + + /* Make sure we are 120 ms after SLEEP_OUT */ + if (time_before(jiffies, jbt->next_sleep)) + mdelay(jiffies_to_msecs(jbt->next_sleep - jiffies)); + + if (jbt->resolution == JBT_RESOLUTION_VGA) { + /* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */ + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80); + + /* Quad mode off */ + ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00); + } else { + /* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */ + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x81); + + /* Quad mode on */ + ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x22); + } + + /* AVDD on, XVDD on */ + ret |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x16); + + /* Output control */ + ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xdff9); + + /* Turn on display */ + ret |= jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON); + + /* Sleep mode off */ + ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT); + jbt->next_sleep = jiffies + msecs_to_jiffies(120); + + /* Allow the booster and display controller to restart stably */ + mdelay(5); + + return ret ? -EIO : 0; +} + +static int jbt_normal_to_sleep(struct jbt_info *jbt) +{ + int ret; + + /* Make sure we are 120 ms after SLEEP_OUT */ + while (time_before(jiffies, jbt->next_sleep)) + cpu_relax(); + + ret = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF); + ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8000 | 1 << 3); + ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN); + jbt->next_sleep = jiffies + msecs_to_jiffies(120); + + /* Allow the internal circuits to stop automatically */ + mdelay(5); + + return ret ? -EIO : 0; +} + +static int jbt_sleep_to_standby(struct jbt_info *jbt) +{ + int ret; + ret = jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00); + ret |= regulator_bulk_disable(ARRAY_SIZE(jbt->supplies), jbt->supplies); + + return ret; +} + +static int jbt6k74_enter_power_mode(struct jbt_info *jbt, + enum jbt_power_mode new_mode) +{ + struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; + int ret = -EINVAL; + + dev_dbg(&jbt->spi->dev, "entering (old_state=%s, new_state=%s)\n", + jbt_power_mode_names[jbt->power_mode], + jbt_power_mode_names[new_mode]); + + mutex_lock(&jbt->lock); + + if (jbt->suspended) { + switch (new_mode) { + case JBT_POWER_MODE_DEEP_STANDBY: + case JBT_POWER_MODE_SLEEP: + case JBT_POWER_MODE_NORMAL: + ret = 0; + jbt->suspend_mode = new_mode; + break; + default: + break; + } + } else if (new_mode == JBT_POWER_MODE_NORMAL && + pdata->enable_pixel_clock) { + pdata->enable_pixel_clock(&jbt->spi->dev, 1); + } + + switch (jbt->power_mode) { + case JBT_POWER_MODE_DEEP_STANDBY: + switch (new_mode) { + case JBT_POWER_MODE_DEEP_STANDBY: + ret = 0; + break; + case JBT_POWER_MODE_SLEEP: + ret = jbt_standby_to_sleep(jbt); + break; + case JBT_POWER_MODE_NORMAL: + /* first transition into sleep */ + ret = jbt_standby_to_sleep(jbt); + /* then transition into normal */ + ret |= jbt_sleep_to_normal(jbt); + break; + } + break; + case JBT_POWER_MODE_SLEEP: + switch (new_mode) { + case JBT_POWER_MODE_SLEEP: + ret = 0; + break; + case JBT_POWER_MODE_DEEP_STANDBY: + ret = jbt_sleep_to_standby(jbt); + break; + case JBT_POWER_MODE_NORMAL: + ret = jbt_sleep_to_normal(jbt); + break; + } + break; + case JBT_POWER_MODE_NORMAL: + switch (new_mode) { + case JBT_POWER_MODE_NORMAL: + ret = 0; + break; + case JBT_POWER_MODE_DEEP_STANDBY: + /* first transition into sleep */ + ret = jbt_normal_to_sleep(jbt); + /* then transition into deep standby */ + ret |= jbt_sleep_to_standby(jbt); + break; + case JBT_POWER_MODE_SLEEP: + ret = jbt_normal_to_sleep(jbt); + break; + } + } + + if (ret == 0) { + jbt->power_mode = new_mode; + if (new_mode != JBT_POWER_MODE_NORMAL && + pdata->enable_pixel_clock) + pdata->enable_pixel_clock(&jbt->spi->dev, 0); + } else { + dev_err(&jbt->spi->dev, "Failed enter state '%s')\n", + jbt_power_mode_names[new_mode]); + } + + mutex_unlock(&jbt->lock); + + return ret; +} + +static int jbt6k74_set_resolution(struct jbt_info *jbt, + enum jbt_resolution new_resolution) +{ + int ret = 0; + enum jbt_resolution old_resolution; + + mutex_lock(&jbt->lock); + + if (jbt->resolution == new_resolution) + goto out_unlock; + + old_resolution = jbt->resolution; + jbt->resolution = new_resolution; + + if (jbt->power_mode == JBT_POWER_MODE_NORMAL) { + + /* first transition into sleep */ + ret = jbt_normal_to_sleep(jbt); + ret |= jbt_sleep_to_normal(jbt); + + if (ret) { + jbt->resolution = old_resolution; + dev_err(&jbt->spi->dev, "Failed to set resolution '%s')\n", + jbt_resolution_names[new_resolution]); + } + } + +out_unlock: + mutex_unlock(&jbt->lock); + + return ret; +} + +static ssize_t resolution_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct jbt_info *jbt = dev_get_drvdata(dev); + + if (jbt->resolution >= ARRAY_SIZE(jbt_resolution_names)) + return -EIO; + + return sprintf(buf, "%s\n", jbt_resolution_names[jbt->resolution]); +} + +static ssize_t resolution_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct jbt_info *jbt = dev_get_drvdata(dev); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(jbt_resolution_names); i++) { + if (!strncmp(buf, jbt_resolution_names[i], + strlen(jbt_resolution_names[i]))) { + ret = jbt6k74_set_resolution(jbt, i); + if (ret) + return ret; + return count; + } + } + + return -EINVAL; +} + +static DEVICE_ATTR(resolution, 0644, resolution_read, resolution_write); + +static int reg_by_string(const char *name) +{ + if (!strcmp(name, "gamma_fine1")) + return JBT_REG_GAMMA1_FINE_1; + else if (!strcmp(name, "gamma_fine2")) + return JBT_REG_GAMMA1_FINE_2; + else if (!strcmp(name, "gamma_inclination")) + return JBT_REG_GAMMA1_INCLINATION; + else + return JBT_REG_GAMMA1_BLUE_OFFSET; +} + +static ssize_t gamma_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct jbt_info *jbt = dev_get_drvdata(dev); + int reg = reg_by_string(attr->attr.name); + uint16_t val; + + mutex_lock(&jbt->lock); + val = jbt->reg_cache[reg]; + mutex_unlock(&jbt->lock); + + return sprintf(buf, "0x%04x\n", val); +} + +static ssize_t gamma_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct jbt_info *jbt = dev_get_drvdata(dev); + int reg = reg_by_string(attr->attr.name); + unsigned long val; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&jbt->lock); + jbt_reg_write(jbt, reg, val & 0xff); + mutex_unlock(&jbt->lock); + + return count; +} + +static ssize_t reset_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct jbt_info *jbt = dev_get_drvdata(dev); + struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; + enum jbt_power_mode old_power_mode = jbt->power_mode; + + mutex_lock(&jbt->lock); + + if (gpio_is_valid(pdata->gpio_reset)) { + /* hard reset the jbt6k74 */ + gpio_set_value_cansleep(pdata->gpio_reset, 0); + mdelay(120); + gpio_set_value_cansleep(pdata->gpio_reset, 1); + mdelay(120); + } + + ret = jbt_reg_write_nodata(jbt, 0x01); + if (ret < 0) + dev_err(&jbt->spi->dev, "cannot soft reset\n"); + mdelay(120); + + mutex_unlock(&jbt->lock); + + jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY; + jbt6k74_enter_power_mode(jbt, old_power_mode); + + return count; +} + +static DEVICE_ATTR(gamma_fine1, 0644, gamma_read, gamma_write); +static DEVICE_ATTR(gamma_fine2, 0644, gamma_read, gamma_write); +static DEVICE_ATTR(gamma_inclination, 0644, gamma_read, gamma_write); +static DEVICE_ATTR(gamma_blue_offset, 0644, gamma_read, gamma_write); +static DEVICE_ATTR(reset, 0600, NULL, reset_write); + +static struct attribute *jbt_sysfs_entries[] = { + &dev_attr_resolution.attr, + &dev_attr_gamma_fine1.attr, + &dev_attr_gamma_fine2.attr, + &dev_attr_gamma_inclination.attr, + &dev_attr_gamma_blue_offset.attr, + &dev_attr_reset.attr, + NULL, +}; + +static struct attribute_group jbt_attr_group = { + .name = NULL, + .attrs = jbt_sysfs_entries, +}; + +/* FIXME: This in an ugly hack to delay display blanking. + When the jbt is in sleep mode it displays an all white screen and thus one + will a see a short flash. + By delaying the blanking we will give the backlight a chance to turn off and + thus avoid getting the flash */ +static void jbt_blank_worker(struct work_struct *work) +{ + struct jbt_info *jbt = container_of(work, struct jbt_info, + blank_work.work); + + switch (jbt->blank_mode) { + case FB_BLANK_NORMAL: + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP); + break; + case FB_BLANK_POWERDOWN: + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY); + break; + default: + break; + } +} + +static int jbt6k74_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + int ret = -EINVAL; + struct jbt_info *jbt = dev_get_drvdata(&ld->dev); + + if (m->xres == 240 && m->yres == 320) { + ret = jbt6k74_set_resolution(jbt, JBT_RESOLUTION_QVGA); + } else if (m->xres == 480 && m->yres == 640) { + ret = jbt6k74_set_resolution(jbt, JBT_RESOLUTION_VGA); + } else { + dev_err(&jbt->spi->dev, "Unknown resolution.\n"); + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP); + } + + return ret; +} + +static int jbt6k74_set_power(struct lcd_device *ld, int power) +{ + int ret = -EINVAL; + struct jbt_info *jbt = dev_get_drvdata(&ld->dev); + + jbt->blank_mode = power; + cancel_rearming_delayed_work(&jbt->blank_work); + + switch (power) { + case FB_BLANK_UNBLANK: + dev_dbg(&jbt->spi->dev, "unblank\n"); + ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_NORMAL); + break; + case FB_BLANK_NORMAL: + dev_dbg(&jbt->spi->dev, "blank\n"); + ret = schedule_delayed_work(&jbt->blank_work, HZ); + break; + case FB_BLANK_POWERDOWN: + dev_dbg(&jbt->spi->dev, "powerdown\n"); + ret = schedule_delayed_work(&jbt->blank_work, HZ); + break; + default: + break; + } + + return ret; +} + +static int jbt6k74_get_power(struct lcd_device *ld) +{ + struct jbt_info *jbt = dev_get_drvdata(&ld->dev); + + switch (jbt->power_mode) { + case JBT_POWER_MODE_NORMAL: + return FB_BLANK_UNBLANK; + case JBT_POWER_MODE_SLEEP: + return FB_BLANK_NORMAL; + default: + return JBT_POWER_MODE_DEEP_STANDBY; + } +} + +struct lcd_ops jbt6k74_lcd_ops = { + .set_power = jbt6k74_set_power, + .get_power = jbt6k74_get_power, + .set_mode = jbt6k74_set_mode, +}; + +/* linux device model infrastructure */ + +static int __devinit jbt_probe(struct spi_device *spi) +{ + int ret; + struct jbt_info *jbt; + struct jbt6k74_platform_data *pdata = spi->dev.platform_data; + + /* the controller doesn't have a MISO pin; we can't do detection */ + + spi->mode = SPI_CPOL | SPI_CPHA; + spi->bits_per_word = 9; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, + "Failed to setup spi\n"); + return ret; + } + + jbt = kzalloc(sizeof(*jbt), GFP_KERNEL); + if (!jbt) + return -ENOMEM; + + jbt->spi = spi; + + jbt->lcd_dev = lcd_device_register("jbt6k74-lcd", &spi->dev, jbt, + &jbt6k74_lcd_ops); + + if (IS_ERR(jbt->lcd_dev)) { + ret = PTR_ERR(jbt->lcd_dev); + goto err_free_drvdata; + } + + INIT_DELAYED_WORK(&jbt->blank_work, jbt_blank_worker); + + jbt->resolution = JBT_RESOLUTION_VGA; + jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY; + jbt->next_sleep = jiffies + msecs_to_jiffies(120); + mutex_init(&jbt->lock); + + dev_set_drvdata(&spi->dev, jbt); + + jbt->supplies[0].supply = "VDC"; + jbt->supplies[1].supply = "VDDIO"; + + ret = regulator_bulk_get(NULL, ARRAY_SIZE(jbt->supplies), + jbt->supplies); + if (ret) { + dev_err(&spi->dev, "Failed to power get supplies: %d\n", ret); + goto err_unregister_lcd; + } + + if (gpio_is_valid(pdata->gpio_reset)) { + ret = gpio_request(pdata->gpio_reset, "jbt6k74 reset"); + if (ret) { + dev_err(&spi->dev, "Failed to request reset gpio: %d\n", + ret); + goto err_free_supplies; + } + + ret = gpio_direction_output(pdata->gpio_reset, 1); + if (ret) { + dev_err(&spi->dev, "Failed to set reset gpio direction: %d\n", + ret); + goto err_gpio_free; + } + } + + ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_NORMAL); + if (ret < 0) { + dev_err(&spi->dev, "cannot enter NORMAL state\n"); + goto err_gpio_free; + } + + ret = sysfs_create_group(&spi->dev.kobj, &jbt_attr_group); + if (ret < 0) { + dev_err(&spi->dev, "cannot create sysfs group\n"); + goto err_standby; + } + + + if (pdata->probe_completed) + (pdata->probe_completed)(&spi->dev); + + return 0; + +err_standby: + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY); +err_gpio_free: + gpio_free(pdata->gpio_reset); +err_free_supplies: + regulator_bulk_free(ARRAY_SIZE(jbt->supplies), jbt->supplies); +err_unregister_lcd: + lcd_device_unregister(jbt->lcd_dev); +err_free_drvdata: + dev_set_drvdata(&spi->dev, NULL); + kfree(jbt); + + return ret; +} + +static int __devexit jbt_remove(struct spi_device *spi) +{ + struct jbt_info *jbt = dev_get_drvdata(&spi->dev); + struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; + + /* We don't want to switch off the display in case the user + * accidentially unloads the module (whose use count normally is 0) */ + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_NORMAL); + + sysfs_remove_group(&spi->dev.kobj, &jbt_attr_group); + + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + + lcd_device_unregister(jbt->lcd_dev); + + dev_set_drvdata(&spi->dev, NULL); + + regulator_bulk_free(ARRAY_SIZE(jbt->supplies), jbt->supplies); + kfree(jbt); + + return 0; +} + +#ifdef CONFIG_PM +static int jbt_suspend(struct spi_device *spi, pm_message_t state) +{ + struct jbt_info *jbt = dev_get_drvdata(&spi->dev); + + jbt->suspend_mode = jbt->power_mode; + + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY); + jbt->suspended = 1; + + dev_info(&spi->dev, "suspended\n"); + + return 0; +} + +int jbt6k74_resume(struct spi_device *spi) +{ + struct jbt_info *jbt = dev_get_drvdata(&spi->dev); + dev_info(&spi->dev, "starting resume: %d\n", jbt->suspend_mode); + + mdelay(20); + + jbt->suspended = 0; + jbt6k74_enter_power_mode(jbt, jbt->suspend_mode); + + dev_info(&spi->dev, "resumed: %d\n", jbt->suspend_mode); + + return 0; +} +EXPORT_SYMBOL_GPL(jbt6k74_resume); + +#else +#define jbt_suspend NULL +#define jbt6k74_resume NULL +#endif + +static struct spi_driver jbt6k74_driver = { + .driver = { + .name = "jbt6k74", + .owner = THIS_MODULE, + }, + + .probe = jbt_probe, + .remove = __devexit_p(jbt_remove), + .suspend = jbt_suspend, + .resume = jbt6k74_resume, +}; + +static int __init jbt_init(void) +{ + return spi_register_driver(&jbt6k74_driver); +} +module_init(jbt_init); + +static void __exit jbt_exit(void) +{ + spi_unregister_driver(&jbt6k74_driver); +} +module_exit(jbt_exit); + +MODULE_DESCRIPTION("SPI driver for tpo JBT6K74-AS LCM control interface"); +MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/touchscreen/ts_filter.h b/include/linux/input/touchscreen/ts_filter.h new file mode 100644 index 00000000000..632e5fb3f4e --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter.h @@ -0,0 +1,74 @@ +#ifndef __TS_FILTER_H__ +#define __TS_FILTER_H__ + +/* + * Touchscreen filter. + * + * (c) 2008,2009 Andy Green <andy@warmcat.com> + */ + +#include <linux/platform_device.h> + +#define MAX_TS_FILTER_COORDS 3 /* X, Y and Z (pressure). */ + +struct ts_filter; +struct ts_filter_configuration; + +/* Operations that a filter can perform. */ + +struct ts_filter_api { + /* Create the filter - mandatory. */ + struct ts_filter * (*create)( + struct platform_device *pdev, + const struct ts_filter_configuration *config, + int count_coords); + /* Destroy the filter - mandatory. */ + void (*destroy)(struct ts_filter *filter); + /* Clear the filter - optional. */ + void (*clear)(struct ts_filter *filter); + + + /* + * The next three API functions only make sense if all of them are + * set for a filter. If a filter has the next three methods then + * it can propagate coordinates in the chain. + */ + + /* + * Process the filter. + * It returns non-zero if the filter reaches an error. + */ + int (*process)(struct ts_filter *filter, int *coords); + /* + * Is the filter ready to return a point? + * Please do not code side effects in this function. + */ + int (*haspoint)(struct ts_filter *filter); + /* + * Get a point. + * Do not call unless the filter actually has a point to deliver. + */ + void (*getpoint)(struct ts_filter *filter, int *coords); + + /* + * Scale the points - optional. + * A filter could only scale coordinates. + */ + void (*scale)(struct ts_filter *filter, int *coords); +}; + +/* + * Generic filter configuration. Actual configurations have this structure + * as a member. + */ +struct ts_filter_configuration { +}; + +struct ts_filter { + /* Operations for this filter. */ + const struct ts_filter_api *api; + /* Number of coordinates to process. */ + int count_coords; +}; + +#endif diff --git a/include/linux/input/touchscreen/ts_filter_chain.h b/include/linux/input/touchscreen/ts_filter_chain.h new file mode 100644 index 00000000000..a021ba25bd4 --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter_chain.h @@ -0,0 +1,58 @@ +#ifndef __TS_FILTER_CHAIN_H__ +#define __TS_FILTER_CHAIN_H__ + +/* + * Touchscreen filter chains. + * + * (c) 2008,2009 Andy Green <andy@warmcat.com> + */ + +#include <linux/input/touchscreen/ts_filter.h> + +#include <linux/err.h> + +struct ts_filter_chain_configuration { + /* API to use. */ + const struct ts_filter_api *api; + /* Generic filter configuration. Different for each filter. */ + const struct ts_filter_configuration *config; +}; + +struct ts_filter_chain; + +#ifdef CONFIG_TOUCHSCREEN_FILTER + +/* + * Create a filter chain. It will allocate an array of + * null-terminated pointers to filters. On error it will return + * an error you can check with IS_ERR. + */ +extern struct ts_filter_chain *ts_filter_chain_create( + struct platform_device *pdev, + const struct ts_filter_chain_configuration conf[], + int count_coords); + +/* Destroy the chain. */ +extern void ts_filter_chain_destroy(struct ts_filter_chain *c); + +/* Clear the filter chain. */ +extern void ts_filter_chain_clear(struct ts_filter_chain *c); + +/* + * Try to get one point. Returns 0 if no points are available. + * coords will be used as temporal space, thus you supply a point + * using coords but you shouldn't rely on its value on return unless + * it returns a nonzero value that is not -1. + * If one of the filters find an error then this function will + * return -1. + */ +int ts_filter_chain_feed(struct ts_filter_chain *c, int *coords); + +#else /* !CONFIG_TOUCHSCREEN_FILTER */ +#define ts_filter_chain_create(pdev, config, count_coords) (NULL) +#define ts_filter_chain_destroy(c) do { } while (0) +#define ts_filter_chain_clear(c) do { } while (0) +#define ts_filter_chain_feed(c, coords) (1) +#endif + +#endif diff --git a/include/linux/input/touchscreen/ts_filter_group.h b/include/linux/input/touchscreen/ts_filter_group.h new file mode 100644 index 00000000000..2d727f30086 --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter_group.h @@ -0,0 +1,36 @@ +#ifndef __TS_FILTER_GROUP_H__ +#define __TS_FILTER_GROUP_H__ + +#include <linux/input/touchscreen/ts_filter.h> + +/* + * Touchscreen group filter. + * + * Copyright (C) 2008,2009 by Openmoko, Inc. + * Author: Nelson Castillo <arhuaco@freaks-unidos.net> + * + */ + +struct ts_filter_group_configuration { + /* Size of the filter. */ + int length; + /* + * If two points are separated by this distance or less they + * are considered to be members of the same group. + */ + int close_enough; + /* Minimum allowed size for the biggest group in the sample set. */ + int threshold; + /* + * Number of times we try to get a group of points with at least + * threshold points. + */ + int attempts; + + /* Generic filter configuration. */ + struct ts_filter_configuration config; +}; + +extern const struct ts_filter_api ts_filter_group_api; + +#endif diff --git a/include/linux/input/touchscreen/ts_filter_linear.h b/include/linux/input/touchscreen/ts_filter_linear.h new file mode 100644 index 00000000000..6f5e8307456 --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter_linear.h @@ -0,0 +1,31 @@ +#ifndef __TS_FILTER_LINEAR_H__ +#define __TS_FILTER_LINEAR_H__ + +#include <linux/input/touchscreen/ts_filter.h> +#include <linux/kobject.h> + +/* + * Touchscreen linear filter. + * + * Copyright (C) 2008,2009 by Openmoko, Inc. + * Author: Nelson Castillo <arhuaco@freaks-unidos.net> + * + */ + +#define TS_FILTER_LINEAR_NCONSTANTS 7 + +struct ts_filter_linear_configuration { + /* Calibration constants. */ + int constants[TS_FILTER_LINEAR_NCONSTANTS]; + /* First coordinate. */ + int coord0; + /* Second coordinate. */ + int coord1; + + /* Generic filter configuration. */ + struct ts_filter_configuration config; +}; + +extern const struct ts_filter_api ts_filter_linear_api; + +#endif diff --git a/include/linux/input/touchscreen/ts_filter_mean.h b/include/linux/input/touchscreen/ts_filter_mean.h new file mode 100644 index 00000000000..d4870413987 --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter_mean.h @@ -0,0 +1,28 @@ +#ifndef __TS_FILTER_MEAN_H__ +#define __TS_FILTER_MEAN_H__ + +#include <linux/input/touchscreen/ts_filter.h> + +/* + * Touchscreen filter. + * + * mean + * + * (c) 2008,2009 + * Andy Green <andy@warmcat.com> + * Nelson Castillo <arhuaco@freaks-unidos.net> + */ + +/* Configuration for this filter. */ +struct ts_filter_mean_configuration { + /* Number of points for the mean. */ + int length; + + /* Generic filter configuration. */ + struct ts_filter_configuration config; +}; + +/* API functions for the mean filter */ +extern const struct ts_filter_api ts_filter_mean_api; + +#endif /* __TS_FILTER_MEAN_H__ */ diff --git a/include/linux/input/touchscreen/ts_filter_median.h b/include/linux/input/touchscreen/ts_filter_median.h new file mode 100644 index 00000000000..08051619531 --- /dev/null +++ b/include/linux/input/touchscreen/ts_filter_median.h @@ -0,0 +1,32 @@ +#ifndef __TS_FILTER_MEDIAN_H__ +#define __TS_FILTER_MEDIAN_H__ + +#include <linux/input/touchscreen/ts_filter.h> + +/* + * Touchscreen filter. + * + * median + * + * (c) 2008 Andy Green <andy@warmcat.com> + */ + +struct ts_filter_median_configuration { + /* Size of the filter. */ + int extent; + /* Precomputed midpoint. */ + int midpoint; + /* A reference value for us to check if we are going fast or slow. */ + int decimation_threshold; + /* How many points to replace if we're going fast. */ + int decimation_above; + /* How many points to replace if we're going slow. */ + int decimation_below; + + /* Generic configuration. */ + struct ts_filter_configuration config; +}; + +extern const struct ts_filter_api ts_filter_median_api; + +#endif diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h new file mode 100644 index 00000000000..75488c41136 --- /dev/null +++ b/include/linux/jbt6k74.h @@ -0,0 +1,21 @@ +#ifndef __JBT6K74_H__ +#define __JBT6K74_H__ + +#include <linux/spi/spi.h> + +/* + * struct jbt6k74_platform_data - Platform data for jbt6k74 driver + * @probe_completed: Callback to be called when the driver has been + * successfully probed. + * @enable_pixel_clock: Callback to enable or disable the pixelclock of the + * gpu. + * @gpio_reset: Reset gpio pin number. + */ +struct jbt6k74_platform_data { + void (*probe_completed)(struct device *dev); + void (*enable_pixel_clock)(struct device *dev, int enable); + + int gpio_reset; +}; + +#endif diff --git a/include/linux/mfd/pcf50606/mbc.h b/include/linux/mfd/pcf50606/mbc.h index b793d703a64..23aeb9e3138 100644 --- a/include/linux/mfd/pcf50606/mbc.h +++ b/include/linux/mfd/pcf50606/mbc.h @@ -46,7 +46,7 @@ #define PCF50606_MBC_CHARGER_ONLINE 0x01 #define PCF50606_MBC_CHARGER_ACTIVE 0x02 -void pcf50606_charge_fast(struct pcf50606 *pcf, int on); +int pcf50606_charge_fast(struct pcf50606 *pcf, int on); #endif diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 251fc1cd500..9b1c0985480 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -642,4 +642,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime); /* ad hoc AC97 device driver access */ extern struct bus_type ac97_bus_type; +/* AC97 platform_data adding function */ +static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data) +{ + ac97->dev.platform_data = data; +} + #endif /* __SOUND_AC97_CODEC_H */ diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 352d7eee9b6..25d62ac53fc 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -27,8 +27,8 @@ struct snd_pcm_substream; #define SND_SOC_DAIFMT_I2S 0 /* I2S mode */ #define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */ #define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */ -#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */ -#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */ +#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */ +#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */ #define SND_SOC_DAIFMT_AC97 5 /* AC97 */ /* left and right justified also known as MSB and LSB respectively */ @@ -38,7 +38,7 @@ struct snd_pcm_substream; /* * DAI Clock gating. * - * DAI bit clocks can be be gated (disabled) when not the DAI is not + * DAI bit clocks can be be gated (disabled) when the DAI is not * sending or receiving PCM data in a frame. This can be used to save power. */ #define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */ @@ -51,21 +51,21 @@ struct snd_pcm_substream; * format. */ #define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ -#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */ -#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */ -#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */ +#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal BCLK + inv FRM */ +#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert BCLK + nor FRM */ +#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert BCLK + FRM */ /* * DAI hardware clock masters. * * This is wrt the codec, the inverse is true for the interface - * i.e. if the codec is clk and frm master then the interface is + * i.e. if the codec is clk and FRM master then the interface is * clk and frame slave. */ -#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */ -#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */ +#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & FRM master */ +#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & FRM master */ #define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */ -#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */ +#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & FRM slave */ #define SND_SOC_DAIFMT_FORMAT_MASK 0x000f #define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0 @@ -116,12 +116,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); /* * Digital Audio Interface. * - * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97 - * operations an capabilities. Codec and platfom drivers will register a this + * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97 + * operations and capabilities. Codec and platform drivers will register this * structure for every DAI they have. * * This structure covers the clocking, formating and ALSA operations for each - * interface a + * interface. */ struct snd_soc_dai_ops { /* @@ -179,6 +179,7 @@ struct snd_soc_dai { int ac97_control; struct device *dev; + void *ac97_pdata; /* platform_data for the ac97 codec */ /* DAI callbacks */ int (*probe)(struct platform_device *pdev, diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ec8a45f9a06..35814ced2d2 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -279,6 +279,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, int event); +void snd_soc_dapm_shutdown(struct snd_soc_device *socdev); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/include/sound/soc.h b/include/sound/soc.h index cf6111d72b1..756fb59772d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -135,6 +135,28 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } +#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ + xhandler_get, xhandler_put, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = shift_left, .rshift = shift_right, \ + .max = xmax, .invert = xinvert} } +#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_2r, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ + .max = xmax, .invert = xinvert} } #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_bool_ext, \ @@ -191,6 +213,12 @@ int snd_soc_register_platform(struct snd_soc_platform *platform); void snd_soc_unregister_platform(struct snd_soc_platform *platform); int snd_soc_register_codec(struct snd_soc_codec *codec); void snd_soc_unregister_codec(struct snd_soc_codec *codec); +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); + +#ifdef CONFIG_PM +int snd_soc_suspend_device(struct device *dev); +int snd_soc_resume_device(struct device *dev); +#endif /* pcm <-> DAI connect */ void snd_soc_free_pcms(struct snd_soc_device *socdev); @@ -216,9 +244,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, /* codec register bit access */ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, - unsigned short mask, unsigned short value); + unsigned int mask, unsigned int value); int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, - unsigned short mask, unsigned short value); + unsigned int mask, unsigned int value); int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, struct snd_ac97_bus_ops *ops, int num); @@ -356,6 +384,7 @@ struct snd_soc_codec { int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*display_register)(struct snd_soc_codec *, char *, size_t, unsigned int); + int (*volatile_register)(unsigned int); hw_write_t hw_write; hw_read_t hw_read; void *reg_cache; @@ -369,8 +398,6 @@ struct snd_soc_codec { enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; - struct list_head up_list; - struct list_head down_list; /* codec DAI's */ struct snd_soc_dai *dai; diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h new file mode 100644 index 00000000000..381319c7000 --- /dev/null +++ b/include/sound/uda1380.h @@ -0,0 +1,22 @@ +/* + * UDA1380 ALSA SoC Codec driver + * + * Copyright 2009 Philipp Zabel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UDA1380_H +#define __UDA1380_H + +struct uda1380_platform_data { + int gpio_power; + int gpio_reset; + int dac_clk; +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 +}; + +#endif /* __UDA1380_H */ diff --git a/include/sound/wm8993.h b/include/sound/wm8993.h new file mode 100644 index 00000000000..9c661f2f8cd --- /dev/null +++ b/include/sound/wm8993.h @@ -0,0 +1,44 @@ +/* + * linux/sound/wm8993.h -- Platform data for WM8993 + * + * Copyright 2009 Wolfson Microelectronics. PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_WM8993_H +#define __LINUX_SND_WM8993_H + +/* Note that EQ1 only contains the enable/disable bit so will be + ignored but is included for simplicity. + */ +struct wm8993_retune_mobile_setting { + const char *name; + unsigned int rate; + u16 config[24]; +}; + +struct wm8993_platform_data { + struct wm8993_retune_mobile_setting *retune_configs; + int num_retune_configs; + + /* LINEOUT can be differential or single ended */ + unsigned int lineout1_diff:1; + unsigned int lineout2_diff:1; + + /* Common mode feedback */ + unsigned int lineout1fb:1; + unsigned int lineout2fb:1; + + /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ + unsigned int micbias1_lvl:1; + unsigned int micbias2_lvl:1; + + /* Jack detect threashold levels, see datasheet for values */ + unsigned int jd_scthr:2; + unsigned int jd_thr:2; +}; + +#endif diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 173a239a541..130b12118d4 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -56,30 +56,14 @@ #define MCLK_RATE 12000000 -static struct clk *mclk; - -static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - int ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, - MCLK_RATE, SND_SOC_CLOCK_IN); - if (ret < 0) { - clk_disable(mclk); - return ret; - } - - return 0; -} - -static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); +/* + * As shipped the board does not have inputs. However, it is relatively + * straightforward to modify the board to hook them up so support is left + * in the driver. + */ +#undef ENABLE_MIC_INPUT - dev_dbg(rtd->socdev->dev, "shutdown"); -} +static struct clk *mclk; static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct atmel_ssc_info *ssc_p = cpu_dai->private_data; - struct ssc_device *ssc = ssc_p->ssc; int ret; - unsigned int rate; - int cmr_div, period; - - if (ssc == NULL) { - printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n"); - return -EINVAL; - } - /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* - * The SSC clock dividers depend on the sample rate. The CMR.DIV - * field divides the system master clock MCK to drive the SSC TK - * signal which provides the codec BCLK. The TCMR.PERIOD and - * RCMR.PERIOD fields further divide the BCLK signal to drive - * the SSC TF and RF signals which provide the codec DACLRC and - * ADCLRC clocks. - * - * The dividers were determined through trial and error, where a - * CMR.DIV value is chosen such that the resulting BCLK value is - * divisible, or almost divisible, by (2 * sample rate), and then - * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. - */ - rate = params_rate(params); - - switch (rate) { - case 8000: - cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */ - period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */ - break; - case 11025: - cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */ - period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */ - break; - case 16000: - cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */ - period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */ - break; - case 22050: - cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */ - period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */ - break; - case 32000: - cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */ - period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */ - break; - case 44100: - cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */ - period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */ - break; - case 48000: - cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */ - period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */ - break; - case 88200: - cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */ - period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */ - break; - case 96000: - cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */ - period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */ - break; - default: - printk(KERN_WARNING "unsupported rate %d" - " on at91sam9g20ek board\n", rate); - return -EINVAL; - } - - /* set the MCK divider for BCLK */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div); - if (ret < 0) - return ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* set the BCLK divider for DACLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - ATMEL_SSC_TCMR_PERIOD, period); - } else { - /* set the BCLK divider for ADCLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - ATMEL_SSC_RCMR_PERIOD, period); - } + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; @@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops at91sam9g20ek_ops = { - .startup = at91sam9g20ek_startup, .hw_params = at91sam9g20ek_hw_params, - .shutdown = at91sam9g20ek_shutdown, }; static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, @@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = { */ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) { + struct snd_soc_dai *codec_dai = &codec->dai[0]; + int ret; + printk(KERN_DEBUG "at91sam9g20ek_wm8731 " ": at91sam9g20ek_wm8731_init() called\n"); + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, + MCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); + return ret; + } + /* Add specific widgets */ snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); @@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) snd_soc_dapm_nc_pin(codec, "RLINEIN"); snd_soc_dapm_nc_pin(codec, "LLINEIN"); - /* always connected */ +#ifdef ENABLE_MIC_INPUT snd_soc_dapm_enable_pin(codec, "Int Mic"); +#else + snd_soc_dapm_nc_pin(codec, "Int Mic"); +#endif + + /* always connected */ snd_soc_dapm_enable_pin(codec, "Ext Spk"); snd_soc_dapm_sync(codec); diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 811596f4c09..8a4de4de30f 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -7,6 +7,15 @@ config SND_BF5XX_I2S mode (supports single stereo In/Out). You will also need to select the audio interfaces to support below. +config SND_BF5XX_TDM + tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" + depends on (BLACKFIN && SND_SOC) + help + Say Y or M if you want to add support for codecs attached to + the Blackfin SPORT (synchronous serial ports) interface in TDM + mode. + You will also need to select the audio interfaces to support below. + config SND_BF5XX_SOC_SSM2602 tristate "SoC SSM2602 Audio support for BF52x ezkit" depends on SND_BF5XX_I2S @@ -69,6 +78,10 @@ config SND_BF5XX_SOC_I2S tristate select SND_BF5XX_SOC_SPORT +config SND_BF5XX_SOC_TDM + tristate + select SND_BF5XX_SOC_SPORT + config SND_BF5XX_SOC_AC97 tristate select AC97_BUS @@ -83,9 +96,17 @@ config SND_BF5XX_SOC_AD1980 help Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. +config SND_BF5XX_SOC_AD1938 + tristate "SoC AD1938 Audio support for Blackfin" + depends on SND_BF5XX_TDM + select SND_BF5XX_SOC_TDM + select SND_SOC_AD1938 + help + Say Y if you want to add support for AD1938 codec on Blackfin. + config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" - depends on (SND_BF5XX_I2S || SND_BF5XX_AC97) + depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM) range 0 3 if BF54x range 0 1 if !BF54x default 0 diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 97bb37a6359..f4d760741fa 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -1,21 +1,27 @@ # Blackfin Platform Support snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o +snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o snd-soc-bf5xx-sport-objs := bf5xx-sport.o snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o +snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o +obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o +obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o # Blackfin Machine Support snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o +snd-ad1938-objs := bf5xx-ad1938.o obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o +obj-$(CONFIG_SND_BF5XX_SOC_AD1938) += snd-ad1938.o diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index b1ed423fabd..2758b9017a7 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -277,28 +277,24 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) if (!dai->active) return 0; - ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); + ret = sport_set_multichannel(sport, 16, 0x1F, 1); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); + ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1)); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); + ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1)); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - if (dai->capture.active) - sport_rx_start(sport); - if (dai->playback.active) - sport_tx_start(sport); return 0; } diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c new file mode 100644 index 00000000000..08269e91810 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad1938.c @@ -0,0 +1,142 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad1938.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: Thur June 4 2009 + * Description: Board driver for ad1938 sound chip + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad1938.h" +#include "bf5xx-sport.h" + +#include "bf5xx-tdm-pcm.h" +#include "bf5xx-tdm.h" + +static struct snd_soc_card bf5xx_ad1938; + +static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + + cpu_dai->private_data = sport_handle; + return 0; +} + +static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + int ret = 0; + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI slots, 8 channels, all channels are enabled */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops bf5xx_ad1938_ops = { + .startup = bf5xx_ad1938_startup, + .hw_params = bf5xx_ad1938_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ad1938_dai = { + .name = "ad1938", + .stream_name = "AD1938", + .cpu_dai = &bf5xx_tdm_dai, + .codec_dai = &ad1938_dai, + .ops = &bf5xx_ad1938_ops, +}; + +static struct snd_soc_card bf5xx_ad1938 = { + .name = "bf5xx_ad1938", + .platform = &bf5xx_tdm_soc_platform, + .dai_link = &bf5xx_ad1938_dai, + .num_links = 1, +}; + +static struct snd_soc_device bf5xx_ad1938_snd_devdata = { + .card = &bf5xx_ad1938, + .codec_dev = &soc_codec_dev_ad1938, +}; + +static struct platform_device *bfxx_ad1938_snd_device; + +static int __init bf5xx_ad1938_init(void) +{ + int ret; + + bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1); + if (!bfxx_ad1938_snd_device) + return -ENOMEM; + + platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata); + bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev; + ret = platform_device_add(bfxx_ad1938_snd_device); + + if (ret) + platform_device_put(bfxx_ad1938_snd_device); + + return ret; +} + +static void __exit bf5xx_ad1938_exit(void) +{ + platform_device_unregister(bfxx_ad1938_snd_device); +} + +module_init(bf5xx_ad1938_init); +module_exit(bf5xx_ad1938_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ALSA SoC AD1938 board driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c index edfbdc024e6..9825b71d0e2 100644 --- a/sound/soc/blackfin/bf5xx-ad73311.c +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = { .codec_dev = &soc_codec_dev_ad73311, }; -static struct platform_device *bf52x_ad73311_snd_device; +static struct platform_device *bf5xx_ad73311_snd_device; static int __init bf5xx_ad73311_init(void) { int ret; pr_debug("%s enter\n", __func__); - bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); - if (!bf52x_ad73311_snd_device) + bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf5xx_ad73311_snd_device) return -ENOMEM; - platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); - bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; - ret = platform_device_add(bf52x_ad73311_snd_device); + platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); + bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev; + ret = platform_device_add(bf5xx_ad73311_snd_device); if (ret) - platform_device_put(bf52x_ad73311_snd_device); + platform_device_put(bf5xx_ad73311_snd_device); return ret; } @@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void) static void __exit bf5xx_ad73311_exit(void) { pr_debug("%s enter\n", __func__); - platform_device_unregister(bf52x_ad73311_snd_device); + platform_device_unregister(bf5xx_ad73311_snd_device); } module_init(bf5xx_ad73311_init); diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index af06904bab0..876abade27e 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -259,22 +259,18 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) if (!dai->active) return 0; - ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0); + ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0); + ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - if (dai->capture.active) - sport_rx_start(sport); - if (dai->playback.active) - sport_tx_start(sport); return 0; } diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index bc0cdded711..3a00fa4dbe6 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = { .codec_data = &bf5xx_ssm2602_setup, }; -static struct platform_device *bf52x_ssm2602_snd_device; +static struct platform_device *bf5xx_ssm2602_snd_device; static int __init bf5xx_ssm2602_init(void) { int ret; pr_debug("%s enter\n", __func__); - bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); - if (!bf52x_ssm2602_snd_device) + bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf5xx_ssm2602_snd_device) return -ENOMEM; - platform_set_drvdata(bf52x_ssm2602_snd_device, + platform_set_drvdata(bf5xx_ssm2602_snd_device, &bf5xx_ssm2602_snd_devdata); - bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev; - ret = platform_device_add(bf52x_ssm2602_snd_device); + bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev; + ret = platform_device_add(bf5xx_ssm2602_snd_device); if (ret) - platform_device_put(bf52x_ssm2602_snd_device); + platform_device_put(bf5xx_ssm2602_snd_device); return ret; } @@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void) static void __exit bf5xx_ssm2602_exit(void) { pr_debug("%s enter\n", __func__); - platform_device_unregister(bf52x_ssm2602_snd_device); + platform_device_unregister(bf5xx_ssm2602_snd_device); } module_init(bf5xx_ssm2602_init); diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c new file mode 100644 index 00000000000..ccb5e823bd1 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -0,0 +1,330 @@ +/* + * File: sound/soc/blackfin/bf5xx-tdm-pcm.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: Tue June 06 2009 + * Description: DMA driver for tdm codec + * + * Modified: + * Copyright 2009 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/dma.h> + +#include "bf5xx-tdm-pcm.h" +#include "bf5xx-tdm.h" +#include "bf5xx-sport.h" + +#define PCM_BUFFER_MAX 0x10000 +#define FRAGMENT_SIZE_MIN (4*1024) +#define FRAGMENTS_MIN 2 +#define FRAGMENTS_MAX 32 + +static void bf5xx_dma_irq(void *data) +{ + struct snd_pcm_substream *pcm = data; + snd_pcm_period_elapsed(pcm); +} + +static const struct snd_pcm_hardware bf5xx_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 8, + .buffer_bytes_max = PCM_BUFFER_MAX, + .period_bytes_min = FRAGMENT_SIZE_MIN, + .period_bytes_max = PCM_BUFFER_MAX/2, + .periods_min = FRAGMENTS_MIN, + .periods_max = FRAGMENTS_MAX, +}; + +static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + snd_pcm_lib_malloc_pages(substream, size * 4); + + return 0; +} + +static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size); + + fragsize_bytes /= runtime->channels; + /* inflate the fragsize to match the dma width of SPORT */ + fragsize_bytes *= 8; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport_set_tx_callback(sport, bf5xx_dma_irq, substream); + sport_config_tx_dma(sport, runtime->dma_area, + runtime->periods, fragsize_bytes); + } else { + sport_set_rx_callback(sport, bf5xx_dma_irq, substream); + sport_config_rx_dma(sport, runtime->dma_area, + runtime->periods, fragsize_bytes); + } + + return 0; +} + +static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_tx_start(sport); + else + sport_rx_start(sport); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_tx_stop(sport); + else + sport_rx_stop(sport); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + unsigned int diff; + snd_pcm_uframes_t frames; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + diff = sport_curr_offset_tx(sport); + frames = diff / (8*4); /* 32 bytes per frame */ + } else { + diff = sport_curr_offset_rx(sport); + frames = diff / (8*4); + } + return frames; +} + +static int bf5xx_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + + if (sport_handle != NULL) + runtime->private_data = sport_handle; + else { + pr_err("sport_handle is NULL\n"); + ret = -ENODEV; + } +out: + return ret; +} + +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) +{ + unsigned int *src; + unsigned int *dst; + int i; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = (unsigned int *)substream->runtime->dma_area; + + dst += pos * 8; + while (count--) { + for (i = 0; i < substream->runtime->channels; i++) + *(dst + i) = *src++; + dst += 8; + } + } else { + src = (unsigned int *)substream->runtime->dma_area; + dst = buf; + + src += pos * 8; + while (count--) { + for (i = 0; i < substream->runtime->channels; i++) + *dst++ = *(src+i); + src += 8; + } + } + + return 0; +} + +static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + unsigned char *buf = substream->runtime->dma_area; + buf += pos * 8 * 4; + memset(buf, '\0', count * 8 * 4); + + return 0; +} + + +struct snd_pcm_ops bf5xx_pcm_tdm_ops = { + .open = bf5xx_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = bf5xx_pcm_hw_params, + .hw_free = bf5xx_pcm_hw_free, + .prepare = bf5xx_pcm_prepare, + .trigger = bf5xx_pcm_trigger, + .pointer = bf5xx_pcm_pointer, + .copy = bf5xx_pcm_copy, + .silence = bf5xx_pcm_silence, +}; + +static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(pcm->card->dev, size * 4, + &buf->addr, GFP_KERNEL); + if (!buf->area) { + pr_err("Failed to allocate dma memory \ + Please increase uncached DMA memory region\n"); + return -ENOMEM; + } + buf->bytes = size; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_handle->tx_buf = buf->area; + else + sport_handle->rx_buf = buf->area; + + return 0; +} + +static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_coherent(NULL, buf->bytes, buf->area, 0); + buf->area = NULL; + } + if (sport_handle) + sport_done(sport_handle); +} + +static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); + +static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &bf5xx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + if (dai->playback.channels_min) { + ret = bf5xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = bf5xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } +out: + return ret; +} + +struct snd_soc_platform bf5xx_tdm_soc_platform = { + .name = "bf5xx-audio", + .pcm_ops = &bf5xx_pcm_tdm_ops, + .pcm_new = bf5xx_pcm_tdm_new, + .pcm_free = bf5xx_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform); + +static int __init bfin_pcm_tdm_init(void) +{ + return snd_soc_register_platform(&bf5xx_tdm_soc_platform); +} +module_init(bfin_pcm_tdm_init); + +static void __exit bfin_pcm_tdm_exit(void) +{ + snd_soc_unregister_platform(&bf5xx_tdm_soc_platform); +} +module_exit(bfin_pcm_tdm_exit); + +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h new file mode 100644 index 00000000000..ddc5047df88 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.h @@ -0,0 +1,21 @@ +/* + * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin + * + * Copyright 2009 Analog Device Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_TDM_PCM_H +#define _BF5XX_TDM_PCM_H + +struct bf5xx_pcm_dma_params { + char *name; /* stream identifier */ +}; + +/* platform data */ +extern struct snd_soc_platform bf5xx_tdm_soc_platform; + +#endif diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c new file mode 100644 index 00000000000..3096badf09a --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -0,0 +1,343 @@ +/* + * File: sound/soc/blackfin/bf5xx-tdm.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: Thurs June 04 2009 + * Description: Blackfin I2S(TDM) CPU DAI driver + * Even though TDM mode can be as part of I2S DAI, but there + * are so much difference in configuration and data flow, + * it's very ugly to integrate I2S and TDM into a module + * + * Modified: + * Copyright 2009 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <asm/irq.h> +#include <asm/portmux.h> +#include <linux/mutex.h> +#include <linux/gpio.h> + +#include "bf5xx-sport.h" +#include "bf5xx-tdm.h" + +struct bf5xx_tdm_port { + u16 tcr1; + u16 rcr1; + u16 tcr2; + u16 rcr2; + int configured; +}; + +static struct bf5xx_tdm_port bf5xx_tdm; +static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; + +static struct sport_param sport_params[2] = { + { + .dma_rx_chan = CH_SPORT0_RX, + .dma_tx_chan = CH_SPORT0_TX, + .err_irq = IRQ_SPORT0_ERROR, + .regs = (struct sport_register *)SPORT0_TCR1, + }, + { + .dma_rx_chan = CH_SPORT1_RX, + .dma_tx_chan = CH_SPORT1_TX, + .err_irq = IRQ_SPORT1_ERROR, + .regs = (struct sport_register *)SPORT1_TCR1, + } +}; + +/* + * Setting the TFS pin selector for SPORT 0 based on whether the selected + * port id F or G. If the port is F then no conflict should exist for the + * TFS. When Port G is selected and EMAC then there is a conflict between + * the PHY interrupt line and TFS. Current settings prevent the conflict + * by ignoring the TFS pin when Port G is selected. This allows both + * ssm2602 using Port G and EMAC concurrently. + */ +#ifdef CONFIG_BF527_SPORT0_PORTF +#define LOCAL_SPORT0_TFS (P_SPORT0_TFS) +#else +#define LOCAL_SPORT0_TFS (0) +#endif + +static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, + P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, + {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, + P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; + +static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + int ret = 0; + + /* interface format:support TDM,slave mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + break; + default: + printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); + ret = -EINVAL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); + ret = -EINVAL; + break; + } + + return ret; +} + +static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + + bf5xx_tdm.tcr2 &= ~0x1f; + bf5xx_tdm.rcr2 &= ~0x1f; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bf5xx_tdm.tcr2 |= 31; + bf5xx_tdm.rcr2 |= 31; + sport_handle->wdsize = 4; + break; + /* at present, we only support 32bit transfer */ + default: + pr_err("not supported PCM format yet\n"); + return -EINVAL; + break; + } + + if (!bf5xx_tdm.configured) { + /* + * TX and RX are not independent,they are enabled at the + * same time, even if only one side is running. So, we + * need to configure both of them at the time when the first + * stream is opened. + * + * CPU DAI:slave mode. + */ + ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1, + bf5xx_tdm.rcr2, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + return -EBUSY; + } + + ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1, + bf5xx_tdm.tcr2, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + return -EBUSY; + } + + bf5xx_tdm.configured = 1; + } + + return 0; +} + +static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* No active stream, SPORT is allowed to be configured again. */ + if (!dai->active) + bf5xx_tdm.configured = 0; +} + +#ifdef CONFIG_PM +static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) +{ + struct sport_device *sport = + (struct sport_device *)dai->private_data; + + if (!dai->active) + return 0; + if (dai->capture.active) + sport_rx_stop(sport); + if (dai->playback.active) + sport_tx_stop(sport); + return 0; +} + +static int bf5xx_tdm_resume(struct snd_soc_dai *dai) +{ + int ret; + struct sport_device *sport = + (struct sport_device *)dai->private_data; + + if (!dai->active) + return 0; + + ret = sport_set_multichannel(sport, 8, 0xFF, 1); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + return 0; +} + +#else +#define bf5xx_tdm_suspend NULL +#define bf5xx_tdm_resume NULL +#endif + +static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { + .hw_params = bf5xx_tdm_hw_params, + .set_fmt = bf5xx_tdm_set_dai_fmt, + .shutdown = bf5xx_tdm_shutdown, +}; + +struct snd_soc_dai bf5xx_tdm_dai = { + .name = "bf5xx-tdm", + .id = 0, + .suspend = bf5xx_tdm_suspend, + .resume = bf5xx_tdm_resume, + .playback = { + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE,}, + .ops = &bf5xx_tdm_dai_ops, +}; +EXPORT_SYMBOL_GPL(bf5xx_tdm_dai); + +static int __devinit bfin_tdm_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { + pr_err("Requesting Peripherals failed\n"); + return -EFAULT; + } + + /* request DMA for SPORT */ + sport_handle = sport_init(&sport_params[sport_num], 4, \ + 8 * sizeof(u32), NULL); + if (!sport_handle) { + peripheral_free_list(&sport_req[sport_num][0]); + return -ENODEV; + } + + /* SPORT works in TDM mode */ + ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = snd_soc_register_dai(&bf5xx_tdm_dai); + if (ret) { + pr_err("Failed to register DAI: %d\n", ret); + goto sport_config_err; + } + return 0; + +sport_config_err: + peripheral_free_list(&sport_req[sport_num][0]); + return ret; +} + +static int __devexit bfin_tdm_remove(struct platform_device *pdev) +{ + peripheral_free_list(&sport_req[sport_num][0]); + snd_soc_unregister_dai(&bf5xx_tdm_dai); + + return 0; +} + +static struct platform_driver bfin_tdm_driver = { + .probe = bfin_tdm_probe, + .remove = __devexit_p(bfin_tdm_remove), + .driver = { + .name = "bfin-tdm", + .owner = THIS_MODULE, + }, +}; + +static int __init bfin_tdm_init(void) +{ + return platform_driver_register(&bfin_tdm_driver); +} +module_init(bfin_tdm_init); + +static void __exit bfin_tdm_exit(void) +{ + platform_driver_unregister(&bfin_tdm_driver); +} +module_exit(bfin_tdm_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("TDM driver for ADI Blackfin"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h new file mode 100644 index 00000000000..618ec3d90cd --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm.h @@ -0,0 +1,14 @@ +/* + * sound/soc/blackfin/bf5xx-tdm.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_TDM_H +#define _BF5XX_TDM_H + +extern struct snd_soc_dai bf5xx_tdm_dai; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index bbc97fd7664..fdfc5024e40 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -12,11 +12,13 @@ config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" select SND_SOC_L3 select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS + select SND_SOC_AD1938 if SPI_MASTER select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_AD73311 if I2C select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 select SND_SOC_SPDIF select SND_SOC_SSM2602 if I2C @@ -30,6 +32,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8400 if MFD_WM8400 select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8523 if I2C select SND_SOC_WM8580 if I2C select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI @@ -39,9 +42,11 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8903 if I2C select SND_SOC_WM8940 if I2C select SND_SOC_WM8960 if I2C + select SND_SOC_WM8961 if I2C select SND_SOC_WM8971 if I2C select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8990 if I2C + select SND_SOC_WM8993 if I2C select SND_SOC_WM9081 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS select SND_SOC_WM9712 if SND_SOC_AC97_BUS @@ -62,6 +67,9 @@ config SND_SOC_AC97_CODEC tristate select SND_AC97_CODEC +config SND_SOC_AD1938 + tristate + config SND_SOC_AD1980 tristate @@ -86,6 +94,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CX20442 + tristate + config SND_SOC_L3 tristate @@ -129,6 +140,9 @@ config SND_SOC_WM8400 config SND_SOC_WM8510 tristate +config SND_SOC_WM8523 + tristate + config SND_SOC_WM8580 tristate @@ -156,6 +170,9 @@ config SND_SOC_WM8940 config SND_SOC_WM8960 tristate +config SND_SOC_WM8961 + tristate + config SND_SOC_WM8971 tristate @@ -165,6 +182,9 @@ config SND_SOC_WM8988 config SND_SOC_WM8990 tristate +config SND_SOC_WM8993 + tristate + config SND_SOC_WM9081 tristate @@ -176,3 +196,7 @@ config SND_SOC_WM9712 config SND_SOC_WM9713 tristate + +# Amp +config SND_SOC_MAX9877 + tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8b7530546f4..1131d6dc761 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,9 +1,11 @@ snd-soc-ac97-objs := ac97.o +snd-soc-ad1938-objs := ad1938.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cx20442-objs := cx20442.o snd-soc-l3-objs := l3.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-spdif-objs := spdif_transciever.o @@ -18,6 +20,7 @@ snd-soc-uda1380-objs := uda1380.o snd-soc-wm8350-objs := wm8350.o snd-soc-wm8400-objs := wm8400.o snd-soc-wm8510-objs := wm8510.o +snd-soc-wm8523-objs := wm8523.o snd-soc-wm8580-objs := wm8580.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o @@ -27,20 +30,27 @@ snd-soc-wm8900-objs := wm8900.o snd-soc-wm8903-objs := wm8903.o snd-soc-wm8940-objs := wm8940.o snd-soc-wm8960-objs := wm8960.o +snd-soc-wm8961-objs := wm8961.o snd-soc-wm8971-objs := wm8971.o snd-soc-wm8988-objs := wm8988.o snd-soc-wm8990-objs := wm8990.o +snd-soc-wm8993-objs := wm8993.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o +# Amp +snd-soc-max9877-objs := max9877.o + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o +obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o @@ -55,6 +65,7 @@ obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o +obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o @@ -65,9 +76,14 @@ obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o +obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o +obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o + +# Amp +obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c new file mode 100644 index 00000000000..aafda743fc8 --- /dev/null +++ b/sound/soc/codecs/ad1938.c @@ -0,0 +1,668 @@ +/* + * File: sound/soc/codecs/ad1938.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: June 04 2009 + * Description: Driver for AD1938 sound chip + * + * Modified: + * Copyright 2009 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/soc-dapm.h> +#include <linux/spi/spi.h> +#include "ad1938.h" + +/* codec private data */ +struct ad1938_priv { + struct snd_soc_codec codec; + u8 reg_cache[AD1938_NUM_REGS]; +}; + +static struct snd_soc_codec *ad1938_codec; +struct snd_soc_codec_device soc_codec_dev_ad1938; +static int ad1938_register(struct ad1938_priv *ad1938); +static void ad1938_unregister(struct ad1938_priv *ad1938); + +/* + * AD1938 volume/mute/de-emphasis etc. controls + */ +static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; + +static const struct soc_enum ad1938_deemp_enum = + SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp); + +static const struct snd_kcontrol_new ad1938_snd_controls[] = { + /* DAC volume control */ + SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL, + AD1938_DAC_R1_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL, + AD1938_DAC_R2_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL, + AD1938_DAC_R3_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL, + AD1938_DAC_R4_VOL, 0, 0xFF, 1), + + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE, + AD1938_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE, + AD1938_ADCR2_MUTE, 1, 1), + + /* DAC switch control */ + SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE, + AD1938_DACR1_MUTE, 1, 1), + SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE, + AD1938_DACR2_MUTE, 1, 1), + SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE, + AD1938_DACR3_MUTE, 1, 1), + SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE, + AD1938_DACR4_MUTE, 1, 1), + + /* ADC high-pass filter */ + SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0, + AD1938_ADC_HIGHPASS_FILTER, 1, 0), + + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum), +}; + +static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0), +}; + +static const struct snd_soc_dapm_route audio_paths[] = { + { "DAC", NULL, "ADC_PWR" }, + { "ADC", NULL, "ADC_PWR" }, +}; + +/* + * DAI ops entries + */ + +static int ad1938_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int reg; + + reg = codec->read(codec, AD1938_DAC_CTRL2); + reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg & + (~AD1938_DAC_MASTER_MUTE); + codec->write(codec, AD1938_DAC_CTRL2, reg); + + return 0; +} + +static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd) +{ + int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0); + reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg | + AD1938_PLL_POWERDOWN; + codec->write(codec, AD1938_PLL_CLK_CTRL0, reg); + + return 0; +} + +static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int mask, int slots) +{ + struct snd_soc_codec *codec = dai->codec; + int dac_reg = codec->read(codec, AD1938_DAC_CTRL1); + int adc_reg = codec->read(codec, AD1938_ADC_CTRL2); + + dac_reg &= ~AD1938_DAC_CHAN_MASK; + adc_reg &= ~AD1938_ADC_CHAN_MASK; + + switch (slots) { + case 2: + dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT; + adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT; + break; + case 4: + dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT; + adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT; + break; + case 8: + dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT; + adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT; + break; + case 16: + dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT; + adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT; + break; + default: + return -EINVAL; + } + + codec->write(codec, AD1938_DAC_CTRL1, dac_reg); + codec->write(codec, AD1938_ADC_CTRL2, adc_reg); + + return 0; +} + +static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int adc_reg, dac_reg; + + adc_reg = codec->read(codec, AD1938_ADC_CTRL2); + dac_reg = codec->read(codec, AD1938_DAC_CTRL1); + + /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S + * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adc_reg &= ~AD1938_ADC_SERFMT_MASK; + adc_reg |= AD1938_ADC_SERFMT_TDM; + break; + case SND_SOC_DAIFMT_DSP_A: + adc_reg &= ~AD1938_ADC_SERFMT_MASK; + adc_reg |= AD1938_ADC_SERFMT_AUX; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + adc_reg &= ~AD1938_ADC_LEFT_HIGH; + adc_reg &= ~AD1938_ADC_BCLK_INV; + dac_reg &= ~AD1938_DAC_LEFT_HIGH; + dac_reg &= ~AD1938_DAC_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ + adc_reg |= AD1938_ADC_LEFT_HIGH; + adc_reg &= ~AD1938_ADC_BCLK_INV; + dac_reg |= AD1938_DAC_LEFT_HIGH; + dac_reg &= ~AD1938_DAC_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ + adc_reg &= ~AD1938_ADC_LEFT_HIGH; + adc_reg |= AD1938_ADC_BCLK_INV; + dac_reg &= ~AD1938_DAC_LEFT_HIGH; + dac_reg |= AD1938_DAC_BCLK_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ + adc_reg |= AD1938_ADC_LEFT_HIGH; + adc_reg |= AD1938_ADC_BCLK_INV; + dac_reg |= AD1938_DAC_LEFT_HIGH; + dac_reg |= AD1938_DAC_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ + adc_reg |= AD1938_ADC_LCR_MASTER; + adc_reg |= AD1938_ADC_BCLK_MASTER; + dac_reg |= AD1938_DAC_LCR_MASTER; + dac_reg |= AD1938_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */ + adc_reg |= AD1938_ADC_LCR_MASTER; + adc_reg &= ~AD1938_ADC_BCLK_MASTER; + dac_reg |= AD1938_DAC_LCR_MASTER; + dac_reg &= ~AD1938_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ + adc_reg &= ~AD1938_ADC_LCR_MASTER; + adc_reg |= AD1938_ADC_BCLK_MASTER; + dac_reg &= ~AD1938_DAC_LCR_MASTER; + dac_reg |= AD1938_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ + adc_reg &= ~AD1938_ADC_LCR_MASTER; + adc_reg &= ~AD1938_ADC_BCLK_MASTER; + dac_reg &= ~AD1938_DAC_LCR_MASTER; + dac_reg &= ~AD1938_DAC_BCLK_MASTER; + break; + default: + return -EINVAL; + } + + codec->write(codec, AD1938_ADC_CTRL2, adc_reg); + codec->write(codec, AD1938_DAC_CTRL1, dac_reg); + + return 0; +} + +static int ad1938_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int word_len = 0, reg = 0; + + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + word_len = 3; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + word_len = 1; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + word_len = 0; + break; + } + + reg = codec->read(codec, AD1938_DAC_CTRL2); + reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len; + codec->write(codec, AD1938_DAC_CTRL2, reg); + + reg = codec->read(codec, AD1938_ADC_CTRL1); + reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len; + codec->write(codec, AD1938_ADC_CTRL1, reg); + + return 0; +} + +static int ad1938_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + ad1938_pll_powerctrl(codec, 1); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + ad1938_pll_powerctrl(codec, 0); + break; + } + codec->bias_level = level; + return 0; +} + +/* + * interface to read/write ad1938 register + */ + +#define AD1938_SPI_ADDR 0x4 +#define AD1938_SPI_READ 0x1 +#define AD1938_SPI_BUFLEN 3 + +/* + * write to the ad1938 register space + */ + +static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 *reg_cache = codec->reg_cache; + int ret = 0; + + if (value != reg_cache[reg]) { + uint8_t buf[AD1938_SPI_BUFLEN]; + struct spi_transfer t = { + .tx_buf = buf, + .len = AD1938_SPI_BUFLEN, + }; + struct spi_message m; + + buf[0] = AD1938_SPI_ADDR << 1; + buf[1] = reg; + buf[2] = value; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + ret = spi_sync(codec->control_data, &m); + if (ret == 0) + reg_cache[reg] = value; + } + + return ret; +} + +/* + * read from the ad1938 register space cache + */ + +static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 *reg_cache = codec->reg_cache; + + if (reg >= codec->reg_cache_size) + return -EINVAL; + + return reg_cache[reg]; +} + +/* + * read from the ad1938 register space + */ + +static unsigned int ad1938_read_reg(struct snd_soc_codec *codec, + unsigned int reg) +{ + char w_buf[AD1938_SPI_BUFLEN]; + char r_buf[AD1938_SPI_BUFLEN]; + int ret; + + struct spi_transfer t = { + .tx_buf = w_buf, + .rx_buf = r_buf, + .len = AD1938_SPI_BUFLEN, + }; + struct spi_message m; + + w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ; + w_buf[1] = reg; + w_buf[2] = 0; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + ret = spi_sync(codec->control_data, &m); + if (ret == 0) + return r_buf[2]; + else + return -EIO; +} + +static int ad1938_fill_cache(struct snd_soc_codec *codec) +{ + int i; + u8 *reg_cache = codec->reg_cache; + struct spi_device *spi = codec->control_data; + + for (i = 0; i < codec->reg_cache_size; i++) { + int ret = ad1938_read_reg(codec, i); + if (ret == -EIO) { + dev_err(&spi->dev, "AD1938 SPI read failure\n"); + return ret; + } + reg_cache[i] = ret; + } + + return 0; +} + +static int __devinit ad1938_spi_probe(struct spi_device *spi) +{ + struct snd_soc_codec *codec; + struct ad1938_priv *ad1938; + + ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL); + if (ad1938 == NULL) + return -ENOMEM; + + codec = &ad1938->codec; + codec->control_data = spi; + codec->dev = &spi->dev; + + dev_set_drvdata(&spi->dev, ad1938); + + return ad1938_register(ad1938); +} + +static int __devexit ad1938_spi_remove(struct spi_device *spi) +{ + struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev); + + ad1938_unregister(ad1938); + return 0; +} + +static struct spi_driver ad1938_spi_driver = { + .driver = { + .name = "ad1938-spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad1938_spi_probe, + .remove = __devexit_p(ad1938_spi_remove), +}; + +static struct snd_soc_dai_ops ad1938_dai_ops = { + .hw_params = ad1938_hw_params, + .digital_mute = ad1938_mute, + .set_tdm_slot = ad1938_set_tdm_slot, + .set_fmt = ad1938_set_dai_fmt, +}; + +/* codec DAI instance */ +struct snd_soc_dai ad1938_dai = { + .name = "AD1938", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad1938_dai_ops, +}; +EXPORT_SYMBOL_GPL(ad1938_dai); + +static int ad1938_register(struct ad1938_priv *ad1938) +{ + int ret; + struct snd_soc_codec *codec = &ad1938->codec; + + if (ad1938_codec) { + dev_err(codec->dev, "Another ad1938 is registered\n"); + return -EINVAL; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + codec->private_data = ad1938; + codec->reg_cache = ad1938->reg_cache; + codec->reg_cache_size = AD1938_NUM_REGS; + codec->name = "AD1938"; + codec->owner = THIS_MODULE; + codec->dai = &ad1938_dai; + codec->num_dai = 1; + codec->write = ad1938_write_reg; + codec->read = ad1938_read_reg_cache; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ad1938_dai.dev = codec->dev; + ad1938_codec = codec; + + /* default setting for ad1938 */ + + /* unmute dac channels */ + codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0); + /* de-emphasis: 48kHz, powedown dac */ + codec->write(codec, AD1938_DAC_CTRL2, 0x1A); + /* powerdown dac, dac in tdm mode */ + codec->write(codec, AD1938_DAC_CTRL0, 0x41); + /* high-pass filter enable */ + codec->write(codec, AD1938_ADC_CTRL0, 0x3); + /* sata delay=1, adc aux mode */ + codec->write(codec, AD1938_ADC_CTRL1, 0x43); + /* pll input: mclki/xi */ + codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D); + codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04); + + ad1938_fill_cache(codec); + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&ad1938_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } + + return 0; +} + +static void ad1938_unregister(struct ad1938_priv *ad1938) +{ + ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&ad1938_dai); + snd_soc_unregister_codec(&ad1938->codec); + kfree(ad1938); + ad1938_codec = NULL; +} + +static int ad1938_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (ad1938_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = ad1938_codec; + codec = ad1938_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, ad1938_snd_controls, + ARRAY_SIZE(ad1938_snd_controls)); + snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets, + ARRAY_SIZE(ad1938_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_new_widgets(codec); + + ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +/* power down chip */ +static int ad1938_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +#ifdef CONFIG_PM +static int ad1938_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int ad1938_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) + ad1938_set_bias_level(codec, SND_SOC_BIAS_ON); + + return 0; +} +#else +#define ad1938_suspend NULL +#define ad1938_resume NULL +#endif + +struct snd_soc_codec_device soc_codec_dev_ad1938 = { + .probe = ad1938_probe, + .remove = ad1938_remove, + .suspend = ad1938_suspend, + .resume = ad1938_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938); + +static int __init ad1938_init(void) +{ + int ret; + + ret = spi_register_driver(&ad1938_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n", + ret); + } + + return ret; +} +module_init(ad1938_init); + +static void __exit ad1938_exit(void) +{ + spi_unregister_driver(&ad1938_spi_driver); +} +module_exit(ad1938_exit); + +MODULE_DESCRIPTION("ASoC ad1938 driver"); +MODULE_AUTHOR("Barry Song "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h new file mode 100644 index 00000000000..fe3c48cd2d5 --- /dev/null +++ b/sound/soc/codecs/ad1938.h @@ -0,0 +1,100 @@ +/* + * File: sound/soc/codecs/ad1836.h + * Based on: + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: May 25, 2009 + * Description: definitions for AD1938 registers + * + * Modified: + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AD1938_H__ +#define __AD1938_H__ + +#define AD1938_PLL_CLK_CTRL0 0 +#define AD1938_PLL_POWERDOWN 0x01 +#define AD1938_PLL_CLK_CTRL1 1 +#define AD1938_DAC_CTRL0 2 +#define AD1938_DAC_POWERDOWN 0x01 +#define AD1938_DAC_SERFMT_MASK 0xC0 +#define AD1938_DAC_SERFMT_STEREO (0 << 6) +#define AD1938_DAC_SERFMT_TDM (1 << 6) +#define AD1938_DAC_CTRL1 3 +#define AD1938_DAC_2_CHANNELS 0 +#define AD1938_DAC_4_CHANNELS 1 +#define AD1938_DAC_8_CHANNELS 2 +#define AD1938_DAC_16_CHANNELS 3 +#define AD1938_DAC_CHAN_SHFT 1 +#define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT) +#define AD1938_DAC_LCR_MASTER (1 << 4) +#define AD1938_DAC_BCLK_MASTER (1 << 5) +#define AD1938_DAC_LEFT_HIGH (1 << 3) +#define AD1938_DAC_BCLK_INV (1 << 7) +#define AD1938_DAC_CTRL2 4 +#define AD1938_DAC_WORD_LEN_MASK 0xC +#define AD1938_DAC_MASTER_MUTE 1 +#define AD1938_DAC_CHNL_MUTE 5 +#define AD1938_DACL1_MUTE 0 +#define AD1938_DACR1_MUTE 1 +#define AD1938_DACL2_MUTE 2 +#define AD1938_DACR2_MUTE 3 +#define AD1938_DACL3_MUTE 4 +#define AD1938_DACR3_MUTE 5 +#define AD1938_DACL4_MUTE 6 +#define AD1938_DACR4_MUTE 7 +#define AD1938_DAC_L1_VOL 6 +#define AD1938_DAC_R1_VOL 7 +#define AD1938_DAC_L2_VOL 8 +#define AD1938_DAC_R2_VOL 9 +#define AD1938_DAC_L3_VOL 10 +#define AD1938_DAC_R3_VOL 11 +#define AD1938_DAC_L4_VOL 12 +#define AD1938_DAC_R4_VOL 13 +#define AD1938_ADC_CTRL0 14 +#define AD1938_ADC_POWERDOWN 0x01 +#define AD1938_ADC_HIGHPASS_FILTER 1 +#define AD1938_ADCL1_MUTE 2 +#define AD1938_ADCR1_MUTE 3 +#define AD1938_ADCL2_MUTE 4 +#define AD1938_ADCR2_MUTE 5 +#define AD1938_ADC_CTRL1 15 +#define AD1938_ADC_SERFMT_MASK 0x60 +#define AD1938_ADC_SERFMT_STEREO (0 << 5) +#define AD1938_ADC_SERFMT_TDM (1 << 2) +#define AD1938_ADC_SERFMT_AUX (2 << 5) +#define AD1938_ADC_WORD_LEN_MASK 0x3 +#define AD1938_ADC_CTRL2 16 +#define AD1938_ADC_2_CHANNELS 0 +#define AD1938_ADC_4_CHANNELS 1 +#define AD1938_ADC_8_CHANNELS 2 +#define AD1938_ADC_16_CHANNELS 3 +#define AD1938_ADC_CHAN_SHFT 4 +#define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT) +#define AD1938_ADC_LCR_MASTER (1 << 3) +#define AD1938_ADC_BCLK_MASTER (1 << 6) +#define AD1938_ADC_LEFT_HIGH (1 << 2) +#define AD1938_ADC_BCLK_INV (1 << 1) + +#define AD1938_NUM_REGS 17 + +extern struct snd_soc_dai ad1938_dai; +extern struct snd_soc_codec_device soc_codec_dev_ad1938; +#endif diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index dd338020276..0abec0d29a9 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -59,21 +59,6 @@ static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec, return cache[reg]; } -static inline unsigned int ak4535_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 data; - data = reg; - - if (codec->hw_write(codec->control_data, &data, 1) != 1) - return -EIO; - - if (codec->hw_read(codec->control_data, &data, 1) != 1) - return -EIO; - - return data; -}; - /* * write ak4535 register cache */ @@ -635,7 +620,6 @@ static int ak4535_probe(struct platform_device *pdev) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { codec->hw_write = (hw_write_t)i2c_master_send; - codec->hw_read = (hw_read_t)i2c_master_recv; ret = ak4535_add_i2c_device(pdev, setup); } #endif diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c new file mode 100644 index 00000000000..7bbb77baa23 --- /dev/null +++ b/sound/soc/codecs/cx20442.c @@ -0,0 +1,398 @@ +/* + * cx20442.c -- CX20442 ALSA Soc Audio driver + * + * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> + * + * Initially based on sound/soc/codecs/wm8400.c + * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/soc-dapm.h> + +#include "cx20442.h" + + +struct cx20442_priv { + struct snd_soc_codec codec; + u8 reg_cache[1]; +}; + +#define CX20442_PM 0x0 + +#define CX20442_TELIN 0 +#define CX20442_TELOUT 1 +#define CX20442_MIC 2 +#define CX20442_SPKOUT 3 +#define CX20442_AGC 4 + +static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("TELOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("AGCOUT"), + + SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0), + SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0), + + SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("TELIN"), + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("AGCIN"), +}; + +static const struct snd_soc_dapm_route cx20442_audio_map[] = { + {"TELOUT", NULL, "TELOUT Amp"}, + + {"SPKOUT", NULL, "SPKOUT Mixer"}, + {"SPKOUT Mixer", NULL, "SPKOUT Amp"}, + + {"TELOUT Amp", NULL, "DAC"}, + {"SPKOUT Amp", NULL, "DAC"}, + + {"SPKOUT Mixer", NULL, "SPKOUT AGC"}, + {"SPKOUT AGC", NULL, "AGCIN"}, + + {"AGCOUT", NULL, "MIC AGC"}, + {"MIC AGC", NULL, "MIC"}, + + {"MIC Bias", NULL, "MIC"}, + {"Input Mixer", NULL, "MIC Bias"}, + + {"TELIN Bias", NULL, "TELIN"}, + {"Input Mixer", NULL, "TELIN Bias"}, + + {"ADC", NULL, "Input Mixer"}, +}; + +static int cx20442_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets, + ARRAY_SIZE(cx20442_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, cx20442_audio_map, + ARRAY_SIZE(cx20442_audio_map)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 *reg_cache = codec->reg_cache; + + if (reg >= codec->reg_cache_size) + return -EINVAL; + + return reg_cache[reg]; +} + +enum v253_vls { + V253_VLS_NONE = 0, + V253_VLS_T, + V253_VLS_L, + V253_VLS_LT, + V253_VLS_S, + V253_VLS_ST, + V253_VLS_M, + V253_VLS_MST, + V253_VLS_S1, + V253_VLS_S1T, + V253_VLS_MS1T, + V253_VLS_M1, + V253_VLS_M1ST, + V253_VLS_M1S1T, + V253_VLS_H, + V253_VLS_HT, + V253_VLS_MS, + V253_VLS_MS1, + V253_VLS_M1S, + V253_VLS_M1S1, + V253_VLS_TEST, +}; + +static int cx20442_pm_to_v253_vls(u8 value) +{ + switch (value & ~(1 << CX20442_AGC)) { + case 0: + return V253_VLS_T; + case (1 << CX20442_SPKOUT): + return V253_VLS_S1; + case (1 << CX20442_MIC): + return V253_VLS_M1; + case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC): + return V253_VLS_M1S1; + case (1 << CX20442_TELOUT): + case (1 << CX20442_TELIN): + case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN): + return V253_VLS_L; + case (1 << CX20442_TELOUT) | (1 << CX20442_MIC): + return V253_VLS_NONE; + } + return -EINVAL; +} +static int cx20442_pm_to_v253_vsp(u8 value) +{ + switch (value & ~(1 << CX20442_AGC)) { + case (1 << CX20442_SPKOUT): + case (1 << CX20442_MIC): + case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC): + return (bool)(value & (1 << CX20442_AGC)); + } + return (value & (1 << CX20442_AGC)) ? -EINVAL : 0; +} + +static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 *reg_cache = codec->reg_cache; + int vls, vsp, old, len; + char buf[18]; + + if (reg >= codec->reg_cache_size) + return -EINVAL; + + /* hw_write and control_data pointers required for talking to the modem + * are expected to be set by the machine driver's line discipline + * initialization code */ + if (!codec->hw_write || !codec->control_data) + return -EIO; + + old = reg_cache[reg]; + reg_cache[reg] = value; + + vls = cx20442_pm_to_v253_vls(value); + if (vls < 0) + return vls; + + vsp = cx20442_pm_to_v253_vsp(value); + if (vsp < 0) + return vsp; + + if ((vls == V253_VLS_T) || + (vls == cx20442_pm_to_v253_vls(old))) { + if (vsp == cx20442_pm_to_v253_vsp(old)) + return 0; + len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp); + } else if (vsp == cx20442_pm_to_v253_vsp(old)) + len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls); + else + len = snprintf(buf, ARRAY_SIZE(buf), + "at+vls=%d;+vsp=%d\r", vls, vsp); + + if (unlikely(len > (ARRAY_SIZE(buf) - 1))) + return -ENOMEM; + + if (codec->hw_write(codec->control_data, buf, len) != len) + return -EIO; + + return 0; +} + +struct snd_soc_dai cx20442_dai = { + .name = "CX20442", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; +EXPORT_SYMBOL_GPL(cx20442_dai); + +static struct snd_soc_codec *cx20442_codec; + +static int cx20442_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret; + + if (!cx20442_codec) { + dev_err(&pdev->dev, "cx20442 not yet discovered\n"); + return -ENODEV; + } + codec = cx20442_codec; + + socdev->card->codec = codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to create pcms\n"); + goto pcm_err; + } + + cx20442_add_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register card\n"); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +/* power down chip */ +static int cx20442_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device cx20442_codec_dev = { + .probe = cx20442_codec_probe, + .remove = cx20442_codec_remove, +}; +EXPORT_SYMBOL_GPL(cx20442_codec_dev); + +static int cx20442_register(struct cx20442_priv *cx20442) +{ + struct snd_soc_codec *codec = &cx20442->codec; + int ret; + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->name = "CX20442"; + codec->owner = THIS_MODULE; + codec->private_data = cx20442; + + codec->dai = &cx20442_dai; + codec->num_dai = 1; + + codec->reg_cache = &cx20442->reg_cache; + codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache); + codec->read = cx20442_read_reg_cache; + codec->write = cx20442_write; + + codec->bias_level = SND_SOC_BIAS_OFF; + + cx20442_dai.dev = codec->dev; + + cx20442_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(&codec->dev, "Failed to register codec: %d\n", ret); + goto err; + } + + ret = snd_soc_register_dai(&cx20442_dai); + if (ret != 0) { + dev_err(&codec->dev, "Failed to register DAI: %d\n", ret); + goto err_codec; + } + + return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + cx20442_codec = NULL; + kfree(cx20442); + return ret; +} + +static void cx20442_unregister(struct cx20442_priv *cx20442) +{ + snd_soc_unregister_dai(&cx20442_dai); + snd_soc_unregister_codec(&cx20442->codec); + + cx20442_codec = NULL; + kfree(cx20442); +} + +static int cx20442_platform_probe(struct platform_device *pdev) +{ + struct cx20442_priv *cx20442; + struct snd_soc_codec *codec; + + cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL); + if (cx20442 == NULL) + return -ENOMEM; + + codec = &cx20442->codec; + + codec->control_data = NULL; + codec->hw_write = NULL; + codec->pop_time = 0; + + codec->dev = &pdev->dev; + platform_set_drvdata(pdev, cx20442); + + return cx20442_register(cx20442); +} + +static int __exit cx20442_platform_remove(struct platform_device *pdev) +{ + struct cx20442_priv *cx20442 = platform_get_drvdata(pdev); + + cx20442_unregister(cx20442); + return 0; +} + +static struct platform_driver cx20442_platform_driver = { + .driver = { + .name = "cx20442", + .owner = THIS_MODULE, + }, + .probe = cx20442_platform_probe, + .remove = __exit_p(cx20442_platform_remove), +}; + +static int __init cx20442_init(void) +{ + return platform_driver_register(&cx20442_platform_driver); +} +module_init(cx20442_init); + +static void __exit cx20442_exit(void) +{ + platform_driver_unregister(&cx20442_platform_driver); +} +module_exit(cx20442_exit); + +MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver"); +MODULE_AUTHOR("Janusz Krzysztofik"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cx20442"); diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h new file mode 100644 index 00000000000..d0a4f297aef --- /dev/null +++ b/sound/soc/codecs/cx20442.h @@ -0,0 +1,19 @@ +/* + * cx20442.h -- audio driver for CX20442 + * + * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _CX20442_CODEC_H +#define _CX20442_CODEC_H + +extern struct snd_soc_dai cx20442_dai; +extern struct snd_soc_codec_device cx20442_codec_dev; + +#endif diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c new file mode 100644 index 00000000000..9e7e964a5fa --- /dev/null +++ b/sound/soc/codecs/max9877.c @@ -0,0 +1,308 @@ +/* + * max9877.c -- amp driver for max9877 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "max9877.h" + +static struct i2c_client *i2c; + +static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 }; + +static void max9877_write_regs(void) +{ + unsigned int i; + u8 data[6]; + + data[0] = MAX9877_INPUT_MODE; + for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) + data[i + 1] = max9877_regs[i]; + + if (i2c_master_send(i2c, data, 6) != 6) + dev_err(&i2c->dev, "i2c write failed\n"); +} + +static int max9877_get_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + unsigned int invert = mc->invert; + + ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; + + if (invert) + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + + return 0; +} + +static int max9877_set_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + unsigned int invert = mc->invert; + unsigned int val = (ucontrol->value.integer.value[0] & mask); + + if (invert) + val = mask - val; + + if (((max9877_regs[reg] >> shift) & mask) == val) + return 0; + + max9877_regs[reg] &= ~(mask << shift); + max9877_regs[reg] |= val << shift; + max9877_write_regs(); + + return 1; +} + +static int max9877_get_2reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + + ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; + ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask; + + return 0; +} + +static int max9877_set_2reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int mask = mc->max; + unsigned int val = (ucontrol->value.integer.value[0] & mask); + unsigned int val2 = (ucontrol->value.integer.value[1] & mask); + unsigned int change = 1; + + if (((max9877_regs[reg] >> shift) & mask) == val) + change = 0; + + if (((max9877_regs[reg2] >> shift) & mask) == val2) + change = 0; + + if (change) { + max9877_regs[reg] &= ~(mask << shift); + max9877_regs[reg] |= val << shift; + max9877_regs[reg2] &= ~(mask << shift); + max9877_regs[reg2] |= val2 << shift; + max9877_write_regs(); + } + + return change; +} + +static int max9877_get_out_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK; + + if (value) + value -= 1; + + ucontrol->value.integer.value[0] = value; + return 0; +} + +static int max9877_set_out_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 value = ucontrol->value.integer.value[0]; + + value += 1; + + if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value) + return 0; + + max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK; + max9877_regs[MAX9877_OUTPUT_MODE] |= value; + max9877_write_regs(); + return 1; +} + +static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK); + + value = value >> MAX9877_OSC_OFFSET; + + ucontrol->value.integer.value[0] = value; + return 0; +} + +static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 value = ucontrol->value.integer.value[0]; + + value = value << MAX9877_OSC_OFFSET; + if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value) + return 0; + + max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK; + max9877_regs[MAX9877_OUTPUT_MODE] |= value; + max9877_write_regs(); + return 1; +} + +static const unsigned int max9877_pgain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0), + 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0), +}; + +static const unsigned int max9877_output_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), + 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), + 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0), +}; + +static const char *max9877_out_mode[] = { + "INA -> SPK", + "INA -> HP", + "INA -> SPK and HP", + "INB -> SPK", + "INB -> HP", + "INB -> SPK and HP", + "INA + INB -> SPK", + "INA + INB -> HP", + "INA + INB -> SPK and HP", +}; + +static const char *max9877_osc_mode[] = { + "1176KHz", + "1100KHz", + "700KHz", +}; + +static const struct soc_enum max9877_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), +}; + +static const struct snd_kcontrol_new max9877_controls[] = { + SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume", + MAX9877_INPUT_MODE, 0, 2, 0, + max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), + SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume", + MAX9877_INPUT_MODE, 2, 2, 0, + max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), + SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume", + MAX9877_SPK_VOLUME, 0, 31, 0, + max9877_get_reg, max9877_set_reg, max9877_output_tlv), + SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume", + MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, + max9877_get_2reg, max9877_set_2reg, max9877_output_tlv), + SOC_SINGLE_EXT("MAX9877 INB Stereo Switch", + MAX9877_INPUT_MODE, 4, 1, 1, + max9877_get_reg, max9877_set_reg), + SOC_SINGLE_EXT("MAX9877 INA Stereo Switch", + MAX9877_INPUT_MODE, 5, 1, 1, + max9877_get_reg, max9877_set_reg), + SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch", + MAX9877_INPUT_MODE, 6, 1, 0, + max9877_get_reg, max9877_set_reg), + SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch", + MAX9877_OUTPUT_MODE, 6, 1, 0, + max9877_get_reg, max9877_set_reg), + SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch", + MAX9877_OUTPUT_MODE, 7, 1, 1, + max9877_get_reg, max9877_set_reg), + SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0], + max9877_get_out_mode, max9877_set_out_mode), + SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1], + max9877_get_osc_mode, max9877_set_osc_mode), +}; + +/* This function is called from ASoC machine driver */ +int max9877_add_controls(struct snd_soc_codec *codec) +{ + return snd_soc_add_controls(codec, max9877_controls, + ARRAY_SIZE(max9877_controls)); +} +EXPORT_SYMBOL_GPL(max9877_add_controls); + +static int __devinit max9877_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + i2c = client; + + max9877_write_regs(); + + return 0; +} + +static __devexit int max9877_i2c_remove(struct i2c_client *client) +{ + i2c = NULL; + + return 0; +} + +static const struct i2c_device_id max9877_i2c_id[] = { + { "max9877", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9877_i2c_id); + +static struct i2c_driver max9877_i2c_driver = { + .driver = { + .name = "max9877", + .owner = THIS_MODULE, + }, + .probe = max9877_i2c_probe, + .remove = __devexit_p(max9877_i2c_remove), + .id_table = max9877_i2c_id, +}; + +static int __init max9877_init(void) +{ + return i2c_add_driver(&max9877_i2c_driver); +} +module_init(max9877_init); + +static void __exit max9877_exit(void) +{ + i2c_del_driver(&max9877_i2c_driver); +} +module_exit(max9877_exit); + +MODULE_DESCRIPTION("ASoC MAX9877 amp driver"); +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h new file mode 100644 index 00000000000..6da72290ac5 --- /dev/null +++ b/sound/soc/codecs/max9877.h @@ -0,0 +1,37 @@ +/* + * max9877.h -- amp driver for max9877 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _MAX9877_H +#define _MAX9877_H + +#define MAX9877_INPUT_MODE 0x00 +#define MAX9877_SPK_VOLUME 0x01 +#define MAX9877_HPL_VOLUME 0x02 +#define MAX9877_HPR_VOLUME 0x03 +#define MAX9877_OUTPUT_MODE 0x04 + +/* MAX9877_INPUT_MODE */ +#define MAX9877_INB (1 << 4) +#define MAX9877_INA (1 << 5) +#define MAX9877_ZCD (1 << 6) + +/* MAX9877_OUTPUT_MODE */ +#define MAX9877_OUTMODE_MASK (15 << 0) +#define MAX9877_OSC_MASK (3 << 4) +#define MAX9877_OSC_OFFSET 4 +#define MAX9877_BYPASS (1 << 6) +#define MAX9877_SHDN (1 << 7) + +extern int max9877_add_controls(struct snd_soc_codec *codec); + +#endif diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c index 218b33adad9..a6319114105 100644 --- a/sound/soc/codecs/spdif_transciever.c +++ b/sound/soc/codecs/spdif_transciever.c @@ -21,6 +21,8 @@ #include "spdif_transciever.h" +MODULE_LICENSE("GPL"); + #define STUB_RATES SNDRV_PCM_RATE_8000_96000 #define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE @@ -34,6 +36,7 @@ struct snd_soc_dai dit_stub_dai = { .formats = STUB_FORMATS, }, }; +EXPORT_SYMBOL_GPL(dit_stub_dai); static int spdif_dit_probe(struct platform_device *pdev) { diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 8ad4b7b3e3b..befc6488c39 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -149,7 +149,7 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, stac9766_ac97_write(codec, AC97_INT_PAGING, 1); return 0; } - if (reg / 2 > ARRAY_SIZE(stac9766_reg)) + if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) return -EIO; soc_ac97_ops.write(codec->ac97, reg, val); @@ -168,7 +168,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, stac9766_ac97_write(codec, AC97_INT_PAGING, 1); return val; } - if (reg / 2 > ARRAY_SIZE(stac9766_reg)) + if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) return -EIO; if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4dbb853eef5..818fb37bd7f 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -620,6 +620,9 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { + struct snd_soc_device *socdev = codec->socdev; + struct twl4030_setup_data *setup = socdev->codec_data; + unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = codec->private_data; /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -629,6 +632,17 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); + /* Enable external mute control, this dramatically reduces + * the pop-noise */ + if (setup && setup->hs_extmute) { + if (setup->set_hs_extmute) { + setup->set_hs_extmute(1); + } else { + hs_pop |= TWL4030_EXTMUTE; + twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); + } + } + if (ramp) { /* Headset ramp-up according to the TRM */ hs_pop |= TWL4030_VMID_EN; @@ -636,6 +650,9 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain); hs_pop |= TWL4030_RAMP_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Wait ramp delay time + 1, so the VMID can settle */ + mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / + twl4030->sysclk) + 1); } else { /* Headset ramp-down _not_ according to * the TRM, but in a way that it is working */ @@ -652,6 +669,16 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) hs_pop &= ~TWL4030_VMID_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); } + + /* Disable external mute */ + if (setup && setup->hs_extmute) { + if (setup->set_hs_extmute) { + setup->set_hs_extmute(0); + } else { + hs_pop &= ~TWL4030_EXTMUTE; + twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); + } + } } static int headsetlpga_event(struct snd_soc_dapm_widget *w, @@ -712,7 +739,19 @@ static int bypass_event(struct snd_soc_dapm_widget *w, reg = twl4030_read_reg_cache(w->codec, m->reg); - if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { + /* + * bypass_state[0:3] - analog HiFi bypass + * bypass_state[4] - analog voice bypass + * bypass_state[5] - digital voice bypass + * bypass_state[6:7] - digital HiFi bypass + */ + if (m->reg == TWL4030_REG_VSTPGA) { + /* Voice digital bypass */ + if (reg) + twl4030->bypass_state |= (1 << 5); + else + twl4030->bypass_state &= ~(1 << 5); + } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { /* Analog bypass */ if (reg & (1 << m->shift)) twl4030->bypass_state |= @@ -726,12 +765,6 @@ static int bypass_event(struct snd_soc_dapm_widget *w, twl4030->bypass_state |= (1 << 4); else twl4030->bypass_state &= ~(1 << 4); - } else if (m->reg == TWL4030_REG_VSTPGA) { - /* Voice digital bypass */ - if (reg) - twl4030->bypass_state |= (1 << 5); - else - twl4030->bypass_state &= ~(1 << 5); } else { /* Digital bypass */ if (reg & (0x7 << m->shift)) @@ -924,7 +957,7 @@ static const struct soc_enum twl4030_op_modes_enum = ARRAY_SIZE(twl4030_op_modes_texts), twl4030_op_modes_texts); -int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, +static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); @@ -1005,6 +1038,16 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); */ static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); +/* AVADC clock priority */ +static const char *twl4030_avadc_clk_priority_texts[] = { + "Voice high priority", "HiFi high priority" +}; + +static const struct soc_enum twl4030_avadc_clk_priority_enum = + SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2, + ARRAY_SIZE(twl4030_avadc_clk_priority_texts), + twl4030_avadc_clk_priority_texts); + static const char *twl4030_rampdelay_texts[] = { "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", @@ -1106,6 +1149,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, 0, 3, 5, 0, input_gain_tlv), + SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum), + SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum), @@ -1240,14 +1285,14 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { /* HandsfreeL/R */ SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_handsfreel_control), - SND_SOC_DAPM_SWITCH("HandsfreeL Switch", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0, &twl4030_dapm_handsfreelmute_control), SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM, 0, 0, NULL, 0, handsfreelpga_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0, &twl4030_dapm_handsfreer_control), - SND_SOC_DAPM_SWITCH("HandsfreeR Switch", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0, &twl4030_dapm_handsfreermute_control), SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM, 0, 0, NULL, 0, handsfreerpga_event, @@ -1359,15 +1404,15 @@ static const struct snd_soc_dapm_route intercon[] = { {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"}, {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"}, - {"HandsfreeL Switch", "Switch", "HandsfreeL Mux"}, - {"HandsfreeL PGA", NULL, "HandsfreeL Switch"}, + {"HandsfreeL", "Switch", "HandsfreeL Mux"}, + {"HandsfreeL PGA", NULL, "HandsfreeL"}, /* HandsfreeR */ {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"}, {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"}, {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"}, {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"}, - {"HandsfreeR Switch", "Switch", "HandsfreeR Mux"}, - {"HandsfreeR PGA", NULL, "HandsfreeR Switch"}, + {"HandsfreeR", "Switch", "HandsfreeR Mux"}, + {"HandsfreeR PGA", NULL, "HandsfreeR"}, /* Vibra */ {"Vibra Mux", "AudioL1", "DAC Left1"}, {"Vibra Mux", "AudioR1", "DAC Right1"}, @@ -1609,8 +1654,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, /* If the substream has 4 channel, do the necessary setup */ if (params_channels(params) == 4) { - u8 format, mode; - format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); @@ -1806,6 +1849,19 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } +static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); + + if (tristate) + reg |= TWL4030_AIF_TRI_EN; + else + reg &= ~TWL4030_AIF_TRI_EN; + + return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg); +} + /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R * (VTXL, VTXR) for uplink has to be enabled/disabled. */ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, @@ -1948,7 +2004,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFM: format &= ~(TWL4030_VIF_SLAVE_EN); break; case SND_SOC_DAIFMT_CBS_CFS: @@ -1980,6 +2036,19 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } +static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); + + if (tristate) + reg |= TWL4030_VIF_TRI_EN; + else + reg &= ~TWL4030_VIF_TRI_EN; + + return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg); +} + #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) @@ -1989,6 +2058,7 @@ static struct snd_soc_dai_ops twl4030_dai_ops = { .hw_params = twl4030_hw_params, .set_sysclk = twl4030_set_dai_sysclk, .set_fmt = twl4030_set_dai_fmt, + .set_tristate = twl4030_set_tristate, }; static struct snd_soc_dai_ops twl4030_dai_voice_ops = { @@ -1997,6 +2067,7 @@ static struct snd_soc_dai_ops twl4030_dai_voice_ops = { .hw_params = twl4030_voice_hw_params, .set_sysclk = twl4030_voice_set_dai_sysclk, .set_fmt = twl4030_voice_set_dai_fmt, + .set_tristate = twl4030_voice_set_tristate, }; struct snd_soc_dai twl4030_dai[] = { diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index fe5f395d9e4..2b4bfa23f98 100644 --- a/sound/soc/codecs/twl4030.h +++ b/sound/soc/codecs/twl4030.h @@ -274,6 +274,8 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030; struct twl4030_setup_data { unsigned int ramp_delay_value; unsigned int sysclk; + unsigned int hs_extmute:1; + void (*set_hs_extmute)(int mute); }; #endif /* End of __TWL4030_AUDIO_H__ */ diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 5b21594e0e5..92ec0344215 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -5,9 +5,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com> - * Improved support for DAPM and audio routing/mixing capabilities, - * added TLV support. + * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com> * * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC * codec model. @@ -19,26 +17,32 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/string.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/ioctl.h> +#include <linux/gpio.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> -#include <sound/info.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> +#include <sound/uda1380.h> #include "uda1380.h" -static struct work_struct uda1380_work; static struct snd_soc_codec *uda1380_codec; +/* codec private data */ +struct uda1380_priv { + struct snd_soc_codec codec; + u16 reg_cache[UDA1380_CACHEREGNUM]; + unsigned int dac_clk; + struct work_struct work; +}; + /* * uda1380 register cache */ @@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->card->codec; + struct uda1380_priv *uda1380 = codec->private_data; int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER); switch (cmd) { @@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: uda1380_write_reg_cache(codec, UDA1380_MIXER, mixer & ~R14_SILENCE); - schedule_work(&uda1380_work); + schedule_work(&uda1380->work); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: uda1380_write_reg_cache(codec, UDA1380_MIXER, mixer | R14_SILENCE); - schedule_work(&uda1380_work); + schedule_work(&uda1380->work); break; } return 0; @@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev) return 0; } -/* - * initialise the UDA1380 driver - * register mixer and dsp interfaces with the kernel - */ -static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) +static int uda1380_probe(struct platform_device *pdev) { - struct snd_soc_codec *codec = socdev->card->codec; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + struct uda1380_platform_data *pdata; int ret = 0; - codec->name = "UDA1380"; - codec->owner = THIS_MODULE; - codec->read = uda1380_read_reg_cache; - codec->write = uda1380_write; - codec->set_bias_level = uda1380_set_bias_level; - codec->dai = uda1380_dai; - codec->num_dai = ARRAY_SIZE(uda1380_dai); - codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg), - GFP_KERNEL); - if (codec->reg_cache == NULL) - return -ENOMEM; - codec->reg_cache_size = ARRAY_SIZE(uda1380_reg); - codec->reg_cache_step = 1; - uda1380_reset(codec); + if (uda1380_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } - uda1380_codec = codec; - INIT_WORK(&uda1380_work, uda1380_flush_work); + socdev->card->codec = uda1380_codec; + codec = uda1380_codec; + pdata = codec->dev->platform_data; /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - pr_err("uda1380: failed to create pcms\n"); + dev_err(codec->dev, "failed to create pcms: %d\n", ret); goto pcm_err; } /* power on device */ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* set clock input */ - switch (dac_clk) { + switch (pdata->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: uda1380_write(codec, UDA1380_CLK, 0); break; @@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) break; } - /* uda1380 init */ snd_soc_add_controls(codec, uda1380_snd_controls, ARRAY_SIZE(uda1380_snd_controls)); uda1380_add_widgets(codec); ret = snd_soc_init_card(socdev); if (ret < 0) { - pr_err("uda1380: failed to register card\n"); + dev_err(codec->dev, "failed to register card: %d\n", ret); goto card_err; } @@ -732,165 +725,201 @@ card_err: snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); pcm_err: - kfree(codec->reg_cache); return ret; } -static struct snd_soc_device *uda1380_socdev; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -static int uda1380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +/* power down chip */ +static int uda1380_remove(struct platform_device *pdev) { - struct snd_soc_device *socdev = uda1380_socdev; - struct uda1380_setup_data *setup = socdev->codec_data; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; - int ret; - - i2c_set_clientdata(i2c, codec); - codec->control_data = i2c; - ret = uda1380_init(socdev, setup->dac_clk); - if (ret < 0) - pr_err("uda1380: failed to initialise UDA1380\n"); + if (codec->control_data) + uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); - return ret; -} + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); -static int uda1380_i2c_remove(struct i2c_client *client) -{ - struct snd_soc_codec *codec = i2c_get_clientdata(client); - kfree(codec->reg_cache); return 0; } -static const struct i2c_device_id uda1380_i2c_id[] = { - { "uda1380", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); - -static struct i2c_driver uda1380_i2c_driver = { - .driver = { - .name = "UDA1380 I2C Codec", - .owner = THIS_MODULE, - }, - .probe = uda1380_i2c_probe, - .remove = uda1380_i2c_remove, - .id_table = uda1380_i2c_id, +struct snd_soc_codec_device soc_codec_dev_uda1380 = { + .probe = uda1380_probe, + .remove = uda1380_remove, + .suspend = uda1380_suspend, + .resume = uda1380_resume, }; +EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380); -static int uda1380_add_i2c_device(struct platform_device *pdev, - const struct uda1380_setup_data *setup) +static int uda1380_register(struct uda1380_priv *uda1380) { - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; - int ret; + int ret, i; + struct snd_soc_codec *codec = &uda1380->codec; + struct uda1380_platform_data *pdata = codec->dev->platform_data; - ret = i2c_add_driver(&uda1380_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add i2c driver\n"); - return ret; + if (uda1380_codec) { + dev_err(codec->dev, "Another UDA1380 is registered\n"); + return -EINVAL; + } + + if (!pdata || !pdata->gpio_power || !pdata->gpio_reset) + return -EINVAL; + + ret = gpio_request(pdata->gpio_power, "uda1380 power"); + if (ret) + goto err_out; + ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); + if (ret) + goto err_gpio; + + gpio_direction_output(pdata->gpio_power, 1); + + /* we may need to have the clock running here - pH5 */ + gpio_direction_output(pdata->gpio_reset, 1); + udelay(5); + gpio_set_value(pdata->gpio_reset, 0); + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = uda1380; + codec->name = "UDA1380"; + codec->owner = THIS_MODULE; + codec->read = uda1380_read_reg_cache; + codec->write = uda1380_write; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = uda1380_set_bias_level; + codec->dai = uda1380_dai; + codec->num_dai = ARRAY_SIZE(uda1380_dai); + codec->reg_cache_size = ARRAY_SIZE(uda1380_reg); + codec->reg_cache = &uda1380->reg_cache; + codec->reg_cache_step = 1; + + memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg)); + + ret = uda1380_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + goto err_reset; } - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = setup->i2c_address; - strlcpy(info.type, "uda1380", I2C_NAME_SIZE); + INIT_WORK(&uda1380->work, uda1380_flush_work); + + for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++) + uda1380_dai[i].dev = codec->dev; - adapter = i2c_get_adapter(setup->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", - setup->i2c_bus); - goto err_driver; + uda1380_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto err_reset; } - client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; + ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAIs: %d\n", ret); + goto err_dai; } return 0; -err_driver: - i2c_del_driver(&uda1380_i2c_driver); - return -ENODEV; +err_dai: + snd_soc_unregister_codec(codec); +err_reset: + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_reset); +err_gpio: + gpio_free(pdata->gpio_power); +err_out: + return ret; } -#endif -static int uda1380_probe(struct platform_device *pdev) +static void uda1380_unregister(struct uda1380_priv *uda1380) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct uda1380_setup_data *setup; + struct snd_soc_codec *codec = &uda1380->codec; + struct uda1380_platform_data *pdata = codec->dev->platform_data; + + snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + snd_soc_unregister_codec(&uda1380->codec); + + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_reset); + gpio_free(pdata->gpio_power); + + kfree(uda1380); + uda1380_codec = NULL; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int uda1380_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct uda1380_priv *uda1380; struct snd_soc_codec *codec; int ret; - setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) + uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL); + if (uda1380 == NULL) return -ENOMEM; - socdev->card->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); + codec = &uda1380->codec; + codec->hw_write = (hw_write_t)i2c_master_send; - uda1380_socdev = socdev; - ret = -ENODEV; + i2c_set_clientdata(i2c, uda1380); + codec->control_data = i2c; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; - ret = uda1380_add_i2c_device(pdev, setup); - } -#endif + codec->dev = &i2c->dev; + ret = uda1380_register(uda1380); if (ret != 0) - kfree(codec); + kfree(uda1380); + return ret; } -/* power down chip */ -static int uda1380_remove(struct platform_device *pdev) +static int __devexit uda1380_i2c_remove(struct i2c_client *i2c) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - - if (codec->control_data) - uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_unregister_device(codec->control_data); - i2c_del_driver(&uda1380_i2c_driver); -#endif - kfree(codec); - + struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c); + uda1380_unregister(uda1380); return 0; } -struct snd_soc_codec_device soc_codec_dev_uda1380 = { - .probe = uda1380_probe, - .remove = uda1380_remove, - .suspend = uda1380_suspend, - .resume = uda1380_resume, +static const struct i2c_device_id uda1380_i2c_id[] = { + { "uda1380", 0 }, + { } }; -EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380); +MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); + +static struct i2c_driver uda1380_i2c_driver = { + .driver = { + .name = "UDA1380 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = uda1380_i2c_probe, + .remove = __devexit_p(uda1380_i2c_remove), + .id_table = uda1380_i2c_id, +}; +#endif static int __init uda1380_modinit(void) { - return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&uda1380_i2c_driver); + if (ret != 0) + pr_err("Failed to register UDA1380 I2C driver: %d\n", ret); +#endif + return 0; } module_init(uda1380_modinit); static void __exit uda1380_exit(void) { - snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&uda1380_i2c_driver); +#endif } module_exit(uda1380_exit); diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index c55c17a52a1..9cefa8a5477 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -72,14 +72,6 @@ #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001 -struct uda1380_setup_data { - int i2c_bus; - unsigned short i2c_address; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - #define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ #define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ #define UDA1380_DAI_CAPTURE 2 /* capture DAI */ diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index e7348d341b7..4ded0e3a35e 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -406,7 +406,6 @@ static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" }; static const char *wm8350_dacmutem[] = { "Normal", "Soft" }; static const char *wm8350_dacmutes[] = { "Fast", "Slow" }; -static const char *wm8350_dacfilter[] = { "Normal", "Sloping" }; static const char *wm8350_adcfilter[] = { "None", "High Pass" }; static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" }; static const char *wm8350_lr[] = { "Left", "Right" }; @@ -416,7 +415,6 @@ static const struct soc_enum wm8350_enum[] = { SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol), SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem), SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes), - SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol), @@ -444,10 +442,9 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = { 0, 255, 0, dac_pcm_tlv), SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]), SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]), - SOC_ENUM("Playback PCM Filter", wm8350_enum[4]), - SOC_ENUM("Capture PCM Filter", wm8350_enum[5]), - SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]), - SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]), + SOC_ENUM("Capture PCM Filter", wm8350_enum[4]), + SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]), + SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]), SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume", WM8350_ADC_DIGITAL_VOLUME_L, WM8350_ADC_DIGITAL_VOLUME_R, @@ -993,6 +990,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; + struct wm8350 *wm8350 = codec->control_data; u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & ~WM8350_AIF_WL_MASK; @@ -1012,6 +1010,19 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, } wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); + + /* The sloping stopband filter is recommended for use with + * lower sample rates to improve performance. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (params_rate(params) < 24000) + wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME, + WM8350_DAC_SB_FILT); + else + wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME, + WM8350_DAC_SB_FILT); + } + return 0; } @@ -1660,6 +1671,21 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m) +{ + return snd_soc_suspend_device(&pdev->dev); +} + +static int wm8350_codec_resume(struct platform_device *pdev) +{ + return snd_soc_resume_device(&pdev->dev); +} +#else +#define wm8350_codec_suspend NULL +#define wm8350_codec_resume NULL +#endif + static struct platform_driver wm8350_codec_driver = { .driver = { .name = "wm8350-codec", @@ -1667,6 +1693,8 @@ static struct platform_driver wm8350_codec_driver = { }, .probe = wm8350_codec_probe, .remove = __devexit_p(wm8350_codec_remove), + .suspend = wm8350_codec_suspend, + .resume = wm8350_codec_resume, }; static __init int wm8350_init(void) diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 502eefac1ec..b9ef4d91522 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1022,10 +1022,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out) return 0; - if (freq_out != 0) { + if (freq_out) { ret = fll_factors(wm8400, &factors, freq_in, freq_out); if (ret != 0) return ret; + } else { + /* Bodge GCC 4.4.0 uninitialised variable warning - it + * doesn't seem capable of working out that we exit if + * freq_out is 0 before any of the uses. */ + memset(&factors, 0, sizeof(factors)); } wm8400->fll_out = freq_out; @@ -1040,7 +1045,7 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, reg &= ~WM8400_FLL_OSC_ENA; wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); - if (freq_out == 0) + if (!freq_out) return 0; reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK); @@ -1553,6 +1558,21 @@ static int __exit wm8400_codec_remove(struct platform_device *dev) return 0; } +#ifdef CONFIG_PM +static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg) +{ + return snd_soc_suspend_device(&pdev->dev); +} + +static int wm8400_pdev_resume(struct platform_device *pdev) +{ + return snd_soc_resume_device(&pdev->dev); +} +#else +#define wm8400_pdev_suspend NULL +#define wm8400_pdev_resume NULL +#endif + static struct platform_driver wm8400_codec_driver = { .driver = { .name = "wm8400-codec", @@ -1560,6 +1580,8 @@ static struct platform_driver wm8400_codec_driver = { }, .probe = wm8400_codec_probe, .remove = __exit_p(wm8400_codec_remove), + .suspend = wm8400_pdev_suspend, + .resume = wm8400_pdev_resume, }; static int __init wm8400_codec_init(void) diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index c8b8dba8589..261d4cb7596 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -577,6 +577,7 @@ struct snd_soc_dai wm8510_dai = { .rates = WM8510_RATES, .formats = WM8510_FORMATS,}, .ops = &wm8510_dai_ops, + .symmetric_rates = 1, }; EXPORT_SYMBOL_GPL(wm8510_dai); diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c new file mode 100644 index 00000000000..3b499ae7ce6 --- /dev/null +++ b/sound/soc/codecs/wm8523.c @@ -0,0 +1,755 @@ +/* + * wm8523.c -- WM8523 ALSA SoC Audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8523.h" + +static struct snd_soc_codec *wm8523_codec; +struct snd_soc_codec_device soc_codec_dev_wm8523; + +#define WM8523_NUM_SUPPLIES 2 +static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = { + "AVDD", + "LINEVDD", +}; + +#define WM8523_NUM_RATES 7 + +/* codec private data */ +struct wm8523_priv { + struct snd_soc_codec codec; + u16 reg_cache[WM8523_REGISTER_COUNT]; + struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES]; + unsigned int sysclk; + unsigned int rate_constraint_list[WM8523_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; +}; + +static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = { + 0x8523, /* R0 - DEVICE_ID */ + 0x0001, /* R1 - REVISION */ + 0x0000, /* R2 - PSCTRL1 */ + 0x1812, /* R3 - AIF_CTRL1 */ + 0x0000, /* R4 - AIF_CTRL2 */ + 0x0001, /* R5 - DAC_CTRL3 */ + 0x0190, /* R6 - DAC_GAINL */ + 0x0190, /* R7 - DAC_GAINR */ + 0x0000, /* R8 - ZERO_DETECT */ +}; + +static int wm8523_volatile(unsigned int reg) +{ + switch (reg) { + case WM8523_DEVICE_ID: + case WM8523_REVISION: + return 1; + default: + return 0; + } +} + +static int wm8523_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct wm8523_priv *wm8523 = codec->private_data; + u8 data[3]; + + BUG_ON(reg > WM8523_MAX_REGISTER); + + data[0] = reg; + data[1] = (value >> 8) & 0x00ff; + data[2] = value & 0x00ff; + + if (!wm8523_volatile(reg)) + wm8523->reg_cache[reg] = value; + if (codec->hw_write(codec->control_data, data, 3) == 3) + return 0; + else + return -EIO; +} + +static int wm8523_reset(struct snd_soc_codec *codec) +{ + return wm8523_write(codec, WM8523_DEVICE_ID, 0); +} + +static unsigned int wm8523_read_hw(struct snd_soc_codec *codec, u8 reg) +{ + struct i2c_msg xfer[2]; + u16 data; + int ret; + struct i2c_client *i2c = codec->control_data; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (u8 *)&data; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret != 2) { + dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret); + return 0; + } + + return (data >> 8) | ((data & 0xff) << 8); +} + + +static unsigned int wm8523_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *reg_cache = codec->reg_cache; + + BUG_ON(reg > WM8523_MAX_REGISTER); + + if (wm8523_volatile(reg)) + return wm8523_read_hw(codec, reg); + else + return reg_cache[reg]; +} + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0); + +static const char *wm8523_zd_count_text[] = { + "1024", + "2048", +}; + +static const struct soc_enum wm8523_zc_count = + SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text); + +static const struct snd_kcontrol_new wm8523_snd_controls[] = { +SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR, + 0, 448, 0, dac_tlv), +SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0), +SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0), +SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1), +SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0), +SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0), +SOC_ENUM("Zero Detect Count", wm8523_zc_count), +}; + +static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LINEVOUTL"), +SND_SOC_DAPM_OUTPUT("LINEVOUTR"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + { "LINEVOUTL", NULL, "DAC" }, + { "LINEVOUTR", NULL, "DAC" }, +}; + +static int wm8523_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, wm8523_dapm_widgets, + ARRAY_SIZE(wm8523_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static struct { + int value; + int ratio; +} lrclk_ratios[WM8523_NUM_RATES] = { + { 1, 128 }, + { 2, 192 }, + { 3, 256 }, + { 4, 384 }, + { 5, 512 }, + { 6, 768 }, + { 7, 1152 }, +}; + +static int wm8523_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8523_priv *wm8523 = codec->private_data; + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8523->sysclk) { + dev_err(codec->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + return 0; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &wm8523->rate_constraint); + + return 0; +} + +static int wm8523_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct wm8523_priv *wm8523 = codec->private_data; + int i; + u16 aifctrl1 = wm8523_read(codec, WM8523_AIF_CTRL1); + u16 aifctrl2 = wm8523_read(codec, WM8523_AIF_CTRL2); + + /* Find a supported LRCLK ratio */ + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + if (wm8523->sysclk / params_rate(params) == + lrclk_ratios[i].ratio) + break; + } + + /* Should never happen, should be handled by constraints */ + if (i == ARRAY_SIZE(lrclk_ratios)) { + dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n", + wm8523->sysclk / params_rate(params)); + return -EINVAL; + } + + aifctrl2 &= ~WM8523_SR_MASK; + aifctrl2 |= lrclk_ratios[i].value; + + aifctrl1 &= ~WM8523_WL_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + aifctrl1 |= 0x8; + break; + case SNDRV_PCM_FORMAT_S24_LE: + aifctrl1 |= 0x10; + break; + case SNDRV_PCM_FORMAT_S32_LE: + aifctrl1 |= 0x18; + break; + } + + wm8523_write(codec, WM8523_AIF_CTRL1, aifctrl1); + wm8523_write(codec, WM8523_AIF_CTRL2, aifctrl2); + + return 0; +} + +static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8523_priv *wm8523 = codec->private_data; + unsigned int val; + int i; + + wm8523->sysclk = freq; + + wm8523->rate_constraint.count = 0; + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + val = freq / lrclk_ratios[i].ratio; + /* Check that it's a standard rate since core can't + * cope with others and having the odd rates confuses + * constraint matching. + */ + switch (val) { + case 8000: + case 11025: + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + case 64000: + case 88200: + case 96000: + case 176400: + case 192000: + dev_dbg(codec->dev, "Supported sample rate: %dHz\n", + val); + wm8523->rate_constraint_list[i] = val; + wm8523->rate_constraint.count++; + break; + default: + dev_dbg(codec->dev, "Skipping sample rate: %dHz\n", + val); + } + } + + /* Need at least one supported rate... */ + if (wm8523->rate_constraint.count == 0) + return -EINVAL; + + return 0; +} + + +static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 aifctrl1 = wm8523_read(codec, WM8523_AIF_CTRL1); + + aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK | + WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aifctrl1 |= WM8523_AIF_MSTR; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aifctrl1 |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aifctrl1 |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + aifctrl1 |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + aifctrl1 |= 0x0023; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aifctrl1 |= WM8523_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aifctrl1 |= WM8523_LRCLK_INV; + break; + default: + return -EINVAL; + } + + wm8523_write(codec, WM8523_AIF_CTRL1, aifctrl1); + + return 0; +} + +static int wm8523_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct wm8523_priv *wm8523 = codec->private_data; + int ret, i; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + snd_soc_update_bits(codec, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 3); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + /* Initial power up */ + snd_soc_update_bits(codec, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 1); + + /* Sync back default/cached values */ + for (i = WM8523_AIF_CTRL1; + i < WM8523_MAX_REGISTER; i++) + wm8523_write(codec, i, wm8523->reg_cache[i]); + + + msleep(100); + } + + /* Power up to mute */ + snd_soc_update_bits(codec, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 2); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us. */ + snd_soc_update_bits(codec, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 0); + msleep(100); + + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + break; + } + codec->bias_level = level; + return 0; +} + +#define WM8523_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops wm8523_dai_ops = { + .startup = wm8523_startup, + .hw_params = wm8523_hw_params, + .set_sysclk = wm8523_set_dai_sysclk, + .set_fmt = wm8523_set_dai_fmt, +}; + +struct snd_soc_dai wm8523_dai = { + .name = "WM8523", + .playback = { + .stream_name = "Playback", + .channels_min = 2, /* Mono modes not yet supported */ + .channels_max = 2, + .rates = WM8523_RATES, + .formats = WM8523_FORMATS, + }, + .ops = &wm8523_dai_ops, +}; +EXPORT_SYMBOL_GPL(wm8523_dai); + +#ifdef CONFIG_PM +static int wm8523_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8523_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define wm8523_suspend NULL +#define wm8523_resume NULL +#endif + +static int wm8523_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (wm8523_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = wm8523_codec; + codec = wm8523_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, wm8523_snd_controls, + ARRAY_SIZE(wm8523_snd_controls)); + wm8523_add_widgets(codec); + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +static int wm8523_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8523 = { + .probe = wm8523_probe, + .remove = wm8523_remove, + .suspend = wm8523_suspend, + .resume = wm8523_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8523); + +static int wm8523_register(struct wm8523_priv *wm8523) +{ + int ret; + struct snd_soc_codec *codec = &wm8523->codec; + int i; + + if (wm8523_codec) { + dev_err(codec->dev, "Another WM8523 is registered\n"); + return -EINVAL; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = wm8523; + codec->name = "WM8523"; + codec->owner = THIS_MODULE; + codec->read = wm8523_read; + codec->write = wm8523_write; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = wm8523_set_bias_level; + codec->dai = &wm8523_dai; + codec->num_dai = 1; + codec->reg_cache_size = WM8523_REGISTER_COUNT; + codec->reg_cache = &wm8523->reg_cache; + + wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0]; + wm8523->rate_constraint.count = + ARRAY_SIZE(wm8523->rate_constraint_list); + + memcpy(codec->reg_cache, wm8523_reg, sizeof(wm8523_reg)); + + for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++) + wm8523->supplies[i].supply = wm8523_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + ret = wm8523_read(codec, WM8523_DEVICE_ID); + if (ret < 0) { + dev_err(codec->dev, "Failed to read ID register\n"); + goto err_enable; + } + if (ret != wm8523_reg[WM8523_DEVICE_ID]) { + dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret); + ret = -EINVAL; + goto err_enable; + } + + ret = wm8523_read(codec, WM8523_REVISION); + if (ret < 0) { + dev_err(codec->dev, "Failed to read revision register\n"); + goto err_enable; + } + dev_info(codec->dev, "revision %c\n", + (ret & WM8523_CHIP_REV_MASK) + 'A'); + + ret = wm8523_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + goto err_enable; + } + + wm8523_dai.dev = codec->dev; + + /* Change some default settings - latch VU and enable ZC */ + wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU; + wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC; + + wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); + + wm8523_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&wm8523_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); +err_get: + regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); +err: + kfree(wm8523); + return ret; +} + +static void wm8523_unregister(struct wm8523_priv *wm8523) +{ + wm8523_set_bias_level(&wm8523->codec, SND_SOC_BIAS_OFF); + regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); + snd_soc_unregister_dai(&wm8523_dai); + snd_soc_unregister_codec(&wm8523->codec); + kfree(wm8523); + wm8523_codec = NULL; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int wm8523_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8523_priv *wm8523; + struct snd_soc_codec *codec; + + wm8523 = kzalloc(sizeof(struct wm8523_priv), GFP_KERNEL); + if (wm8523 == NULL) + return -ENOMEM; + + codec = &wm8523->codec; + codec->hw_write = (hw_write_t)i2c_master_send; + + i2c_set_clientdata(i2c, wm8523); + codec->control_data = i2c; + + codec->dev = &i2c->dev; + + return wm8523_register(wm8523); +} + +static __devexit int wm8523_i2c_remove(struct i2c_client *client) +{ + struct wm8523_priv *wm8523 = i2c_get_clientdata(client); + wm8523_unregister(wm8523); + return 0; +} + +#ifdef CONFIG_PM +static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg) +{ + return snd_soc_suspend_device(&i2c->dev); +} + +static int wm8523_i2c_resume(struct i2c_client *i2c) +{ + return snd_soc_resume_device(&i2c->dev); +} +#else +#define wm8523_i2c_suspend NULL +#define wm8523_i2c_resume NULL +#endif + +static const struct i2c_device_id wm8523_i2c_id[] = { + { "wm8523", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id); + +static struct i2c_driver wm8523_i2c_driver = { + .driver = { + .name = "WM8523", + .owner = THIS_MODULE, + }, + .probe = wm8523_i2c_probe, + .remove = __devexit_p(wm8523_i2c_remove), + .suspend = wm8523_i2c_suspend, + .resume = wm8523_i2c_resume, + .id_table = wm8523_i2c_id, +}; +#endif + +static int __init wm8523_modinit(void) +{ + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8523_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n", + ret); + } +#endif + return 0; +} +module_init(wm8523_modinit); + +static void __exit wm8523_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8523_i2c_driver); +#endif +} +module_exit(wm8523_exit); + +MODULE_DESCRIPTION("ASoC WM8523 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h new file mode 100644 index 00000000000..1aa9ce3e135 --- /dev/null +++ b/sound/soc/codecs/wm8523.h @@ -0,0 +1,160 @@ +/* + * wm8523.h -- WM8423 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics, plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * Based on wm8753.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8523_H +#define _WM8523_H + +/* + * Register values. + */ +#define WM8523_DEVICE_ID 0x00 +#define WM8523_REVISION 0x01 +#define WM8523_PSCTRL1 0x02 +#define WM8523_AIF_CTRL1 0x03 +#define WM8523_AIF_CTRL2 0x04 +#define WM8523_DAC_CTRL3 0x05 +#define WM8523_DAC_GAINL 0x06 +#define WM8523_DAC_GAINR 0x07 +#define WM8523_ZERO_DETECT 0x08 + +#define WM8523_REGISTER_COUNT 9 +#define WM8523_MAX_REGISTER 0x08 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - DEVICE_ID + */ +#define WM8523_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - REVISION + */ +#define WM8523_CHIP_REV_MASK 0x0007 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_SHIFT 0 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_WIDTH 3 /* CHIP_REV - [2:0] */ + +/* + * R2 (0x02) - PSCTRL1 + */ +#define WM8523_SYS_ENA_MASK 0x0003 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_SHIFT 0 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_WIDTH 2 /* SYS_ENA - [1:0] */ + +/* + * R3 (0x03) - AIF_CTRL1 + */ +#define WM8523_TDM_MODE_MASK 0x1800 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_SHIFT 11 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_WIDTH 2 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_SLOT_MASK 0x0600 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_SHIFT 9 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_WIDTH 2 /* TDM_SLOT - [10:9] */ +#define WM8523_DEEMPH 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_MASK 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_SHIFT 8 /* DEEMPH */ +#define WM8523_DEEMPH_WIDTH 1 /* DEEMPH */ +#define WM8523_AIF_MSTR 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_MASK 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_SHIFT 7 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_WIDTH 1 /* AIF_MSTR */ +#define WM8523_LRCLK_INV 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_MASK 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_SHIFT 6 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_WIDTH 1 /* LRCLK_INV */ +#define WM8523_BCLK_INV 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_MASK 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_SHIFT 5 /* BCLK_INV */ +#define WM8523_BCLK_INV_WIDTH 1 /* BCLK_INV */ +#define WM8523_WL_MASK 0x0018 /* WL - [4:3] */ +#define WM8523_WL_SHIFT 3 /* WL - [4:3] */ +#define WM8523_WL_WIDTH 2 /* WL - [4:3] */ +#define WM8523_FMT_MASK 0x0007 /* FMT - [2:0] */ +#define WM8523_FMT_SHIFT 0 /* FMT - [2:0] */ +#define WM8523_FMT_WIDTH 3 /* FMT - [2:0] */ + +/* + * R4 (0x04) - AIF_CTRL2 + */ +#define WM8523_DAC_OP_MUX_MASK 0x00C0 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_SHIFT 6 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_WIDTH 2 /* DAC_OP_MUX - [7:6] */ +#define WM8523_BCLKDIV_MASK 0x0038 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_SHIFT 3 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_WIDTH 3 /* BCLKDIV - [5:3] */ +#define WM8523_SR_MASK 0x0007 /* SR - [2:0] */ +#define WM8523_SR_SHIFT 0 /* SR - [2:0] */ +#define WM8523_SR_WIDTH 3 /* SR - [2:0] */ + +/* + * R5 (0x05) - DAC_CTRL3 + */ +#define WM8523_ZC 0x0010 /* ZC */ +#define WM8523_ZC_MASK 0x0010 /* ZC */ +#define WM8523_ZC_SHIFT 4 /* ZC */ +#define WM8523_ZC_WIDTH 1 /* ZC */ +#define WM8523_DACR 0x0008 /* DACR */ +#define WM8523_DACR_MASK 0x0008 /* DACR */ +#define WM8523_DACR_SHIFT 3 /* DACR */ +#define WM8523_DACR_WIDTH 1 /* DACR */ +#define WM8523_DACL 0x0004 /* DACL */ +#define WM8523_DACL_MASK 0x0004 /* DACL */ +#define WM8523_DACL_SHIFT 2 /* DACL */ +#define WM8523_DACL_WIDTH 1 /* DACL */ +#define WM8523_VOL_UP_RAMP 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_MASK 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_SHIFT 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_WIDTH 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_DOWN_RAMP 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_MASK 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_SHIFT 0 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_WIDTH 1 /* VOL_DOWN_RAMP */ + +/* + * R6 (0x06) - DAC_GAINL + */ +#define WM8523_DACL_VU 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_MASK 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_SHIFT 9 /* DACL_VU */ +#define WM8523_DACL_VU_WIDTH 1 /* DACL_VU */ +#define WM8523_DACL_VOL_MASK 0x01FF /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_SHIFT 0 /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_WIDTH 9 /* DACL_VOL - [8:0] */ + +/* + * R7 (0x07) - DAC_GAINR + */ +#define WM8523_DACR_VU 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_MASK 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_SHIFT 9 /* DACR_VU */ +#define WM8523_DACR_VU_WIDTH 1 /* DACR_VU */ +#define WM8523_DACR_VOL_MASK 0x01FF /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_SHIFT 0 /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_WIDTH 9 /* DACR_VOL - [8:0] */ + +/* + * R8 (0x08) - ZERO_DETECT + */ +#define WM8523_ZD_COUNT_MASK 0x0003 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_SHIFT 0 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_WIDTH 2 /* ZD_COUNT - [1:0] */ + +extern struct snd_soc_dai wm8523_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8523; + +#endif diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 86c4b24db81..97b9ed95d28 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -24,6 +24,8 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -187,15 +189,22 @@ struct pll_state { unsigned int out; }; +#define WM8580_NUM_SUPPLIES 3 +static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "PVDD", +}; + /* codec private data */ struct wm8580_priv { struct snd_soc_codec codec; + struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; u16 reg_cache[WM8580_MAX_REGISTER + 1]; struct pll_state a; struct pll_state b; }; - /* * read wm8580 register cache */ @@ -922,11 +931,28 @@ static int wm8580_register(struct wm8580_priv *wm8580) memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg)); + for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++) + wm8580->supplies[i].supply = wm8580_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_regulator_get; + } + /* Get the codec into a known state */ ret = wm8580_write(codec, WM8580_RESET, 0); if (ret != 0) { dev_err(codec->dev, "Failed to reset codec: %d\n", ret); - goto err; + goto err_regulator_enable; } for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++) @@ -939,7 +965,7 @@ static int wm8580_register(struct wm8580_priv *wm8580) ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - goto err; + goto err_regulator_enable; } ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); @@ -952,6 +978,10 @@ static int wm8580_register(struct wm8580_priv *wm8580) err_codec: snd_soc_unregister_codec(codec); +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); +err_regulator_get: + regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); err: kfree(wm8580); return ret; @@ -962,6 +992,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580) wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF); snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); snd_soc_unregister_codec(&wm8580->codec); + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); + regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); kfree(wm8580); wm8580_codec = NULL; } @@ -995,6 +1027,21 @@ static int wm8580_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8580_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8580_i2c_suspend NULL +#define wm8580_i2c_resume NULL +#endif + static const struct i2c_device_id wm8580_i2c_id[] = { { "wm8580", 0 }, { } @@ -1008,6 +1055,8 @@ static struct i2c_driver wm8580_i2c_driver = { }, .probe = wm8580_i2c_probe, .remove = wm8580_i2c_remove, + .suspend = wm8580_i2c_suspend, + .resume = wm8580_i2c_resume, .id_table = wm8580_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 7a205876ef4..15600285207 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -26,6 +26,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h> #include "wm8731.h" @@ -113,20 +114,26 @@ static const struct soc_enum wm8731_enum[] = { SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), }; +static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); + static const struct snd_kcontrol_new wm8731_snd_controls[] = { -SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, - 0, 127, 0), +SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, + 0, 127, 0, out_tlv), SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, 7, 1, 0), -SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0), +SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, + in_tlv), SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), -SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1), +SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), -SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1), +SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, + sidetone_tlv), SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), @@ -457,9 +464,11 @@ struct snd_soc_dai wm8731_dai = { .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, .ops = &wm8731_dai_ops, + .symmetric_rates = 1, }; EXPORT_SYMBOL_GPL(wm8731_dai); +#ifdef CONFIG_PM static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -488,6 +497,10 @@ static int wm8731_resume(struct platform_device *pdev) wm8731_set_bias_level(codec, codec->suspend_bias_level); return 0; } +#else +#define wm8731_suspend NULL +#define wm8731_resume NULL +#endif static int wm8731_probe(struct platform_device *pdev) { @@ -555,7 +568,8 @@ static int wm8731_register(struct wm8731_priv *wm8731) if (wm8731_codec) { dev_err(codec->dev, "Another WM8731 is registered\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } mutex_init(&codec->mutex); @@ -578,8 +592,8 @@ static int wm8731_register(struct wm8731_priv *wm8731) ret = wm8731_reset(codec); if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset\n"); - return ret; + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + goto err; } wm8731_dai.dev = codec->dev; @@ -605,17 +619,23 @@ static int wm8731_register(struct wm8731_priv *wm8731) ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - return ret; + goto err; } ret = snd_soc_register_dai(&wm8731_dai); if (ret != 0) { dev_err(codec->dev, "Failed to register DAI: %d\n", ret); snd_soc_unregister_codec(codec); - return ret; + goto err_codec; } return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + kfree(wm8731); + return ret; } static void wm8731_unregister(struct wm8731_priv *wm8731) @@ -680,6 +700,21 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM +static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return snd_soc_suspend_device(&spi->dev); +} + +static int wm8731_spi_resume(struct spi_device *spi) +{ + return snd_soc_resume_device(&spi->dev); +} +#else +#define wm8731_spi_suspend NULL +#define wm8731_spi_resume NULL +#endif + static struct spi_driver wm8731_spi_driver = { .driver = { .name = "wm8731", @@ -687,6 +722,8 @@ static struct spi_driver wm8731_spi_driver = { .owner = THIS_MODULE, }, .probe = wm8731_spi_probe, + .suspend = wm8731_spi_suspend, + .resume = wm8731_spi_resume, .remove = __devexit_p(wm8731_spi_remove), }; #endif /* CONFIG_SPI_MASTER */ @@ -720,6 +757,21 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg) +{ + return snd_soc_suspend_device(&i2c->dev); +} + +static int wm8731_i2c_resume(struct i2c_client *i2c) +{ + return snd_soc_resume_device(&i2c->dev); +} +#else +#define wm8731_i2c_suspend NULL +#define wm8731_i2c_resume NULL +#endif + static const struct i2c_device_id wm8731_i2c_id[] = { { "wm8731", 0 }, { } @@ -733,6 +785,8 @@ static struct i2c_driver wm8731_i2c_driver = { }, .probe = wm8731_i2c_probe, .remove = __devexit_p(wm8731_i2c_remove), + .suspend = wm8731_i2c_suspend, + .resume = wm8731_i2c_resume, .id_table = wm8731_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 49c4b2898af..d80d414cfbb 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1766,6 +1766,21 @@ static int wm8753_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8753_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8753_i2c_suspend NULL +#define wm8753_i2c_resume NULL +#endif + static const struct i2c_device_id wm8753_i2c_id[] = { { "wm8753", 0 }, { } @@ -1779,6 +1794,8 @@ static struct i2c_driver wm8753_i2c_driver = { }, .probe = wm8753_i2c_probe, .remove = wm8753_i2c_remove, + .suspend = wm8753_i2c_suspend, + .resume = wm8753_i2c_resume, .id_table = wm8753_i2c_id, }; #endif @@ -1834,6 +1851,22 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM +static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return snd_soc_suspend_device(&spi->dev); +} + +static int wm8753_spi_resume(struct spi_device *spi) +{ + return snd_soc_resume_device(&spi->dev); +} + +#else +#define wm8753_spi_suspend NULL +#define wm8753_spi_resume NULL +#endif + static struct spi_driver wm8753_spi_driver = { .driver = { .name = "wm8753", @@ -1842,6 +1875,8 @@ static struct spi_driver wm8753_spi_driver = { }, .probe = wm8753_spi_probe, .remove = __devexit_p(wm8753_spi_remove), + .suspend = wm8753_spi_suspend, + .resume = wm8753_spi_resume, }; #endif diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 3c78945244b..ac308993ac5 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -116,6 +116,7 @@ #define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c #define WM8900_REG_DACCTRL_MUTE 0x004 +#define WM8900_REG_DACCTRL_DAC_SB_FILT 0x100 #define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400 #define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800 @@ -439,7 +440,6 @@ SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1), SOC_ENUM("DAC Mute Rate", dac_mute_rate), SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0), SOC_ENUM("DAC Deemphasis", dac_deemphasis), -SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0), SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL, 12, 1, 0), @@ -743,6 +743,17 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream, wm8900_write(codec, WM8900_REG_AUDIO1, reg); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg = wm8900_read(codec, WM8900_REG_DACCTRL); + + if (params_rate(params) <= 24000) + reg |= WM8900_REG_DACCTRL_DAC_SB_FILT; + else + reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT; + + wm8900_write(codec, WM8900_REG_DACCTRL, reg); + } + return 0; } @@ -1388,6 +1399,21 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8900_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8900_i2c_suspend NULL +#define wm8900_i2c_resume NULL +#endif + static const struct i2c_device_id wm8900_i2c_id[] = { { "wm8900", 0 }, { } @@ -1401,6 +1427,8 @@ static struct i2c_driver wm8900_i2c_driver = { }, .probe = wm8900_i2c_probe, .remove = __devexit_p(wm8900_i2c_remove), + .suspend = wm8900_i2c_suspend, + .resume = wm8900_i2c_resume, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index e8d2e3e14c4..c9baeae3e27 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -715,8 +715,6 @@ SOC_ENUM("DAC Soft Mute Rate", soft_mute), SOC_ENUM("DAC Mute Mode", mute_mode), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), SOC_ENUM("DAC De-emphasis", dac_deemphasis), -SOC_SINGLE("DAC Sloping Stopband Filter Switch", - WM8903_DAC_DIGITAL_1, 11, 1, 0), SOC_ENUM("DAC Companding Mode", dac_companding), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), @@ -1373,12 +1371,19 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3); u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0); u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1); + u16 dac_digital1 = wm8903_read(codec, WM8903_DAC_DIGITAL_1); if (substream == wm8903->slave_substream) { dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); return 0; } + /* Enable sloping stopband filter for low sample rates */ + if (fs <= 24000) + dac_digital1 |= WM8903_DAC_SB_FILT; + else + dac_digital1 &= ~WM8903_DAC_SB_FILT; + /* Configure sample rate logic for DSP - choose nearest rate */ dsp_config = 0; best_val = abs(sample_rates[dsp_config].rate - fs); @@ -1503,6 +1508,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2); wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3); + wm8903_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1); return 0; } @@ -1721,6 +1727,21 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8903_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8903_i2c_suspend NULL +#define wm8903_i2c_resume NULL +#endif + /* i2c codec control layer */ static const struct i2c_device_id wm8903_i2c_id[] = { { "wm8903", 0 }, @@ -1735,6 +1756,8 @@ static struct i2c_driver wm8903_i2c_driver = { }, .probe = wm8903_i2c_probe, .remove = __devexit_p(wm8903_i2c_remove), + .suspend = wm8903_i2c_suspend, + .resume = wm8903_i2c_resume, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index b8e17d6bc1f..b69210a7742 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -916,6 +916,21 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8940_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8940_i2c_suspend NULL +#define wm8940_i2c_resume NULL +#endif + static const struct i2c_device_id wm8940_i2c_id[] = { { "wm8940", 0 }, { } @@ -929,6 +944,8 @@ static struct i2c_driver wm8940_i2c_driver = { }, .probe = wm8940_i2c_probe, .remove = __devexit_p(wm8940_i2c_remove), + .suspend = wm8940_i2c_suspend, + .resume = wm8940_i2c_resume, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index e224d8add17..d1769e6c0c4 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -810,7 +810,8 @@ static int wm8960_register(struct wm8960_priv *wm8960) if (wm8960_codec) { dev_err(codec->dev, "Another WM8960 is registered\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } if (!pdata) { @@ -843,7 +844,7 @@ static int wm8960_register(struct wm8960_priv *wm8960) ret = wm8960_reset(codec); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset\n"); - return ret; + goto err; } wm8960_dai.dev = codec->dev; @@ -877,17 +878,22 @@ static int wm8960_register(struct wm8960_priv *wm8960) ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - return ret; + goto err; } ret = snd_soc_register_dai(&wm8960_dai); if (ret != 0) { dev_err(codec->dev, "Failed to register DAI: %d\n", ret); - snd_soc_unregister_codec(codec); - return ret; + goto err_codec; } return 0; + +err_codec: + snd_soc_unregister_codec(codec); +err: + kfree(wm8960); + return ret; } static void wm8960_unregister(struct wm8960_priv *wm8960) @@ -927,6 +933,21 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8960_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8960_i2c_suspend NULL +#define wm8960_i2c_resume NULL +#endif + static const struct i2c_device_id wm8960_i2c_id[] = { { "wm8960", 0 }, { } @@ -940,6 +961,8 @@ static struct i2c_driver wm8960_i2c_driver = { }, .probe = wm8960_i2c_probe, .remove = __devexit_p(wm8960_i2c_remove), + .suspend = wm8960_i2c_suspend, + .resume = wm8960_i2c_resume, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c new file mode 100644 index 00000000000..bd1af92a122 --- /dev/null +++ b/sound/soc/codecs/wm8961.c @@ -0,0 +1,1326 @@ +/* + * wm8961.c -- WM8961 ALSA SoC Audio driver + * + * Author: Mark Brown + * + * 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. + * + * Currently unimplemented features: + * - ALC + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8961.h" + +#define WM8961_MAX_REGISTER 0xFC + +static u16 wm8961_reg_defaults[] = { + 0x009F, /* R0 - Left Input volume */ + 0x009F, /* R1 - Right Input volume */ + 0x0000, /* R2 - LOUT1 volume */ + 0x0000, /* R3 - ROUT1 volume */ + 0x0020, /* R4 - Clocking1 */ + 0x0008, /* R5 - ADC & DAC Control 1 */ + 0x0000, /* R6 - ADC & DAC Control 2 */ + 0x000A, /* R7 - Audio Interface 0 */ + 0x01F4, /* R8 - Clocking2 */ + 0x0000, /* R9 - Audio Interface 1 */ + 0x00FF, /* R10 - Left DAC volume */ + 0x00FF, /* R11 - Right DAC volume */ + 0x0000, /* R12 */ + 0x0000, /* R13 */ + 0x0040, /* R14 - Audio Interface 2 */ + 0x0000, /* R15 - Software Reset */ + 0x0000, /* R16 */ + 0x007B, /* R17 - ALC1 */ + 0x0000, /* R18 - ALC2 */ + 0x0032, /* R19 - ALC3 */ + 0x0000, /* R20 - Noise Gate */ + 0x00C0, /* R21 - Left ADC volume */ + 0x00C0, /* R22 - Right ADC volume */ + 0x0120, /* R23 - Additional control(1) */ + 0x0000, /* R24 - Additional control(2) */ + 0x0000, /* R25 - Pwr Mgmt (1) */ + 0x0000, /* R26 - Pwr Mgmt (2) */ + 0x0000, /* R27 - Additional Control (3) */ + 0x0000, /* R28 - Anti-pop */ + 0x0000, /* R29 */ + 0x005F, /* R30 - Clocking 3 */ + 0x0000, /* R31 */ + 0x0000, /* R32 - ADCL signal path */ + 0x0000, /* R33 - ADCR signal path */ + 0x0000, /* R34 */ + 0x0000, /* R35 */ + 0x0000, /* R36 */ + 0x0000, /* R37 */ + 0x0000, /* R38 */ + 0x0000, /* R39 */ + 0x0000, /* R40 - LOUT2 volume */ + 0x0000, /* R41 - ROUT2 volume */ + 0x0000, /* R42 */ + 0x0000, /* R43 */ + 0x0000, /* R44 */ + 0x0000, /* R45 */ + 0x0000, /* R46 */ + 0x0000, /* R47 - Pwr Mgmt (3) */ + 0x0023, /* R48 - Additional Control (4) */ + 0x0000, /* R49 - Class D Control 1 */ + 0x0000, /* R50 */ + 0x0003, /* R51 - Class D Control 2 */ + 0x0000, /* R52 */ + 0x0000, /* R53 */ + 0x0000, /* R54 */ + 0x0000, /* R55 */ + 0x0106, /* R56 - Clocking 4 */ + 0x0000, /* R57 - DSP Sidetone 0 */ + 0x0000, /* R58 - DSP Sidetone 1 */ + 0x0000, /* R59 */ + 0x0000, /* R60 - DC Servo 0 */ + 0x0000, /* R61 - DC Servo 1 */ + 0x0000, /* R62 */ + 0x015E, /* R63 - DC Servo 3 */ + 0x0010, /* R64 */ + 0x0010, /* R65 - DC Servo 5 */ + 0x0000, /* R66 */ + 0x0001, /* R67 */ + 0x0003, /* R68 - Analogue PGA Bias */ + 0x0000, /* R69 - Analogue HP 0 */ + 0x0060, /* R70 */ + 0x01FB, /* R71 - Analogue HP 2 */ + 0x0000, /* R72 - Charge Pump 1 */ + 0x0065, /* R73 */ + 0x005F, /* R74 */ + 0x0059, /* R75 */ + 0x006B, /* R76 */ + 0x0038, /* R77 */ + 0x000C, /* R78 */ + 0x000A, /* R79 */ + 0x006B, /* R80 */ + 0x0000, /* R81 */ + 0x0000, /* R82 - Charge Pump B */ + 0x0087, /* R83 */ + 0x0000, /* R84 */ + 0x005C, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 - Write Sequencer 1 */ + 0x0000, /* R88 - Write Sequencer 2 */ + 0x0000, /* R89 - Write Sequencer 3 */ + 0x0000, /* R90 - Write Sequencer 4 */ + 0x0000, /* R91 - Write Sequencer 5 */ + 0x0000, /* R92 - Write Sequencer 6 */ + 0x0000, /* R93 - Write Sequencer 7 */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 */ + 0x0000, /* R97 */ + 0x0000, /* R98 */ + 0x0000, /* R99 */ + 0x0000, /* R100 */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x0000, /* R104 */ + 0x0000, /* R105 */ + 0x0000, /* R106 */ + 0x0000, /* R107 */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 */ + 0x0000, /* R112 */ + 0x0000, /* R113 */ + 0x0000, /* R114 */ + 0x0000, /* R115 */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x0000, /* R128 */ + 0x0000, /* R129 */ + 0x0000, /* R130 */ + 0x0000, /* R131 */ + 0x0000, /* R132 */ + 0x0000, /* R133 */ + 0x0000, /* R134 */ + 0x0000, /* R135 */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0000, /* R140 */ + 0x0000, /* R141 */ + 0x0000, /* R142 */ + 0x0000, /* R143 */ + 0x0000, /* R144 */ + 0x0000, /* R145 */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x0000, /* R152 */ + 0x0000, /* R153 */ + 0x0000, /* R154 */ + 0x0000, /* R155 */ + 0x0000, /* R156 */ + 0x0000, /* R157 */ + 0x0000, /* R158 */ + 0x0000, /* R159 */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 */ + 0x0000, /* R164 */ + 0x0000, /* R165 */ + 0x0000, /* R166 */ + 0x0000, /* R167 */ + 0x0000, /* R168 */ + 0x0000, /* R169 */ + 0x0000, /* R170 */ + 0x0000, /* R171 */ + 0x0000, /* R172 */ + 0x0000, /* R173 */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 */ + 0x0000, /* R177 */ + 0x0000, /* R178 */ + 0x0000, /* R179 */ + 0x0000, /* R180 */ + 0x0000, /* R181 */ + 0x0000, /* R182 */ + 0x0000, /* R183 */ + 0x0000, /* R184 */ + 0x0000, /* R185 */ + 0x0000, /* R186 */ + 0x0000, /* R187 */ + 0x0000, /* R188 */ + 0x0000, /* R189 */ + 0x0000, /* R190 */ + 0x0000, /* R191 */ + 0x0000, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x0000, /* R195 */ + 0x0030, /* R196 */ + 0x0006, /* R197 */ + 0x0000, /* R198 */ + 0x0060, /* R199 */ + 0x0000, /* R200 */ + 0x003F, /* R201 */ + 0x0000, /* R202 */ + 0x0000, /* R203 */ + 0x0000, /* R204 */ + 0x0001, /* R205 */ + 0x0000, /* R206 */ + 0x0181, /* R207 */ + 0x0005, /* R208 */ + 0x0008, /* R209 */ + 0x0008, /* R210 */ + 0x0000, /* R211 */ + 0x013B, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 */ + 0x0000, /* R216 */ + 0x0070, /* R217 */ + 0x0000, /* R218 */ + 0x0000, /* R219 */ + 0x0000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0003, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 */ + 0x0001, /* R226 */ + 0x0008, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0x0000, /* R230 */ + 0x0000, /* R231 */ + 0x0004, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0080, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0052, /* R245 */ + 0x0110, /* R246 */ + 0x0040, /* R247 */ + 0x0000, /* R248 */ + 0x0030, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0001, /* R252 - General test 1 */ +}; + +struct wm8961_priv { + struct snd_soc_codec codec; + int sysclk; + u16 reg_cache[WM8961_MAX_REGISTER]; +}; + +static int wm8961_reg_is_volatile(int reg) +{ + switch (reg) { + case WM8961_WRITE_SEQUENCER_7: + case WM8961_DC_SERVO_1: + return 1; + + default: + return 0; + } +} + +static unsigned int wm8961_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + BUG_ON(reg > WM8961_MAX_REGISTER); + return cache[reg]; +} + +static unsigned int wm8961_read_hw(struct snd_soc_codec *codec, u8 reg) +{ + struct i2c_msg xfer[2]; + u16 data; + int ret; + struct i2c_client *client = codec->control_data; + + BUG_ON(reg > WM8961_MAX_REGISTER); + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (u8 *)&data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + return 0; + } + + return (data >> 8) | ((data & 0xff) << 8); +} + +static unsigned int wm8961_read(struct snd_soc_codec *codec, unsigned int reg) +{ + if (wm8961_reg_is_volatile(reg)) + return wm8961_read_hw(codec, reg); + else + return wm8961_read_reg_cache(codec, reg); +} + +static int wm8961_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 *cache = codec->reg_cache; + u8 data[3]; + + BUG_ON(reg > WM8961_MAX_REGISTER); + + if (!wm8961_reg_is_volatile(reg)) + cache[reg] = value; + + data[0] = reg; + data[1] = value >> 8; + data[2] = value & 0x00ff; + + if (codec->hw_write(codec->control_data, data, 3) == 3) + return 0; + else + return -EIO; +} + +static int wm8961_reset(struct snd_soc_codec *codec) +{ + return wm8961_write(codec, WM8961_SOFTWARE_RESET, 0); +} + +/* + * The headphone output supports special anti-pop sequences giving + * silent power up and power down. + */ +static int wm8961_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + u16 hp_reg = wm8961_read(codec, WM8961_ANALOGUE_HP_0); + u16 cp_reg = wm8961_read(codec, WM8961_CHARGE_PUMP_1); + u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2); + u16 dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1); + int timeout = 500; + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Make sure the output is shorted */ + hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Enable the charge pump */ + cp_reg |= WM8961_CP_ENA; + wm8961_write(codec, WM8961_CHARGE_PUMP_1, cp_reg); + mdelay(5); + + /* Enable the PGA */ + pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA; + wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg); + + /* Enable the amplifier */ + hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA; + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Second stage enable */ + hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY; + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Enable the DC servo & trigger startup */ + dcs_reg |= + WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL; + dev_dbg(codec->dev, "Enabling DC servo\n"); + + wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg); + do { + msleep(1); + dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1); + } while (--timeout && + dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_TRIG_STARTUP_HPL)); + if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_TRIG_STARTUP_HPL)) + dev_err(codec->dev, "DC servo timed out\n"); + else + dev_dbg(codec->dev, "DC servo startup complete\n"); + + /* Enable the output stage */ + hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP; + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Remove the short on the output stage */ + hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT; + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + } + + if (event & SND_SOC_DAPM_PRE_PMD) { + /* Short the output */ + hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable the output stage */ + hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP); + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable DC offset cancellation */ + dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR | + WM8961_DCS_ENA_CHAN_HPL); + wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg); + + /* Finish up */ + hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA | + WM8961_HPL_ENA_DLY | WM8961_HPL_ENA); + wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable the PGA */ + pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA); + wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg); + + /* Disable the charge pump */ + dev_dbg(codec->dev, "Disabling charge pump\n"); + wm8961_write(codec, WM8961_CHARGE_PUMP_1, + cp_reg & ~WM8961_CP_ENA); + } + + return 0; +} + +static int wm8961_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2); + u16 spk_reg = wm8961_read(codec, WM8961_CLASS_D_CONTROL_1); + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Enable the PGA */ + pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA; + wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg); + + /* Enable the amplifier */ + spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA; + wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg); + } + + if (event & SND_SOC_DAPM_PRE_PMD) { + /* Enable the amplifier */ + spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA); + wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg); + + /* Enable the PGA */ + pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA); + wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg); + } + + return 0; +} + +static const char *adc_hpf_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3", +}; + +static const struct soc_enum adc_hpf = + SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text); + +static const char *dac_deemph_text[] = { + "None", "32kHz", "44.1kHz", "48kHz", +}; + +static const struct soc_enum dac_deemph = + SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text); + +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); +static unsigned int boost_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0), +}; +static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); + +static const struct snd_kcontrol_new wm8961_snd_controls[] = { +SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, + 0, 127, 0, out_tlv), +SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2, + 6, 3, 7, 0, hp_sec_tlv), +SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, + 7, 1, 0), + +SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, + 7, 1, 0), +SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0), + +SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0), +SOC_ENUM("DAC Deemphasis", dac_deemph), +SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0), + +SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0, + WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv), + +SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0), +SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), + +SOC_DOUBLE_R_TLV("Capture Volume", + WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME, + 1, 119, 0, adc_tlv), +SOC_DOUBLE_R_TLV("Capture Boost Volume", + WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH, + 4, 3, 0, boost_tlv), +SOC_DOUBLE_R_TLV("Capture PGA Volume", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 0, 62, 0, pga_tlv), +SOC_DOUBLE_R("Capture PGA ZC Switch", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 6, 1, 1), +SOC_DOUBLE_R("Capture PGA Switch", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 7, 1, 1), +}; + +static const char *sidetone_text[] = { + "None", "Left", "Right" +}; + +static const struct soc_enum dacl_sidetone = + SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text); + +static const struct soc_enum dacr_sidetone = + SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text); + +static const struct snd_kcontrol_new dacl_mux = + SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone); + +static const struct snd_kcontrol_new dacr_mux = + SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone); + +static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT"), +SND_SOC_DAPM_INPUT("RINPUT"), + +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0), +SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0), + +SND_SOC_DAPM_MICBIAS("MICBIAS", WM8961_PWR_MGMT_1, 1, 0), + +SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux), +SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux), + +SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0), +SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0), + +/* Handle as a mono path for DCS */ +SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, + 4, 0, NULL, 0, wm8961_hp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM, + 4, 0, NULL, 0, wm8961_spk_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), +SND_SOC_DAPM_OUTPUT("SPK_LN"), +SND_SOC_DAPM_OUTPUT("SPK_LP"), +SND_SOC_DAPM_OUTPUT("SPK_RN"), +SND_SOC_DAPM_OUTPUT("SPK_RP"), +}; + + +static const struct snd_soc_dapm_route audio_paths[] = { + { "DACL", NULL, "CLK_DSP" }, + { "DACL", NULL, "DACL Sidetone" }, + { "DACR", NULL, "CLK_DSP" }, + { "DACR", NULL, "DACR Sidetone" }, + + { "DACL Sidetone", "Left", "ADCL" }, + { "DACL Sidetone", "Right", "ADCR" }, + + { "DACR Sidetone", "Left", "ADCL" }, + { "DACR Sidetone", "Right", "ADCR" }, + + { "HP_L", NULL, "Headphone Output" }, + { "HP_R", NULL, "Headphone Output" }, + { "Headphone Output", NULL, "DACL" }, + { "Headphone Output", NULL, "DACR" }, + + { "SPK_LN", NULL, "Speaker Output" }, + { "SPK_LP", NULL, "Speaker Output" }, + { "SPK_RN", NULL, "Speaker Output" }, + { "SPK_RP", NULL, "Speaker Output" }, + + { "Speaker Output", NULL, "DACL" }, + { "Speaker Output", NULL, "DACR" }, + + { "ADCL", NULL, "Left Input" }, + { "ADCL", NULL, "CLK_DSP" }, + { "ADCR", NULL, "Right Input" }, + { "ADCR", NULL, "CLK_DSP" }, + + { "Left Input", NULL, "LINPUT" }, + { "Right Input", NULL, "RINPUT" }, + +}; + +/* Values for CLK_SYS_RATE */ +static struct { + int ratio; + u16 val; +} wm8961_clk_sys_ratio[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 768, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +/* Values for SAMPLE_RATE */ +static struct { + int rate; + u16 val; +} wm8961_srate[] = { + { 48000, 0 }, + { 44100, 0 }, + { 32000, 1 }, + { 22050, 2 }, + { 24000, 2 }, + { 16000, 3 }, + { 11250, 4 }, + { 12000, 4 }, + { 8000, 5 }, +}; + +static int wm8961_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8961_priv *wm8961 = codec->private_data; + int i, best, target, fs; + u16 reg; + + fs = params_rate(params); + + if (!wm8961->sysclk) { + dev_err(codec->dev, "MCLK has not been specified\n"); + return -EINVAL; + } + + /* Find the closest sample rate for the filters */ + best = 0; + for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) { + if (abs(wm8961_srate[i].rate - fs) < + abs(wm8961_srate[best].rate - fs)) + best = i; + } + reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_3); + reg &= ~WM8961_SAMPLE_RATE_MASK; + reg |= wm8961_srate[best].val; + wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg); + dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n", + wm8961_srate[best].rate, fs); + + /* Select a CLK_SYS/fs ratio equal to or higher than required */ + target = wm8961->sysclk / fs; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) { + dev_err(codec->dev, + "SYSCLK must be at least 64*fs for DAC\n"); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) { + dev_err(codec->dev, + "SYSCLK must be at least 256*fs for ADC\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) { + if (wm8961_clk_sys_ratio[i].ratio >= target) + break; + } + if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) { + dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n"); + return -EINVAL; + } + dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n", + wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs, + wm8961->sysclk / fs); + + reg = wm8961_read(codec, WM8961_CLOCKING_4); + reg &= ~WM8961_CLK_SYS_RATE_MASK; + reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT; + wm8961_write(codec, WM8961_CLOCKING_4, reg); + + reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0); + reg &= ~WM8961_WL_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg |= 1 << WM8961_WL_SHIFT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + reg |= 2 << WM8961_WL_SHIFT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + reg |= 3 << WM8961_WL_SHIFT; + break; + default: + return -EINVAL; + } + wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, reg); + + /* Sloping stop-band filter is recommended for <= 24kHz */ + reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2); + if (fs <= 24000) + reg |= WM8961_DACSLOPE; + else + reg &= WM8961_DACSLOPE; + wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); + + return 0; +} + +static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, + int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8961_priv *wm8961 = codec->private_data; + u16 reg = wm8961_read(codec, WM8961_CLOCKING1); + + if (freq > 33000000) { + dev_err(codec->dev, "MCLK must be <33MHz\n"); + return -EINVAL; + } + + if (freq > 16500000) { + dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq); + reg |= WM8961_MCLKDIV; + freq /= 2; + } else { + dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq); + reg &= WM8961_MCLKDIV; + } + + wm8961_write(codec, WM8961_CLOCKING1, reg); + + wm8961->sysclk = freq; + + return 0; +} + +static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u16 aif = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0); + + aif &= ~(WM8961_BCLKINV | WM8961_LRP | + WM8961_MS | WM8961_FORMAT_MASK); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aif |= WM8961_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + break; + + case SND_SOC_DAIFMT_LEFT_J: + aif |= 1; + break; + + case SND_SOC_DAIFMT_I2S: + aif |= 2; + break; + + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8961_LRP; + case SND_SOC_DAIFMT_DSP_A: + aif |= 3; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8961_LRP; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8961_BCLKINV; + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8961_BCLKINV | WM8961_LRP; + break; + default: + return -EINVAL; + } + + return wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, aif); +} + +static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + u16 reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_2); + + if (tristate) + reg |= WM8961_TRIS; + else + reg &= ~WM8961_TRIS; + + return wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg); +} + +static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_1); + + if (mute) + reg |= WM8961_DACMU; + else + reg &= ~WM8961_DACMU; + + msleep(17); + + return wm8961_write(codec, WM8961_ADC_DAC_CONTROL_1, reg); +} + +static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +{ + struct snd_soc_codec *codec = dai->codec; + u16 reg; + + switch (div_id) { + case WM8961_BCLK: + reg = wm8961_read(codec, WM8961_CLOCKING2); + reg &= ~WM8961_BCLKDIV_MASK; + reg |= div; + wm8961_write(codec, WM8961_CLOCKING2, reg); + break; + + case WM8961_LRCLK: + reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_2); + reg &= ~WM8961_LRCLK_RATE_MASK; + reg |= div; + wm8961_write(codec, WM8961_AUDIO_INTERFACE_2, reg); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8961_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 reg; + + /* This is all slightly unusual since we have no bypass paths + * and the output amplifier structure means we can just slam + * the biases straight up rather than having to ramp them + * slowly. + */ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (codec->bias_level == SND_SOC_BIAS_STANDBY) { + /* Enable bias generation */ + reg = wm8961_read(codec, WM8961_ANTI_POP); + reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; + wm8961_write(codec, WM8961_ANTI_POP, reg); + + /* VMID=2*50k, VREF */ + reg = wm8961_read(codec, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VMIDSEL_MASK; + reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF; + wm8961_write(codec, WM8961_PWR_MGMT_1, reg); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_PREPARE) { + /* VREF off */ + reg = wm8961_read(codec, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VREF; + wm8961_write(codec, WM8961_PWR_MGMT_1, reg); + + /* Bias generation off */ + reg = wm8961_read(codec, WM8961_ANTI_POP); + reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN); + wm8961_write(codec, WM8961_ANTI_POP, reg); + + /* VMID off */ + reg = wm8961_read(codec, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VMIDSEL_MASK; + wm8961_write(codec, WM8961_PWR_MGMT_1, reg); + } + break; + + case SND_SOC_BIAS_OFF: + break; + } + + codec->bias_level = level; + + return 0; +} + + +#define WM8961_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8961_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops wm8961_dai_ops = { + .hw_params = wm8961_hw_params, + .set_sysclk = wm8961_set_sysclk, + .set_fmt = wm8961_set_fmt, + .digital_mute = wm8961_digital_mute, + .set_tristate = wm8961_set_tristate, + .set_clkdiv = wm8961_set_clkdiv, +}; + +struct snd_soc_dai wm8961_dai = { + .name = "WM8961", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8961_RATES, + .formats = WM8961_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8961_RATES, + .formats = WM8961_FORMATS,}, + .ops = &wm8961_dai_ops, +}; +EXPORT_SYMBOL_GPL(wm8961_dai); + + +static struct snd_soc_codec *wm8961_codec; + +static int wm8961_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (wm8961_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = wm8961_codec; + codec = wm8961_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, wm8961_snd_controls, + ARRAY_SIZE(wm8961_snd_controls)); + snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets, + ARRAY_SIZE(wm8961_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_new_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +static int wm8961_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +#ifdef CONFIG_PM +static int wm8961_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int wm8961_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + u16 *reg_cache = codec->reg_cache; + int i; + + for (i = 0; i < codec->reg_cache_size; i++) { + if (i == WM8961_SOFTWARE_RESET) + continue; + + wm8961_write(codec, i, reg_cache[i]); + } + + wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define wm8961_suspend NULL +#define wm8961_resume NULL +#endif + +struct snd_soc_codec_device soc_codec_dev_wm8961 = { + .probe = wm8961_probe, + .remove = wm8961_remove, + .suspend = wm8961_suspend, + .resume = wm8961_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961); + +static int wm8961_register(struct wm8961_priv *wm8961) +{ + struct snd_soc_codec *codec = &wm8961->codec; + int ret; + u16 reg; + + if (wm8961_codec) { + dev_err(codec->dev, "Another WM8961 is registered\n"); + ret = -EINVAL; + goto err; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = wm8961; + codec->name = "WM8961"; + codec->owner = THIS_MODULE; + codec->read = wm8961_read; + codec->write = wm8961_write; + codec->dai = &wm8961_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache); + codec->reg_cache = &wm8961->reg_cache; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = wm8961_set_bias_level; + + memcpy(codec->reg_cache, wm8961_reg_defaults, + sizeof(wm8961_reg_defaults)); + + reg = wm8961_read_hw(codec, WM8961_SOFTWARE_RESET); + if (reg != 0x1801) { + dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg); + ret = -EINVAL; + goto err; + } + + reg = wm8961_read_hw(codec, WM8961_RIGHT_INPUT_VOLUME); + dev_info(codec->dev, "WM8961 family %d revision %c\n", + (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT, + ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT) + + 'A'); + + ret = wm8961_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + return ret; + } + + /* Enable class W */ + reg = wm8961_read(codec, WM8961_CHARGE_PUMP_B); + reg |= WM8961_CP_DYN_PWR_MASK; + wm8961_write(codec, WM8961_CHARGE_PUMP_B, reg); + + /* Latch volume update bits (right channel only, we always + * write both out) and default ZC on. */ + reg = wm8961_read(codec, WM8961_ROUT1_VOLUME); + wm8961_write(codec, WM8961_ROUT1_VOLUME, + reg | WM8961_LO1ZC | WM8961_OUT1VU); + wm8961_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC); + reg = wm8961_read(codec, WM8961_ROUT2_VOLUME); + wm8961_write(codec, WM8961_ROUT2_VOLUME, + reg | WM8961_SPKRZC | WM8961_SPKVU); + wm8961_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC); + + reg = wm8961_read(codec, WM8961_RIGHT_ADC_VOLUME); + wm8961_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU); + reg = wm8961_read(codec, WM8961_RIGHT_INPUT_VOLUME); + wm8961_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU); + + /* Use soft mute by default */ + reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2); + reg |= WM8961_DACSMM; + wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); + + /* Use automatic clocking mode by default; for now this is all + * we support. + */ + reg = wm8961_read(codec, WM8961_CLOCKING_3); + reg &= ~WM8961_MANUAL_MODE; + wm8961_write(codec, WM8961_CLOCKING_3, reg); + + wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + wm8961_dai.dev = codec->dev; + + wm8961_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&wm8961_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } + + return 0; + +err: + kfree(wm8961); + return ret; +} + +static void wm8961_unregister(struct wm8961_priv *wm8961) +{ + wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&wm8961_dai); + snd_soc_unregister_codec(&wm8961->codec); + kfree(wm8961); + wm8961_codec = NULL; +} + +static __devinit int wm8961_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8961_priv *wm8961; + struct snd_soc_codec *codec; + + wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL); + if (wm8961 == NULL) + return -ENOMEM; + + codec = &wm8961->codec; + codec->hw_write = (hw_write_t)i2c_master_send; + + i2c_set_clientdata(i2c, wm8961); + codec->control_data = i2c; + + codec->dev = &i2c->dev; + + return wm8961_register(wm8961); +} + +static __devexit int wm8961_i2c_remove(struct i2c_client *client) +{ + struct wm8961_priv *wm8961 = i2c_get_clientdata(client); + wm8961_unregister(wm8961); + return 0; +} + +#ifdef CONFIG_PM +static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8961_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8961_i2c_suspend NULL +#define wm8961_i2c_resume NULL +#endif + +static const struct i2c_device_id wm8961_i2c_id[] = { + { "wm8961", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); + +static struct i2c_driver wm8961_i2c_driver = { + .driver = { + .name = "wm8961", + .owner = THIS_MODULE, + }, + .probe = wm8961_i2c_probe, + .remove = __devexit_p(wm8961_i2c_remove), + .suspend = wm8961_i2c_suspend, + .resume = wm8961_i2c_resume, + .id_table = wm8961_i2c_id, +}; + +static int __init wm8961_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&wm8961_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n", + ret); + } + + return ret; +} +module_init(wm8961_modinit); + +static void __exit wm8961_exit(void) +{ + i2c_del_driver(&wm8961_i2c_driver); +} +module_exit(wm8961_exit); + + +MODULE_DESCRIPTION("ASoC WM8961 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h new file mode 100644 index 00000000000..5513bfd720d --- /dev/null +++ b/sound/soc/codecs/wm8961.h @@ -0,0 +1,866 @@ +/* + * wm8961.h -- WM8961 Soc Audio 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 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8961_H +#define _WM8961_H + +#include <sound/soc.h> + +extern struct snd_soc_codec_device soc_codec_dev_wm8961; +extern struct snd_soc_dai wm8961_dai; + +#define WM8961_BCLK 1 +#define WM8961_LRCLK 2 + +#define WM8961_BCLK_DIV_1 0 +#define WM8961_BCLK_DIV_1_5 1 +#define WM8961_BCLK_DIV_2 2 +#define WM8961_BCLK_DIV_3 3 +#define WM8961_BCLK_DIV_4 4 +#define WM8961_BCLK_DIV_5_5 5 +#define WM8961_BCLK_DIV_6 6 +#define WM8961_BCLK_DIV_8 7 +#define WM8961_BCLK_DIV_11 8 +#define WM8961_BCLK_DIV_12 9 +#define WM8961_BCLK_DIV_16 10 +#define WM8961_BCLK_DIV_24 11 +#define WM8961_BCLK_DIV_32 13 + + +/* + * Register values. + */ +#define WM8961_LEFT_INPUT_VOLUME 0x00 +#define WM8961_RIGHT_INPUT_VOLUME 0x01 +#define WM8961_LOUT1_VOLUME 0x02 +#define WM8961_ROUT1_VOLUME 0x03 +#define WM8961_CLOCKING1 0x04 +#define WM8961_ADC_DAC_CONTROL_1 0x05 +#define WM8961_ADC_DAC_CONTROL_2 0x06 +#define WM8961_AUDIO_INTERFACE_0 0x07 +#define WM8961_CLOCKING2 0x08 +#define WM8961_AUDIO_INTERFACE_1 0x09 +#define WM8961_LEFT_DAC_VOLUME 0x0A +#define WM8961_RIGHT_DAC_VOLUME 0x0B +#define WM8961_AUDIO_INTERFACE_2 0x0E +#define WM8961_SOFTWARE_RESET 0x0F +#define WM8961_ALC1 0x11 +#define WM8961_ALC2 0x12 +#define WM8961_ALC3 0x13 +#define WM8961_NOISE_GATE 0x14 +#define WM8961_LEFT_ADC_VOLUME 0x15 +#define WM8961_RIGHT_ADC_VOLUME 0x16 +#define WM8961_ADDITIONAL_CONTROL_1 0x17 +#define WM8961_ADDITIONAL_CONTROL_2 0x18 +#define WM8961_PWR_MGMT_1 0x19 +#define WM8961_PWR_MGMT_2 0x1A +#define WM8961_ADDITIONAL_CONTROL_3 0x1B +#define WM8961_ANTI_POP 0x1C +#define WM8961_CLOCKING_3 0x1E +#define WM8961_ADCL_SIGNAL_PATH 0x20 +#define WM8961_ADCR_SIGNAL_PATH 0x21 +#define WM8961_LOUT2_VOLUME 0x28 +#define WM8961_ROUT2_VOLUME 0x29 +#define WM8961_PWR_MGMT_3 0x2F +#define WM8961_ADDITIONAL_CONTROL_4 0x30 +#define WM8961_CLASS_D_CONTROL_1 0x31 +#define WM8961_CLASS_D_CONTROL_2 0x33 +#define WM8961_CLOCKING_4 0x38 +#define WM8961_DSP_SIDETONE_0 0x39 +#define WM8961_DSP_SIDETONE_1 0x3A +#define WM8961_DC_SERVO_0 0x3C +#define WM8961_DC_SERVO_1 0x3D +#define WM8961_DC_SERVO_3 0x3F +#define WM8961_DC_SERVO_5 0x41 +#define WM8961_ANALOGUE_PGA_BIAS 0x44 +#define WM8961_ANALOGUE_HP_0 0x45 +#define WM8961_ANALOGUE_HP_2 0x47 +#define WM8961_CHARGE_PUMP_1 0x48 +#define WM8961_CHARGE_PUMP_B 0x52 +#define WM8961_WRITE_SEQUENCER_1 0x57 +#define WM8961_WRITE_SEQUENCER_2 0x58 +#define WM8961_WRITE_SEQUENCER_3 0x59 +#define WM8961_WRITE_SEQUENCER_4 0x5A +#define WM8961_WRITE_SEQUENCER_5 0x5B +#define WM8961_WRITE_SEQUENCER_6 0x5C +#define WM8961_WRITE_SEQUENCER_7 0x5D +#define WM8961_GENERAL_TEST_1 0xFC + + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Left Input volume + */ +#define WM8961_IPVU 0x0100 /* IPVU */ +#define WM8961_IPVU_MASK 0x0100 /* IPVU */ +#define WM8961_IPVU_SHIFT 8 /* IPVU */ +#define WM8961_IPVU_WIDTH 1 /* IPVU */ +#define WM8961_LINMUTE 0x0080 /* LINMUTE */ +#define WM8961_LINMUTE_MASK 0x0080 /* LINMUTE */ +#define WM8961_LINMUTE_SHIFT 7 /* LINMUTE */ +#define WM8961_LINMUTE_WIDTH 1 /* LINMUTE */ +#define WM8961_LIZC 0x0040 /* LIZC */ +#define WM8961_LIZC_MASK 0x0040 /* LIZC */ +#define WM8961_LIZC_SHIFT 6 /* LIZC */ +#define WM8961_LIZC_WIDTH 1 /* LIZC */ +#define WM8961_LINVOL_MASK 0x003F /* LINVOL - [5:0] */ +#define WM8961_LINVOL_SHIFT 0 /* LINVOL - [5:0] */ +#define WM8961_LINVOL_WIDTH 6 /* LINVOL - [5:0] */ + +/* + * R1 (0x01) - Right Input volume + */ +#define WM8961_DEVICE_ID_MASK 0xF000 /* DEVICE_ID - [15:12] */ +#define WM8961_DEVICE_ID_SHIFT 12 /* DEVICE_ID - [15:12] */ +#define WM8961_DEVICE_ID_WIDTH 4 /* DEVICE_ID - [15:12] */ +#define WM8961_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */ +#define WM8961_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */ +#define WM8961_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */ +#define WM8961_IPVU 0x0100 /* IPVU */ +#define WM8961_IPVU_MASK 0x0100 /* IPVU */ +#define WM8961_IPVU_SHIFT 8 /* IPVU */ +#define WM8961_IPVU_WIDTH 1 /* IPVU */ +#define WM8961_RINMUTE 0x0080 /* RINMUTE */ +#define WM8961_RINMUTE_MASK 0x0080 /* RINMUTE */ +#define WM8961_RINMUTE_SHIFT 7 /* RINMUTE */ +#define WM8961_RINMUTE_WIDTH 1 /* RINMUTE */ +#define WM8961_RIZC 0x0040 /* RIZC */ +#define WM8961_RIZC_MASK 0x0040 /* RIZC */ +#define WM8961_RIZC_SHIFT 6 /* RIZC */ +#define WM8961_RIZC_WIDTH 1 /* RIZC */ +#define WM8961_RINVOL_MASK 0x003F /* RINVOL - [5:0] */ +#define WM8961_RINVOL_SHIFT 0 /* RINVOL - [5:0] */ +#define WM8961_RINVOL_WIDTH 6 /* RINVOL - [5:0] */ + +/* + * R2 (0x02) - LOUT1 volume + */ +#define WM8961_OUT1VU 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8961_LO1ZC 0x0080 /* LO1ZC */ +#define WM8961_LO1ZC_MASK 0x0080 /* LO1ZC */ +#define WM8961_LO1ZC_SHIFT 7 /* LO1ZC */ +#define WM8961_LO1ZC_WIDTH 1 /* LO1ZC */ +#define WM8961_LOUT1VOL_MASK 0x007F /* LOUT1VOL - [6:0] */ +#define WM8961_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [6:0] */ +#define WM8961_LOUT1VOL_WIDTH 7 /* LOUT1VOL - [6:0] */ + +/* + * R3 (0x03) - ROUT1 volume + */ +#define WM8961_OUT1VU 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8961_RO1ZC 0x0080 /* RO1ZC */ +#define WM8961_RO1ZC_MASK 0x0080 /* RO1ZC */ +#define WM8961_RO1ZC_SHIFT 7 /* RO1ZC */ +#define WM8961_RO1ZC_WIDTH 1 /* RO1ZC */ +#define WM8961_ROUT1VOL_MASK 0x007F /* ROUT1VOL - [6:0] */ +#define WM8961_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [6:0] */ +#define WM8961_ROUT1VOL_WIDTH 7 /* ROUT1VOL - [6:0] */ + +/* + * R4 (0x04) - Clocking1 + */ +#define WM8961_ADCDIV_MASK 0x01C0 /* ADCDIV - [8:6] */ +#define WM8961_ADCDIV_SHIFT 6 /* ADCDIV - [8:6] */ +#define WM8961_ADCDIV_WIDTH 3 /* ADCDIV - [8:6] */ +#define WM8961_DACDIV_MASK 0x0038 /* DACDIV - [5:3] */ +#define WM8961_DACDIV_SHIFT 3 /* DACDIV - [5:3] */ +#define WM8961_DACDIV_WIDTH 3 /* DACDIV - [5:3] */ +#define WM8961_MCLKDIV 0x0004 /* MCLKDIV */ +#define WM8961_MCLKDIV_MASK 0x0004 /* MCLKDIV */ +#define WM8961_MCLKDIV_SHIFT 2 /* MCLKDIV */ +#define WM8961_MCLKDIV_WIDTH 1 /* MCLKDIV */ + +/* + * R5 (0x05) - ADC & DAC Control 1 + */ +#define WM8961_ADCPOL_MASK 0x0060 /* ADCPOL - [6:5] */ +#define WM8961_ADCPOL_SHIFT 5 /* ADCPOL - [6:5] */ +#define WM8961_ADCPOL_WIDTH 2 /* ADCPOL - [6:5] */ +#define WM8961_DACMU 0x0008 /* DACMU */ +#define WM8961_DACMU_MASK 0x0008 /* DACMU */ +#define WM8961_DACMU_SHIFT 3 /* DACMU */ +#define WM8961_DACMU_WIDTH 1 /* DACMU */ +#define WM8961_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM8961_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM8961_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ +#define WM8961_ADCHPD 0x0001 /* ADCHPD */ +#define WM8961_ADCHPD_MASK 0x0001 /* ADCHPD */ +#define WM8961_ADCHPD_SHIFT 0 /* ADCHPD */ +#define WM8961_ADCHPD_WIDTH 1 /* ADCHPD */ + +/* + * R6 (0x06) - ADC & DAC Control 2 + */ +#define WM8961_ADC_HPF_CUT_MASK 0x0180 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_DACPOL_MASK 0x0060 /* DACPOL - [6:5] */ +#define WM8961_DACPOL_SHIFT 5 /* DACPOL - [6:5] */ +#define WM8961_DACPOL_WIDTH 2 /* DACPOL - [6:5] */ +#define WM8961_DACSMM 0x0008 /* DACSMM */ +#define WM8961_DACSMM_MASK 0x0008 /* DACSMM */ +#define WM8961_DACSMM_SHIFT 3 /* DACSMM */ +#define WM8961_DACSMM_WIDTH 1 /* DACSMM */ +#define WM8961_DACMR 0x0004 /* DACMR */ +#define WM8961_DACMR_MASK 0x0004 /* DACMR */ +#define WM8961_DACMR_SHIFT 2 /* DACMR */ +#define WM8961_DACMR_WIDTH 1 /* DACMR */ +#define WM8961_DACSLOPE 0x0002 /* DACSLOPE */ +#define WM8961_DACSLOPE_MASK 0x0002 /* DACSLOPE */ +#define WM8961_DACSLOPE_SHIFT 1 /* DACSLOPE */ +#define WM8961_DACSLOPE_WIDTH 1 /* DACSLOPE */ +#define WM8961_DAC_OSR128 0x0001 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ + +/* + * R7 (0x07) - Audio Interface 0 + */ +#define WM8961_ALRSWAP 0x0100 /* ALRSWAP */ +#define WM8961_ALRSWAP_MASK 0x0100 /* ALRSWAP */ +#define WM8961_ALRSWAP_SHIFT 8 /* ALRSWAP */ +#define WM8961_ALRSWAP_WIDTH 1 /* ALRSWAP */ +#define WM8961_BCLKINV 0x0080 /* BCLKINV */ +#define WM8961_BCLKINV_MASK 0x0080 /* BCLKINV */ +#define WM8961_BCLKINV_SHIFT 7 /* BCLKINV */ +#define WM8961_BCLKINV_WIDTH 1 /* BCLKINV */ +#define WM8961_MS 0x0040 /* MS */ +#define WM8961_MS_MASK 0x0040 /* MS */ +#define WM8961_MS_SHIFT 6 /* MS */ +#define WM8961_MS_WIDTH 1 /* MS */ +#define WM8961_DLRSWAP 0x0020 /* DLRSWAP */ +#define WM8961_DLRSWAP_MASK 0x0020 /* DLRSWAP */ +#define WM8961_DLRSWAP_SHIFT 5 /* DLRSWAP */ +#define WM8961_DLRSWAP_WIDTH 1 /* DLRSWAP */ +#define WM8961_LRP 0x0010 /* LRP */ +#define WM8961_LRP_MASK 0x0010 /* LRP */ +#define WM8961_LRP_SHIFT 4 /* LRP */ +#define WM8961_LRP_WIDTH 1 /* LRP */ +#define WM8961_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8961_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8961_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8961_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */ +#define WM8961_FORMAT_SHIFT 0 /* FORMAT - [1:0] */ +#define WM8961_FORMAT_WIDTH 2 /* FORMAT - [1:0] */ + +/* + * R8 (0x08) - Clocking2 + */ +#define WM8961_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8961_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */ +#define WM8961_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */ +#define WM8961_CLK_SYS_ENA 0x0020 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_MASK 0x0020 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_SHIFT 5 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ +#define WM8961_CLK_DSP_ENA 0x0010 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_MASK 0x0010 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_SHIFT 4 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ +#define WM8961_BCLKDIV_MASK 0x000F /* BCLKDIV - [3:0] */ +#define WM8961_BCLKDIV_SHIFT 0 /* BCLKDIV - [3:0] */ +#define WM8961_BCLKDIV_WIDTH 4 /* BCLKDIV - [3:0] */ + +/* + * R9 (0x09) - Audio Interface 1 + */ +#define WM8961_DACCOMP_MASK 0x0018 /* DACCOMP - [4:3] */ +#define WM8961_DACCOMP_SHIFT 3 /* DACCOMP - [4:3] */ +#define WM8961_DACCOMP_WIDTH 2 /* DACCOMP - [4:3] */ +#define WM8961_ADCCOMP_MASK 0x0006 /* ADCCOMP - [2:1] */ +#define WM8961_ADCCOMP_SHIFT 1 /* ADCCOMP - [2:1] */ +#define WM8961_ADCCOMP_WIDTH 2 /* ADCCOMP - [2:1] */ +#define WM8961_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8961_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8961_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8961_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R10 (0x0A) - Left DAC volume + */ +#define WM8961_DACVU 0x0100 /* DACVU */ +#define WM8961_DACVU_MASK 0x0100 /* DACVU */ +#define WM8961_DACVU_SHIFT 8 /* DACVU */ +#define WM8961_DACVU_WIDTH 1 /* DACVU */ +#define WM8961_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */ +#define WM8961_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */ +#define WM8961_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */ + +/* + * R11 (0x0B) - Right DAC volume + */ +#define WM8961_DACVU 0x0100 /* DACVU */ +#define WM8961_DACVU_MASK 0x0100 /* DACVU */ +#define WM8961_DACVU_SHIFT 8 /* DACVU */ +#define WM8961_DACVU_WIDTH 1 /* DACVU */ +#define WM8961_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */ +#define WM8961_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */ +#define WM8961_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */ + +/* + * R14 (0x0E) - Audio Interface 2 + */ +#define WM8961_LRCLK_RATE_MASK 0x01FF /* LRCLK_RATE - [8:0] */ +#define WM8961_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [8:0] */ +#define WM8961_LRCLK_RATE_WIDTH 9 /* LRCLK_RATE - [8:0] */ + +/* + * R15 (0x0F) - Software Reset + */ +#define WM8961_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8961_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8961_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R17 (0x11) - ALC1 + */ +#define WM8961_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8961_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8961_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8961_MAXGAIN_MASK 0x0070 /* MAXGAIN - [6:4] */ +#define WM8961_MAXGAIN_SHIFT 4 /* MAXGAIN - [6:4] */ +#define WM8961_MAXGAIN_WIDTH 3 /* MAXGAIN - [6:4] */ +#define WM8961_ALCL_MASK 0x000F /* ALCL - [3:0] */ +#define WM8961_ALCL_SHIFT 0 /* ALCL - [3:0] */ +#define WM8961_ALCL_WIDTH 4 /* ALCL - [3:0] */ + +/* + * R18 (0x12) - ALC2 + */ +#define WM8961_ALCZC 0x0080 /* ALCZC */ +#define WM8961_ALCZC_MASK 0x0080 /* ALCZC */ +#define WM8961_ALCZC_SHIFT 7 /* ALCZC */ +#define WM8961_ALCZC_WIDTH 1 /* ALCZC */ +#define WM8961_MINGAIN_MASK 0x0070 /* MINGAIN - [6:4] */ +#define WM8961_MINGAIN_SHIFT 4 /* MINGAIN - [6:4] */ +#define WM8961_MINGAIN_WIDTH 3 /* MINGAIN - [6:4] */ +#define WM8961_HLD_MASK 0x000F /* HLD - [3:0] */ +#define WM8961_HLD_SHIFT 0 /* HLD - [3:0] */ +#define WM8961_HLD_WIDTH 4 /* HLD - [3:0] */ + +/* + * R19 (0x13) - ALC3 + */ +#define WM8961_ALCMODE 0x0100 /* ALCMODE */ +#define WM8961_ALCMODE_MASK 0x0100 /* ALCMODE */ +#define WM8961_ALCMODE_SHIFT 8 /* ALCMODE */ +#define WM8961_ALCMODE_WIDTH 1 /* ALCMODE */ +#define WM8961_DCY_MASK 0x00F0 /* DCY - [7:4] */ +#define WM8961_DCY_SHIFT 4 /* DCY - [7:4] */ +#define WM8961_DCY_WIDTH 4 /* DCY - [7:4] */ +#define WM8961_ATK_MASK 0x000F /* ATK - [3:0] */ +#define WM8961_ATK_SHIFT 0 /* ATK - [3:0] */ +#define WM8961_ATK_WIDTH 4 /* ATK - [3:0] */ + +/* + * R20 (0x14) - Noise Gate + */ +#define WM8961_NGTH_MASK 0x00F8 /* NGTH - [7:3] */ +#define WM8961_NGTH_SHIFT 3 /* NGTH - [7:3] */ +#define WM8961_NGTH_WIDTH 5 /* NGTH - [7:3] */ +#define WM8961_NGG 0x0002 /* NGG */ +#define WM8961_NGG_MASK 0x0002 /* NGG */ +#define WM8961_NGG_SHIFT 1 /* NGG */ +#define WM8961_NGG_WIDTH 1 /* NGG */ +#define WM8961_NGAT 0x0001 /* NGAT */ +#define WM8961_NGAT_MASK 0x0001 /* NGAT */ +#define WM8961_NGAT_SHIFT 0 /* NGAT */ +#define WM8961_NGAT_WIDTH 1 /* NGAT */ + +/* + * R21 (0x15) - Left ADC volume + */ +#define WM8961_ADCVU 0x0100 /* ADCVU */ +#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8961_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8961_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8961_LADCVOL_MASK 0x00FF /* LADCVOL - [7:0] */ +#define WM8961_LADCVOL_SHIFT 0 /* LADCVOL - [7:0] */ +#define WM8961_LADCVOL_WIDTH 8 /* LADCVOL - [7:0] */ + +/* + * R22 (0x16) - Right ADC volume + */ +#define WM8961_ADCVU 0x0100 /* ADCVU */ +#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8961_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8961_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8961_RADCVOL_MASK 0x00FF /* RADCVOL - [7:0] */ +#define WM8961_RADCVOL_SHIFT 0 /* RADCVOL - [7:0] */ +#define WM8961_RADCVOL_WIDTH 8 /* RADCVOL - [7:0] */ + +/* + * R23 (0x17) - Additional control(1) + */ +#define WM8961_TSDEN 0x0100 /* TSDEN */ +#define WM8961_TSDEN_MASK 0x0100 /* TSDEN */ +#define WM8961_TSDEN_SHIFT 8 /* TSDEN */ +#define WM8961_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8961_DMONOMIX 0x0010 /* DMONOMIX */ +#define WM8961_DMONOMIX_MASK 0x0010 /* DMONOMIX */ +#define WM8961_DMONOMIX_SHIFT 4 /* DMONOMIX */ +#define WM8961_DMONOMIX_WIDTH 1 /* DMONOMIX */ +#define WM8961_TOEN 0x0001 /* TOEN */ +#define WM8961_TOEN_MASK 0x0001 /* TOEN */ +#define WM8961_TOEN_SHIFT 0 /* TOEN */ +#define WM8961_TOEN_WIDTH 1 /* TOEN */ + +/* + * R24 (0x18) - Additional control(2) + */ +#define WM8961_TRIS 0x0008 /* TRIS */ +#define WM8961_TRIS_MASK 0x0008 /* TRIS */ +#define WM8961_TRIS_SHIFT 3 /* TRIS */ +#define WM8961_TRIS_WIDTH 1 /* TRIS */ + +/* + * R25 (0x19) - Pwr Mgmt (1) + */ +#define WM8961_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */ +#define WM8961_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */ +#define WM8961_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */ +#define WM8961_VREF 0x0040 /* VREF */ +#define WM8961_VREF_MASK 0x0040 /* VREF */ +#define WM8961_VREF_SHIFT 6 /* VREF */ +#define WM8961_VREF_WIDTH 1 /* VREF */ +#define WM8961_AINL 0x0020 /* AINL */ +#define WM8961_AINL_MASK 0x0020 /* AINL */ +#define WM8961_AINL_SHIFT 5 /* AINL */ +#define WM8961_AINL_WIDTH 1 /* AINL */ +#define WM8961_AINR 0x0010 /* AINR */ +#define WM8961_AINR_MASK 0x0010 /* AINR */ +#define WM8961_AINR_SHIFT 4 /* AINR */ +#define WM8961_AINR_WIDTH 1 /* AINR */ +#define WM8961_ADCL 0x0008 /* ADCL */ +#define WM8961_ADCL_MASK 0x0008 /* ADCL */ +#define WM8961_ADCL_SHIFT 3 /* ADCL */ +#define WM8961_ADCL_WIDTH 1 /* ADCL */ +#define WM8961_ADCR 0x0004 /* ADCR */ +#define WM8961_ADCR_MASK 0x0004 /* ADCR */ +#define WM8961_ADCR_SHIFT 2 /* ADCR */ +#define WM8961_ADCR_WIDTH 1 /* ADCR */ +#define WM8961_MICB 0x0002 /* MICB */ +#define WM8961_MICB_MASK 0x0002 /* MICB */ +#define WM8961_MICB_SHIFT 1 /* MICB */ +#define WM8961_MICB_WIDTH 1 /* MICB */ + +/* + * R26 (0x1A) - Pwr Mgmt (2) + */ +#define WM8961_DACL 0x0100 /* DACL */ +#define WM8961_DACL_MASK 0x0100 /* DACL */ +#define WM8961_DACL_SHIFT 8 /* DACL */ +#define WM8961_DACL_WIDTH 1 /* DACL */ +#define WM8961_DACR 0x0080 /* DACR */ +#define WM8961_DACR_MASK 0x0080 /* DACR */ +#define WM8961_DACR_SHIFT 7 /* DACR */ +#define WM8961_DACR_WIDTH 1 /* DACR */ +#define WM8961_LOUT1_PGA 0x0040 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_MASK 0x0040 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_SHIFT 6 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_WIDTH 1 /* LOUT1_PGA */ +#define WM8961_ROUT1_PGA 0x0020 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_MASK 0x0020 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_SHIFT 5 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_WIDTH 1 /* ROUT1_PGA */ +#define WM8961_SPKL_PGA 0x0010 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_MASK 0x0010 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_SHIFT 4 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_WIDTH 1 /* SPKL_PGA */ +#define WM8961_SPKR_PGA 0x0008 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_MASK 0x0008 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_SHIFT 3 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_WIDTH 1 /* SPKR_PGA */ + +/* + * R27 (0x1B) - Additional Control (3) + */ +#define WM8961_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */ +#define WM8961_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */ +#define WM8961_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */ + +/* + * R28 (0x1C) - Anti-pop + */ +#define WM8961_BUFDCOPEN 0x0010 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_MASK 0x0010 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_SHIFT 4 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8961_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8961_BUFIOEN_MASK 0x0008 /* BUFIOEN */ +#define WM8961_BUFIOEN_SHIFT 3 /* BUFIOEN */ +#define WM8961_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8961_SOFT_ST 0x0004 /* SOFT_ST */ +#define WM8961_SOFT_ST_MASK 0x0004 /* SOFT_ST */ +#define WM8961_SOFT_ST_SHIFT 2 /* SOFT_ST */ +#define WM8961_SOFT_ST_WIDTH 1 /* SOFT_ST */ + +/* + * R30 (0x1E) - Clocking 3 + */ +#define WM8961_CLK_TO_DIV_MASK 0x0180 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_TO_DIV_SHIFT 7 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */ +#define WM8961_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */ +#define WM8961_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */ +#define WM8961_MANUAL_MODE 0x0001 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_MASK 0x0001 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_SHIFT 0 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_WIDTH 1 /* MANUAL_MODE */ + +/* + * R32 (0x20) - ADCL signal path + */ +#define WM8961_LMICBOOST_MASK 0x0030 /* LMICBOOST - [5:4] */ +#define WM8961_LMICBOOST_SHIFT 4 /* LMICBOOST - [5:4] */ +#define WM8961_LMICBOOST_WIDTH 2 /* LMICBOOST - [5:4] */ + +/* + * R33 (0x21) - ADCR signal path + */ +#define WM8961_RMICBOOST_MASK 0x0030 /* RMICBOOST - [5:4] */ +#define WM8961_RMICBOOST_SHIFT 4 /* RMICBOOST - [5:4] */ +#define WM8961_RMICBOOST_WIDTH 2 /* RMICBOOST - [5:4] */ + +/* + * R40 (0x28) - LOUT2 volume + */ +#define WM8961_SPKVU 0x0100 /* SPKVU */ +#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */ +#define WM8961_SPKVU_SHIFT 8 /* SPKVU */ +#define WM8961_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8961_SPKLZC 0x0080 /* SPKLZC */ +#define WM8961_SPKLZC_MASK 0x0080 /* SPKLZC */ +#define WM8961_SPKLZC_SHIFT 7 /* SPKLZC */ +#define WM8961_SPKLZC_WIDTH 1 /* SPKLZC */ +#define WM8961_SPKLVOL_MASK 0x007F /* SPKLVOL - [6:0] */ +#define WM8961_SPKLVOL_SHIFT 0 /* SPKLVOL - [6:0] */ +#define WM8961_SPKLVOL_WIDTH 7 /* SPKLVOL - [6:0] */ + +/* + * R41 (0x29) - ROUT2 volume + */ +#define WM8961_SPKVU 0x0100 /* SPKVU */ +#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */ +#define WM8961_SPKVU_SHIFT 8 /* SPKVU */ +#define WM8961_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8961_SPKRZC 0x0080 /* SPKRZC */ +#define WM8961_SPKRZC_MASK 0x0080 /* SPKRZC */ +#define WM8961_SPKRZC_SHIFT 7 /* SPKRZC */ +#define WM8961_SPKRZC_WIDTH 1 /* SPKRZC */ +#define WM8961_SPKRVOL_MASK 0x007F /* SPKRVOL - [6:0] */ +#define WM8961_SPKRVOL_SHIFT 0 /* SPKRVOL - [6:0] */ +#define WM8961_SPKRVOL_WIDTH 7 /* SPKRVOL - [6:0] */ + +/* + * R47 (0x2F) - Pwr Mgmt (3) + */ +#define WM8961_TEMP_SHUT 0x0002 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_MASK 0x0002 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_SHIFT 1 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */ +#define WM8961_TEMP_WARN 0x0001 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_MASK 0x0001 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_SHIFT 0 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_WIDTH 1 /* TEMP_WARN */ + +/* + * R48 (0x30) - Additional Control (4) + */ +#define WM8961_TSENSEN 0x0002 /* TSENSEN */ +#define WM8961_TSENSEN_MASK 0x0002 /* TSENSEN */ +#define WM8961_TSENSEN_SHIFT 1 /* TSENSEN */ +#define WM8961_TSENSEN_WIDTH 1 /* TSENSEN */ +#define WM8961_MBSEL 0x0001 /* MBSEL */ +#define WM8961_MBSEL_MASK 0x0001 /* MBSEL */ +#define WM8961_MBSEL_SHIFT 0 /* MBSEL */ +#define WM8961_MBSEL_WIDTH 1 /* MBSEL */ + +/* + * R49 (0x31) - Class D Control 1 + */ +#define WM8961_SPKR_ENA 0x0080 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_MASK 0x0080 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_SHIFT 7 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_WIDTH 1 /* SPKR_ENA */ +#define WM8961_SPKL_ENA 0x0040 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_MASK 0x0040 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_SHIFT 6 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_WIDTH 1 /* SPKL_ENA */ + +/* + * R51 (0x33) - Class D Control 2 + */ +#define WM8961_CLASSD_ACGAIN_MASK 0x0007 /* CLASSD_ACGAIN - [2:0] */ +#define WM8961_CLASSD_ACGAIN_SHIFT 0 /* CLASSD_ACGAIN - [2:0] */ +#define WM8961_CLASSD_ACGAIN_WIDTH 3 /* CLASSD_ACGAIN - [2:0] */ + +/* + * R56 (0x38) - Clocking 4 + */ +#define WM8961_CLK_DCS_DIV_MASK 0x01E0 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_DCS_DIV_SHIFT 5 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */ +#define WM8961_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */ +#define WM8961_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */ + +/* + * R57 (0x39) - DSP Sidetone 0 + */ +#define WM8961_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */ +#define WM8961_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */ +#define WM8961_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */ + +/* + * R58 (0x3A) - DSP Sidetone 1 + */ +#define WM8961_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8961_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8961_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ + +/* + * R60 (0x3C) - DC Servo 0 + */ +#define WM8961_DCS_ENA_CHAN_INL 0x0080 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_MASK 0x0080 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_SHIFT 7 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_WIDTH 1 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL 0x0040 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_MASK 0x0040 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT 6 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH 1 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_SERIES_INL 0x0010 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_MASK 0x0010 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_SHIFT 4 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_WIDTH 1 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_ENA_CHAN_INR 0x0008 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_MASK 0x0008 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_SHIFT 3 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_WIDTH 1 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR 0x0004 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_MASK 0x0004 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT 2 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH 1 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_SERIES_INR 0x0001 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_MASK 0x0001 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_SHIFT 0 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_WIDTH 1 /* DCS_TRIG_SERIES_INR */ + +/* + * R61 (0x3D) - DC Servo 1 + */ +#define WM8961_DCS_ENA_CHAN_HPL 0x0080 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_MASK 0x0080 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_SHIFT 7 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_WIDTH 1 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL 0x0040 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_MASK 0x0040 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT 6 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH 1 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL 0x0010 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_MASK 0x0010 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT 4 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH 1 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_ENA_CHAN_HPR 0x0008 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_MASK 0x0008 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_SHIFT 3 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_WIDTH 1 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR 0x0004 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_MASK 0x0004 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT 2 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH 1 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR 0x0001 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_MASK 0x0001 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT 0 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH 1 /* DCS_TRIG_SERIES_HPR */ + +/* + * R63 (0x3F) - DC Servo 3 + */ +#define WM8961_DCS_FILT_BW_SERIES_MASK 0x0030 /* DCS_FILT_BW_SERIES - [5:4] */ +#define WM8961_DCS_FILT_BW_SERIES_SHIFT 4 /* DCS_FILT_BW_SERIES - [5:4] */ +#define WM8961_DCS_FILT_BW_SERIES_WIDTH 2 /* DCS_FILT_BW_SERIES - [5:4] */ + +/* + * R65 (0x41) - DC Servo 5 + */ +#define WM8961_DCS_SERIES_NO_HP_MASK 0x007F /* DCS_SERIES_NO_HP - [6:0] */ +#define WM8961_DCS_SERIES_NO_HP_SHIFT 0 /* DCS_SERIES_NO_HP - [6:0] */ +#define WM8961_DCS_SERIES_NO_HP_WIDTH 7 /* DCS_SERIES_NO_HP - [6:0] */ + +/* + * R68 (0x44) - Analogue PGA Bias + */ +#define WM8961_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */ +#define WM8961_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */ +#define WM8961_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */ + +/* + * R69 (0x45) - Analogue HP 0 + */ +#define WM8961_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */ +#define WM8961_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA 0x0010 /* HPL_ENA */ +#define WM8961_HPL_ENA_MASK 0x0010 /* HPL_ENA */ +#define WM8961_HPL_ENA_SHIFT 4 /* HPL_ENA */ +#define WM8961_HPL_ENA_WIDTH 1 /* HPL_ENA */ +#define WM8961_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */ +#define WM8961_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA 0x0001 /* HPR_ENA */ +#define WM8961_HPR_ENA_MASK 0x0001 /* HPR_ENA */ +#define WM8961_HPR_ENA_SHIFT 0 /* HPR_ENA */ +#define WM8961_HPR_ENA_WIDTH 1 /* HPR_ENA */ + +/* + * R71 (0x47) - Analogue HP 2 + */ +#define WM8961_HPL_VOL_MASK 0x01C0 /* HPL_VOL - [8:6] */ +#define WM8961_HPL_VOL_SHIFT 6 /* HPL_VOL - [8:6] */ +#define WM8961_HPL_VOL_WIDTH 3 /* HPL_VOL - [8:6] */ +#define WM8961_HPR_VOL_MASK 0x0038 /* HPR_VOL - [5:3] */ +#define WM8961_HPR_VOL_SHIFT 3 /* HPR_VOL - [5:3] */ +#define WM8961_HPR_VOL_WIDTH 3 /* HPR_VOL - [5:3] */ +#define WM8961_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */ +#define WM8961_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */ +#define WM8961_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */ + +/* + * R72 (0x48) - Charge Pump 1 + */ +#define WM8961_CP_ENA 0x0001 /* CP_ENA */ +#define WM8961_CP_ENA_MASK 0x0001 /* CP_ENA */ +#define WM8961_CP_ENA_SHIFT 0 /* CP_ENA */ +#define WM8961_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R82 (0x52) - Charge Pump B + */ +#define WM8961_CP_DYN_PWR_MASK 0x0003 /* CP_DYN_PWR - [1:0] */ +#define WM8961_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR - [1:0] */ +#define WM8961_CP_DYN_PWR_WIDTH 2 /* CP_DYN_PWR - [1:0] */ + +/* + * R87 (0x57) - Write Sequencer 1 + */ +#define WM8961_WSEQ_ENA 0x0020 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8961_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8961_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8961_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R88 (0x58) - Write Sequencer 2 + */ +#define WM8961_WSEQ_EOS 0x0100 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_MASK 0x0100 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_SHIFT 8 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8961_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8961_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8961_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R89 (0x59) - Write Sequencer 3 + */ +#define WM8961_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8961_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8961_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R90 (0x5A) - Write Sequencer 4 + */ +#define WM8961_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8961_WSEQ_START 0x0080 /* WSEQ_START */ +#define WM8961_WSEQ_START_MASK 0x0080 /* WSEQ_START */ +#define WM8961_WSEQ_START_SHIFT 7 /* WSEQ_START */ +#define WM8961_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8961_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8961_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8961_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R91 (0x5B) - Write Sequencer 5 + */ +#define WM8961_WSEQ_DATA_WIDTH_MASK 0x0070 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_WIDTH_SHIFT 4 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_START_MASK 0x000F /* WSEQ_DATA_START - [3:0] */ +#define WM8961_WSEQ_DATA_START_SHIFT 0 /* WSEQ_DATA_START - [3:0] */ +#define WM8961_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [3:0] */ + +/* + * R92 (0x5C) - Write Sequencer 6 + */ +#define WM8961_WSEQ_DELAY_MASK 0x000F /* WSEQ_DELAY - [3:0] */ +#define WM8961_WSEQ_DELAY_SHIFT 0 /* WSEQ_DELAY - [3:0] */ +#define WM8961_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [3:0] */ + +/* + * R93 (0x5D) - Write Sequencer 7 + */ +#define WM8961_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R252 (0xFC) - General test 1 + */ +#define WM8961_ARA_ENA 0x0002 /* ARA_ENA */ +#define WM8961_ARA_ENA_MASK 0x0002 /* ARA_ENA */ +#define WM8961_ARA_ENA_SHIFT 1 /* ARA_ENA */ +#define WM8961_ARA_ENA_WIDTH 1 /* ARA_ENA */ +#define WM8961_AUTO_INC 0x0001 /* AUTO_INC */ +#define WM8961_AUTO_INC_MASK 0x0001 /* AUTO_INC */ +#define WM8961_AUTO_INC_SHIFT 0 /* AUTO_INC */ +#define WM8961_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +#endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 8c0fdf84aac..f9c3b80da5d 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -902,7 +902,7 @@ static int wm8988_register(struct wm8988_priv *wm8988) ret = wm8988_reset(codec); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset\n"); - return ret; + goto err; } /* set the update bits (we always update left then right) */ @@ -926,18 +926,20 @@ static int wm8988_register(struct wm8988_priv *wm8988) ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - return ret; + goto err; } ret = snd_soc_register_dai(&wm8988_dai); if (ret != 0) { dev_err(codec->dev, "Failed to register DAI: %d\n", ret); snd_soc_unregister_codec(codec); - return ret; + goto err_codec; } return 0; +err_codec: + snd_soc_unregister_codec(codec); err: kfree(wm8988); return ret; @@ -981,6 +983,21 @@ static int wm8988_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm8988_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm8988_i2c_suspend NULL +#define wm8988_i2c_resume NULL +#endif + static const struct i2c_device_id wm8988_i2c_id[] = { { "wm8988", 0 }, { } @@ -994,6 +1011,8 @@ static struct i2c_driver wm8988_i2c_driver = { }, .probe = wm8988_i2c_probe, .remove = wm8988_i2c_remove, + .suspend = wm8988_i2c_suspend, + .resume = wm8988_i2c_resume, .id_table = wm8988_i2c_id, }; #endif @@ -1051,6 +1070,21 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM +static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return snd_soc_suspend_device(&spi->dev); +} + +static int wm8988_spi_resume(struct spi_device *spi) +{ + return snd_soc_resume_device(&spi->dev); +} +#else +#define wm8988_spi_suspend NULL +#define wm8988_spi_resume NULL +#endif + static struct spi_driver wm8988_spi_driver = { .driver = { .name = "wm8988", @@ -1059,6 +1093,8 @@ static struct spi_driver wm8988_spi_driver = { }, .probe = wm8988_spi_probe, .remove = __devexit_p(wm8988_spi_remove), + .suspend = wm8988_spi_suspend, + .resume = wm8988_spi_resume, }; #endif diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c new file mode 100644 index 00000000000..e246ca07989 --- /dev/null +++ b/sound/soc/codecs/wm8993.c @@ -0,0 +1,2206 @@ +/* + * wm8993.c -- WM8993 ALSA SoC audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/wm8993.h> + +#include "wm8993.h" + +static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = { + 0x8993, /* R0 - Software Reset */ + 0x0000, /* R1 - Power Management (1) */ + 0x6000, /* R2 - Power Management (2) */ + 0x0000, /* R3 - Power Management (3) */ + 0x4050, /* R4 - Audio Interface (1) */ + 0x4000, /* R5 - Audio Interface (2) */ + 0x01C8, /* R6 - Clocking 1 */ + 0x0000, /* R7 - Clocking 2 */ + 0x0000, /* R8 - Audio Interface (3) */ + 0x0040, /* R9 - Audio Interface (4) */ + 0x0004, /* R10 - DAC CTRL */ + 0x00C0, /* R11 - Left DAC Digital Volume */ + 0x00C0, /* R12 - Right DAC Digital Volume */ + 0x0000, /* R13 - Digital Side Tone */ + 0x0300, /* R14 - ADC CTRL */ + 0x00C0, /* R15 - Left ADC Digital Volume */ + 0x00C0, /* R16 - Right ADC Digital Volume */ + 0x0000, /* R17 */ + 0x0000, /* R18 - GPIO CTRL 1 */ + 0x0010, /* R19 - GPIO1 */ + 0x0000, /* R20 - IRQ_DEBOUNCE */ + 0x0000, /* R21 */ + 0x8000, /* R22 - GPIOCTRL 2 */ + 0x0800, /* R23 - GPIO_POL */ + 0x008B, /* R24 - Left Line Input 1&2 Volume */ + 0x008B, /* R25 - Left Line Input 3&4 Volume */ + 0x008B, /* R26 - Right Line Input 1&2 Volume */ + 0x008B, /* R27 - Right Line Input 3&4 Volume */ + 0x006D, /* R28 - Left Output Volume */ + 0x006D, /* R29 - Right Output Volume */ + 0x0066, /* R30 - Line Outputs Volume */ + 0x0020, /* R31 - HPOUT2 Volume */ + 0x0079, /* R32 - Left OPGA Volume */ + 0x0079, /* R33 - Right OPGA Volume */ + 0x0003, /* R34 - SPKMIXL Attenuation */ + 0x0003, /* R35 - SPKMIXR Attenuation */ + 0x0011, /* R36 - SPKOUT Mixers */ + 0x0100, /* R37 - SPKOUT Boost */ + 0x0079, /* R38 - Speaker Volume Left */ + 0x0079, /* R39 - Speaker Volume Right */ + 0x0000, /* R40 - Input Mixer2 */ + 0x0000, /* R41 - Input Mixer3 */ + 0x0000, /* R42 - Input Mixer4 */ + 0x0000, /* R43 - Input Mixer5 */ + 0x0000, /* R44 - Input Mixer6 */ + 0x0000, /* R45 - Output Mixer1 */ + 0x0000, /* R46 - Output Mixer2 */ + 0x0000, /* R47 - Output Mixer3 */ + 0x0000, /* R48 - Output Mixer4 */ + 0x0000, /* R49 - Output Mixer5 */ + 0x0000, /* R50 - Output Mixer6 */ + 0x0000, /* R51 - HPOUT2 Mixer */ + 0x0000, /* R52 - Line Mixer1 */ + 0x0000, /* R53 - Line Mixer2 */ + 0x0000, /* R54 - Speaker Mixer */ + 0x0000, /* R55 - Additional Control */ + 0x0000, /* R56 - AntiPOP1 */ + 0x0000, /* R57 - AntiPOP2 */ + 0x0000, /* R58 - MICBIAS */ + 0x0000, /* R59 */ + 0x0000, /* R60 - FLL Control 1 */ + 0x0000, /* R61 - FLL Control 2 */ + 0x0000, /* R62 - FLL Control 3 */ + 0x2EE0, /* R63 - FLL Control 4 */ + 0x0002, /* R64 - FLL Control 5 */ + 0x2287, /* R65 - Clocking 3 */ + 0x025F, /* R66 - Clocking 4 */ + 0x0000, /* R67 - MW Slave Control */ + 0x0000, /* R68 */ + 0x0002, /* R69 - Bus Control 1 */ + 0x0000, /* R70 - Write Sequencer 0 */ + 0x0000, /* R71 - Write Sequencer 1 */ + 0x0000, /* R72 - Write Sequencer 2 */ + 0x0000, /* R73 - Write Sequencer 3 */ + 0x0000, /* R74 - Write Sequencer 4 */ + 0x0000, /* R75 - Write Sequencer 5 */ + 0x1F25, /* R76 - Charge Pump 1 */ + 0x0000, /* R77 */ + 0x0000, /* R78 */ + 0x0000, /* R79 */ + 0x0000, /* R80 */ + 0x0000, /* R81 - Class W 0 */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 - DC Servo 0 */ + 0x054A, /* R85 - DC Servo 1 */ + 0x0000, /* R86 */ + 0x0000, /* R87 - DC Servo 3 */ + 0x0000, /* R88 - DC Servo Readback 0 */ + 0x0000, /* R89 - DC Servo Readback 1 */ + 0x0000, /* R90 - DC Servo Readback 2 */ + 0x0000, /* R91 */ + 0x0000, /* R92 */ + 0x0000, /* R93 */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0100, /* R96 - Analogue HP 0 */ + 0x0000, /* R97 */ + 0x0000, /* R98 - EQ1 */ + 0x000C, /* R99 - EQ2 */ + 0x000C, /* R100 - EQ3 */ + 0x000C, /* R101 - EQ4 */ + 0x000C, /* R102 - EQ5 */ + 0x000C, /* R103 - EQ6 */ + 0x0FCA, /* R104 - EQ7 */ + 0x0400, /* R105 - EQ8 */ + 0x00D8, /* R106 - EQ9 */ + 0x1EB5, /* R107 - EQ10 */ + 0xF145, /* R108 - EQ11 */ + 0x0B75, /* R109 - EQ12 */ + 0x01C5, /* R110 - EQ13 */ + 0x1C58, /* R111 - EQ14 */ + 0xF373, /* R112 - EQ15 */ + 0x0A54, /* R113 - EQ16 */ + 0x0558, /* R114 - EQ17 */ + 0x168E, /* R115 - EQ18 */ + 0xF829, /* R116 - EQ19 */ + 0x07AD, /* R117 - EQ20 */ + 0x1103, /* R118 - EQ21 */ + 0x0564, /* R119 - EQ22 */ + 0x0559, /* R120 - EQ23 */ + 0x4000, /* R121 - EQ24 */ + 0x0000, /* R122 - Digital Pulls */ + 0x0F08, /* R123 - DRC Control 1 */ + 0x0000, /* R124 - DRC Control 2 */ + 0x0080, /* R125 - DRC Control 3 */ + 0x0000, /* R126 - DRC Control 4 */ +}; + +static struct { + int ratio; + int clk_sys_rate; +} clk_sys_rates[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 768, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +static struct { + int rate; + int sample_rate; +} sample_rates[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 1 }, + { 16000, 2 }, + { 22050, 3 }, + { 24000, 3 }, + { 32000, 4 }, + { 44100, 5 }, + { 48000, 5 }, +}; + +static struct { + int div; /* *10 due to .5s */ + int bclk_div; +} bclk_divs[] = { + { 10, 0 }, + { 15, 1 }, + { 20, 2 }, + { 30, 3 }, + { 40, 4 }, + { 55, 5 }, + { 60, 6 }, + { 80, 7 }, + { 110, 8 }, + { 120, 9 }, + { 160, 10 }, + { 220, 11 }, + { 240, 12 }, + { 320, 13 }, + { 440, 14 }, + { 480, 15 }, +}; + +struct wm8993_priv { + u16 reg_cache[WM8993_REGISTER_COUNT]; + struct wm8993_platform_data pdata; + struct snd_soc_codec codec; + int master; + int sysclk_source; + unsigned int mclk_rate; + unsigned int sysclk_rate; + unsigned int fs; + unsigned int bclk; + int class_w_users; + unsigned int fll_fref; + unsigned int fll_fout; +}; + +static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg) +{ + struct i2c_msg xfer[2]; + u16 data; + int ret; + struct i2c_client *i2c = codec->control_data; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (u8 *)&data; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret != 2) { + dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret); + return 0; + } + + return (data >> 8) | ((data & 0xff) << 8); +} + +static int wm8993_volatile(unsigned int reg) +{ + switch (reg) { + case WM8993_SOFTWARE_RESET: + case WM8993_DC_SERVO_0: + case WM8993_DC_SERVO_READBACK_0: + case WM8993_DC_SERVO_READBACK_1: + case WM8993_DC_SERVO_READBACK_2: + return 1; + default: + return 0; + } +} + +static unsigned int wm8993_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *reg_cache = codec->reg_cache; + + BUG_ON(reg > WM8993_MAX_REGISTER); + + if (wm8993_volatile(reg)) + return wm8993_read_hw(codec, reg); + else + return reg_cache[reg]; +} + +static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 *reg_cache = codec->reg_cache; + u8 data[3]; + int ret; + + BUG_ON(reg > WM8993_MAX_REGISTER); + + /* data is + * D15..D9 WM8993 register offset + * D8...D0 register data + */ + data[0] = reg; + data[1] = value >> 8; + data[2] = value & 0x00ff; + + if (!wm8993_volatile(reg)) + reg_cache[reg] = value; + + ret = codec->hw_write(codec->control_data, data, 3); + + if (ret == 3) + return 0; + if (ret < 0) + return ret; + return -EIO; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_clk_ref_div; + u16 n; + u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + unsigned int div; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_clk_ref_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_clk_ref_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 0; + target = Fout * 2; + while (target < 90000000) { + div++; + target *= 2; + if (div > 7) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + fll_div->fll_outdiv = div; + + pr_debug("Fvco=%dHz\n", target); + + /* Find an appropraite FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + target /= fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + /* Now, calculate N.K */ + Ndiv = target / Fref; + + fll_div->n = Ndiv; + Nmod = target % Fref; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll_div->k = K / 10; + + pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n", + fll_div->n, fll_div->k, + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_clk_ref_div); + + return 0; +} + +static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, + unsigned int Fref, unsigned int Fout) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8993_priv *wm8993 = codec->private_data; + u16 reg1, reg4, reg5; + struct _fll_div fll_div; + int ret; + + /* Any change? */ + if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout) + return 0; + + /* Disable the FLL */ + if (Fout == 0) { + dev_dbg(codec->dev, "FLL disabled\n"); + wm8993->fll_fref = 0; + wm8993->fll_fout = 0; + + reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1); + reg1 &= ~WM8993_FLL_ENA; + wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1); + + return 0; + } + + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + + reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5); + reg5 &= ~WM8993_FLL_CLK_SRC_MASK; + + switch (fll_id) { + case WM8993_FLL_MCLK: + break; + + case WM8993_FLL_LRCLK: + reg5 |= 1; + break; + + case WM8993_FLL_BCLK: + reg5 |= 2; + break; + + default: + dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id); + return -EINVAL; + } + + /* Any FLL configuration change requires that the FLL be + * disabled first. */ + reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1); + reg1 &= ~WM8993_FLL_ENA; + wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1); + + /* Apply the configuration */ + if (fll_div.k) + reg1 |= WM8993_FLL_FRAC_MASK; + else + reg1 &= ~WM8993_FLL_FRAC_MASK; + wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1); + + wm8993_write(codec, WM8993_FLL_CONTROL_2, + (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) | + (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT)); + wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k); + + reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4); + reg4 &= ~WM8993_FLL_N_MASK; + reg4 |= fll_div.n << WM8993_FLL_N_SHIFT; + wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4); + + reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK; + reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT; + wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5); + + /* Enable the FLL */ + wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA); + + dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout); + + wm8993->fll_fref = Fref; + wm8993->fll_fout = Fout; + + return 0; +} + +static int configure_clock(struct snd_soc_codec *codec) +{ + struct wm8993_priv *wm8993 = codec->private_data; + unsigned int reg; + + /* This should be done on init() for bypass paths */ + switch (wm8993->sysclk_source) { + case WM8993_SYSCLK_MCLK: + dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate); + + reg = wm8993_read(codec, WM8993_CLOCKING_2); + reg &= ~WM8993_SYSCLK_SRC; + if (wm8993->mclk_rate > 13500000) { + reg |= WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->mclk_rate / 2; + } else { + reg &= ~WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->mclk_rate; + } + reg &= ~WM8993_MCLK_DIV; + reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC); + wm8993_write(codec, WM8993_CLOCKING_2, reg); + break; + + case WM8993_SYSCLK_FLL: + dev_dbg(codec->dev, "Using %dHz FLL clock\n", + wm8993->fll_fout); + + reg = wm8993_read(codec, WM8993_CLOCKING_2); + reg |= WM8993_SYSCLK_SRC; + if (wm8993->fll_fout > 13500000) { + reg |= WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->fll_fout / 2; + } else { + reg &= ~WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->fll_fout; + } + wm8993_write(codec, WM8993_CLOCKING_2, reg); + break; + + default: + dev_err(codec->dev, "System clock not configured\n"); + return -EINVAL; + } + + dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate); + + return 0; +} + +static void wait_for_dc_servo(struct snd_soc_codec *codec, int mask) +{ + unsigned int reg; + int count = 0; + + dev_dbg(codec->dev, "Waiting for DC servo...\n"); + do { + count++; + msleep(1); + reg = wm8993_read(codec, WM8993_DC_SERVO_READBACK_0); + dev_dbg(codec->dev, "DC servo status: %x\n", reg); + } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK) + != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000); + + if ((reg & WM8993_DCS_CAL_COMPLETE_MASK) + != WM8993_DCS_CAL_COMPLETE_MASK) + dev_err(codec->dev, "Timed out waiting for DC Servo\n"); +} + +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0); +static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0); +static const unsigned int drc_max_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), + 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0), +}; +static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0); +static const DECLARE_TLV_DB_SCALE(spkmix_tlv, -300, 300, 0); +static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1); +static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0); +static const unsigned int spkboost_tlv[] = { + TLV_DB_RANGE_HEAD(7), + 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), +}; +static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0); + +static const char *speaker_ref_text[] = { + "SPKVDD/2", + "VMID", +}; + +static const struct soc_enum speaker_ref = + SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text); + +static const char *speaker_mode_text[] = { + "Class D", + "Class AB", +}; + +static const struct soc_enum speaker_mode = + SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text); + +static const char *dac_deemph_text[] = { + "None", + "32kHz", + "44.1kHz", + "48kHz", +}; + +static const struct soc_enum dac_deemph = + SOC_ENUM_SINGLE(WM8993_DAC_CTRL, 4, 4, dac_deemph_text); + +static const char *adc_hpf_text[] = { + "Hi-Fi", + "Voice 1", + "Voice 2", + "Voice 3", +}; + +static const struct soc_enum adc_hpf = + SOC_ENUM_SINGLE(WM8993_ADC_CTRL, 5, 4, adc_hpf_text); + +static const char *drc_path_text[] = { + "ADC", + "DAC" +}; + +static const struct soc_enum drc_path = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text); + +static const char *drc_r0_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "1/16", + "0", +}; + +static const struct soc_enum drc_r0 = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 8, 6, drc_r0_text); + +static const char *drc_r1_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "0", +}; + +static const struct soc_enum drc_r1 = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_4, 13, 5, drc_r1_text); + +static const char *drc_attack_text[] = { + "Reserved", + "181us", + "363us", + "726us", + "1.45ms", + "2.9ms", + "5.8ms", + "11.6ms", + "23.2ms", + "46.4ms", + "92.8ms", + "185.6ms", +}; + +static const struct soc_enum drc_attack = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 12, 12, drc_attack_text); + +static const char *drc_decay_text[] = { + "186ms", + "372ms", + "743ms", + "1.49s", + "2.97ms", + "5.94ms", + "11.89ms", + "23.78ms", + "47.56ms", +}; + +static const struct soc_enum drc_decay = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 8, 9, drc_decay_text); + +static const char *drc_ff_text[] = { + "5 samples", + "9 samples", +}; + +static const struct soc_enum drc_ff = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 7, 2, drc_ff_text); + +static const char *drc_qr_rate_text[] = { + "0.725ms", + "1.45ms", + "5.8ms", +}; + +static const struct soc_enum drc_qr_rate = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 0, 3, drc_qr_rate_text); + +static const char *drc_smooth_text[] = { + "Low", + "Medium", + "High", +}; + +static const struct soc_enum drc_smooth = + SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 4, 3, drc_smooth_text); + + +/* + * Update the DC servo calibration on gain changes + */ +static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int ret; + + ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + + /* Only need to do this if the outputs are active */ + if (wm8993_read(codec, WM8993_POWER_MANAGEMENT_1) + & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA)) + snd_soc_update_bits(codec, + WM8993_DC_SERVO_0, + WM8993_DCS_TRIG_SINGLE_0 | + WM8993_DCS_TRIG_SINGLE_1, + WM8993_DCS_TRIG_SINGLE_0 | + WM8993_DCS_TRIG_SINGLE_1); + + return ret; +} + +static const struct snd_kcontrol_new wm8993_snd_controls[] = { +SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 0), + +SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 0), + + +SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 0), + +SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 0), + +SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0, + inmix_tlv), +SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv), +SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0, + inmix_tlv), + +SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0, + inmix_tlv), +SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv), +SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0, + inmix_tlv), + +SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE, + 5, 9, 12, 0, sidetone_tlv), + +SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0), +SOC_ENUM("DRC Path", drc_path), +SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8993_DRC_CONTROL_2, + 2, 60, 1, drc_comp_threash), +SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3, + 11, 30, 1, drc_comp_amp), +SOC_ENUM("DRC R0", drc_r0), +SOC_ENUM("DRC R1", drc_r1), +SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1, + drc_min_tlv), +SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0, + drc_max_tlv), +SOC_ENUM("DRC Attack Rate", drc_attack), +SOC_ENUM("DRC Decay Rate", drc_decay), +SOC_ENUM("DRC FF Delay", drc_ff), +SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0), +SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0), +SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0, + drc_qr_tlv), +SOC_ENUM("DRC Quick Release Rate", drc_qr_rate), +SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0), +SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0), +SOC_ENUM("DRC Smoothing Hysteresis Threashold", drc_smooth), +SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0, + drc_startup_tlv), + +SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME, + WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv), +SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0), +SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), + +SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME, + WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv), +SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0, + dac_boost_tlv), +SOC_ENUM("DAC Deemphasis", dac_deemph), + +SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer Right Input Volume", + WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer Left Input Volume", + WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1, + outmix_tlv), + +SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume", + WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume", + WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN1L Volume", + WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN1R Volume", + WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume", + WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer Left Input Volume", + WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer Right Input Volume", + WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer DAC Volume", + WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv), + +SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv), +SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0), +SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0), + +SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1), +SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv), + +SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION, + 5, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION, + 4, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION, + 3, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION, + 2, 1, 1, spkmix_tlv), + +SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION, + 5, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION, + 4, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION, + 3, 1, 1, spkmix_tlv), +SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION, + 2, 1, 1, spkmix_tlv), + +SOC_DOUBLE_R_TLV("Speaker Mixer Volume", + WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION, + 0, 3, 1, spkmixout_tlv), +SOC_DOUBLE_R_TLV("Speaker Volume", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 0, 63, 0, outpga_tlv), +SOC_DOUBLE_R("Speaker Switch", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 6, 1, 0), +SOC_DOUBLE_R("Speaker ZC Switch", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 7, 1, 0), +SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0, + spkboost_tlv), +SOC_ENUM("Speaker Reference", speaker_ref), +SOC_ENUM("Speaker Mode", speaker_mode), + +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = outpga_tlv, + .info = snd_soc_info_volsw_2r, + .get = snd_soc_get_volsw_2r, .put = wm8993_put_dc_servo, + .private_value = (unsigned long)&(struct soc_mixer_control) { + .reg = WM8993_LEFT_OUTPUT_VOLUME, + .rreg = WM8993_RIGHT_OUTPUT_VOLUME, + .shift = 0, .max = 63 + }, +}, +SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME, + WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0), +SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME, + WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0), + +SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1), +SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1), +SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1, + line_tlv), + +SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1), +SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1), +SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1, + line_tlv), +}; + +static const struct snd_kcontrol_new wm8993_eq_controls[] = { +SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv), +}; + +static int wm8993_earpiece_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_codec *codec = w->codec; + u16 reg = wm8993_read(codec, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + reg |= WM8993_HPOUT2_IN_ENA; + wm8993_write(codec, WM8993_ANTIPOP1, reg); + udelay(50); + break; + + case SND_SOC_DAPM_POST_PMD: + wm8993_write(codec, WM8993_ANTIPOP1, reg); + break; + + default: + BUG(); + break; + } + + return 0; +} + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return configure_clock(codec); + + case SND_SOC_DAPM_POST_PMD: + break; + } + + return 0; +} + +/* + * When used with DAC outputs only the WM8993 charge pump supports + * operation in class W mode, providing very low power consumption + * when used with digital sources. Enable and disable this mode + * automatically depending on the mixer configuration. + * + * Currently the only supported paths are the direct DAC->headphone + * paths (which provide minimum power consumption anyway). + */ +static int wm8993_class_w_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = widget->codec; + struct wm8993_priv *wm8993 = codec->private_data; + int ret; + + /* Turn it off if we're using the main output mixer */ + if (ucontrol->value.integer.value[0] == 0) { + if (wm8993->class_w_users == 0) { + dev_dbg(codec->dev, "Disabling Class W\n"); + snd_soc_update_bits(codec, WM8993_CLASS_W_0, + WM8993_CP_DYN_FREQ | + WM8993_CP_DYN_V, + 0); + } + wm8993->class_w_users++; + } + + /* Implement the change */ + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + + /* Enable it if we're using the direct DAC path */ + if (ucontrol->value.integer.value[0] == 1) { + if (wm8993->class_w_users == 1) { + dev_dbg(codec->dev, "Enabling Class W\n"); + snd_soc_update_bits(codec, WM8993_CLASS_W_0, + WM8993_CP_DYN_FREQ | + WM8993_CP_DYN_V, + WM8993_CP_DYN_FREQ | + WM8993_CP_DYN_V); + } + wm8993->class_w_users--; + } + + dev_dbg(codec->dev, "Indirect DAC use count now %d\n", + wm8993->class_w_users); + + return ret; +} + +#define SOC_DAPM_ENUM_W(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = wm8993_class_w_put, \ + .private_value = (unsigned long)&xenum } + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int reg = wm8993_read(codec, WM8993_ANALOGUE_HP_0); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, + WM8993_CP_ENA, WM8993_CP_ENA); + + msleep(5); + + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA); + + reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; + wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg); + + /* Start the DC servo */ + snd_soc_update_bits(codec, WM8993_DC_SERVO_0, + WM8993_DCS_ENA_CHAN_0 | + WM8993_DCS_ENA_CHAN_1 | + WM8993_DCS_TRIG_STARTUP_1 | + WM8993_DCS_TRIG_STARTUP_0, + WM8993_DCS_ENA_CHAN_0 | + WM8993_DCS_ENA_CHAN_1 | + WM8993_DCS_TRIG_STARTUP_1 | + WM8993_DCS_TRIG_STARTUP_0); + wait_for_dc_servo(codec, WM8993_DCS_TRIG_STARTUP_0 | + WM8993_DCS_TRIG_STARTUP_1); + snd_soc_update_bits(codec, WM8993_DC_SERVO_1, + WM8993_DCS_TIMER_PERIOD_01_MASK, 0xa); + + reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT | + WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT; + wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg); + break; + + case SND_SOC_DAPM_PRE_PMD: + reg &= ~(WM8993_HPOUT1L_RMV_SHORT | + WM8993_HPOUT1L_DLY | + WM8993_HPOUT1L_OUTP | + WM8993_HPOUT1R_RMV_SHORT | + WM8993_HPOUT1R_DLY | + WM8993_HPOUT1R_OUTP); + + snd_soc_update_bits(codec, WM8993_DC_SERVO_1, + WM8993_DCS_TIMER_PERIOD_01_MASK, 0); + snd_soc_update_bits(codec, WM8993_DC_SERVO_0, + WM8993_DCS_ENA_CHAN_0 | + WM8993_DCS_ENA_CHAN_1, 0); + + wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg); + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, + 0); + + snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, + WM8993_CP_ENA, 0); + break; + } + + return 0; +} + +static const struct snd_kcontrol_new in1l_pga[] = { +SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0), +}; + +static const struct snd_kcontrol_new in1r_pga[] = { +SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new in2l_pga[] = { +SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0), +SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new in2r_pga[] = { +SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0), +SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0), +}; + +static const struct snd_kcontrol_new mixinl[] = { +SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0), +}; + +static const struct snd_kcontrol_new mixinr[] = { +SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0), +}; + +static const struct snd_kcontrol_new left_output_mixer[] = { +SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), +SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), +SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), +SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), +SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_output_mixer[] = { +SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), +SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), +SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new earpiece_mixer[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_speaker_mixer[] = { +SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0), +SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_mixer[] = { +SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0), +SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0), +}; + +static const struct snd_kcontrol_new left_speaker_boost[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0), +SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0), +SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_boost[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0), +SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0), +SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0), +}; + +static const char *hp_mux_text[] = { + "Mixer", + "DAC", +}; + +static const struct soc_enum hpl_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text); + +static const struct snd_kcontrol_new hpl_mux = + SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum); + +static const struct soc_enum hpr_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text); + +static const struct snd_kcontrol_new hpr_mux = + SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum); + +static const struct snd_kcontrol_new line1_mix[] = { +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line1n_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0), +}; + +static const struct snd_kcontrol_new line1p_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line2_mix[] = { +SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line2n_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +}; + +static const struct snd_kcontrol_new line2p_mix[] = { +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1LN"), +SND_SOC_DAPM_INPUT("IN1LP"), +SND_SOC_DAPM_INPUT("IN2LN"), +SND_SOC_DAPM_INPUT("IN2LP/VXRN"), +SND_SOC_DAPM_INPUT("IN1RN"), +SND_SOC_DAPM_INPUT("IN1RP"), +SND_SOC_DAPM_INPUT("IN2RN"), +SND_SOC_DAPM_INPUT("IN2RP/VXRP"), + +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0), + +SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0), +SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0), + +SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0, + in1l_pga, ARRAY_SIZE(in1l_pga)), +SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0, + in1r_pga, ARRAY_SIZE(in1r_pga)), + +SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0, + in2l_pga, ARRAY_SIZE(in2l_pga)), +SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0, + in2r_pga, ARRAY_SIZE(in2r_pga)), + +/* Dummy widgets to represent differential paths */ +SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0, + mixinl, ARRAY_SIZE(mixinl)), +SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0, + mixinr, ARRAY_SIZE(mixinr)), + +SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0), +SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0), + +SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0), +SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0, + left_output_mixer, ARRAY_SIZE(left_output_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0, + right_output_mixer, ARRAY_SIZE(right_output_mixer)), + +SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, + earpiece_mixer, ARRAY_SIZE(earpiece_mixer)), +SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0, + NULL, 0, wm8993_earpiece_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), + +SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0, + left_speaker_boost, ARRAY_SIZE(left_speaker_boost)), +SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0, + right_speaker_boost, ARRAY_SIZE(right_speaker_boost)), + +SND_SOC_DAPM_PGA("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0, + NULL, 0), +SND_SOC_DAPM_PGA("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0, + NULL, 0), + +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, + NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0, + line1_mix, ARRAY_SIZE(line1_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0, + line2_mix, ARRAY_SIZE(line2_mix)), + +SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0, + line1n_mix, ARRAY_SIZE(line1n_mix)), +SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0, + line1p_mix, ARRAY_SIZE(line1p_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0, + line2n_mix, ARRAY_SIZE(line2n_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0, + line2p_mix, ARRAY_SIZE(line2p_mix)), + +SND_SOC_DAPM_PGA("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0, + NULL, 0), + +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2P"), +SND_SOC_DAPM_OUTPUT("HPOUT2N"), +SND_SOC_DAPM_OUTPUT("LINEOUT1P"), +SND_SOC_DAPM_OUTPUT("LINEOUT1N"), +SND_SOC_DAPM_OUTPUT("LINEOUT2P"), +SND_SOC_DAPM_OUTPUT("LINEOUT2N"), +}; + +static const struct snd_soc_dapm_route routes[] = { + { "IN1L PGA", "IN1LP Switch", "IN1LP" }, + { "IN1L PGA", "IN1LN Switch", "IN1LN" }, + + { "IN1R PGA", "IN1RP Switch", "IN1RP" }, + { "IN1R PGA", "IN1RN Switch", "IN1RN" }, + + { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" }, + { "IN2L PGA", "IN2LN Switch", "IN2LN" }, + + { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" }, + { "IN2R PGA", "IN2RN Switch", "IN2RN" }, + + { "Direct Voice", NULL, "IN2LP/VXRN" }, + { "Direct Voice", NULL, "IN2RP/VXRP" }, + + { "MIXINL", "IN1L Switch", "IN1L PGA" }, + { "MIXINL", "IN2L Switch", "IN2L PGA" }, + { "MIXINL", NULL, "Direct Voice" }, + { "MIXINL", NULL, "IN1LP" }, + { "MIXINL", NULL, "Left Output Mixer" }, + + { "MIXINR", "IN1R Switch", "IN1R PGA" }, + { "MIXINR", "IN2R Switch", "IN2R PGA" }, + { "MIXINR", NULL, "Direct Voice" }, + { "MIXINR", NULL, "IN1RP" }, + { "MIXINR", NULL, "Right Output Mixer" }, + + { "ADCL", NULL, "MIXINL" }, + { "ADCL", NULL, "CLK_SYS" }, + { "ADCL", NULL, "CLK_DSP" }, + { "ADCR", NULL, "MIXINR" }, + { "ADCR", NULL, "CLK_SYS" }, + { "ADCR", NULL, "CLK_DSP" }, + + { "DACL", NULL, "CLK_SYS" }, + { "DACL", NULL, "CLK_DSP" }, + { "DACR", NULL, "CLK_SYS" }, + { "DACR", NULL, "CLK_DSP" }, + + { "Left Output Mixer", "Left Input Switch", "MIXINL" }, + { "Left Output Mixer", "Right Input Switch", "MIXINR" }, + { "Left Output Mixer", "IN2RN Switch", "IN2RN" }, + { "Left Output Mixer", "IN2LN Switch", "IN2LN" }, + { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" }, + { "Left Output Mixer", "IN1L Switch", "IN1L PGA" }, + { "Left Output Mixer", "IN1R Switch", "IN1R PGA" }, + { "Left Output Mixer", "DAC Switch", "DACL" }, + + { "Right Output Mixer", "Left Input Switch", "MIXINL" }, + { "Right Output Mixer", "Right Input Switch", "MIXINR" }, + { "Right Output Mixer", "IN2LN Switch", "IN2LN" }, + { "Right Output Mixer", "IN2RN Switch", "IN2RN" }, + { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" }, + { "Right Output Mixer", "IN1L Switch", "IN1L PGA" }, + { "Right Output Mixer", "IN1R Switch", "IN1R PGA" }, + { "Right Output Mixer", "DAC Switch", "DACR" }, + + { "Left Output PGA", NULL, "Left Output Mixer" }, + { "Left Output PGA", NULL, "CLK_SYS" }, + { "Left Output PGA", NULL, "TOCLK" }, + + { "Right Output PGA", NULL, "Right Output Mixer" }, + { "Right Output PGA", NULL, "CLK_SYS" }, + { "Right Output PGA", NULL, "TOCLK" }, + + { "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" }, + { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" }, + { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" }, + + { "Earpiece Driver", NULL, "Earpiece Mixer" }, + { "HPOUT2N", NULL, "Earpiece Driver" }, + { "HPOUT2P", NULL, "Earpiece Driver" }, + + { "SPKL", "Input Switch", "MIXINL" }, + { "SPKL", "IN1LP Switch", "IN1LP" }, + { "SPKL", "Output Switch", "Left Output Mixer" }, + { "SPKL", "DAC Switch", "DACL" }, + { "SPKL", NULL, "CLK_SYS" }, + { "SPKL", NULL, "TOCLK" }, + + { "SPKR", "Input Switch", "MIXINR" }, + { "SPKR", "IN1RP Switch", "IN1RP" }, + { "SPKR", "Output Switch", "Right Output Mixer" }, + { "SPKR", "DAC Switch", "DACR" }, + { "SPKR", NULL, "CLK_SYS" }, + { "SPKR", NULL, "TOCLK" }, + + { "SPKL Boost", "Direct Voice Switch", "Direct Voice" }, + { "SPKL Boost", "SPKL Switch", "SPKL" }, + { "SPKL Boost", "SPKR Switch", "SPKR" }, + + { "SPKR Boost", "Direct Voice Switch", "Direct Voice" }, + { "SPKR Boost", "SPKR Switch", "SPKR" }, + { "SPKR Boost", "SPKL Switch", "SPKL" }, + + { "SPKL Driver", NULL, "SPKL Boost" }, + { "SPKL Driver", NULL, "CLK_SYS" }, + + { "SPKR Driver", NULL, "SPKR Boost" }, + { "SPKR Driver", NULL, "CLK_SYS" }, + + { "SPKOUTLP", NULL, "SPKL Driver" }, + { "SPKOUTLN", NULL, "SPKL Driver" }, + { "SPKOUTRP", NULL, "SPKR Driver" }, + { "SPKOUTRN", NULL, "SPKR Driver" }, + + { "Left Headphone Mux", "DAC", "DACL" }, + { "Left Headphone Mux", "Mixer", "Left Output Mixer" }, + { "Right Headphone Mux", "DAC", "DACR" }, + { "Right Headphone Mux", "Mixer", "Right Output Mixer" }, + + { "Headphone PGA", NULL, "Left Headphone Mux" }, + { "Headphone PGA", NULL, "Right Headphone Mux" }, + { "Headphone PGA", NULL, "CLK_SYS" }, + { "Headphone PGA", NULL, "TOCLK" }, + + { "HPOUT1L", NULL, "Headphone PGA" }, + { "HPOUT1R", NULL, "Headphone PGA" }, + + { "LINEOUT1N", NULL, "LINEOUT1N Driver" }, + { "LINEOUT1P", NULL, "LINEOUT1P Driver" }, + { "LINEOUT2N", NULL, "LINEOUT2N Driver" }, + { "LINEOUT2P", NULL, "LINEOUT2P Driver" }, +}; + +static const struct snd_soc_dapm_route lineout1_diff_routes[] = { + { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" }, + { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" }, + { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" }, + + { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" }, + { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout1_se_routes[] = { + { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" }, + { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" }, + + { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" }, + + { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, + { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout2_diff_routes[] = { + { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" }, + { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" }, + { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" }, + + { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, + { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout2_se_routes[] = { + { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" }, + { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" }, + + { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" }, + + { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, + { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" }, +}; + +static int wm8993_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct wm8993_priv *wm8993 = codec->private_data; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* VMID=2*40k */ + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK, 0x2); + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2, + WM8993_TSHUT_ENA, WM8993_TSHUT_ENA); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_OFF) { + /* Bring up VMID with fast soft start */ + snd_soc_update_bits(codec, WM8993_ANTIPOP2, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC); + + /* If either line output is single ended we + * need the VMID buffer */ + if (!wm8993->pdata.lineout1_diff || + !wm8993->pdata.lineout2_diff) + snd_soc_update_bits(codec, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, + WM8993_LINEOUT_VMID_BUF_ENA); + + /* VMID=2*40k */ + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK | + WM8993_BIAS_ENA, + WM8993_BIAS_ENA | 0x2); + msleep(32); + + /* Switch to normal bias */ + snd_soc_update_bits(codec, WM8993_ANTIPOP2, + WM8993_BIAS_SRC | + WM8993_STARTUP_BIAS_ENA, 0); + } + + /* VMID=2*240k */ + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK, 0x4); + + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2, + WM8993_TSHUT_ENA, 0); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, 0); + + snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA, + 0); + break; + } + + codec->bias_level = level; + + return 0; +} + +static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8993_priv *wm8993 = codec->private_data; + + switch (clk_id) { + case WM8993_SYSCLK_MCLK: + wm8993->mclk_rate = freq; + case WM8993_SYSCLK_FLL: + wm8993->sysclk_source = clk_id; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8993_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8993_priv *wm8993 = codec->private_data; + unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1); + unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4); + + aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV | + WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK); + aif4 &= ~WM8993_LRCLK_DIR; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + wm8993->master = 0; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif4 |= WM8993_LRCLK_DIR; + wm8993->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif1 |= WM8993_BCLK_DIR; + wm8993->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 |= WM8993_BCLK_DIR; + aif4 |= WM8993_LRCLK_DIR; + wm8993->master = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif1 |= WM8993_AIF_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif1 |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif1 |= 0x8; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8993_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8993_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8993_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1); + wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4); + + return 0; +} + +static int wm8993_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8993_priv *wm8993 = codec->private_data; + int ret, i, best, best_val, cur_val; + unsigned int clocking1, clocking3, aif1, aif4; + + clocking1 = wm8993_read(codec, WM8993_CLOCKING_1); + clocking1 &= ~WM8993_BCLK_DIV_MASK; + + clocking3 = wm8993_read(codec, WM8993_CLOCKING_3); + clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK); + + aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1); + aif1 &= ~WM8993_AIF_WL_MASK; + + aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4); + aif4 &= ~WM8993_LRCLK_RATE_MASK; + + /* What BCLK do we need? */ + wm8993->fs = params_rate(params); + wm8993->bclk = 2 * wm8993->fs; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + wm8993->bclk *= 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + wm8993->bclk *= 20; + aif1 |= 0x8; + break; + case SNDRV_PCM_FORMAT_S24_LE: + wm8993->bclk *= 24; + aif1 |= 0x10; + break; + case SNDRV_PCM_FORMAT_S32_LE: + wm8993->bclk *= 32; + aif1 |= 0x18; + break; + default: + return -EINVAL; + } + + dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk); + + ret = configure_clock(codec); + if (ret != 0) + return ret; + + /* Select nearest CLK_SYS_RATE */ + best = 0; + best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio) + - wm8993->fs); + for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { + cur_val = abs((wm8993->sysclk_rate / + clk_sys_rates[i].ratio) - wm8993->fs);; + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n", + clk_sys_rates[best].ratio); + clocking3 |= (clk_sys_rates[best].clk_sys_rate + << WM8993_CLK_SYS_RATE_SHIFT); + + /* SAMPLE_RATE */ + best = 0; + best_val = abs(wm8993->fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(wm8993->fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n", + sample_rates[best].rate); + clocking3 |= (sample_rates[best].sample_rate + << WM8993_SAMPLE_RATE_SHIFT); + + /* BCLK_DIV */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div) + - wm8993->bclk; + if (cur_val < 0) /* Table is sorted */ + break; + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div; + dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n", + bclk_divs[best].div, wm8993->bclk); + clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT; + + /* LRCLK is a simple fraction of BCLK */ + dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs); + aif4 |= wm8993->bclk / wm8993->fs; + + wm8993_write(codec, WM8993_CLOCKING_1, clocking1); + wm8993_write(codec, WM8993_CLOCKING_3, clocking3); + wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1); + wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4); + + /* ReTune Mobile? */ + if (wm8993->pdata.num_retune_configs) { + u16 eq1 = wm8993_read(codec, WM8993_EQ1); + struct wm8993_retune_mobile_setting *s; + + best = 0; + best_val = abs(wm8993->pdata.retune_configs[0].rate + - wm8993->fs); + for (i = 0; i < wm8993->pdata.num_retune_configs; i++) { + cur_val = abs(wm8993->pdata.retune_configs[i].rate + - wm8993->fs); + if (cur_val < best_val) { + best_val = cur_val; + best = i; + } + } + s = &wm8993->pdata.retune_configs[best]; + + dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", + s->name, s->rate); + + /* Disable EQ while we reconfigure */ + snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0); + + for (i = 1; i < ARRAY_SIZE(s->config); i++) + wm8993_write(codec, WM8993_EQ1 + i, s->config[i]); + + snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1); + } + + return 0; +} + +static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + unsigned int reg; + + reg = wm8993_read(codec, WM8993_DAC_CTRL); + + if (mute) + reg |= WM8993_DAC_MUTE; + else + reg &= ~WM8993_DAC_MUTE; + + wm8993_write(codec, WM8993_DAC_CTRL, reg); + + return 0; +} + +static struct snd_soc_dai_ops wm8993_ops = { + .set_sysclk = wm8993_set_sysclk, + .set_fmt = wm8993_set_dai_fmt, + .hw_params = wm8993_hw_params, + .digital_mute = wm8993_digital_mute, + .set_pll = wm8993_set_fll, +}; + +#define WM8993_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai wm8993_dai = { + .name = "WM8993", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8993_RATES, + .formats = WM8993_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8993_RATES, + .formats = WM8993_FORMATS, + }, + .ops = &wm8993_ops, + .symmetric_rates = 1, +}; +EXPORT_SYMBOL_GPL(wm8993_dai); + +static struct snd_soc_codec *wm8993_codec; + +static int wm8993_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + struct wm8993_priv *wm8993; + int ret = 0; + + if (!wm8993_codec) { + dev_err(&pdev->dev, "I2C device not yet probed\n"); + goto err; + } + + socdev->card->codec = wm8993_codec; + codec = wm8993_codec; + wm8993 = codec->private_data; + + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms\n"); + goto err; + } + + snd_soc_add_controls(codec, wm8993_snd_controls, + ARRAY_SIZE(wm8993_snd_controls)); + if (wm8993->pdata.num_retune_configs != 0) { + dev_dbg(codec->dev, "Using ReTune Mobile\n"); + } else { + dev_dbg(codec->dev, "No ReTune Mobile, using normal EQ\n"); + snd_soc_add_controls(codec, wm8993_eq_controls, + ARRAY_SIZE(wm8993_eq_controls)); + } + + snd_soc_dapm_new_controls(codec, wm8993_dapm_widgets, + ARRAY_SIZE(wm8993_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes)); + + if (wm8993->pdata.lineout1_diff) + snd_soc_dapm_add_routes(codec, + lineout1_diff_routes, + ARRAY_SIZE(lineout1_diff_routes)); + else + snd_soc_dapm_add_routes(codec, + lineout1_se_routes, + ARRAY_SIZE(lineout1_se_routes)); + + if (wm8993->pdata.lineout2_diff) + snd_soc_dapm_add_routes(codec, + lineout2_diff_routes, + ARRAY_SIZE(lineout2_diff_routes)); + else + snd_soc_dapm_add_routes(codec, + lineout2_se_routes, + ARRAY_SIZE(lineout2_se_routes)); + + snd_soc_dapm_new_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card\n"); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +err: + return ret; +} + +static int wm8993_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8993 = { + .probe = wm8993_probe, + .remove = wm8993_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993); + +static int wm8993_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8993_priv *wm8993; + struct snd_soc_codec *codec; + unsigned int val; + int ret; + + if (wm8993_codec) { + dev_err(&i2c->dev, "A WM8993 is already registered\n"); + return -EINVAL; + } + + wm8993 = kzalloc(sizeof(struct wm8993_priv), GFP_KERNEL); + if (wm8993 == NULL) + return -ENOMEM; + + codec = &wm8993->codec; + if (i2c->dev.platform_data) + memcpy(&wm8993->pdata, i2c->dev.platform_data, + sizeof(wm8993->pdata)); + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->name = "WM8993"; + codec->read = wm8993_read; + codec->write = wm8993_write; + codec->hw_write = (hw_write_t)i2c_master_send; + codec->reg_cache = wm8993->reg_cache; + codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache); + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = wm8993_set_bias_level; + codec->dai = &wm8993_dai; + codec->num_dai = 1; + codec->private_data = wm8993; + + memcpy(wm8993->reg_cache, wm8993_reg_defaults, + sizeof(wm8993->reg_cache)); + + i2c_set_clientdata(i2c, wm8993); + codec->control_data = i2c; + wm8993_codec = codec; + + codec->dev = &i2c->dev; + + val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET); + if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) { + dev_err(codec->dev, "Invalid ID register value %x\n", val); + ret = -EINVAL; + goto err; + } + + ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff); + if (ret != 0) + goto err; + + /* By default we're using the output mixers */ + wm8993->class_w_users = 2; + + /* Latch volume update bits and default ZC on */ + snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME, + WM8993_IN1_VU, WM8993_IN1_VU); + snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8993_IN1_VU, WM8993_IN1_VU); + snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_3_4_VOLUME, + WM8993_IN2_VU, WM8993_IN2_VU); + snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8993_IN2_VU, WM8993_IN2_VU); + + snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT, + WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); + + snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME, + WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC); + snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME, + WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC, + WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC); + + snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME, + WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC); + snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME, + WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU, + WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU); + + snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME, + WM8993_DAC_VU, WM8993_DAC_VU); + snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME, + WM8993_ADC_VU, WM8993_ADC_VU); + + /* Manualy manage the HPOUT sequencing for independent stereo + * control. */ + snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0, + WM8993_HPOUT1_AUTO_PU, 0); + + /* Use automatic clock configuration */ + snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0); + + if (!wm8993->pdata.lineout1_diff) + snd_soc_update_bits(codec, WM8993_LINE_MIXER1, + WM8993_LINEOUT1_MODE, + WM8993_LINEOUT1_MODE); + if (!wm8993->pdata.lineout2_diff) + snd_soc_update_bits(codec, WM8993_LINE_MIXER2, + WM8993_LINEOUT2_MODE, + WM8993_LINEOUT2_MODE); + + if (wm8993->pdata.lineout1fb) + snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); + + if (wm8993->pdata.lineout2fb) + snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB); + + /* Apply the microphone bias/detection configuration - the + * platform data is directly applicable to the register. */ + snd_soc_update_bits(codec, WM8993_MICBIAS, + WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK | + WM8993_MICB1_LVL | WM8993_MICB2_LVL, + wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT | + wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT | + wm8993->pdata.micbias1_lvl | + wm8993->pdata.micbias1_lvl << 1); + + ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (ret != 0) + goto err; + + wm8993_dai.dev = codec->dev; + + ret = snd_soc_register_dai(&wm8993_dai); + if (ret != 0) + goto err_bias; + + ret = snd_soc_register_codec(codec); + + return 0; + +err_bias: + wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF); +err: + wm8993_codec = NULL; + kfree(wm8993); + return ret; +} + +static int wm8993_i2c_remove(struct i2c_client *client) +{ + struct wm8993_priv *wm8993 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&wm8993->codec); + snd_soc_unregister_dai(&wm8993_dai); + + wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF); + kfree(wm8993); + + return 0; +} + +static const struct i2c_device_id wm8993_i2c_id[] = { + { "wm8993", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id); + +static struct i2c_driver wm8993_i2c_driver = { + .driver = { + .name = "WM8993", + .owner = THIS_MODULE, + }, + .probe = wm8993_i2c_probe, + .remove = wm8993_i2c_remove, + .id_table = wm8993_i2c_id, +}; + + +static int __init wm8993_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&wm8993_i2c_driver); + if (ret != 0) + pr_err("WM8993: Unable to register I2C driver: %d\n", ret); + + return ret; +} +module_init(wm8993_modinit); + +static void __exit wm8993_exit(void) +{ + i2c_del_driver(&wm8993_i2c_driver); +} +module_exit(wm8993_exit); + + +MODULE_DESCRIPTION("ASoC WM8993 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h new file mode 100644 index 00000000000..30e71ca88da --- /dev/null +++ b/sound/soc/codecs/wm8993.h @@ -0,0 +1,2132 @@ +#ifndef WM8993_H +#define WM8993_H + +extern struct snd_soc_dai wm8993_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8993; + +#define WM8993_SYSCLK_MCLK 1 +#define WM8993_SYSCLK_FLL 2 + +#define WM8993_FLL_MCLK 1 +#define WM8993_FLL_BCLK 2 +#define WM8993_FLL_LRCLK 3 + +/* + * Register values. + */ +#define WM8993_SOFTWARE_RESET 0x00 +#define WM8993_POWER_MANAGEMENT_1 0x01 +#define WM8993_POWER_MANAGEMENT_2 0x02 +#define WM8993_POWER_MANAGEMENT_3 0x03 +#define WM8993_AUDIO_INTERFACE_1 0x04 +#define WM8993_AUDIO_INTERFACE_2 0x05 +#define WM8993_CLOCKING_1 0x06 +#define WM8993_CLOCKING_2 0x07 +#define WM8993_AUDIO_INTERFACE_3 0x08 +#define WM8993_AUDIO_INTERFACE_4 0x09 +#define WM8993_DAC_CTRL 0x0A +#define WM8993_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8993_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8993_DIGITAL_SIDE_TONE 0x0D +#define WM8993_ADC_CTRL 0x0E +#define WM8993_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8993_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8993_GPIO_CTRL_1 0x12 +#define WM8993_GPIO1 0x13 +#define WM8993_IRQ_DEBOUNCE 0x14 +#define WM8993_GPIOCTRL_2 0x16 +#define WM8993_GPIO_POL 0x17 +#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8993_LEFT_OUTPUT_VOLUME 0x1C +#define WM8993_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8993_LINE_OUTPUTS_VOLUME 0x1E +#define WM8993_HPOUT2_VOLUME 0x1F +#define WM8993_LEFT_OPGA_VOLUME 0x20 +#define WM8993_RIGHT_OPGA_VOLUME 0x21 +#define WM8993_SPKMIXL_ATTENUATION 0x22 +#define WM8993_SPKMIXR_ATTENUATION 0x23 +#define WM8993_SPKOUT_MIXERS 0x24 +#define WM8993_SPKOUT_BOOST 0x25 +#define WM8993_SPEAKER_VOLUME_LEFT 0x26 +#define WM8993_SPEAKER_VOLUME_RIGHT 0x27 +#define WM8993_INPUT_MIXER2 0x28 +#define WM8993_INPUT_MIXER3 0x29 +#define WM8993_INPUT_MIXER4 0x2A +#define WM8993_INPUT_MIXER5 0x2B +#define WM8993_INPUT_MIXER6 0x2C +#define WM8993_OUTPUT_MIXER1 0x2D +#define WM8993_OUTPUT_MIXER2 0x2E +#define WM8993_OUTPUT_MIXER3 0x2F +#define WM8993_OUTPUT_MIXER4 0x30 +#define WM8993_OUTPUT_MIXER5 0x31 +#define WM8993_OUTPUT_MIXER6 0x32 +#define WM8993_HPOUT2_MIXER 0x33 +#define WM8993_LINE_MIXER1 0x34 +#define WM8993_LINE_MIXER2 0x35 +#define WM8993_SPEAKER_MIXER 0x36 +#define WM8993_ADDITIONAL_CONTROL 0x37 +#define WM8993_ANTIPOP1 0x38 +#define WM8993_ANTIPOP2 0x39 +#define WM8993_MICBIAS 0x3A +#define WM8993_FLL_CONTROL_1 0x3C +#define WM8993_FLL_CONTROL_2 0x3D +#define WM8993_FLL_CONTROL_3 0x3E +#define WM8993_FLL_CONTROL_4 0x3F +#define WM8993_FLL_CONTROL_5 0x40 +#define WM8993_CLOCKING_3 0x41 +#define WM8993_CLOCKING_4 0x42 +#define WM8993_MW_SLAVE_CONTROL 0x43 +#define WM8993_BUS_CONTROL_1 0x45 +#define WM8993_WRITE_SEQUENCER_0 0x46 +#define WM8993_WRITE_SEQUENCER_1 0x47 +#define WM8993_WRITE_SEQUENCER_2 0x48 +#define WM8993_WRITE_SEQUENCER_3 0x49 +#define WM8993_WRITE_SEQUENCER_4 0x4A +#define WM8993_WRITE_SEQUENCER_5 0x4B +#define WM8993_CHARGE_PUMP_1 0x4C +#define WM8993_CLASS_W_0 0x51 +#define WM8993_DC_SERVO_0 0x54 +#define WM8993_DC_SERVO_1 0x55 +#define WM8993_DC_SERVO_3 0x57 +#define WM8993_DC_SERVO_READBACK_0 0x58 +#define WM8993_DC_SERVO_READBACK_1 0x59 +#define WM8993_DC_SERVO_READBACK_2 0x5A +#define WM8993_ANALOGUE_HP_0 0x60 +#define WM8993_EQ1 0x62 +#define WM8993_EQ2 0x63 +#define WM8993_EQ3 0x64 +#define WM8993_EQ4 0x65 +#define WM8993_EQ5 0x66 +#define WM8993_EQ6 0x67 +#define WM8993_EQ7 0x68 +#define WM8993_EQ8 0x69 +#define WM8993_EQ9 0x6A +#define WM8993_EQ10 0x6B +#define WM8993_EQ11 0x6C +#define WM8993_EQ12 0x6D +#define WM8993_EQ13 0x6E +#define WM8993_EQ14 0x6F +#define WM8993_EQ15 0x70 +#define WM8993_EQ16 0x71 +#define WM8993_EQ17 0x72 +#define WM8993_EQ18 0x73 +#define WM8993_EQ19 0x74 +#define WM8993_EQ20 0x75 +#define WM8993_EQ21 0x76 +#define WM8993_EQ22 0x77 +#define WM8993_EQ23 0x78 +#define WM8993_EQ24 0x79 +#define WM8993_DIGITAL_PULLS 0x7A +#define WM8993_DRC_CONTROL_1 0x7B +#define WM8993_DRC_CONTROL_2 0x7C +#define WM8993_DRC_CONTROL_3 0x7D +#define WM8993_DRC_CONTROL_4 0x7E + +#define WM8993_REGISTER_COUNT 0x7F +#define WM8993_MAX_REGISTER 0x7E + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8993_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8993_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8993_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8993_SPKOUTR_ENA 0x2000 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_MASK 0x2000 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_SHIFT 13 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */ +#define WM8993_HPOUT2_ENA 0x0800 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_MASK 0x0800 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_SHIFT 11 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_WIDTH 1 /* HPOUT2_ENA */ +#define WM8993_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM8993_MICB2_ENA 0x0020 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_MASK 0x0020 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_SHIFT 5 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ +#define WM8993_MICB1_ENA 0x0010 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_MASK 0x0010 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_SHIFT 4 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ +#define WM8993_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */ +#define WM8993_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */ +#define WM8993_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */ +#define WM8993_BIAS_ENA 0x0001 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8993_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM8993_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */ +#define WM8993_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8993_MIXINL_ENA 0x0200 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_MASK 0x0200 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_SHIFT 9 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */ +#define WM8993_MIXINR_ENA 0x0100 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_MASK 0x0100 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_SHIFT 8 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */ +#define WM8993_IN2L_ENA 0x0080 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_MASK 0x0080 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_SHIFT 7 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define WM8993_IN1L_ENA 0x0040 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_MASK 0x0040 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_SHIFT 6 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM8993_IN2R_ENA 0x0020 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_MASK 0x0020 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_SHIFT 5 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define WM8993_IN1R_ENA 0x0010 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_SHIFT 4 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ +#define WM8993_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8993_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8993_LINEOUT1N_ENA 0x2000 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_MASK 0x2000 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_SHIFT 13 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_WIDTH 1 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1P_ENA 0x1000 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_MASK 0x1000 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_SHIFT 12 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_WIDTH 1 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT2N_ENA 0x0800 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_MASK 0x0800 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_SHIFT 11 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_WIDTH 1 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2P_ENA 0x0400 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_MASK 0x0400 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_SHIFT 10 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_WIDTH 1 /* LINEOUT2P_ENA */ +#define WM8993_SPKRVOL_ENA 0x0200 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_MASK 0x0200 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_SHIFT 9 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_WIDTH 1 /* SPKRVOL_ENA */ +#define WM8993_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA 0x0080 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_MASK 0x0080 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_SHIFT 7 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_WIDTH 1 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA 0x0040 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_MASK 0x0040 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_SHIFT 6 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_WIDTH 1 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */ +#define WM8993_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8993_DACL_ENA_MASK 0x0002 /* DACL_ENA */ +#define WM8993_DACL_ENA_SHIFT 1 /* DACL_ENA */ +#define WM8993_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8993_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8993_DACR_ENA_MASK 0x0001 /* DACR_ENA */ +#define WM8993_DACR_ENA_SHIFT 0 /* DACR_ENA */ +#define WM8993_DACR_ENA_WIDTH 1 /* DACR_ENA */ + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8993_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8993_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8993_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8993_BCLK_DIR 0x0200 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_MASK 0x0200 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_SHIFT 9 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ +#define WM8993_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8993_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8993_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8993_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */ +#define WM8993_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */ +#define WM8993_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8993_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */ +#define WM8993_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */ + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8993_AIFDACL_SRC 0x8000 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_MASK 0x8000 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_SHIFT 15 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */ +#define WM8993_AIFDACR_SRC 0x4000 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_MASK 0x4000 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_SHIFT 14 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */ +#define WM8993_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8993_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8993_DAC_COMP_MASK 0x0010 /* DAC_COMP */ +#define WM8993_DAC_COMP_SHIFT 4 /* DAC_COMP */ +#define WM8993_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8993_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ +#define WM8993_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8993_ADC_COMP_MASK 0x0004 /* ADC_COMP */ +#define WM8993_ADC_COMP_SHIFT 2 /* ADC_COMP */ +#define WM8993_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8993_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8993_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8993_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8993_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8993_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking 1 + */ +#define WM8993_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM8993_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8993_OPCLK_DIV_MASK 0x1E00 /* OPCLK_DIV - [12:9] */ +#define WM8993_OPCLK_DIV_SHIFT 9 /* OPCLK_DIV - [12:9] */ +#define WM8993_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [12:9] */ +#define WM8993_DCLK_DIV_MASK 0x01C0 /* DCLK_DIV - [8:6] */ +#define WM8993_DCLK_DIV_SHIFT 6 /* DCLK_DIV - [8:6] */ +#define WM8993_DCLK_DIV_WIDTH 3 /* DCLK_DIV - [8:6] */ +#define WM8993_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8993_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */ +#define WM8993_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */ + +/* + * R7 (0x07) - Clocking 2 + */ +#define WM8993_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_SHIFT 15 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_WIDTH 1 /* MCLK_SRC */ +#define WM8993_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ +#define WM8993_MCLK_DIV 0x1000 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_MASK 0x1000 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_SHIFT 12 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_WIDTH 1 /* MCLK_DIV */ +#define WM8993_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8993_MCLK_INV_MASK 0x0400 /* MCLK_INV */ +#define WM8993_MCLK_INV_SHIFT 10 /* MCLK_INV */ +#define WM8993_MCLK_INV_WIDTH 1 /* MCLK_INV */ +#define WM8993_ADC_DIV_MASK 0x00E0 /* ADC_DIV - [7:5] */ +#define WM8993_ADC_DIV_SHIFT 5 /* ADC_DIV - [7:5] */ +#define WM8993_ADC_DIV_WIDTH 3 /* ADC_DIV - [7:5] */ +#define WM8993_DAC_DIV_MASK 0x001C /* DAC_DIV - [4:2] */ +#define WM8993_DAC_DIV_SHIFT 2 /* DAC_DIV - [4:2] */ +#define WM8993_DAC_DIV_WIDTH 3 /* DAC_DIV - [4:2] */ + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8993_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8993_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_SHIFT 13 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM8993_LRCLK_DIR 0x0800 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ +#define WM8993_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ +#define WM8993_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ +#define WM8993_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8993_DAC_OSR128 0x2000 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_MASK 0x2000 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_SHIFT 13 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ +#define WM8993_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8993_DAC_MONO_MASK 0x0200 /* DAC_MONO */ +#define WM8993_DAC_MONO_SHIFT 9 /* DAC_MONO */ +#define WM8993_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8993_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8993_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8993_DAC_UNMUTE_RAMP 0x0040 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_MASK 0x0040 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_SHIFT 6 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */ +#define WM8993_DEEMPH_MASK 0x0030 /* DEEMPH - [5:4] */ +#define WM8993_DEEMPH_SHIFT 4 /* DEEMPH - [5:4] */ +#define WM8993_DEEMPH_WIDTH 2 /* DEEMPH - [5:4] */ +#define WM8993_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_SHIFT 2 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8993_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_SHIFT 1 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8993_DACR_DATINV 0x0001 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_SHIFT 0 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8993_DAC_VU 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8993_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8993_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8993_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8993_DAC_VU 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8993_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8993_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8993_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8993_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8993_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8993_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8993_ADC_OSR128 0x0200 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_MASK 0x0200 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_SHIFT 9 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ +#define WM8993_ADC_HPF 0x0100 /* ADC_HPF */ +#define WM8993_ADC_HPF_MASK 0x0100 /* ADC_HPF */ +#define WM8993_ADC_HPF_SHIFT 8 /* ADC_HPF */ +#define WM8993_ADC_HPF_WIDTH 1 /* ADC_HPF */ +#define WM8993_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8993_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8993_ADC_VU 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8993_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8993_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8993_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8993_ADC_VU 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8993_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8993_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8993_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8993_JD2_SC_EINT 0x8000 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_MASK 0x8000 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_SHIFT 15 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_WIDTH 1 /* JD2_SC_EINT */ +#define WM8993_JD2_EINT 0x4000 /* JD2_EINT */ +#define WM8993_JD2_EINT_MASK 0x4000 /* JD2_EINT */ +#define WM8993_JD2_EINT_SHIFT 14 /* JD2_EINT */ +#define WM8993_JD2_EINT_WIDTH 1 /* JD2_EINT */ +#define WM8993_WSEQ_EINT 0x2000 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_MASK 0x2000 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_SHIFT 13 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */ +#define WM8993_IRQ 0x1000 /* IRQ */ +#define WM8993_IRQ_MASK 0x1000 /* IRQ */ +#define WM8993_IRQ_SHIFT 12 /* IRQ */ +#define WM8993_IRQ_WIDTH 1 /* IRQ */ +#define WM8993_TEMPOK_EINT 0x0800 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_MASK 0x0800 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_SHIFT 11 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_WIDTH 1 /* TEMPOK_EINT */ +#define WM8993_JD1_SC_EINT 0x0400 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_MASK 0x0400 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_SHIFT 10 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_WIDTH 1 /* JD1_SC_EINT */ +#define WM8993_JD1_EINT 0x0200 /* JD1_EINT */ +#define WM8993_JD1_EINT_MASK 0x0200 /* JD1_EINT */ +#define WM8993_JD1_EINT_SHIFT 9 /* JD1_EINT */ +#define WM8993_JD1_EINT_WIDTH 1 /* JD1_EINT */ +#define WM8993_FLL_LOCK_EINT 0x0100 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_MASK 0x0100 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_SHIFT 8 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM8993_GPI8_EINT 0x0080 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_MASK 0x0080 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_SHIFT 7 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_WIDTH 1 /* GPI8_EINT */ +#define WM8993_GPI7_EINT 0x0040 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_MASK 0x0040 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_SHIFT 6 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_WIDTH 1 /* GPI7_EINT */ +#define WM8993_GPIO1_EINT 0x0001 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_MASK 0x0001 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_SHIFT 0 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */ + +/* + * R19 (0x13) - GPIO1 + */ +#define WM8993_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_SHIFT 5 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_WIDTH 1 /* GPIO1_PU */ +#define WM8993_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_SHIFT 4 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_WIDTH 1 /* GPIO1_PD */ +#define WM8993_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ +#define WM8993_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */ +#define WM8993_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - IRQ_DEBOUNCE + */ +#define WM8993_JD2_SC_DB 0x8000 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_MASK 0x8000 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_SHIFT 15 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_WIDTH 1 /* JD2_SC_DB */ +#define WM8993_JD2_DB 0x4000 /* JD2_DB */ +#define WM8993_JD2_DB_MASK 0x4000 /* JD2_DB */ +#define WM8993_JD2_DB_SHIFT 14 /* JD2_DB */ +#define WM8993_JD2_DB_WIDTH 1 /* JD2_DB */ +#define WM8993_WSEQ_DB 0x2000 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_MASK 0x2000 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_SHIFT 13 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_WIDTH 1 /* WSEQ_DB */ +#define WM8993_TEMPOK_DB 0x0800 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_MASK 0x0800 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_SHIFT 11 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_WIDTH 1 /* TEMPOK_DB */ +#define WM8993_JD1_SC_DB 0x0400 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_MASK 0x0400 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_SHIFT 10 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_WIDTH 1 /* JD1_SC_DB */ +#define WM8993_JD1_DB 0x0200 /* JD1_DB */ +#define WM8993_JD1_DB_MASK 0x0200 /* JD1_DB */ +#define WM8993_JD1_DB_SHIFT 9 /* JD1_DB */ +#define WM8993_JD1_DB_WIDTH 1 /* JD1_DB */ +#define WM8993_FLL_LOCK_DB 0x0100 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_MASK 0x0100 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_SHIFT 8 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */ +#define WM8993_GPI8_DB 0x0080 /* GPI8_DB */ +#define WM8993_GPI8_DB_MASK 0x0080 /* GPI8_DB */ +#define WM8993_GPI8_DB_SHIFT 7 /* GPI8_DB */ +#define WM8993_GPI8_DB_WIDTH 1 /* GPI8_DB */ +#define WM8993_GPI7_DB 0x0008 /* GPI7_DB */ +#define WM8993_GPI7_DB_MASK 0x0008 /* GPI7_DB */ +#define WM8993_GPI7_DB_SHIFT 3 /* GPI7_DB */ +#define WM8993_GPI7_DB_WIDTH 1 /* GPI7_DB */ +#define WM8993_GPIO1_DB 0x0001 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_MASK 0x0001 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_SHIFT 0 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_WIDTH 1 /* GPIO1_DB */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8993_IM_JD2_EINT 0x2000 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_MASK 0x2000 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_SHIFT 13 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_WIDTH 1 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_SC_EINT 0x1000 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_MASK 0x1000 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_SHIFT 12 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_WIDTH 1 /* IM_JD2_SC_EINT */ +#define WM8993_IM_TEMPOK_EINT 0x0800 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_MASK 0x0800 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_SHIFT 11 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_WIDTH 1 /* IM_TEMPOK_EINT */ +#define WM8993_IM_JD1_SC_EINT 0x0400 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_MASK 0x0400 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_SHIFT 10 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_WIDTH 1 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_EINT 0x0200 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_MASK 0x0200 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_SHIFT 9 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_WIDTH 1 /* IM_JD1_EINT */ +#define WM8993_IM_FLL_LOCK_EINT 0x0100 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_MASK 0x0100 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_SHIFT 8 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_GPI8_EINT 0x0040 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_MASK 0x0040 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_SHIFT 6 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */ +#define WM8993_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */ +#define WM8993_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_SHIFT 4 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_WIDTH 1 /* GPI8_ENA */ +#define WM8993_IM_GPI7_EINT 0x0004 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_MASK 0x0004 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_SHIFT 2 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */ +#define WM8993_IM_WSEQ_EINT 0x0002 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_MASK 0x0002 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_SHIFT 1 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */ +#define WM8993_GPI7_ENA 0x0001 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_SHIFT 0 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_WIDTH 1 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8993_JD2_SC_POL 0x8000 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_MASK 0x8000 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_SHIFT 15 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_WIDTH 1 /* JD2_SC_POL */ +#define WM8993_JD2_POL 0x4000 /* JD2_POL */ +#define WM8993_JD2_POL_MASK 0x4000 /* JD2_POL */ +#define WM8993_JD2_POL_SHIFT 14 /* JD2_POL */ +#define WM8993_JD2_POL_WIDTH 1 /* JD2_POL */ +#define WM8993_WSEQ_POL 0x2000 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_MASK 0x2000 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_SHIFT 13 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_WIDTH 1 /* WSEQ_POL */ +#define WM8993_IRQ_POL 0x1000 /* IRQ_POL */ +#define WM8993_IRQ_POL_MASK 0x1000 /* IRQ_POL */ +#define WM8993_IRQ_POL_SHIFT 12 /* IRQ_POL */ +#define WM8993_IRQ_POL_WIDTH 1 /* IRQ_POL */ +#define WM8993_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */ +#define WM8993_JD1_SC_POL 0x0400 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_MASK 0x0400 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_SHIFT 10 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_WIDTH 1 /* JD1_SC_POL */ +#define WM8993_JD1_POL 0x0200 /* JD1_POL */ +#define WM8993_JD1_POL_MASK 0x0200 /* JD1_POL */ +#define WM8993_JD1_POL_SHIFT 9 /* JD1_POL */ +#define WM8993_JD1_POL_WIDTH 1 /* JD1_POL */ +#define WM8993_FLL_LOCK_POL 0x0100 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_MASK 0x0100 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_SHIFT 8 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_WIDTH 1 /* FLL_LOCK_POL */ +#define WM8993_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8993_GPI8_POL_MASK 0x0080 /* GPI8_POL */ +#define WM8993_GPI8_POL_SHIFT 7 /* GPI8_POL */ +#define WM8993_GPI8_POL_WIDTH 1 /* GPI8_POL */ +#define WM8993_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8993_GPI7_POL_MASK 0x0040 /* GPI7_POL */ +#define WM8993_GPI7_POL_SHIFT 6 /* GPI7_POL */ +#define WM8993_GPI7_POL_WIDTH 1 /* GPI7_POL */ +#define WM8993_GPIO1_POL 0x0001 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_MASK 0x0001 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_SHIFT 0 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_WIDTH 1 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8993_IN1_VU 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8993_IN1L_MUTE 0x0080 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_MASK 0x0080 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_SHIFT 7 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define WM8993_IN1L_ZC 0x0040 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_MASK 0x0040 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_SHIFT 6 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_WIDTH 1 /* IN1L_ZC */ +#define WM8993_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */ +#define WM8993_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */ +#define WM8993_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */ + +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8993_IN2_VU 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM8993_IN2L_MUTE 0x0080 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_MASK 0x0080 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_SHIFT 7 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define WM8993_IN2L_ZC 0x0040 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_MASK 0x0040 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_SHIFT 6 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_WIDTH 1 /* IN2L_ZC */ +#define WM8993_IN2L_VOL_MASK 0x001F /* IN2L_VOL - [4:0] */ +#define WM8993_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [4:0] */ +#define WM8993_IN2L_VOL_WIDTH 5 /* IN2L_VOL - [4:0] */ + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8993_IN1_VU 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8993_IN1R_MUTE 0x0080 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_MASK 0x0080 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_SHIFT 7 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define WM8993_IN1R_ZC 0x0040 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_MASK 0x0040 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_SHIFT 6 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_WIDTH 1 /* IN1R_ZC */ +#define WM8993_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */ +#define WM8993_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */ +#define WM8993_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */ + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8993_IN2_VU 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM8993_IN2R_MUTE 0x0080 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_MASK 0x0080 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_SHIFT 7 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define WM8993_IN2R_ZC 0x0040 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_MASK 0x0040 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_SHIFT 6 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_WIDTH 1 /* IN2R_ZC */ +#define WM8993_IN2R_VOL_MASK 0x001F /* IN2R_VOL - [4:0] */ +#define WM8993_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [4:0] */ +#define WM8993_IN2R_VOL_WIDTH 5 /* IN2R_VOL - [4:0] */ + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM8993_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_MUTE_N 0x0040 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_MASK 0x0040 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_SHIFT 6 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_WIDTH 1 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */ +#define WM8993_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */ +#define WM8993_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */ + +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM8993_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_MUTE_N 0x0040 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_MASK 0x0040 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_SHIFT 6 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_WIDTH 1 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */ +#define WM8993_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */ +#define WM8993_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */ + +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8993_LINEOUT1N_MUTE 0x0040 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_MASK 0x0040 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_SHIFT 6 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_WIDTH 1 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1P_MUTE 0x0020 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_MASK 0x0020 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_SHIFT 5 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_WIDTH 1 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1_VOL 0x0010 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_MASK 0x0010 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_SHIFT 4 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_WIDTH 1 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT2N_MUTE 0x0004 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_MASK 0x0004 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_SHIFT 2 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_WIDTH 1 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2P_MUTE 0x0002 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_MASK 0x0002 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_SHIFT 1 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_WIDTH 1 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2_VOL 0x0001 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_MASK 0x0001 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_SHIFT 0 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_WIDTH 1 /* LINEOUT2_VOL */ + +/* + * R31 (0x1F) - HPOUT2 Volume + */ +#define WM8993_HPOUT2_MUTE 0x0020 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_MASK 0x0020 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_SHIFT 5 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_WIDTH 1 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_VOL 0x0010 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_MASK 0x0010 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_SHIFT 4 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_WIDTH 1 /* HPOUT2_VOL */ + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */ +#define WM8993_MIXOUTL_ZC 0x0080 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_MASK 0x0080 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_SHIFT 7 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_WIDTH 1 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_MUTE_N 0x0040 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_MASK 0x0040 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_SHIFT 6 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_WIDTH 1 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_VOL_MASK 0x003F /* MIXOUTL_VOL - [5:0] */ +#define WM8993_MIXOUTL_VOL_SHIFT 0 /* MIXOUTL_VOL - [5:0] */ +#define WM8993_MIXOUTL_VOL_WIDTH 6 /* MIXOUTL_VOL - [5:0] */ + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */ +#define WM8993_MIXOUTR_ZC 0x0080 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_MASK 0x0080 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_SHIFT 7 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_WIDTH 1 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_MUTE_N 0x0040 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_MASK 0x0040 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_SHIFT 6 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_WIDTH 1 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_VOL_MASK 0x003F /* MIXOUTR_VOL - [5:0] */ +#define WM8993_MIXOUTR_VOL_SHIFT 0 /* MIXOUTR_VOL - [5:0] */ +#define WM8993_MIXOUTR_VOL_WIDTH 6 /* MIXOUTR_VOL - [5:0] */ + +/* + * R34 (0x22) - SPKMIXL Attenuation + */ +#define WM8993_MIXINL_SPKMIXL_VOL 0x0020 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_MASK 0x0020 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT 5 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL 0x0010 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_MASK 0x0010 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT 4 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH 1 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL 0x0008 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK 0x0008 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT 3 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH 1 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL 0x0004 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_MASK 0x0004 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_SHIFT 2 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */ +#define WM8993_SPKMIXL_VOL_MASK 0x0003 /* SPKMIXL_VOL - [1:0] */ +#define WM8993_SPKMIXL_VOL_SHIFT 0 /* SPKMIXL_VOL - [1:0] */ +#define WM8993_SPKMIXL_VOL_WIDTH 2 /* SPKMIXL_VOL - [1:0] */ + +/* + * R35 (0x23) - SPKMIXR Attenuation + */ +#define WM8993_SPKOUT_CLASSAB_MODE 0x0100 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_MASK 0x0100 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT 8 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH 1 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_MIXINR_SPKMIXR_VOL 0x0020 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_MASK 0x0020 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT 5 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL 0x0010 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_MASK 0x0010 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT 4 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH 1 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL 0x0008 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK 0x0008 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT 3 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH 1 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL 0x0004 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_MASK 0x0004 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_SHIFT 2 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */ +#define WM8993_SPKMIXR_VOL_MASK 0x0003 /* SPKMIXR_VOL - [1:0] */ +#define WM8993_SPKMIXR_VOL_SHIFT 0 /* SPKMIXR_VOL - [1:0] */ +#define WM8993_SPKMIXR_VOL_WIDTH 2 /* SPKMIXR_VOL - [1:0] */ + +/* + * R36 (0x24) - SPKOUT Mixers + */ +#define WM8993_VRX_TO_SPKOUTL 0x0020 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_MASK 0x0020 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_SHIFT 5 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_WIDTH 1 /* VRX_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL 0x0008 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_MASK 0x0008 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT 3 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH 1 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTR 0x0004 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_MASK 0x0004 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_SHIFT 2 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_WIDTH 1 /* VRX_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR 0x0002 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_MASK 0x0002 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT 1 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH 1 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR 0x0001 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_MASK 0x0001 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT 0 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH 1 /* SPKMIXR_TO_SPKOUTR */ + +/* + * R37 (0x25) - SPKOUT Boost + */ +#define WM8993_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTR_BOOST_MASK 0x0007 /* SPKOUTR_BOOST - [2:0] */ +#define WM8993_SPKOUTR_BOOST_SHIFT 0 /* SPKOUTR_BOOST - [2:0] */ +#define WM8993_SPKOUTR_BOOST_WIDTH 3 /* SPKOUTR_BOOST - [2:0] */ + +/* + * R38 (0x26) - Speaker Volume Left + */ +#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM8993_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_MUTE_N 0x0040 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_MASK 0x0040 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_SHIFT 6 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_WIDTH 1 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */ +#define WM8993_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */ +#define WM8993_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */ + +/* + * R39 (0x27) - Speaker Volume Right + */ +#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM8993_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_MUTE_N 0x0040 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_MASK 0x0040 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_SHIFT 6 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_WIDTH 1 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_VOL_MASK 0x003F /* SPKOUTR_VOL - [5:0] */ +#define WM8993_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [5:0] */ +#define WM8993_SPKOUTR_VOL_WIDTH 6 /* SPKOUTR_VOL - [5:0] */ + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8993_IN2LP_TO_IN2L 0x0080 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_MASK 0x0080 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_SHIFT 7 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_WIDTH 1 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L 0x0040 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_MASK 0x0040 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_SHIFT 6 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_WIDTH 1 /* IN2LN_TO_IN2L */ +#define WM8993_IN1LP_TO_IN1L 0x0020 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_MASK 0x0020 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_SHIFT 5 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_WIDTH 1 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L 0x0010 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_MASK 0x0010 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_SHIFT 4 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_WIDTH 1 /* IN1LN_TO_IN1L */ +#define WM8993_IN2RP_TO_IN2R 0x0008 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_MASK 0x0008 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_SHIFT 3 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_WIDTH 1 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R 0x0004 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_MASK 0x0004 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_SHIFT 2 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_WIDTH 1 /* IN2RN_TO_IN2R */ +#define WM8993_IN1RP_TO_IN1R 0x0002 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_MASK 0x0002 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_SHIFT 1 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_WIDTH 1 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R 0x0001 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_MASK 0x0001 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_SHIFT 0 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_WIDTH 1 /* IN1RN_TO_IN1R */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8993_IN2L_TO_MIXINL 0x0100 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_MASK 0x0100 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_SHIFT 8 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_MIXINL_VOL 0x0080 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_MASK 0x0080 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_SHIFT 7 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_WIDTH 1 /* IN2L_MIXINL_VOL */ +#define WM8993_IN1L_TO_MIXINL 0x0020 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_MASK 0x0020 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_SHIFT 5 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_WIDTH 1 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_MIXINL_VOL 0x0010 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_MASK 0x0010 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_SHIFT 4 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_WIDTH 1 /* IN1L_MIXINL_VOL */ +#define WM8993_MIXOUTL_MIXINL_VOL_MASK 0x0007 /* MIXOUTL_MIXINL_VOL - [2:0] */ +#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT 0 /* MIXOUTL_MIXINL_VOL - [2:0] */ +#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH 3 /* MIXOUTL_MIXINL_VOL - [2:0] */ + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8993_IN2R_TO_MIXINR 0x0100 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_MASK 0x0100 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_SHIFT 8 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_MIXINR_VOL 0x0080 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_MASK 0x0080 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_SHIFT 7 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_WIDTH 1 /* IN2R_MIXINR_VOL */ +#define WM8993_IN1R_TO_MIXINR 0x0020 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_MASK 0x0020 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_SHIFT 5 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_WIDTH 1 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_MIXINR_VOL 0x0010 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_MASK 0x0010 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_SHIFT 4 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_WIDTH 1 /* IN1R_MIXINR_VOL */ +#define WM8993_MIXOUTR_MIXINR_VOL_MASK 0x0007 /* MIXOUTR_MIXINR_VOL - [2:0] */ +#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT 0 /* MIXOUTR_MIXINR_VOL - [2:0] */ +#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH 3 /* MIXOUTR_MIXINR_VOL - [2:0] */ + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8993_IN1LP_MIXINL_VOL_MASK 0x01C0 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_IN1LP_MIXINL_VOL_SHIFT 6 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_IN1LP_MIXINL_VOL_WIDTH 3 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_VRX_MIXINL_VOL_MASK 0x0007 /* VRX_MIXINL_VOL - [2:0] */ +#define WM8993_VRX_MIXINL_VOL_SHIFT 0 /* VRX_MIXINL_VOL - [2:0] */ +#define WM8993_VRX_MIXINL_VOL_WIDTH 3 /* VRX_MIXINL_VOL - [2:0] */ + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8993_IN1RP_MIXINR_VOL_MASK 0x01C0 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_IN1RP_MIXINR_VOL_SHIFT 6 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_IN1RP_MIXINR_VOL_WIDTH 3 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_VRX_MIXINR_VOL_MASK 0x0007 /* VRX_MIXINR_VOL - [2:0] */ +#define WM8993_VRX_MIXINR_VOL_SHIFT 0 /* VRX_MIXINR_VOL - [2:0] */ +#define WM8993_VRX_MIXINR_VOL_WIDTH 3 /* VRX_MIXINR_VOL - [2:0] */ + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8993_DACL_TO_HPOUT1L 0x0100 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_MASK 0x0100 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_SHIFT 8 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_WIDTH 1 /* DACL_TO_HPOUT1L */ +#define WM8993_MIXINR_TO_MIXOUTL 0x0080 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_MASK 0x0080 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_SHIFT 7 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_WIDTH 1 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL 0x0040 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_MASK 0x0040 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_SHIFT 6 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_WIDTH 1 /* MIXINL_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL 0x0020 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_MASK 0x0020 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_SHIFT 5 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_WIDTH 1 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL 0x0010 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_MASK 0x0010 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_SHIFT 4 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_WIDTH 1 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL 0x0008 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_MASK 0x0008 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_SHIFT 3 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_WIDTH 1 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL 0x0004 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_MASK 0x0004 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_SHIFT 2 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_WIDTH 1 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL 0x0002 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_MASK 0x0002 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_SHIFT 1 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_WIDTH 1 /* IN2LP_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL 0x0001 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_MASK 0x0001 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_SHIFT 0 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */ + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8993_DACR_TO_HPOUT1R 0x0100 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_MASK 0x0100 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_SHIFT 8 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_WIDTH 1 /* DACR_TO_HPOUT1R */ +#define WM8993_MIXINL_TO_MIXOUTR 0x0080 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_MASK 0x0080 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_SHIFT 7 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_WIDTH 1 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR 0x0040 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_MASK 0x0040 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_SHIFT 6 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_WIDTH 1 /* MIXINR_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR 0x0020 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_MASK 0x0020 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_SHIFT 5 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_WIDTH 1 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR 0x0010 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_MASK 0x0010 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_SHIFT 4 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_WIDTH 1 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR 0x0008 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_MASK 0x0008 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_SHIFT 3 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_WIDTH 1 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR 0x0004 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_MASK 0x0004 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_SHIFT 2 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_WIDTH 1 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR 0x0002 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_MASK 0x0002 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_SHIFT 1 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_WIDTH 1 /* IN2RP_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR 0x0001 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_MASK 0x0001 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_SHIFT 0 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */ + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8993_IN2LP_MIXOUTL_VOL_MASK 0x0E00 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT 9 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH 3 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LN_MIXOUTL_VOL_MASK 0x01C0 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT 6 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH 3 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN1R_MIXOUTL_VOL_MASK 0x0038 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTL_VOL_SHIFT 3 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTL_VOL_WIDTH 3 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTL_VOL_MASK 0x0007 /* IN1L_MIXOUTL_VOL - [2:0] */ +#define WM8993_IN1L_MIXOUTL_VOL_SHIFT 0 /* IN1L_MIXOUTL_VOL - [2:0] */ +#define WM8993_IN1L_MIXOUTL_VOL_WIDTH 3 /* IN1L_MIXOUTL_VOL - [2:0] */ + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8993_IN2RP_MIXOUTR_VOL_MASK 0x0E00 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT 9 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH 3 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RN_MIXOUTR_VOL_MASK 0x01C0 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT 6 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH 3 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN1L_MIXOUTR_VOL_MASK 0x0038 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTR_VOL_SHIFT 3 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTR_VOL_WIDTH 3 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTR_VOL_MASK 0x0007 /* IN1R_MIXOUTR_VOL - [2:0] */ +#define WM8993_IN1R_MIXOUTR_VOL_SHIFT 0 /* IN1R_MIXOUTR_VOL - [2:0] */ +#define WM8993_IN1R_MIXOUTR_VOL_WIDTH 3 /* IN1R_MIXOUTR_VOL - [2:0] */ + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8993_DACL_MIXOUTL_VOL_MASK 0x0E00 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_DACL_MIXOUTL_VOL_SHIFT 9 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_DACL_MIXOUTL_VOL_WIDTH 3 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2RN_MIXOUTL_VOL_MASK 0x01C0 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT 6 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH 3 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_MIXINR_MIXOUTL_VOL_MASK 0x0038 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT 3 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH 3 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTL_VOL_MASK 0x0007 /* MIXINL_MIXOUTL_VOL - [2:0] */ +#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT 0 /* MIXINL_MIXOUTL_VOL - [2:0] */ +#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH 3 /* MIXINL_MIXOUTL_VOL - [2:0] */ + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8993_DACR_MIXOUTR_VOL_MASK 0x0E00 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_DACR_MIXOUTR_VOL_SHIFT 9 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_DACR_MIXOUTR_VOL_WIDTH 3 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2LN_MIXOUTR_VOL_MASK 0x01C0 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT 6 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH 3 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_MIXINL_MIXOUTR_VOL_MASK 0x0038 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT 3 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH 3 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTR_VOL_MASK 0x0007 /* MIXINR_MIXOUTR_VOL - [2:0] */ +#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT 0 /* MIXINR_MIXOUTR_VOL - [2:0] */ +#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH 3 /* MIXINR_MIXOUTR_VOL - [2:0] */ + +/* + * R51 (0x33) - HPOUT2 Mixer + */ +#define WM8993_VRX_TO_HPOUT2 0x0020 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_MASK 0x0020 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_SHIFT 5 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_WIDTH 1 /* VRX_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT 4 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT 3 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTRVOL_TO_HPOUT2 */ + +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8993_MIXOUTL_TO_LINEOUT1N 0x0040 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK 0x0040 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT 6 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH 1 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N 0x0020 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK 0x0020 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT 5 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH 1 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_LINEOUT1_MODE 0x0010 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_MASK 0x0010 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_SHIFT 4 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_WIDTH 1 /* LINEOUT1_MODE */ +#define WM8993_IN1R_TO_LINEOUT1P 0x0004 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_MASK 0x0004 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_SHIFT 2 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_WIDTH 1 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P 0x0002 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_MASK 0x0002 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_SHIFT 1 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_WIDTH 1 /* IN1L_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P 0x0001 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK 0x0001 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT 0 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH 1 /* MIXOUTL_TO_LINEOUT1P */ + +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8993_MIXOUTR_TO_LINEOUT2N 0x0040 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK 0x0040 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT 6 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH 1 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N 0x0020 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK 0x0020 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT 5 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH 1 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_LINEOUT2_MODE 0x0010 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_MASK 0x0010 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_SHIFT 4 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_WIDTH 1 /* LINEOUT2_MODE */ +#define WM8993_IN1L_TO_LINEOUT2P 0x0004 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_MASK 0x0004 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_SHIFT 2 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_WIDTH 1 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P 0x0002 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_MASK 0x0002 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_SHIFT 1 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_WIDTH 1 /* IN1R_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P 0x0001 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK 0x0001 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT 0 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH 1 /* MIXOUTR_TO_LINEOUT2P */ + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8993_SPKAB_REF_SEL 0x0100 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_MASK 0x0100 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_SHIFT 8 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_WIDTH 1 /* SPKAB_REF_SEL */ +#define WM8993_MIXINL_TO_SPKMIXL 0x0080 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_MASK 0x0080 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_SHIFT 7 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINR_TO_SPKMIXR 0x0040 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_MASK 0x0040 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_SHIFT 6 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */ +#define WM8993_IN1LP_TO_SPKMIXL 0x0020 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_MASK 0x0020 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_SHIFT 5 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_WIDTH 1 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1RP_TO_SPKMIXR 0x0010 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_MASK 0x0010 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_SHIFT 4 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_WIDTH 1 /* IN1RP_TO_SPKMIXR */ +#define WM8993_MIXOUTL_TO_SPKMIXL 0x0008 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_MASK 0x0008 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT 3 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH 1 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTR_TO_SPKMIXR 0x0004 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_MASK 0x0004 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT 2 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH 1 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_DACL_TO_SPKMIXL 0x0002 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_MASK 0x0002 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_SHIFT 1 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */ +#define WM8993_DACR_TO_SPKMIXR 0x0001 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_MASK 0x0001 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_SHIFT 0 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */ + +/* + * R55 (0x37) - Additional Control + */ +#define WM8993_LINEOUT1_FB 0x0080 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_MASK 0x0080 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_SHIFT 7 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_WIDTH 1 /* LINEOUT1_FB */ +#define WM8993_LINEOUT2_FB 0x0040 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_MASK 0x0040 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_SHIFT 6 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_WIDTH 1 /* LINEOUT2_FB */ +#define WM8993_VROI 0x0001 /* VROI */ +#define WM8993_VROI_MASK 0x0001 /* VROI */ +#define WM8993_VROI_SHIFT 0 /* VROI */ +#define WM8993_VROI_WIDTH 1 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8993_LINEOUT_VMID_BUF_ENA 0x0080 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_MASK 0x0080 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT 7 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH 1 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_HPOUT2_IN_ENA 0x0040 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_MASK 0x0040 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_SHIFT 6 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_WIDTH 1 /* HPOUT2_IN_ENA */ +#define WM8993_LINEOUT1_DISCH 0x0020 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_MASK 0x0020 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_SHIFT 5 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_WIDTH 1 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT2_DISCH 0x0010 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_MASK 0x0010 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_SHIFT 4 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_WIDTH 1 /* LINEOUT2_DISCH */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8993_VMID_RAMP_MASK 0x0060 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_RAMP_SHIFT 5 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_RAMP_WIDTH 2 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM8993_STARTUP_BIAS_ENA 0x0004 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_MASK 0x0004 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_SHIFT 2 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ +#define WM8993_BIAS_SRC 0x0002 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_MASK 0x0002 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_SHIFT 1 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_WIDTH 1 /* BIAS_SRC */ +#define WM8993_VMID_DISCH 0x0001 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_MASK 0x0001 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_SHIFT 0 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_WIDTH 1 /* VMID_DISCH */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8993_JD_SCTHR_MASK 0x00C0 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_SCTHR_SHIFT 6 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_SCTHR_WIDTH 2 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_THR_MASK 0x0030 /* JD_THR - [5:4] */ +#define WM8993_JD_THR_SHIFT 4 /* JD_THR - [5:4] */ +#define WM8993_JD_THR_WIDTH 2 /* JD_THR - [5:4] */ +#define WM8993_JD_ENA 0x0004 /* JD_ENA */ +#define WM8993_JD_ENA_MASK 0x0004 /* JD_ENA */ +#define WM8993_JD_ENA_SHIFT 2 /* JD_ENA */ +#define WM8993_JD_ENA_WIDTH 1 /* JD_ENA */ +#define WM8993_MICB2_LVL 0x0002 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_MASK 0x0002 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_SHIFT 1 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_WIDTH 1 /* MICB2_LVL */ +#define WM8993_MICB1_LVL 0x0001 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_MASK 0x0001 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_SHIFT 0 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_WIDTH 1 /* MICB1_LVL */ + +/* + * R60 (0x3C) - FLL Control 1 + */ +#define WM8993_FLL_FRAC 0x0004 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_SHIFT 2 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM8993_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8993_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM8993_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM8993_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM8993_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R61 (0x3D) - FLL Control 2 + */ +#define WM8993_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM8993_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM8993_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R62 (0x3E) - FLL Control 3 + */ +#define WM8993_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM8993_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM8993_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R63 (0x3F) - FLL Control 4 + */ +#define WM8993_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM8993_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM8993_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM8993_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */ +#define WM8993_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */ +#define WM8993_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */ + +/* + * R64 (0x40) - FLL Control 5 + */ +#define WM8993_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */ +#define WM8993_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */ +#define WM8993_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */ +#define WM8993_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */ + +/* + * R65 (0x41) - Clocking 3 + */ +#define WM8993_CLK_DCS_DIV_MASK 0x3C00 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_CLK_DCS_DIV_SHIFT 10 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_SAMPLE_RATE_MASK 0x0380 /* SAMPLE_RATE - [9:7] */ +#define WM8993_SAMPLE_RATE_SHIFT 7 /* SAMPLE_RATE - [9:7] */ +#define WM8993_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [9:7] */ +#define WM8993_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_DSP_ENA 0x0001 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_MASK 0x0001 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_SHIFT 0 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ + +/* + * R66 (0x42) - Clocking 4 + */ +#define WM8993_DAC_DIV4 0x0200 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_MASK 0x0200 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_SHIFT 9 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_WIDTH 1 /* DAC_DIV4 */ +#define WM8993_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */ +#define WM8993_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */ +#define WM8993_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */ +#define WM8993_SR_MODE 0x0001 /* SR_MODE */ +#define WM8993_SR_MODE_MASK 0x0001 /* SR_MODE */ +#define WM8993_SR_MODE_SHIFT 0 /* SR_MODE */ +#define WM8993_SR_MODE_WIDTH 1 /* SR_MODE */ + +/* + * R67 (0x43) - MW Slave Control + */ +#define WM8993_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */ + +/* + * R69 (0x45) - Bus Control 1 + */ +#define WM8993_CLK_SYS_ENA 0x0002 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_MASK 0x0002 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_SHIFT 1 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ + +/* + * R70 (0x46) - Write Sequencer 0 + */ +#define WM8993_WSEQ_ENA 0x0100 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8993_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8993_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8993_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R71 (0x47) - Write Sequencer 1 + */ +#define WM8993_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8993_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8993_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R72 (0x48) - Write Sequencer 2 + */ +#define WM8993_WSEQ_EOS 0x4000 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8993_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8993_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8993_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R73 (0x49) - Write Sequencer 3 + */ +#define WM8993_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8993_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8993_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8993_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8993_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8993_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8993_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8993_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R74 (0x4A) - Write Sequencer 4 + */ +#define WM8993_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R75 (0x4B) - Write Sequencer 5 + */ +#define WM8993_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM8993_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM8993_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */ + +/* + * R76 (0x4C) - Charge Pump 1 + */ +#define WM8993_CP_ENA 0x8000 /* CP_ENA */ +#define WM8993_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM8993_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM8993_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R81 (0x51) - Class W 0 + */ +#define WM8993_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_V 0x0001 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_SHIFT 0 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_WIDTH 1 /* CP_DYN_V */ + +/* + * R84 (0x54) - DC Servo 0 + */ +#define WM8993_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R85 (0x55) - DC Servo 1 + */ +#define WM8993_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8993_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8993_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R87 (0x57) - DC Servo 3 + */ +#define WM8993_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8993_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8993_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R88 (0x58) - DC Servo Readback 0 + */ +#define WM8993_DCS_DATAPATH_BUSY 0x4000 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_MASK 0x4000 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_SHIFT 14 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_WIDTH 1 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_CHANNEL_MASK 0x3000 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CHANNEL_SHIFT 12 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CHANNEL_WIDTH 2 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM8993_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM8993_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */ + +/* + * R89 (0x59) - DC Servo Readback 1 + */ +#define WM8993_DCS_INTEG_CHAN_1_MASK 0x00FF /* DCS_INTEG_CHAN_1 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_1_SHIFT 0 /* DCS_INTEG_CHAN_1 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_1_WIDTH 8 /* DCS_INTEG_CHAN_1 - [7:0] */ + +/* + * R90 (0x5A) - DC Servo Readback 2 + */ +#define WM8993_DCS_INTEG_CHAN_0_MASK 0x00FF /* DCS_INTEG_CHAN_0 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_0_SHIFT 0 /* DCS_INTEG_CHAN_0 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_0_WIDTH 8 /* DCS_INTEG_CHAN_0 - [7:0] */ + +/* + * R96 (0x60) - Analogue HP 0 + */ +#define WM8993_HPOUT1_AUTO_PU 0x0100 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_MASK 0x0100 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_SHIFT 8 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_WIDTH 1 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R98 (0x62) - EQ1 + */ +#define WM8993_EQ_ENA 0x0001 /* EQ_ENA */ +#define WM8993_EQ_ENA_MASK 0x0001 /* EQ_ENA */ +#define WM8993_EQ_ENA_SHIFT 0 /* EQ_ENA */ +#define WM8993_EQ_ENA_WIDTH 1 /* EQ_ENA */ + +/* + * R99 (0x63) - EQ2 + */ +#define WM8993_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */ +#define WM8993_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */ +#define WM8993_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */ + +/* + * R100 (0x64) - EQ3 + */ +#define WM8993_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */ +#define WM8993_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */ +#define WM8993_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */ + +/* + * R101 (0x65) - EQ4 + */ +#define WM8993_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */ +#define WM8993_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */ +#define WM8993_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */ + +/* + * R102 (0x66) - EQ5 + */ +#define WM8993_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */ +#define WM8993_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */ +#define WM8993_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */ + +/* + * R103 (0x67) - EQ6 + */ +#define WM8993_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */ +#define WM8993_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */ +#define WM8993_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */ + +/* + * R104 (0x68) - EQ7 + */ +#define WM8993_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */ +#define WM8993_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */ +#define WM8993_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */ + +/* + * R105 (0x69) - EQ8 + */ +#define WM8993_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */ +#define WM8993_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */ +#define WM8993_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */ + +/* + * R106 (0x6A) - EQ9 + */ +#define WM8993_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */ +#define WM8993_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */ +#define WM8993_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */ + +/* + * R107 (0x6B) - EQ10 + */ +#define WM8993_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */ +#define WM8993_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */ +#define WM8993_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */ + +/* + * R108 (0x6C) - EQ11 + */ +#define WM8993_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */ +#define WM8993_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */ +#define WM8993_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */ + +/* + * R109 (0x6D) - EQ12 + */ +#define WM8993_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */ +#define WM8993_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */ +#define WM8993_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */ + +/* + * R110 (0x6E) - EQ13 + */ +#define WM8993_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */ +#define WM8993_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */ +#define WM8993_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */ + +/* + * R111 (0x6F) - EQ14 + */ +#define WM8993_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */ +#define WM8993_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */ +#define WM8993_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */ + +/* + * R112 (0x70) - EQ15 + */ +#define WM8993_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */ +#define WM8993_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */ +#define WM8993_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */ + +/* + * R113 (0x71) - EQ16 + */ +#define WM8993_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */ +#define WM8993_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */ +#define WM8993_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */ + +/* + * R114 (0x72) - EQ17 + */ +#define WM8993_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */ +#define WM8993_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */ +#define WM8993_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */ + +/* + * R115 (0x73) - EQ18 + */ +#define WM8993_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */ +#define WM8993_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */ +#define WM8993_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */ + +/* + * R116 (0x74) - EQ19 + */ +#define WM8993_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */ +#define WM8993_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */ +#define WM8993_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */ + +/* + * R117 (0x75) - EQ20 + */ +#define WM8993_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */ +#define WM8993_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */ +#define WM8993_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */ + +/* + * R118 (0x76) - EQ21 + */ +#define WM8993_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */ +#define WM8993_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */ +#define WM8993_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */ + +/* + * R119 (0x77) - EQ22 + */ +#define WM8993_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */ +#define WM8993_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */ +#define WM8993_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */ + +/* + * R120 (0x78) - EQ23 + */ +#define WM8993_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */ +#define WM8993_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */ +#define WM8993_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */ + +/* + * R121 (0x79) - EQ24 + */ +#define WM8993_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */ +#define WM8993_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */ +#define WM8993_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */ + +/* + * R122 (0x7A) - Digital Pulls + */ +#define WM8993_MCLK_PU 0x0080 /* MCLK_PU */ +#define WM8993_MCLK_PU_MASK 0x0080 /* MCLK_PU */ +#define WM8993_MCLK_PU_SHIFT 7 /* MCLK_PU */ +#define WM8993_MCLK_PU_WIDTH 1 /* MCLK_PU */ +#define WM8993_MCLK_PD 0x0040 /* MCLK_PD */ +#define WM8993_MCLK_PD_MASK 0x0040 /* MCLK_PD */ +#define WM8993_MCLK_PD_SHIFT 6 /* MCLK_PD */ +#define WM8993_MCLK_PD_WIDTH 1 /* MCLK_PD */ +#define WM8993_DACDAT_PU 0x0020 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_SHIFT 5 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_WIDTH 1 /* DACDAT_PU */ +#define WM8993_DACDAT_PD 0x0010 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_SHIFT 4 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_WIDTH 1 /* DACDAT_PD */ +#define WM8993_LRCLK_PU 0x0008 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_SHIFT 3 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_WIDTH 1 /* LRCLK_PU */ +#define WM8993_LRCLK_PD 0x0004 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_SHIFT 2 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_WIDTH 1 /* LRCLK_PD */ +#define WM8993_BCLK_PU 0x0002 /* BCLK_PU */ +#define WM8993_BCLK_PU_MASK 0x0002 /* BCLK_PU */ +#define WM8993_BCLK_PU_SHIFT 1 /* BCLK_PU */ +#define WM8993_BCLK_PU_WIDTH 1 /* BCLK_PU */ +#define WM8993_BCLK_PD 0x0001 /* BCLK_PD */ +#define WM8993_BCLK_PD_MASK 0x0001 /* BCLK_PD */ +#define WM8993_BCLK_PD_SHIFT 0 /* BCLK_PD */ +#define WM8993_BCLK_PD_WIDTH 1 /* BCLK_PD */ + +/* + * R123 (0x7B) - DRC Control 1 + */ +#define WM8993_DRC_ENA 0x8000 /* DRC_ENA */ +#define WM8993_DRC_ENA_MASK 0x8000 /* DRC_ENA */ +#define WM8993_DRC_ENA_SHIFT 15 /* DRC_ENA */ +#define WM8993_DRC_ENA_WIDTH 1 /* DRC_ENA */ +#define WM8993_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */ +#define WM8993_DRC_SMOOTH_ENA 0x0800 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_MASK 0x0800 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_SHIFT 11 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_QR_ENA 0x0400 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_MASK 0x0400 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_SHIFT 10 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */ +#define WM8993_DRC_ANTICLIP_ENA 0x0200 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_MASK 0x0200 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_SHIFT 9 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_HYST_ENA 0x0100 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_MASK 0x0100 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_SHIFT 8 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */ +#define WM8993_DRC_THRESH_HYST_MASK 0x0030 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_THRESH_HYST_SHIFT 4 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM8993_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM8993_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R124 (0x7C) - DRC Control 2 + */ +#define WM8993_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_THRESH_COMP_MASK 0x00FC /* DRC_THRESH_COMP - [7:2] */ +#define WM8993_DRC_THRESH_COMP_SHIFT 2 /* DRC_THRESH_COMP - [7:2] */ +#define WM8993_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [7:2] */ + +/* + * R125 (0x7D) - DRC Control 3 + */ +#define WM8993_DRC_AMP_COMP_MASK 0xF800 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_AMP_COMP_SHIFT 11 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_R0_SLOPE_COMP_MASK 0x0700 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_R0_SLOPE_COMP_SHIFT 8 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_FF_DELAY 0x0080 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_MASK 0x0080 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_SHIFT 7 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */ +#define WM8993_DRC_THRESH_QR_MASK 0x000C /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_THRESH_QR_SHIFT 2 /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_RATE_QR_MASK 0x0003 /* DRC_RATE_QR - [1:0] */ +#define WM8993_DRC_RATE_QR_SHIFT 0 /* DRC_RATE_QR - [1:0] */ +#define WM8993_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [1:0] */ + +/* + * R126 (0x7E) - DRC Control 4 + */ +#define WM8993_DRC_R1_SLOPE_COMP_MASK 0xE000 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_R1_SLOPE_COMP_SHIFT 13 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_STARTUP_GAIN_MASK 0x1F00 /* DRC_STARTUP_GAIN - [12:8] */ +#define WM8993_DRC_STARTUP_GAIN_SHIFT 8 /* DRC_STARTUP_GAIN - [12:8] */ +#define WM8993_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [12:8] */ + +#endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 86fc57e25f9..156f2a4a593 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -707,6 +707,10 @@ static int configure_clock(struct snd_soc_codec *codec) target > 3000000) break; } + + if (i == ARRAY_SIZE(clk_sys_rates)) + return -EINVAL; + } else if (wm9081->fs) { for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { new_sysclk = clk_sys_rates[i].ratio @@ -714,6 +718,10 @@ static int configure_clock(struct snd_soc_codec *codec) if (new_sysclk > 3000000) break; } + + if (i == ARRAY_SIZE(clk_sys_rates)) + return -EINVAL; + } else { new_sysclk = 12288000; } @@ -1492,6 +1500,21 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + return snd_soc_suspend_device(&client->dev); +} + +static int wm9081_i2c_resume(struct i2c_client *client) +{ + return snd_soc_resume_device(&client->dev); +} +#else +#define wm9081_i2c_suspend NULL +#define wm9081_i2c_resume NULL +#endif + static const struct i2c_device_id wm9081_i2c_id[] = { { "wm9081", 0 }, { } @@ -1505,6 +1528,8 @@ static struct i2c_driver wm9081_i2c_driver = { }, .probe = wm9081_i2c_probe, .remove = __devexit_p(wm9081_i2c_remove), + .suspend = wm9081_i2c_suspend, + .resume = wm9081_i2c_resume, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 411a710be66..6802dd5e473 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -9,6 +9,9 @@ config SND_DAVINCI_SOC config SND_DAVINCI_SOC_I2S tristate +config SND_DAVINCI_SOC_MCASP + tristate + config SND_DAVINCI_SOC_EVM tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM" depends on SND_DAVINCI_SOC @@ -19,6 +22,16 @@ config SND_DAVINCI_SOC_EVM Say Y if you want to add support for SoC audio on TI DaVinci DM6446 or DM355 EVM platforms. +config SND_DM6467_SOC_EVM + tristate "SoC Audio support for DaVinci DM6467 EVM" + depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM + select SND_DAVINCI_SOC_MCASP + select SND_SOC_TLV320AIC3X + select SND_SOC_SPDIF + + help + Say Y if you want to add support for SoC audio on TI + config SND_DAVINCI_SOC_SFFSDR tristate "SoC Audio support for SFFSDR" depends on SND_DAVINCI_SOC && MACH_SFFSDR diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index ca8bae1fc3f..67be54f3a3a 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,13 +1,16 @@ # DAVINCI Platform Support snd-soc-davinci-objs := davinci-pcm.o snd-soc-davinci-i2s-objs := davinci-i2s.o +snd-soc-davinci-mcasp-objs:= davinci-mcasp.o obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o +obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o # DAVINCI Machine Support snd-soc-evm-objs := davinci-evm.o snd-soc-sffsdr-objs := davinci-sffsdr.o obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o +obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 58fd1cbedd8..f3bb6f60f20 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -27,9 +27,10 @@ #include <mach/mux.h> #include "../codecs/tlv320aic3x.h" +#include "../codecs/spdif_transciever.h" #include "davinci-pcm.h" #include "davinci-i2s.h" - +#include "davinci-mcasp.h" #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) @@ -43,7 +44,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream, unsigned sysclk; /* ASP1 on DM355 EVM is clocked by an external oscillator */ - if (machine_is_davinci_dm355_evm()) + if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm()) sysclk = 27000000; /* ASP0 in DM6446 EVM is clocked by U55, as configured by @@ -144,6 +145,24 @@ static struct snd_soc_dai_link evm_dai = { .ops = &evm_ops, }; +static struct snd_soc_dai_link dm6467_evm_dai[] = { + { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI], + .codec_dai = &aic3x_dai, + .init = evm_aic3x_init, + .ops = &evm_ops, + }, + { + .name = "McASP", + .stream_name = "spdif", + .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI], + .codec_dai = &dit_stub_dai, + .ops = &evm_ops, + }, +}; + /* davinci-evm audio machine driver */ static struct snd_soc_card snd_soc_card_evm = { .name = "DaVinci EVM", @@ -152,12 +171,26 @@ static struct snd_soc_card snd_soc_card_evm = { .num_links = 1, }; +/* davinci dm6467 evm audio machine driver */ +static struct snd_soc_card dm6467_snd_soc_card_evm = { + .name = "DaVinci DM6467 EVM", + .platform = &davinci_soc_platform, + .dai_link = dm6467_evm_dai, + .num_links = ARRAY_SIZE(dm6467_evm_dai), +}; + /* evm audio private data */ static struct aic3x_setup_data evm_aic3x_setup = { .i2c_bus = 1, .i2c_address = 0x1b, }; +/* dm6467 evm audio private data */ +static struct aic3x_setup_data dm6467_evm_aic3x_setup = { + .i2c_bus = 1, + .i2c_address = 0x18, +}; + /* evm audio subsystem */ static struct snd_soc_device evm_snd_devdata = { .card = &snd_soc_card_evm, @@ -165,60 +198,30 @@ static struct snd_soc_device evm_snd_devdata = { .codec_data = &evm_aic3x_setup, }; -/* DM6446 EVM uses ASP0; line-out is a pair of RCA jacks */ -static struct resource evm_snd_resources[] = { - { - .start = DAVINCI_ASP0_BASE, - .end = DAVINCI_ASP0_BASE + SZ_8K - 1, - .flags = IORESOURCE_MEM, - }, -}; - -static struct evm_snd_platform_data evm_snd_data = { - .tx_dma_ch = DAVINCI_DMA_ASP0_TX, - .rx_dma_ch = DAVINCI_DMA_ASP0_RX, -}; - -/* DM335 EVM uses ASP1; line-out is a stereo mini-jack */ -static struct resource dm335evm_snd_resources[] = { - { - .start = DAVINCI_ASP1_BASE, - .end = DAVINCI_ASP1_BASE + SZ_8K - 1, - .flags = IORESOURCE_MEM, - }, -}; - -static struct evm_snd_platform_data dm335evm_snd_data = { - .tx_dma_ch = DAVINCI_DMA_ASP1_TX, - .rx_dma_ch = DAVINCI_DMA_ASP1_RX, +/* evm audio subsystem */ +static struct snd_soc_device dm6467_evm_snd_devdata = { + .card = &dm6467_snd_soc_card_evm, + .codec_dev = &soc_codec_dev_aic3x, + .codec_data = &dm6467_evm_aic3x_setup, }; static struct platform_device *evm_snd_device; static int __init evm_init(void) { - struct resource *resources; - unsigned num_resources; - struct evm_snd_platform_data *data; + struct snd_soc_device *evm_snd_dev_data; int index; int ret; if (machine_is_davinci_evm()) { - davinci_cfg_reg(DM644X_MCBSP); - - resources = evm_snd_resources; - num_resources = ARRAY_SIZE(evm_snd_resources); - data = &evm_snd_data; + evm_snd_dev_data = &evm_snd_devdata; index = 0; } else if (machine_is_davinci_dm355_evm()) { - /* we don't use ASP1 IRQs, or we'd need to mux them ... */ - davinci_cfg_reg(DM355_EVT8_ASP1_TX); - davinci_cfg_reg(DM355_EVT9_ASP1_RX); - - resources = dm335evm_snd_resources; - num_resources = ARRAY_SIZE(dm335evm_snd_resources); - data = &dm335evm_snd_data; + evm_snd_dev_data = &evm_snd_devdata; index = 1; + } else if (machine_is_davinci_dm6467_evm()) { + evm_snd_dev_data = &dm6467_evm_snd_devdata; + index = 0; } else return -EINVAL; @@ -226,17 +229,8 @@ static int __init evm_init(void) if (!evm_snd_device) return -ENOMEM; - platform_set_drvdata(evm_snd_device, &evm_snd_devdata); - evm_snd_devdata.dev = &evm_snd_device->dev; - platform_device_add_data(evm_snd_device, data, sizeof(*data)); - - ret = platform_device_add_resources(evm_snd_device, resources, - num_resources); - if (ret) { - platform_device_put(evm_snd_device); - return ret; - } - + platform_set_drvdata(evm_snd_device, evm_snd_dev_data); + evm_snd_dev_data->dev = &evm_snd_device->dev; ret = platform_device_add(evm_snd_device); if (ret) platform_device_put(evm_snd_device); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index b1ea52fc83c..e5cd97b74c5 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -22,6 +22,8 @@ #include <sound/initval.h> #include <sound/soc.h> +#include <mach/asp.h> + #include "davinci-pcm.h" @@ -63,6 +65,7 @@ #define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5) #define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8) #define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_RCR_RFIG (1 << 18) #define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) #define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) @@ -85,14 +88,6 @@ #define DAVINCI_MCBSP_PCR_FSRM (1 << 10) #define DAVINCI_MCBSP_PCR_FSXM (1 << 11) -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) { \ - val |= mask; \ - } else { \ - val &= ~mask; \ - } \ -} while (0) - enum { DAVINCI_MCBSP_WORD_8 = 0, DAVINCI_MCBSP_WORD_12, @@ -112,6 +107,10 @@ static struct davinci_pcm_dma_params davinci_i2s_pcm_in = { struct davinci_mcbsp_dev { void __iomem *base; +#define MOD_DSP_A 0 +#define MOD_DSP_B 1 + int mode; + u32 pcr; struct clk *clk; struct davinci_pcm_dma_params *dma_params[2]; }; @@ -127,96 +126,100 @@ static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg) return __raw_readl(dev->base + reg); } -static void davinci_mcbsp_start(struct snd_pcm_substream *substream) +static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback) +{ + u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP; + /* The clock needs to toggle to complete reset. + * So, fake it by toggling the clk polarity. + */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr); +} + +static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_platform *platform = socdev->card->platform; - u32 w; - int ret; - - /* Start the sample generator and enable transmitter/receiver */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + u32 spcr; + u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST; + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + if (spcr & mask) { + /* start off disabled */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, + spcr & ~mask); + toggle_clock(dev, playback); + } + if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM | + DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) { + /* Start the sample generator */ + spcr |= DAVINCI_MCBSP_SPCR_GRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (playback) { /* Stop the DMA to avoid data loss */ /* while the transmitter is out of reset to handle XSYNCERR */ if (platform->pcm_ops->trigger) { - ret = platform->pcm_ops->trigger(substream, + int ret = platform->pcm_ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); if (ret < 0) printk(KERN_DEBUG "Playback DMA stop failed\n"); } /* Enable the transmitter */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr |= DAVINCI_MCBSP_SPCR_XRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); /* wait for any unexpected frame sync error to occur */ udelay(100); /* Disable the transmitter to clear any outstanding XSYNCERR */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr &= ~DAVINCI_MCBSP_SPCR_XRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + toggle_clock(dev, playback); /* Restart the DMA */ if (platform->pcm_ops->trigger) { - ret = platform->pcm_ops->trigger(substream, + int ret = platform->pcm_ops->trigger(substream, SNDRV_PCM_TRIGGER_START); if (ret < 0) printk(KERN_DEBUG "Playback DMA start failed\n"); } - /* Enable the transmitter */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); - - } else { - - /* Enable the reciever */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); } + /* Enable transmitter or receiver */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr |= mask; - /* Start frame sync */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) { + /* Start frame sync */ + spcr |= DAVINCI_MCBSP_SPCR_FRST; + } + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); } -static void davinci_mcbsp_stop(struct snd_pcm_substream *substream) +static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; - u32 w; + u32 spcr; /* Reset transmitter/receiver and sample rate/frame sync generators */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST | - DAVINCI_MCBSP_SPCR_FRST, 0); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0); - else - MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST); + spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + toggle_clock(dev, playback); } static int davinci_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; - + struct davinci_mcbsp_dev *dev = cpu_dai->private_data; cpu_dai->dma_data = dev->dma_params[substream->stream]; - return 0; } @@ -228,12 +231,11 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, struct davinci_mcbsp_dev *dev = cpu_dai->private_data; unsigned int pcr; unsigned int srgr; - unsigned int rcr; - unsigned int xcr; srgr = DAVINCI_MCBSP_SRGR_FSGM | DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); + /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* cpu is master */ @@ -258,11 +260,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1); - xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1); + /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_B: - break; case SND_SOC_DAIFMT_I2S: /* Davinci doesn't support TRUE I2S, but some codecs will have * the left and right channels contiguous. This allows @@ -282,8 +281,10 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, */ fmt ^= SND_SOC_DAIFMT_NB_IF; case SND_SOC_DAIFMT_DSP_A: - rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1); - xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); + dev->mode = MOD_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + dev->mode = MOD_DSP_B; break; default: printk(KERN_ERR "%s:bad format\n", __func__); @@ -343,9 +344,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); + dev->pcr = pcr; davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); return 0; } @@ -358,26 +358,36 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; struct snd_interval *i = NULL; int mcbsp_word_length; - u32 w; + unsigned int rcr, xcr, srgr; + u32 spcr; /* general line settings */ - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); } else { - w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); } i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - w = DAVINCI_MCBSP_SRGR_FSGM; - MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1); + srgr = DAVINCI_MCBSP_SRGR_FSGM; + srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); - MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w); + srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); + rcr = DAVINCI_MCBSP_RCR_RFIG; + xcr = DAVINCI_MCBSP_XCR_XFIG; + if (dev->mode == MOD_DSP_B) { + rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0); + xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0); + } else { + rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1); + xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); + } /* Determine xfer data type */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: @@ -397,18 +407,31 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | - DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w); + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); - } else { - w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG); - MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | - DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w); + rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); + xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); + else + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); + return 0; +} + +static int davinci_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + davinci_mcbsp_stop(dev, playback); + if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) { + /* codec is master */ + davinci_mcbsp_start(dev, substream); } return 0; } @@ -416,35 +439,74 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; int ret = 0; + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) + return 0; /* return if codec is master */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - davinci_mcbsp_start(substream); + davinci_mcbsp_start(dev, substream); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - davinci_mcbsp_stop(substream); + davinci_mcbsp_stop(dev, playback); break; default: ret = -EINVAL; } - return ret; } -static int davinci_i2s_probe(struct platform_device *pdev, - struct snd_soc_dai *dai) +static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + davinci_mcbsp_stop(dev, playback); +} + +#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 + +static struct snd_soc_dai_ops davinci_i2s_dai_ops = { + .startup = davinci_i2s_startup, + .shutdown = davinci_i2s_shutdown, + .prepare = davinci_i2s_prepare, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params, + .set_fmt = davinci_i2s_set_dai_fmt, + +}; + +struct snd_soc_dai davinci_i2s_dai = { + .name = "davinci-i2s", + .id = 0, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &davinci_i2s_dai_ops, + +}; +EXPORT_SYMBOL_GPL(davinci_i2s_dai); + +static int davinci_i2s_probe(struct platform_device *pdev) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_card *card = socdev->card; - struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai; + struct snd_platform_data *pdata = pdev->dev.platform_data; struct davinci_mcbsp_dev *dev; - struct resource *mem, *ioarea; - struct evm_snd_platform_data *pdata; + struct resource *mem, *ioarea, *res; int ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -466,8 +528,6 @@ static int davinci_i2s_probe(struct platform_device *pdev, goto err_release_region; } - cpu_dai->private_data = dev; - dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; @@ -476,18 +536,37 @@ static int davinci_i2s_probe(struct platform_device *pdev, clk_enable(dev->clk); dev->base = (void __iomem *)IO_ADDRESS(mem->start); - pdata = pdev->dev.platform_data; dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch; dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr = (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch; dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr = (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + /* first TX, then RX */ + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + ret = -ENXIO; + goto err_free_mem; + } + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + ret = -ENXIO; + goto err_free_mem; + } + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = res->start; + + davinci_i2s_dai.private_data = dev; + ret = snd_soc_register_dai(&davinci_i2s_dai); + if (ret != 0) + goto err_free_mem; + return 0; err_free_mem: @@ -498,62 +577,40 @@ err_release_region: return ret; } -static void davinci_i2s_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int davinci_i2s_remove(struct platform_device *pdev) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_card *card = socdev->card; - struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai; - struct davinci_mcbsp_dev *dev = cpu_dai->private_data; + struct davinci_mcbsp_dev *dev = davinci_i2s_dai.private_data; struct resource *mem; + snd_soc_unregister_dai(&davinci_i2s_dai); clk_disable(dev->clk); clk_put(dev->clk); dev->clk = NULL; - kfree(dev); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, (mem->end - mem->start) + 1); -} - -#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 -static struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .startup = davinci_i2s_startup, - .trigger = davinci_i2s_trigger, - .hw_params = davinci_i2s_hw_params, - .set_fmt = davinci_i2s_set_dai_fmt, -}; + return 0; +} -struct snd_soc_dai davinci_i2s_dai = { - .name = "davinci-i2s", - .id = 0, - .probe = davinci_i2s_probe, - .remove = davinci_i2s_remove, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = DAVINCI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = DAVINCI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = &davinci_i2s_dai_ops, +static struct platform_driver davinci_mcbsp_driver = { + .probe = davinci_i2s_probe, + .remove = davinci_i2s_remove, + .driver = { + .name = "davinci-asp", + .owner = THIS_MODULE, + }, }; -EXPORT_SYMBOL_GPL(davinci_i2s_dai); static int __init davinci_i2s_init(void) { - return snd_soc_register_dai(&davinci_i2s_dai); + return platform_driver_register(&davinci_mcbsp_driver); } module_init(davinci_i2s_init); static void __exit davinci_i2s_exit(void) { - snd_soc_unregister_dai(&davinci_i2s_dai); + platform_driver_unregister(&davinci_mcbsp_driver); } module_exit(davinci_i2s_exit); diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c new file mode 100644 index 00000000000..f0c03477106 --- /dev/null +++ b/sound/soc/davinci/davinci-mcasp.c @@ -0,0 +1,874 @@ +/* + * ALSA SoC McASP Audio Layer for TI DAVINCI processor + * + * Multi-channel Audio Serial Port Driver + * + * Author: Nirmal Pandey <n-pandey@ti.com>, + * Suresh Rajashekara <suresh.r@ti.com> + * Steve Chen <schen@.mvista.com> + * + * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com> + * Copyright: (C) 2009 Texas Instruments, India + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "davinci-pcm.h" +#include "davinci-mcasp.h" + +/* + * McASP register definitions + */ +#define DAVINCI_MCASP_PID_REG 0x00 +#define DAVINCI_MCASP_PWREMUMGT_REG 0x04 + +#define DAVINCI_MCASP_PFUNC_REG 0x10 +#define DAVINCI_MCASP_PDIR_REG 0x14 +#define DAVINCI_MCASP_PDOUT_REG 0x18 +#define DAVINCI_MCASP_PDSET_REG 0x1c + +#define DAVINCI_MCASP_PDCLR_REG 0x20 + +#define DAVINCI_MCASP_TLGC_REG 0x30 +#define DAVINCI_MCASP_TLMR_REG 0x34 + +#define DAVINCI_MCASP_GBLCTL_REG 0x44 +#define DAVINCI_MCASP_AMUTE_REG 0x48 +#define DAVINCI_MCASP_LBCTL_REG 0x4c + +#define DAVINCI_MCASP_TXDITCTL_REG 0x50 + +#define DAVINCI_MCASP_GBLCTLR_REG 0x60 +#define DAVINCI_MCASP_RXMASK_REG 0x64 +#define DAVINCI_MCASP_RXFMT_REG 0x68 +#define DAVINCI_MCASP_RXFMCTL_REG 0x6c + +#define DAVINCI_MCASP_ACLKRCTL_REG 0x70 +#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74 +#define DAVINCI_MCASP_RXTDM_REG 0x78 +#define DAVINCI_MCASP_EVTCTLR_REG 0x7c + +#define DAVINCI_MCASP_RXSTAT_REG 0x80 +#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84 +#define DAVINCI_MCASP_RXCLKCHK_REG 0x88 +#define DAVINCI_MCASP_REVTCTL_REG 0x8c + +#define DAVINCI_MCASP_GBLCTLX_REG 0xa0 +#define DAVINCI_MCASP_TXMASK_REG 0xa4 +#define DAVINCI_MCASP_TXFMT_REG 0xa8 +#define DAVINCI_MCASP_TXFMCTL_REG 0xac + +#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0 +#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4 +#define DAVINCI_MCASP_TXTDM_REG 0xb8 +#define DAVINCI_MCASP_EVTCTLX_REG 0xbc + +#define DAVINCI_MCASP_TXSTAT_REG 0xc0 +#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4 +#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8 +#define DAVINCI_MCASP_XEVTCTL_REG 0xcc + +/* Left(even TDM Slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRA_REG 0x100 +/* Right(odd TDM slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRB_REG 0x118 +/* Left(even TDM slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRA_REG 0x130 +/* Right(odd TDM Slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRB_REG 0x148 + +/* Serializer n Control Register */ +#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180 +#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \ + (n << 2)) + +/* Transmit Buffer for Serializer n */ +#define DAVINCI_MCASP_TXBUF_REG 0x200 +/* Receive Buffer for Serializer n */ +#define DAVINCI_MCASP_RXBUF_REG 0x280 + + +/* + * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management + * Register Bits + */ +#define MCASP_FREE BIT(0) +#define MCASP_SOFT BIT(1) + +/* + * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits + */ +#define AXR(n) (1<<n) +#define PFUNC_AMUTE BIT(25) +#define ACLKX BIT(26) +#define AHCLKX BIT(27) +#define AFSX BIT(28) +#define ACLKR BIT(29) +#define AHCLKR BIT(30) +#define AFSR BIT(31) + +/* + * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits + */ +#define AXR(n) (1<<n) +#define PDIR_AMUTE BIT(25) +#define ACLKX BIT(26) +#define AHCLKX BIT(27) +#define AFSX BIT(28) +#define ACLKR BIT(29) +#define AHCLKR BIT(30) +#define AFSR BIT(31) + +/* + * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits + */ +#define DITEN BIT(0) /* Transmit DIT mode enable/disable */ +#define VA BIT(2) +#define VB BIT(3) + +/* + * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits + */ +#define TXROT(val) (val) +#define TXSEL BIT(3) +#define TXSSZ(val) (val<<4) +#define TXPBIT(val) (val<<8) +#define TXPAD(val) (val<<13) +#define TXORD BIT(15) +#define FSXDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits + */ +#define RXROT(val) (val) +#define RXSEL BIT(3) +#define RXSSZ(val) (val<<4) +#define RXPBIT(val) (val<<8) +#define RXPAD(val) (val<<13) +#define RXORD BIT(15) +#define FSRDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits + */ +#define FSXPOL BIT(0) +#define AFSXE BIT(1) +#define FSXDUR BIT(4) +#define FSXMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits + */ +#define FSRPOL BIT(0) +#define AFSRE BIT(1) +#define FSRDUR BIT(4) +#define FSRMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits + */ +#define ACLKXDIV(val) (val) +#define ACLKXE BIT(5) +#define TX_ASYNC BIT(6) +#define ACLKXPOL BIT(7) + +/* + * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits + */ +#define ACLKRDIV(val) (val) +#define ACLKRE BIT(5) +#define RX_ASYNC BIT(6) +#define ACLKRPOL BIT(7) + +/* + * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control + * Register Bits + */ +#define AHCLKXDIV(val) (val) +#define AHCLKXPOL BIT(14) +#define AHCLKXE BIT(15) + +/* + * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control + * Register Bits + */ +#define AHCLKRDIV(val) (val) +#define AHCLKRPOL BIT(14) +#define AHCLKRE BIT(15) + +/* + * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits + */ +#define MODE(val) (val) +#define DISMOD (val)(val<<2) +#define TXSTATE BIT(4) +#define RXSTATE BIT(5) + +/* + * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits + */ +#define LBEN BIT(0) +#define LBORD BIT(1) +#define LBGENMODE(val) (val<<2) + +/* + * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration + */ +#define TXTDMS(n) (1<<n) + +/* + * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration + */ +#define RXTDMS(n) (1<<n) + +/* + * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits + */ +#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */ +#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */ +#define RXSERCLR BIT(2) /* Receiver Serializer Clear */ +#define RXSMRST BIT(3) /* Receiver State Machine Reset */ +#define RXFSRST BIT(4) /* Frame Sync Generator Reset */ +#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */ +#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/ +#define TXSERCLR BIT(10) /* Transmit Serializer Clear */ +#define TXSMRST BIT(11) /* Transmitter State Machine Reset */ +#define TXFSRST BIT(12) /* Frame Sync Generator Reset */ + +/* + * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits + */ +#define MUTENA(val) (val) +#define MUTEINPOL BIT(2) +#define MUTEINENA BIT(3) +#define MUTEIN BIT(4) +#define MUTER BIT(5) +#define MUTEX BIT(6) +#define MUTEFSR BIT(7) +#define MUTEFSX BIT(8) +#define MUTEBADCLKR BIT(9) +#define MUTEBADCLKX BIT(10) +#define MUTERXDMAERR BIT(11) +#define MUTETXDMAERR BIT(12) + +/* + * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits + */ +#define RXDATADMADIS BIT(0) + +/* + * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits + */ +#define TXDATADMADIS BIT(0) + +#define DAVINCI_MCASP_NUM_SERIALIZER 16 + +static inline void mcasp_set_bits(void __iomem *reg, u32 val) +{ + __raw_writel(__raw_readl(reg) | val, reg); +} + +static inline void mcasp_clr_bits(void __iomem *reg, u32 val) +{ + __raw_writel((__raw_readl(reg) & ~(val)), reg); +} + +static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask) +{ + __raw_writel((__raw_readl(reg) & ~mask) | val, reg); +} + +static inline void mcasp_set_reg(void __iomem *reg, u32 val) +{ + __raw_writel(val, reg); +} + +static inline u32 mcasp_get_reg(void __iomem *reg) +{ + return (unsigned int)__raw_readl(reg); +} + +static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val) +{ + int i = 0; + + mcasp_set_bits(regs, val); + + /* programming GBLCTL needs to read back from GBLCTL and verfiy */ + /* loop count is to avoid the lock-up */ + for (i = 0; i < 1000; i++) { + if ((mcasp_get_reg(regs) & val) == val) + break; + } + + if (i == 1000 && ((mcasp_get_reg(regs) & val) != val)) + printk(KERN_ERR "GBLCTL write error\n"); +} + +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_audio_dev *dev = cpu_dai->private_data; + cpu_dai->dma_data = dev->dma_params[substream->stream]; + return 0; +} + +static void mcasp_start_rx(struct davinci_audio_dev *dev) +{ + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); +} + +static void mcasp_start_tx(struct davinci_audio_dev *dev) +{ + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); + + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); + mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); +} + +static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mcasp_start_tx(dev); + else + mcasp_start_rx(dev); +} + +static void mcasp_stop_rx(struct davinci_audio_dev *dev) +{ + mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0); + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); +} + +static void mcasp_stop_tx(struct davinci_audio_dev *dev) +{ + mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0); + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); +} + +static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mcasp_stop_tx(dev); + else + mcasp_stop_rx(dev); +} + +static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct davinci_audio_dev *dev = cpu_dai->private_data; + void __iomem *base = dev->base; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* codec is clock and frame slave */ + mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26)); + break; + + case SND_SOC_DAIFMT_CBM_CFM: + /* codec is clock and frame master */ + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26)); + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + + mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + break; + + case SND_SOC_DAIFMT_NB_IF: + mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + break; + + case SND_SOC_DAIFMT_IB_IF: + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + + mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + break; + + case SND_SOC_DAIFMT_NB_NF: + mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int davinci_config_channel_size(struct davinci_audio_dev *dev, + int channel_size) +{ + u32 fmt = 0; + + switch (channel_size) { + case DAVINCI_AUDIO_WORD_8: + fmt = 0x03; + break; + + case DAVINCI_AUDIO_WORD_12: + fmt = 0x05; + break; + + case DAVINCI_AUDIO_WORD_16: + fmt = 0x07; + break; + + case DAVINCI_AUDIO_WORD_20: + fmt = 0x09; + break; + + case DAVINCI_AUDIO_WORD_24: + fmt = 0x0B; + break; + + case DAVINCI_AUDIO_WORD_28: + fmt = 0x0D; + break; + + case DAVINCI_AUDIO_WORD_32: + fmt = 0x0F; + break; + + default: + return -EINVAL; + } + + mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, + RXSSZ(fmt), RXSSZ(0x0F)); + mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, + TXSSZ(fmt), TXSSZ(0x0F)); + return 0; +} + +static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream) +{ + int i; + + /* Default configuration */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); + + /* All PINS as McASP */ + mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, + TXDATADMADIS); + } else { + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG, + RXDATADMADIS); + } + + for (i = 0; i < dev->num_serializer; i++) { + mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i), + dev->serial_dir[i]); + if (dev->serial_dir[i] == TX_MODE) + mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, + AXR(i)); + else if (dev->serial_dir[i] == RX_MODE) + mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, + AXR(i)); + } +} + +static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) +{ + int i, active_slots; + u32 mask = 0; + + active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots; + for (i = 0; i < active_slots; i++) + mask |= (1 << i); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* bit stream is MSB first with no delay */ + /* DSP_B mode */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD); + + if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); + else + printk(KERN_ERR "playback tdm slot %d not supported\n", + dev->tdm_slots); + + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF); + mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + } else { + /* bit stream is MSB first with no delay */ + /* DSP_B mode */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD); + mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask); + + if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); + else + printk(KERN_ERR "capture tdm slot %d not supported\n", + dev->tdm_slots); + + mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF); + mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + } +} + +/* S/PDIF */ +static void davinci_hw_dit_param(struct davinci_audio_dev *dev) +{ + /* Set the PDIR for Serialiser as output */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX); + + /* TXMASK for 24 bits */ + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF); + + /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0 + and LSB first */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, + TXROT(6) | TXSSZ(15)); + + /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */ + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG, + AFSXE | FSXMOD(0x180)); + + /* Set the TX tdm : for all the slots */ + mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); + + /* Set the TX clock controls : div = 1 and internal */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, + ACLKXE | TX_ASYNC); + + mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); + + /* Only 44100 and 48000 are valid, both have the same setting */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); + + /* Enable the DIT */ + mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN); +} + +static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_audio_dev *dev = cpu_dai->private_data; + struct davinci_pcm_dma_params *dma_params = + dev->dma_params[substream->stream]; + int word_length; + + davinci_hw_common_param(dev, substream->stream); + + if (dev->op_mode == DAVINCI_MCASP_DIT_MODE) + davinci_hw_dit_param(dev); + else + davinci_hw_param(dev, substream->stream); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + dma_params->data_type = 1; + word_length = DAVINCI_AUDIO_WORD_8; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + dma_params->data_type = 2; + word_length = DAVINCI_AUDIO_WORD_16; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + dma_params->data_type = 4; + word_length = DAVINCI_AUDIO_WORD_32; + break; + + default: + printk(KERN_WARNING "davinci-mcasp: unsupported PCM format"); + return -EINVAL; + } + davinci_config_channel_size(dev, word_length); + + return 0; +} + +static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_mcasp_start(dev, substream->stream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_mcasp_stop(dev, substream->stream); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, + .trigger = davinci_mcasp_trigger, + .hw_params = davinci_mcasp_hw_params, + .set_fmt = davinci_mcasp_set_dai_fmt, + +}; + +struct snd_soc_dai davinci_mcasp_dai[] = { + { + .name = "davinci-i2s", + .id = 0, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_MCASP_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_MCASP_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &davinci_mcasp_dai_ops, + + }, + { + .name = "davinci-dit", + .id = 1, + .playback = { + .channels_min = 1, + .channels_max = 384, + .rates = DAVINCI_MCASP_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &davinci_mcasp_dai_ops, + }, + +}; +EXPORT_SYMBOL_GPL(davinci_mcasp_dai); + +static int davinci_mcasp_probe(struct platform_device *pdev) +{ + struct davinci_pcm_dma_params *dma_data; + struct resource *mem, *ioarea, *res; + struct snd_platform_data *pdata; + struct davinci_audio_dev *dev; + int count = 0; + int ret = 0; + + dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dma_data = kzalloc(sizeof(struct davinci_pcm_dma_params) * 2, + GFP_KERNEL); + if (!dma_data) { + ret = -ENOMEM; + goto err_release_dev; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + ret = -ENODEV; + goto err_release_data; + } + + ioarea = request_mem_region(mem->start, + (mem->end - mem->start) + 1, pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "Audio region already claimed\n"); + ret = -EBUSY; + goto err_release_data; + } + + pdata = pdev->dev.platform_data; + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + ret = -ENODEV; + goto err_release_region; + } + + clk_enable(dev->clk); + + dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->op_mode = pdata->op_mode; + dev->tdm_slots = pdata->tdm_slots; + dev->num_serializer = pdata->num_serializer; + dev->serial_dir = pdata->serial_dir; + dev->codec_fmt = pdata->codec_fmt; + + dma_data[count].name = "I2S PCM Stereo out"; + dma_data[count].eventq_no = pdata->eventq_no; + dma_data[count].dma_addr = (dma_addr_t) (pdata->tx_dma_offset + + io_v2p(dev->base)); + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &dma_data[count]; + + /* first TX, then RX */ + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + goto err_release_region; + } + + dma_data[count].channel = res->start; + count++; + dma_data[count].name = "I2S PCM Stereo in"; + dma_data[count].eventq_no = pdata->eventq_no; + dma_data[count].dma_addr = (dma_addr_t)(pdata->rx_dma_offset + + io_v2p(dev->base)); + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &dma_data[count]; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + goto err_release_region; + } + + dma_data[count].channel = res->start; + davinci_mcasp_dai[pdev->id].private_data = dev; + davinci_mcasp_dai[pdev->id].dev = &pdev->dev; + ret = snd_soc_register_dai(&davinci_mcasp_dai[pdev->id]); + + if (ret != 0) + goto err_release_region; + return 0; + +err_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); +err_release_data: + kfree(dma_data); +err_release_dev: + kfree(dev); + + return ret; +} + +static int davinci_mcasp_remove(struct platform_device *pdev) +{ + struct davinci_pcm_dma_params *dma_data; + struct davinci_audio_dev *dev; + struct resource *mem; + + snd_soc_unregister_dai(&davinci_mcasp_dai[pdev->id]); + dev = davinci_mcasp_dai[pdev->id].private_data; + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + dma_data = dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; + kfree(dma_data); + kfree(dev); + + return 0; +} + +static struct platform_driver davinci_mcasp_driver = { + .probe = davinci_mcasp_probe, + .remove = davinci_mcasp_remove, + .driver = { + .name = "davinci-mcasp", + .owner = THIS_MODULE, + }, +}; + +static int __init davinci_mcasp_init(void) +{ + return platform_driver_register(&davinci_mcasp_driver); +} +module_init(davinci_mcasp_init); + +static void __exit davinci_mcasp_exit(void) +{ + platform_driver_unregister(&davinci_mcasp_driver); +} +module_exit(davinci_mcasp_exit); + +MODULE_AUTHOR("Steve Chen"); +MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h new file mode 100644 index 00000000000..36b71047a06 --- /dev/null +++ b/sound/soc/davinci/davinci-mcasp.h @@ -0,0 +1,55 @@ +/* + * ALSA SoC McASP Audio Layer for TI DAVINCI processor + * + * MCASP related definitions + * + * Author: Nirmal Pandey <n-pandey@ti.com>, + * Suresh Rajashekara <suresh.r@ti.com> + * Steve Chen <schen@.mvista.com> + * + * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com> + * Copyright: (C) 2009 Texas Instruments, India + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef DAVINCI_MCASP_H +#define DAVINCI_MCASP_H + +#include <linux/io.h> +#include <mach/asp.h> +#include "davinci-pcm.h" + +extern struct snd_soc_dai davinci_mcasp_dai[]; + +#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_96000 +#define DAVINCI_MCASP_I2S_DAI 0 +#define DAVINCI_MCASP_DIT_DAI 1 + +enum { + DAVINCI_AUDIO_WORD_8 = 0, + DAVINCI_AUDIO_WORD_12, + DAVINCI_AUDIO_WORD_16, + DAVINCI_AUDIO_WORD_20, + DAVINCI_AUDIO_WORD_24, + DAVINCI_AUDIO_WORD_32, + DAVINCI_AUDIO_WORD_28, /* This is only valid for McASP */ +}; + +struct davinci_audio_dev { + void __iomem *base; + int sample_rate; + struct clk *clk; + struct davinci_pcm_dma_params *dma_params[2]; + unsigned int codec_fmt; + + /* McASP specific data */ + int tdm_slots; + u8 op_mode; + u8 num_serializer; + u8 *serial_dir; +}; + +#endif /* DAVINCI_MCASP_H */ diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index a0599658848..8fd0c3cdc71 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -206,6 +206,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) /* Copy self-linked parameter RAM entry into master channel */ edma_read_slot(prtd->slave_lch, &temp); edma_write_slot(prtd->master_lch, &temp); + davinci_pcm_enqueue_dma(substream); return 0; } diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 62cb4eb07e3..eb4287faa3d 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -12,17 +12,19 @@ #ifndef _DAVINCI_PCM_H #define _DAVINCI_PCM_H +#include <mach/edma.h> +#include <mach/asp.h> + + struct davinci_pcm_dma_params { - char *name; /* stream identifier */ - int channel; /* sync dma channel ID */ - dma_addr_t dma_addr; /* device physical address for DMA */ - unsigned int data_type; /* xfer data type */ + char *name; /* stream identifier */ + int channel; /* sync dma channel ID */ + dma_addr_t dma_addr; /* device physical address for DMA */ + enum dma_event_q eventq_no; /* event queue number */ + unsigned char data_type; /* xfer data type */ + unsigned char convert_mono_stereo; }; -struct evm_snd_platform_data { - int tx_dma_ch; - int rx_dma_ch; -}; extern struct snd_soc_platform davinci_soc_platform; diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 7eb549985d4..c4ae3e096bb 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/delay.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -112,7 +113,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97) out_8(®s->op1, MPC52xx_PSC_OP_RES); udelay(10); out_8(®s->op0, MPC52xx_PSC_OP_RES); - udelay(50); + msleep(1); psc_ac97_warm_reset(ac97); } diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index b771238662b..a5a90e59453 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -72,4 +72,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE help Say Y if you want to add support for SoC audio on the Beagleboard. +config SND_OMAP_SOC_ZOOM2 + tristate "SoC Audio support for Zoom2" + depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2 + select SND_OMAP_SOC_MCBSP + select SND_SOC_TWL4030 + help + Say Y if you want to add support for Soc audio on Zoom2 board. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index a37f4986238..fefc48f02bd 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -14,6 +14,7 @@ snd-soc-omap3evm-objs := omap3evm.o snd-soc-sdp3430-objs := sdp3430.o snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o +snd-soc-zoom2-objs := zoom2.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o @@ -23,3 +24,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o +obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 84a1950880e..c3c931d4537 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -330,7 +330,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) } } -int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { int ret = 0; diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index b719e5db4f5..f7e5b7488c3 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -39,6 +40,9 @@ #include "omap-pcm.h" #include "../codecs/twl4030.h" +#define TWL4030_INTBR_PMBR1 0x0D +#define EXTMUTE(value) (value << 2) + static struct snd_soc_card snd_soc_sdp3430; static int sdp3430_hw_params(struct snd_pcm_substream *substream, @@ -96,7 +100,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBS_CFM); + SND_SOC_DAIFMT_CBM_CFM); if (ret) { printk(KERN_ERR "can't set codec DAI configuration\n"); return ret; @@ -280,6 +284,7 @@ static struct snd_soc_card snd_soc_sdp3430 = { static struct twl4030_setup_data twl4030_setup = { .ramp_delay_value = 3, .sysclk = 26000, + .hs_extmute = 1, }; /* Audio subsystem */ @@ -312,6 +317,10 @@ static int __init sdp3430_soc_init(void) *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ + /* Set TWL4030 GPIO6 as EXTMUTE signal */ + twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, EXTMUTE(0x02), + TWL4030_MODULE_INTBR); + ret = platform_device_add(sdp3430_snd_device); if (ret) goto err1; diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c new file mode 100644 index 00000000000..f90b45f5622 --- /dev/null +++ b/sound/soc/omap/zoom2.c @@ -0,0 +1,314 @@ +/* + * zoom2.c -- SoC audio for Zoom2 + * + * Author: Misael Lopez Cruz <x0052729@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/gpio.h> +#include <mach/mcbsp.h> + +#include "omap-mcbsp.h" +#include "omap-pcm.h" +#include "../codecs/twl4030.h" + +#define ZOOM2_HEADSET_MUX_GPIO (OMAP_MAX_GPIO_LINES + 15) +#define ZOOM2_HEADSET_EXTMUTE_GPIO 153 + +static int zoom2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops zoom2_ops = { + .hw_params = zoom2_hw_params, +}; + +static int zoom2_hw_voice_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops zoom2_voice_ops = { + .hw_params = zoom2_hw_voice_params, +}; + +/* Zoom2 machine DAPM */ +static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Ext Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_LINE("Aux In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* External Mics: MAINMIC, SUBMIC with bias*/ + {"MAINMIC", NULL, "Mic Bias 1"}, + {"SUBMIC", NULL, "Mic Bias 2"}, + {"Mic Bias 1", NULL, "Ext Mic"}, + {"Mic Bias 2", NULL, "Ext Mic"}, + + /* External Speakers: HFL, HFR */ + {"Ext Spk", NULL, "HFL"}, + {"Ext Spk", NULL, "HFR"}, + + /* Headset Stereophone: HSOL, HSOR */ + {"Headset Stereophone", NULL, "HSOL"}, + {"Headset Stereophone", NULL, "HSOR"}, + + /* Headset Mic: HSMIC with bias */ + {"HSMIC", NULL, "Headset Mic Bias"}, + {"Headset Mic Bias", NULL, "Headset Mic"}, + + /* Aux In: AUXL, AUXR */ + {"Aux In", NULL, "AUXL"}, + {"Aux In", NULL, "AUXR"}, +}; + +static int zoom2_twl4030_init(struct snd_soc_codec *codec) +{ + int ret; + + /* Add Zoom2 specific widgets */ + ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets, + ARRAY_SIZE(zoom2_twl4030_dapm_widgets)); + if (ret) + return ret; + + /* Set up Zoom2 specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + /* Zoom2 connected pins */ + snd_soc_dapm_enable_pin(codec, "Ext Mic"); + snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(codec, "Headset Mic"); + snd_soc_dapm_enable_pin(codec, "Headset Stereophone"); + snd_soc_dapm_enable_pin(codec, "Aux In"); + + /* TWL4030 not connected pins */ + snd_soc_dapm_nc_pin(codec, "CARKITMIC"); + snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); + snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); + + snd_soc_dapm_nc_pin(codec, "OUTL"); + snd_soc_dapm_nc_pin(codec, "OUTR"); + snd_soc_dapm_nc_pin(codec, "EARPIECE"); + snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); + snd_soc_dapm_nc_pin(codec, "PREDRIVER"); + snd_soc_dapm_nc_pin(codec, "CARKITL"); + snd_soc_dapm_nc_pin(codec, "CARKITR"); + + ret = snd_soc_dapm_sync(codec); + + return ret; +} + +static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec) +{ + unsigned short reg; + + /* Enable voice interface */ + reg = codec->read(codec, TWL4030_REG_VOICE_IF); + reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN; + codec->write(codec, TWL4030_REG_VOICE_IF, reg); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link zoom2_dai[] = { + { + .name = "TWL4030 I2S", + .stream_name = "TWL4030 Audio", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], + .init = zoom2_twl4030_init, + .ops = &zoom2_ops, + }, + { + .name = "TWL4030 PCM", + .stream_name = "TWL4030 Voice", + .cpu_dai = &omap_mcbsp_dai[1], + .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE], + .init = zoom2_twl4030_voice_init, + .ops = &zoom2_voice_ops, + }, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_zoom2 = { + .name = "Zoom2", + .platform = &omap_soc_platform, + .dai_link = zoom2_dai, + .num_links = ARRAY_SIZE(zoom2_dai), +}; + +/* EXTMUTE callback function */ +void zoom2_set_hs_extmute(int mute) +{ + gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute); +} + +/* twl4030 setup */ +static struct twl4030_setup_data twl4030_setup = { + .ramp_delay_value = 3, /* 161 ms */ + .sysclk = 26000, + .hs_extmute = 1, + .set_hs_extmute = zoom2_set_hs_extmute, +}; + +/* Audio subsystem */ +static struct snd_soc_device zoom2_snd_devdata = { + .card = &snd_soc_zoom2, + .codec_dev = &soc_codec_dev_twl4030, + .codec_data = &twl4030_setup, +}; + +static struct platform_device *zoom2_snd_device; + +static int __init zoom2_soc_init(void) +{ + int ret; + + if (!machine_is_omap_zoom2()) { + pr_debug("Not Zoom2!\n"); + return -ENODEV; + } + printk(KERN_INFO "Zoom2 SoC init\n"); + + zoom2_snd_device = platform_device_alloc("soc-audio", -1); + if (!zoom2_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata); + zoom2_snd_devdata.dev = &zoom2_snd_device->dev; + *(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ + *(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ + + ret = platform_device_add(zoom2_snd_device); + if (ret) + goto err1; + + BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0); + gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0); + + BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0); + gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0); + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(zoom2_snd_device); + + return ret; +} +module_init(zoom2_soc_init); + +static void __exit zoom2_soc_exit(void) +{ + gpio_free(ZOOM2_HEADSET_MUX_GPIO); + gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO); + + platform_device_unregister(zoom2_snd_device); +} +module_exit(zoom2_soc_exit); + +MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); +MODULE_DESCRIPTION("ALSA SoC Zoom2"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 326955dea36..8889cd37160 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -20,12 +20,14 @@ #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/i2c.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/uda1380.h> #include <mach/magician.h> #include <asm/mach-types.h> @@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = { .platform = &pxa2xx_soc_platform, }; -/* magician audio private data */ -static struct uda1380_setup_data magician_uda1380_setup = { - .i2c_address = 0x18, - .dac_clk = UDA1380_DAC_CLK_WSPLL, -}; - /* magician audio subsystem */ static struct snd_soc_device magician_snd_devdata = { .card = &snd_soc_card_magician, .codec_dev = &soc_codec_dev_uda1380, - .codec_data = &magician_uda1380_setup, }; static struct platform_device *magician_snd_device; +/* + * FIXME: move into magician board file once merged into the pxa tree + */ +static struct uda1380_platform_data uda1380_info = { + .gpio_power = EGPIO_MAGICIAN_CODEC_POWER, + .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET, + .dac_clk = UDA1380_DAC_CLK_WSPLL, +}; + +static struct i2c_board_info i2c_board_info[] = { + { + I2C_BOARD_INFO("uda1380", 0x18), + .platform_data = &uda1380_info, + }, +}; + static int __init magician_init(void) { int ret; + struct i2c_adapter *adapter; + struct i2c_client *client; if (!machine_is_magician()) return -ENODEV; - ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER"); - if (ret) - goto err_request_power; - ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET"); - if (ret) - goto err_request_reset; + adapter = i2c_get_adapter(0); + if (!adapter) + return -ENODEV; + client = i2c_new_device(adapter, i2c_board_info); + i2c_put_adapter(adapter); + if (!client) + return -ENODEV; + ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); if (ret) goto err_request_spk; @@ -491,14 +506,8 @@ static int __init magician_init(void) if (ret) goto err_request_in_sel1; - gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1); gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0); - /* we may need to have the clock running here - pH5 */ - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1); - udelay(5); - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0); - magician_snd_device = platform_device_alloc("soc-audio", -1); if (!magician_snd_device) { ret = -ENOMEM; @@ -526,10 +535,6 @@ err_request_mic: err_request_ep: gpio_free(EGPIO_MAGICIAN_SPK_POWER); err_request_spk: - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); -err_request_reset: - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); -err_request_power: return ret; } @@ -540,15 +545,12 @@ static void __exit magician_exit(void) gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0); - gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0); gpio_free(EGPIO_MAGICIAN_IN_SEL1); gpio_free(EGPIO_MAGICIAN_IN_SEL0); gpio_free(EGPIO_MAGICIAN_MIC_POWER); gpio_free(EGPIO_MAGICIAN_EP_POWER); gpio_free(EGPIO_MAGICIAN_SPK_POWER); - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); } module_init(magician_init); diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index e6102fda0a7..1f96e3227be 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -17,13 +17,12 @@ #include <linux/moduleparam.h> #include <linux/device.h> #include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/jack.h> #include <asm/mach-types.h> #include <mach/audio.h> @@ -33,90 +32,31 @@ #include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -static int palm27x_jack_func = 1; -static int palm27x_spk_func = 1; -static int palm27x_ep_gpio = -1; +static struct snd_soc_jack hs_jack; -static void palm27x_ext_control(struct snd_soc_codec *codec) -{ - if (!palm27x_spk_func) - snd_soc_dapm_enable_pin(codec, "Speaker"); - else - snd_soc_dapm_disable_pin(codec, "Speaker"); - - if (!palm27x_jack_func) - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - else - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - - snd_soc_dapm_sync(codec); -} - -static int palm27x_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->card->codec; - - /* check the jack status at stream startup */ - palm27x_ext_control(codec); - return 0; -} - -static struct snd_soc_ops palm27x_ops = { - .startup = palm27x_startup, +/* Headphones jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, }; -static irqreturn_t palm27x_interrupt(int irq, void *v) -{ - palm27x_spk_func = gpio_get_value(palm27x_ep_gpio); - palm27x_jack_func = !palm27x_spk_func; - return IRQ_HANDLED; -} - -static int palm27x_get_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = palm27x_jack_func; - return 0; -} - -static int palm27x_set_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (palm27x_jack_func == ucontrol->value.integer.value[0]) - return 0; - - palm27x_jack_func = ucontrol->value.integer.value[0]; - palm27x_ext_control(codec); - return 1; -} - -static int palm27x_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = palm27x_spk_func; - return 0; -} - -static int palm27x_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (palm27x_spk_func == ucontrol->value.integer.value[0]) - return 0; - - palm27x_spk_func = ucontrol->value.integer.value[0]; - palm27x_ext_control(codec); - return 1; -} +/* Headphones jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + [0] = { + /* gpio is set on per-platform basis */ + .name = "hp-gpio", + .report = SND_JACK_HEADPHONE, + .debounce_time = 200, + }, +}; -/* PalmTX machine dapm widgets */ +/* Palm27x machine dapm widgets */ static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SPK("Ext. Speaker", NULL), + SND_SOC_DAPM_MIC("Ext. Microphone", NULL), }; /* PalmTX audio map */ @@ -126,46 +66,66 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Headphone Jack", NULL, "HPOUTR"}, /* ext speaker connected to ROUT2, LOUT2 */ - {"Speaker", NULL, "LOUT2"}, - {"Speaker", NULL, "ROUT2"}, -}; + {"Ext. Speaker", NULL, "LOUT2"}, + {"Ext. Speaker", NULL, "ROUT2"}, -static const char *jack_function[] = {"Headphone", "Off"}; -static const char *spk_function[] = {"On", "Off"}; -static const struct soc_enum palm27x_enum[] = { - SOC_ENUM_SINGLE_EXT(2, jack_function), - SOC_ENUM_SINGLE_EXT(2, spk_function), + /* mic connected to MIC1 */ + {"Ext. Microphone", NULL, "MIC1"}, }; -static const struct snd_kcontrol_new palm27x_controls[] = { - SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack, - palm27x_set_jack), - SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk, - palm27x_set_spk), -}; +static struct snd_soc_card palm27x_asoc; static int palm27x_ac97_init(struct snd_soc_codec *codec) { int err; + /* add palm27x specific widgets */ + err = snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, + ARRAY_SIZE(palm27x_dapm_widgets)); + if (err) + return err; + + /* set up palm27x specific audio path audio_map */ + err = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + if (err) + return err; + + /* connected pins */ + if (machine_is_palmld()) + snd_soc_dapm_enable_pin(codec, "MIC1"); + snd_soc_dapm_enable_pin(codec, "HPOUTL"); + snd_soc_dapm_enable_pin(codec, "HPOUTR"); + snd_soc_dapm_enable_pin(codec, "LOUT2"); + snd_soc_dapm_enable_pin(codec, "ROUT2"); + + /* not connected pins */ snd_soc_dapm_nc_pin(codec, "OUT3"); snd_soc_dapm_nc_pin(codec, "MONOOUT"); + snd_soc_dapm_nc_pin(codec, "LINEINL"); + snd_soc_dapm_nc_pin(codec, "LINEINR"); + snd_soc_dapm_nc_pin(codec, "PCBEEP"); + snd_soc_dapm_nc_pin(codec, "PHONE"); + snd_soc_dapm_nc_pin(codec, "MIC2"); + + err = snd_soc_dapm_sync(codec); + if (err) + return err; - /* add palm27x specific controls */ - err = snd_soc_add_controls(codec, palm27x_controls, - ARRAY_SIZE(palm27x_controls)); - if (err < 0) + /* Jack detection API stuff */ + err = snd_soc_jack_new(&palm27x_asoc, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack); + if (err) return err; - /* add palm27x specific widgets */ - snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, - ARRAY_SIZE(palm27x_dapm_widgets)); + err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + if (err) + return err; - /* set up palm27x specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); - snd_soc_dapm_sync(codec); - return 0; + return err; } static struct snd_soc_dai_link palm27x_dai[] = { @@ -175,14 +135,12 @@ static struct snd_soc_dai_link palm27x_dai[] = { .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], .init = palm27x_ac97_init, - .ops = &palm27x_ops, }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], - .ops = &palm27x_ops, }, }; @@ -208,27 +166,17 @@ static int palm27x_asoc_probe(struct platform_device *pdev) machine_is_palmld() || machine_is_palmte2())) return -ENODEV; - if (pdev->dev.platform_data) - palm27x_ep_gpio = ((struct palm27x_asoc_info *) - (pdev->dev.platform_data))->jack_gpio; - - ret = gpio_request(palm27x_ep_gpio, "Headphone Jack"); - if (ret) - return ret; - ret = gpio_direction_input(palm27x_ep_gpio); - if (ret) - goto err_alloc; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "please supply platform_data\n"); + return -ENODEV; + } - if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Headphone jack", NULL)) - goto err_alloc; + hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *) + (pdev->dev.platform_data))->jack_gpio; palm27x_snd_device = platform_device_alloc("soc-audio", -1); - if (!palm27x_snd_device) { - ret = -ENOMEM; - goto err_dev; - } + if (!palm27x_snd_device) + return -ENOMEM; platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata); palm27x_snd_devdata.dev = &palm27x_snd_device->dev; @@ -241,18 +189,12 @@ static int palm27x_asoc_probe(struct platform_device *pdev) put_device: platform_device_put(palm27x_snd_device); -err_dev: - free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); -err_alloc: - gpio_free(palm27x_ep_gpio); return ret; } static int __devexit palm27x_asoc_remove(struct platform_device *pdev) { - free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); - gpio_free(palm27x_ep_gpio); platform_device_unregister(palm27x_snd_device); return 0; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 19c45409d94..e22c5cef8fe 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -457,31 +457,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - ssp_write_reg(ssp, SSCR0, sscr0); - ssp_write_reg(ssp, SSCR1, sscr1); - ssp_write_reg(ssp, SSPSP, sspsp); + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + sspsp |= SSPSP_SFRMP; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + case SND_SOC_DAIFMT_IB_IF: + sspsp |= SSPSP_SCMODE(2); + break; + case SND_SOC_DAIFMT_IB_NF: + sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; + break; + default: + return -EINVAL; + } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sscr0 |= SSCR0_PSP; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; - /* See hw_params() */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_SFRMP; - break; - case SND_SOC_DAIFMT_NB_IF: - break; - case SND_SOC_DAIFMT_IB_IF: - sspsp |= SSPSP_SCMODE(2); - break; - case SND_SOC_DAIFMT_IB_NF: - sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; - break; - default: - return -EINVAL; - } break; case SND_SOC_DAIFMT_DSP_A: @@ -489,22 +485,6 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_DSP_B: sscr0 |= SSCR0_MOD | SSCR0_PSP; sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_SFRMP; - break; - case SND_SOC_DAIFMT_NB_IF: - break; - case SND_SOC_DAIFMT_IB_IF: - sspsp |= SSPSP_SCMODE(2); - break; - case SND_SOC_DAIFMT_IB_NF: - sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; - break; - default: - return -EINVAL; - } break; default: diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index df494d1e346..5dd17338e56 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -30,14 +30,14 @@ config SND_S3C2443_SOC_AC97 select SND_SOC_AC97_BUS config SND_S3C24XX_SOC_NEO1973_WM8753 - tristate "SoC I2S Audio support for NEO1973 - WM8753" - depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01 + tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)" + depends on SND_S3C24XX_SOC && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02) select SND_S3C24XX_SOC_I2S select SND_SOC_WM8753 help - Say Y if you want to add support for SoC audio on smdk2440 - with the WM8753. - + Say Y here to enable audio support for the Openmoko Neo1973 + Smartphones. + config SND_S3C24XX_SOC_JIVE_WM8750 tristate "SoC I2S Audio support for Jive" depends on SND_S3C24XX_SOC && MACH_JIVE diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 906709e6dd5..7f28af2058e 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -1,15 +1,16 @@ /* - * neo1973_wm8753.c -- SoC audio for Neo1973 + * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02) * + * Copyright 2007 Openmoko Inc + * Author: Graeme Gregory <graeme@openmoko.org> * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory - * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Graeme Gregory <linux@wolfsonmicro.com> + * Copyright 2009 Wolfson Microelectronics * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. - * */ #include <linux/module.h> @@ -17,7 +18,7 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/i2c.h> +#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -25,34 +26,158 @@ #include <sound/tlv.h> #include <asm/mach-types.h> -#include <asm/hardware/scoop.h> -#include <mach/regs-clock.h> -#include <mach/regs-gpio.h> -#include <mach/hardware.h> -#include <plat/audio.h> -#include <linux/io.h> -#include <mach/spi-gpio.h> #include <plat/regs-iis.h> +#include <mach/regs-clock.h> +#include <mach/gta02.h> #include "../codecs/wm8753.h" -#include "lm4857.h" #include "s3c24xx-pcm.h" #include "s3c24xx-i2s.h" -/* define the scenarios */ -#define NEO_AUDIO_OFF 0 -#define NEO_GSM_CALL_AUDIO_HANDSET 1 -#define NEO_GSM_CALL_AUDIO_HEADSET 2 -#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3 -#define NEO_STEREO_TO_SPEAKERS 4 -#define NEO_STEREO_TO_HEADPHONES 5 -#define NEO_CAPTURE_HANDSET 6 -#define NEO_CAPTURE_HEADSET 7 -#define NEO_CAPTURE_BLUETOOTH 8 +#include "lm4857.h" +#include <linux/i2c.h> + +#ifdef CONFIG_MACH_NEO1973_GTA01 + +static struct lm4857 { + struct i2c_client *i2c; + uint8_t regs[4]; + uint8_t state; +} lm4857 = { + .regs = {0x00, 0x40, 0x80, 0xC0}, +}; + +static void lm4857_write_regs(void) +{ + if (i2c_master_send(lm4857.i2c, lm4857.regs, 4) != 4) + printk(KERN_ERR "lm4857: i2c write failed\n"); +} + +static int lm4857_get_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + int shift = mc->shift; + int mask = mc->max; + + ucontrol->value.integer.value[0] = (lm4857.regs[reg] >> shift) & mask; + return 0; +} + +static int lm4857_set_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + int shift = mc->shift; + int mask = mc->max; + + if (((lm4857.regs[reg] >> shift) & mask) == + ucontrol->value.integer.value[0]) + return 0; + + lm4857.regs[reg] &= ~(mask << shift); + lm4857.regs[reg] |= ucontrol->value.integer.value[0] << shift; + lm4857_write_regs(); + return 1; +} + +static int lm4857_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint8_t value = lm4857.regs[LM4857_CTRL] & 0x0F; + + if (value) + value -= 5; + + ucontrol->value.integer.value[0] = value; + return 0; +} + +static int lm4857_set_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint8_t value = ucontrol->value.integer.value[0]; + + if (value) + value += 5; + + if ((lm4857.regs[LM4857_CTRL] & 0x0F) == value) + return 0; + + lm4857.regs[LM4857_CTRL] &= 0xF0; + lm4857.regs[LM4857_CTRL] |= value; + lm4857_write_regs(); + return 1; +} + +static int lm4857_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (lm4857.i2c) + return -EBUSY; + + lm4857.i2c = client; + + lm4857_write_regs(); + return 0; +} + +static int lm4857_i2c_remove(struct i2c_client *client) +{ + lm4857.i2c = NULL; + + return 0; +} + +static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) +{ + lm4857.state = lm4857.regs[LM4857_CTRL] & 0xf; + if (lm4857.state) { + lm4857.regs[LM4857_CTRL] &= 0xf0; + lm4857_write_regs(); + } + return 0; +} + +static int lm4857_resume(struct i2c_client *dev) +{ + if (lm4857.state) { + lm4857.regs[LM4857_CTRL] |= (lm4857.state & 0x0f); + lm4857_write_regs(); + } + return 0; +} + +static void lm4857_shutdown(struct i2c_client *dev) +{ + lm4857.regs[LM4857_CTRL] &= 0xf0; + lm4857_write_regs(); +} + +static const struct i2c_device_id lm4857_i2c_id[] = { + { "neo1973_lm4857", 0 }, + { } +}; + +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "LM4857 I2C Amp", + .owner = THIS_MODULE, + }, + .suspend = lm4857_suspend, + .resume = lm4857_resume, + .shutdown = lm4857_shutdown, + .probe = lm4857_i2c_probe, + .remove = lm4857_i2c_remove, + .id_table = lm4857_i2c_id, +}; -static struct snd_soc_card neo1973; -static struct i2c_client *i2c; +#endif static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -64,8 +189,6 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); switch (params_rate(params)) { @@ -126,7 +249,8 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, return ret; /* set codec BCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk); + ret = snd_soc_dai_set_clkdiv(codec_dai, + WM8753_BCLKDIV, bclk); if (ret < 0) return ret; @@ -150,8 +274,6 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); } @@ -164,7 +286,8 @@ static struct snd_soc_ops neo1973_hifi_ops = { .hw_free = neo1973_hifi_hw_free, }; -static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, +static int neo1973_voice_hw_params( + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -173,8 +296,6 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); if (params_rate(params) != 8000) @@ -192,13 +313,14 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, return ret; /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, + 12288000, SND_SOC_CLOCK_IN); if (ret < 0) return ret; /* set codec PCM division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv); + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, + pcmdiv); if (ret < 0) return ret; @@ -216,8 +338,6 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0); } @@ -227,202 +347,20 @@ static struct snd_soc_ops neo1973_voice_ops = { .hw_free = neo1973_voice_hw_free, }; -static int neo1973_scenario; - -static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = neo1973_scenario; - return 0; -} - -static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) -{ - pr_debug("Entered %s\n", __func__); - - switch (neo1973_scenario) { - case NEO_AUDIO_OFF: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HANDSET: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HEADSET: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_BLUETOOTH: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_STEREO_TO_SPEAKERS: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_STEREO_TO_HEADPHONES: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_HANDSET: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_HEADSET: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_BLUETOOTH: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - default: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - } - - snd_soc_dapm_sync(codec); - - return 0; -} - -static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - pr_debug("Entered %s\n", __func__); - - if (neo1973_scenario == ucontrol->value.integer.value[0]) - return 0; - - neo1973_scenario = ucontrol->value.integer.value[0]; - set_scenario_endpoints(codec, neo1973_scenario); - return 1; -} - -static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; - -static void lm4857_write_regs(void) -{ - pr_debug("Entered %s\n", __func__); - - if (i2c_master_send(i2c, lm4857_regs, 4) != 4) - printk(KERN_ERR "lm4857: i2c write failed\n"); -} - -static int lm4857_get_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - pr_debug("Entered %s\n", __func__); - - ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; - return 0; -} - -static int lm4857_set_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - if (((lm4857_regs[reg] >> shift) & mask) == - ucontrol->value.integer.value[0]) - return 0; - - lm4857_regs[reg] &= ~(mask << shift); - lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift; - lm4857_write_regs(); - return 1; -} - -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; - - pr_debug("Entered %s\n", __func__); - - if (value) - value -= 5; - - ucontrol->value.integer.value[0] = value; - return 0; -} - -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - pr_debug("Entered %s\n", __func__); - - if (value) - value += 5; - - if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value) - return 0; - - lm4857_regs[LM4857_CTRL] &= 0xF0; - lm4857_regs[LM4857_CTRL] |= value; - lm4857_write_regs(); - return 1; -} +/* Shared routes and controls */ static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_LINE("Audio Out", NULL), SND_SOC_DAPM_LINE("GSM Line Out", NULL), SND_SOC_DAPM_LINE("GSM Line In", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Call Mic", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), }; +static const struct snd_soc_dapm_route audio_map[] = { -static const struct snd_soc_dapm_route dapm_routes[] = { - - /* Connections to the lm4857 amp */ - {"Audio Out", NULL, "LOUT1"}, - {"Audio Out", NULL, "ROUT1"}, + /* Connections to the lm4853 amp */ + {"Stereo Out", NULL, "LOUT1"}, + {"Stereo Out", NULL, "ROUT1"}, /* Connections to the GSM Module */ {"GSM Line Out", NULL, "MONO1"}, @@ -437,12 +375,24 @@ static const struct snd_soc_dapm_route dapm_routes[] = { /* Call Mic */ {"MIC2", NULL, "Mic Bias"}, {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Call Mic"}, + {"Mic Bias", NULL, "Handset Mic"}, /* Connect the ALC pins */ {"ACIN", NULL, "ACOP"}, }; +static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { + SOC_DAPM_PIN_SWITCH("Stereo Out"), + SOC_DAPM_PIN_SWITCH("GSM Line Out"), + SOC_DAPM_PIN_SWITCH("GSM Line In"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Handset Mic"), +}; + +/* GTA01 specific controlls */ + +#ifdef CONFIG_MACH_NEO1973_GTA01 + static const char *lm4857_mode[] = { "Off", "Call Speaker", @@ -455,26 +405,10 @@ static const struct soc_enum lm4857_mode_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), }; -static const char *neo_scenarios[] = { - "Off", - "GSM Handset", - "GSM Headset", - "GSM Bluetooth", - "Speakers", - "Headphones", - "Capture Handset", - "Capture Headset", - "Capture Bluetooth" -}; - -static const struct soc_enum neo_scenario_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios), -}; - static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); -static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { +static const struct snd_kcontrol_new wm8753_neo1973_gta01_controls[] = { SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, lm4857_get_reg, lm4857_set_reg, stereo_tlv), SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, @@ -483,8 +417,6 @@ static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { lm4857_get_reg, lm4857_set_reg, mono_tlv), SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0], lm4857_get_mode, lm4857_set_mode), - SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0], - neo1973_get_scenario, neo1973_set_scenario), SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0, lm4857_get_reg, lm4857_set_reg), SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0, @@ -495,20 +427,98 @@ static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { lm4857_get_reg, lm4857_set_reg), }; -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 II. It is missing logic to detect hp/mic insertions and logic - * to re-route the audio in such an event. - */ +static const struct snd_soc_dapm_widget wm8753_dapm_widgets_gta01[] = { + SND_SOC_DAPM_SPK("Stereo Out", NULL), +}; + +#else +static const struct snd_kcontrol_new wm8753_neo1973_gta01_controls[] = {}; +static const struct snd_soc_dapm_widget wm8753_dapm_widgets_gta01[] = {}; +#endif + +/* GTA02 specific routes and controlls */ + +#ifdef CONFIG_MACH_NEO1973_GTA02 + +static int lm4853_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int val = ucontrol->value.integer.value[0]; + + gpio_set_value(GTA02_GPIO_HP_IN, !val); + + return 0; +} + +static int lm4853_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = !gpio_get_value(GTA02_GPIO_HP_IN); + + return 0; +} + +static int lm4853_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, + int event) +{ + gpio_set_value(GTA02_GPIO_AMP_SHUT, !SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_route audio_map_gta02[] = { + /* Call Speaker */ + {"Handset Spk", NULL, "LOUT2"}, + {"Handset Spk", NULL, "ROUT2"}, +}; + +static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = { + SOC_DAPM_PIN_SWITCH("Handset Spk"), + + SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0, + lm4853_get_spk, + lm4853_set_spk), +}; + +static const struct snd_soc_dapm_widget wm8753_dapm_widgets_gta02[] = { + SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), + SND_SOC_DAPM_SPK("Handset Spk", NULL), +}; + +#else +static const struct snd_soc_dapm_route audio_map_gta02[]= {}; +static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {}; +static const struct snd_soc_dapm_widget wm8753_dapm_widgets_gta02[] = {}; +#endif + static int neo1973_wm8753_init(struct snd_soc_codec *codec) { int err; - - pr_debug("Entered %s\n", __func__); + const struct snd_soc_dapm_widget *machine_widgets; + size_t num_machine_widgets; + const struct snd_kcontrol_new *machine_controls; + size_t num_machine_controls; + + if (machine_is_neo1973_gta01()) { + machine_widgets = wm8753_dapm_widgets_gta01; + num_machine_widgets = ARRAY_SIZE(wm8753_dapm_widgets_gta01); + + machine_controls = wm8753_neo1973_gta01_controls; + num_machine_controls = ARRAY_SIZE(wm8753_neo1973_gta01_controls); + } else { + machine_widgets = wm8753_dapm_widgets_gta02; + num_machine_widgets = ARRAY_SIZE(wm8753_dapm_widgets_gta02); + + machine_controls = wm8753_neo1973_gta01_controls; + num_machine_controls = ARRAY_SIZE(wm8753_neo1973_gta01_controls); + } /* set up NC codec pins */ - snd_soc_dapm_nc_pin(codec, "LOUT2"); - snd_soc_dapm_nc_pin(codec, "ROUT2"); + if (!machine_is_neo1973_gta02()) { + snd_soc_dapm_nc_pin(codec, "LOUT2"); + snd_soc_dapm_nc_pin(codec, "ROUT2"); + } snd_soc_dapm_nc_pin(codec, "OUT3"); snd_soc_dapm_nc_pin(codec, "OUT4"); snd_soc_dapm_nc_pin(codec, "LINE1"); @@ -518,20 +528,41 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, ARRAY_SIZE(wm8753_dapm_widgets)); - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); + snd_soc_dapm_new_controls(codec, machine_widgets, num_machine_widgets); + /* add neo1973 specific controls */ err = snd_soc_add_controls(codec, wm8753_neo1973_controls, - ARRAY_SIZE(8753_neo1973_controls)); + ARRAY_SIZE(wm8753_neo1973_controls)); + + if (err < 0) + return err; + + err = snd_soc_add_controls(codec, machine_controls, + num_machine_controls); + if (err < 0) return err; - /* set up neo1973 specific audio routes */ - err = snd_soc_dapm_add_routes(codec, dapm_routes, - ARRAY_SIZE(dapm_routes)); + /* set up neo1973 gta02 specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + if (machine_is_neo1973_gta02()) { + snd_soc_dapm_add_routes(codec, audio_map_gta02, + ARRAY_SIZE(audio_map_gta02)); + } + + /* set endpoints to default off mode */ + snd_soc_dapm_disable_pin(codec, "Stereo Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Handset Mic"); + if (machine_is_neo1973_gta02()) + snd_soc_dapm_disable_pin(codec, "Handset Spk"); snd_soc_dapm_sync(codec); + return 0; } @@ -555,126 +586,107 @@ static struct snd_soc_dai bt_dai = { static struct snd_soc_dai_link neo1973_dai[] = { { /* Hifi Playback - for similatious use with voice below */ - .name = "WM8753", - .stream_name = "WM8753 HiFi", - .cpu_dai = &s3c24xx_i2s_dai, - .codec_dai = &wm8753_dai[WM8753_DAI_HIFI], - .init = neo1973_wm8753_init, - .ops = &neo1973_hifi_ops, + .name = "WM8753", + .stream_name = "WM8753 HiFi", + .cpu_dai = &s3c24xx_i2s_dai, + .codec_dai = &wm8753_dai[WM8753_DAI_HIFI], + .init = neo1973_wm8753_init, + .ops = &neo1973_hifi_ops, }, { /* Voice via BT */ - .name = "Bluetooth", - .stream_name = "Voice", - .cpu_dai = &bt_dai, - .codec_dai = &wm8753_dai[WM8753_DAI_VOICE], - .ops = &neo1973_voice_ops, + .name = "Bluetooth", + .stream_name = "Voice", + .cpu_dai = &bt_dai, + .codec_dai = &wm8753_dai[WM8753_DAI_VOICE], + .ops = &neo1973_voice_ops, }, }; static struct snd_soc_card neo1973 = { - .name = "neo1973", - .platform = &s3c24xx_soc_platform, - .dai_link = neo1973_dai, - .num_links = ARRAY_SIZE(neo1973_dai), + .name = "neo1973", + .platform = &s3c24xx_soc_platform, + .dai_link = neo1973_dai, + .num_links = ARRAY_SIZE(neo1973_dai), }; static struct snd_soc_device neo1973_snd_devdata = { - .card = &neo1973, - .codec_dev = &soc_codec_dev_wm8753, + .card = &neo1973, + .codec_dev = &soc_codec_dev_wm8753, }; -static int lm4857_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - pr_debug("Entered %s\n", __func__); - - i2c = client; - - lm4857_write_regs(); - return 0; -} +static struct platform_device *neo1973_snd_device; -static int lm4857_i2c_remove(struct i2c_client *client) +#ifdef CONFIG_MACH_NEO1973_GTA01 +static int __init neo1973_gta01_init(void) { - pr_debug("Entered %s\n", __func__); - - i2c = NULL; - - return 0; + return i2c_add_driver(&lm4857_i2c_driver); } +#else +static inline int neo1973_gta01_init(void) { return 0; } +#endif -static u8 lm4857_state; - -static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) +#ifdef CONFIG_MACH_NEO1973_GTA02 +static int __init neo1973_gta02_init(void) { - pr_debug("Entered %s\n", __func__); + int ret; - dev_dbg(&dev->dev, "lm4857_suspend\n"); - lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); + /* Initialise GPIOs used by amp */ + ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN"); + if (ret) { + pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN); + goto err; } - return 0; -} - -static int lm4857_resume(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); - lm4857_write_regs(); + ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1); + if (ret) { + pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN); + goto err_free_gpio_hp_in; } - return 0; -} - -static void lm4857_shutdown(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - dev_dbg(&dev->dev, "lm4857_shutdown\n"); - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); -} + ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT"); + if (ret) { + pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT); + goto err_free_gpio_hp_in; + } -static const struct i2c_device_id lm4857_i2c_id[] = { - { "neo1973_lm4857", 0 }, - { } -}; + ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1); + if (ret) { + pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT); + goto err_free_gpio_amp_shut; + } -static struct i2c_driver lm4857_i2c_driver = { - .driver = { - .name = "LM4857 I2C Amp", - .owner = THIS_MODULE, - }, - .suspend = lm4857_suspend, - .resume = lm4857_resume, - .shutdown = lm4857_shutdown, - .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, - .id_table = lm4857_i2c_id, -}; + return 0; -static struct platform_device *neo1973_snd_device; +err_free_gpio_amp_shut: + gpio_free(GTA02_GPIO_AMP_SHUT); +err_free_gpio_hp_in: + gpio_free(GTA02_GPIO_HP_IN); +err: + return ret; +} +#else +static inline int neo1973_gta02_init(void) { return 0; } +#endif static int __init neo1973_init(void) { int ret; - pr_debug("Entered %s\n", __func__); - - if (!machine_is_neo1973_gta01()) { - printk(KERN_INFO - "Only GTA01 hardware supported by ASoC driver\n"); + if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) { return -ENODEV; } + /* register bluetooth DAI here */ + ret = snd_soc_register_dai(&bt_dai); + if (ret) + return ret; + neo1973_snd_device = platform_device_alloc("soc-audio", -1); if (!neo1973_snd_device) return -ENOMEM; - platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata); + platform_set_drvdata(neo1973_snd_device, + &neo1973_snd_devdata); neo1973_snd_devdata.dev = &neo1973_snd_device->dev; ret = platform_device_add(neo1973_snd_device); @@ -683,26 +695,54 @@ static int __init neo1973_init(void) return ret; } - ret = i2c_add_driver(&lm4857_i2c_driver); + if (machine_is_neo1973_gta01()) + ret = neo1973_gta01_init(); + else + ret = neo1973_gta02_init(); + + if (ret) + goto err_unregister_device; - if (ret != 0) - platform_device_unregister(neo1973_snd_device); + return 0; +err_unregister_device: + platform_device_unregister(neo1973_snd_device); return ret; } +module_init(neo1973_init); -static void __exit neo1973_exit(void) +#ifdef CONFIG_MACH_NEO1973_GTA01 +static void __exit neo1973_gta01_exit(void) { - pr_debug("Entered %s\n", __func__); - i2c_del_driver(&lm4857_i2c_driver); - platform_device_unregister(neo1973_snd_device); } +#else +static inline void neo1973_gta01_exit(void) {} +#endif -module_init(neo1973_init); +#ifdef CONFIG_MACH_NEO1973_GTA02 +static void __exit neo1973_gta02_exit(void) +{ + gpio_free(GTA02_GPIO_HP_IN); + gpio_free(GTA02_GPIO_AMP_SHUT); +} +#else +static inline void neo1973_gta02_exit(void) {} +#endif + +static void __exit neo1973_exit(void) +{ + snd_soc_unregister_dai(&bt_dai); + platform_device_unregister(neo1973_snd_device); + + if (machine_is_neo1973_gta01()) + neo1973_gta01_exit(); + else + neo1973_gta02_exit(); +} module_exit(neo1973_exit); /* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); +MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org"); +MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 devices"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1d70829464e..5eabb711760 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -28,6 +28,7 @@ #include <linux/bitops.h> #include <linux/debugfs.h> #include <linux/platform_device.h> +#include <sound/ac97_codec.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -619,8 +620,9 @@ static struct snd_pcm_ops soc_pcm_ops = { #ifdef CONFIG_PM /* powers down audio subsystem for suspend */ -static int soc_suspend(struct platform_device *pdev, pm_message_t state) +static int soc_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_card *card = socdev->card; struct snd_soc_platform *platform = card->platform; @@ -656,7 +658,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) snd_pcm_suspend_all(card->dai_link[i].pcm); if (card->suspend_pre) - card->suspend_pre(pdev, state); + card->suspend_pre(pdev, PMSG_SUSPEND); for (i = 0; i < card->num_links; i++) { struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; @@ -682,7 +684,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) } if (codec_dev->suspend) - codec_dev->suspend(pdev, state); + codec_dev->suspend(pdev, PMSG_SUSPEND); for (i = 0; i < card->num_links; i++) { struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; @@ -691,7 +693,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) } if (card->suspend_post) - card->suspend_post(pdev, state); + card->suspend_post(pdev, PMSG_SUSPEND); return 0; } @@ -765,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct platform_device *pdev) +static int soc_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_card *card = socdev->card; struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai; @@ -788,6 +791,44 @@ static int soc_resume(struct platform_device *pdev) return 0; } +/** + * snd_soc_suspend_device: Notify core of device suspend + * + * @dev: Device being suspended. + * + * In order to ensure that the entire audio subsystem is suspended in a + * coordinated fashion ASoC devices should suspend themselves when + * called by ASoC. When the standard kernel suspend process asks the + * device to suspend it should call this function to initiate a suspend + * of the entire ASoC card. + * + * \note Currently this function is stubbed out. + */ +int snd_soc_suspend_device(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_suspend_device); + +/** + * snd_soc_resume_device: Notify core of device resume + * + * @dev: Device being resumed. + * + * In order to ensure that the entire audio subsystem is resumed in a + * coordinated fashion ASoC devices should resume themselves when called + * by ASoC. When the standard kernel resume process asks the device + * to resume it should call this function. Once all the components of + * the card have notified that they are ready to be resumed the card + * will be resumed. + * + * \note Currently this function is stubbed out. + */ +int snd_soc_resume_device(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_resume_device); #else #define soc_suspend NULL #define soc_resume NULL @@ -981,16 +1022,39 @@ static int soc_remove(struct platform_device *pdev) return 0; } +static int soc_poweroff(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_card *card = socdev->card; + + if (!card->instantiated) + return 0; + + /* Flush out pmdown_time work - we actually do want to run it + * now, we're shutting down so no imminent restart. */ + run_delayed_work(&card->delayed_work); + + snd_soc_dapm_shutdown(socdev); + + return 0; +} + +static struct dev_pm_ops soc_pm_ops = { + .suspend = soc_suspend, + .resume = soc_resume, + .poweroff = soc_poweroff, +}; + /* ASoC platform driver */ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, + .pm = &soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, - .suspend = soc_suspend, - .resume = soc_resume, }; /* create a new pcm */ @@ -1062,6 +1126,23 @@ static int soc_new_pcm(struct snd_soc_device *socdev, return ret; } +/** + * snd_soc_codec_volatile_register: Report if a register is volatile. + * + * @codec: CODEC to query. + * @reg: Register to query. + * + * Boolean function indiciating if a CODEC register is volatile. + */ +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +{ + if (codec->volatile_register) + return codec->volatile_register(reg); + else + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); + /* codec register dump */ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) { @@ -1264,10 +1345,10 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); * Returns 1 for change else 0. */ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, - unsigned short mask, unsigned short value) + unsigned int mask, unsigned int value) { int change; - unsigned short old, new; + unsigned int old, new; mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); @@ -1294,10 +1375,10 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits); * Returns 1 for change else 0. */ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, - unsigned short mask, unsigned short value) + unsigned int mask, unsigned int value) { int change; - unsigned short old, new; + unsigned int old, new; mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); @@ -1381,8 +1462,11 @@ int snd_soc_init_card(struct snd_soc_device *socdev) continue; } } - if (card->dai_link[i].codec_dai->ac97_control) + if (card->dai_link[i].codec_dai->ac97_control) { ac97 = 1; + snd_ac97_dev_add_pdata(codec->ac97, + card->dai_link[i].cpu_dai->ac97_pdata); + } } snprintf(codec->card->shortname, sizeof(codec->card->shortname), "%s", card->name); @@ -1586,7 +1670,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val, bitmask; + unsigned int val, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1615,8 +1699,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val; - unsigned short mask, bitmask; + unsigned int val; + unsigned int mask, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1652,7 +1736,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short reg_val, val, mux; + unsigned int reg_val, val, mux; reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; @@ -1691,8 +1775,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val; - unsigned short mask; + unsigned int val; + unsigned int mask; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -1852,7 +1936,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned short val, val2, val_mask; + unsigned int val, val2, val_mask; val = (ucontrol->value.integer.value[0] & mask); if (invert) @@ -1918,7 +2002,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; int max = mc->max; - unsigned int mask = (1<<fls(max))-1; + unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; ucontrol->value.integer.value[0] = @@ -1958,7 +2042,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; int err; - unsigned short val, val2, val_mask; + unsigned int val, val2, val_mask; val_mask = mask << shift; val = (ucontrol->value.integer.value[0] & mask); @@ -2050,7 +2134,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; int min = mc->min; - unsigned short val; + unsigned int val; val = (ucontrol->value.integer.value[0]+min) & 0xff; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 21c69074aa1..c68c204a48a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -52,19 +52,37 @@ /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { - snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, - snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, - snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, - snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, - snd_soc_dapm_post + [snd_soc_dapm_pre] = 0, + [snd_soc_dapm_supply] = 1, + [snd_soc_dapm_micbias] = 2, + [snd_soc_dapm_mic] = 3, + [snd_soc_dapm_mux] = 4, + [snd_soc_dapm_value_mux] = 4, + [snd_soc_dapm_dac] = 5, + [snd_soc_dapm_mixer] = 6, + [snd_soc_dapm_mixer_named_ctl] = 6, + [snd_soc_dapm_pga] = 7, + [snd_soc_dapm_adc] = 8, + [snd_soc_dapm_hp] = 9, + [snd_soc_dapm_spk] = 10, + [snd_soc_dapm_post] = 11, }; static int dapm_down_seq[] = { - snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, - snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, - snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, - snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, - snd_soc_dapm_post + [snd_soc_dapm_pre] = 0, + [snd_soc_dapm_adc] = 1, + [snd_soc_dapm_hp] = 2, + [snd_soc_dapm_spk] = 3, + [snd_soc_dapm_pga] = 4, + [snd_soc_dapm_mixer_named_ctl] = 5, + [snd_soc_dapm_mixer] = 5, + [snd_soc_dapm_dac] = 6, + [snd_soc_dapm_mic] = 7, + [snd_soc_dapm_micbias] = 8, + [snd_soc_dapm_mux] = 9, + [snd_soc_dapm_value_mux] = 9, + [snd_soc_dapm_supply] = 10, + [snd_soc_dapm_post] = 11, }; static void pop_wait(u32 pop_time) @@ -268,7 +286,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec, static int dapm_update_bits(struct snd_soc_dapm_widget *widget) { int change, power; - unsigned short old, new; + unsigned int old, new; struct snd_soc_codec *codec = widget->codec; /* check for valid widgets */ @@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) return power; } -/* - * Scan a single DAPM widget for a complete audio path and update the - * power status appropriately. - */ -static int dapm_power_widget(struct snd_soc_codec *codec, int event, - struct snd_soc_dapm_widget *w) +static int dapm_seq_compare(struct snd_soc_dapm_widget *a, + struct snd_soc_dapm_widget *b, + int sort[]) { - int ret; + if (sort[a->id] != sort[b->id]) + return sort[a->id] - sort[b->id]; + if (a->reg != b->reg) + return a->reg - b->reg; - switch (w->id) { - case snd_soc_dapm_pre: - if (!w->event) - return 0; + return 0; +} - if (event == SND_SOC_DAPM_STREAM_START) { - ret = w->event(w, - NULL, SND_SOC_DAPM_PRE_PMU); +/* Insert a widget in order into a DAPM power sequence. */ +static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, + struct list_head *list, + int sort[]) +{ + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, list, power_list) + if (dapm_seq_compare(new_widget, w, sort) < 0) { + list_add_tail(&new_widget->power_list, &w->power_list); + return; + } + + list_add_tail(&new_widget->power_list, list); +} + +/* Apply the coalesced changes from a DAPM sequence */ +static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, + struct list_head *pending) +{ + struct snd_soc_dapm_widget *w; + int reg, power, ret; + unsigned int value = 0; + unsigned int mask = 0; + unsigned int cur_mask; + + reg = list_first_entry(pending, struct snd_soc_dapm_widget, + power_list)->reg; + + list_for_each_entry(w, pending, power_list) { + cur_mask = 1 << w->shift; + BUG_ON(reg != w->reg); + + if (w->invert) + power = !w->power; + else + power = w->power; + + mask |= cur_mask; + if (power) + value |= cur_mask; + + pop_dbg(codec->pop_time, + "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", + w->name, reg, value, mask); + + /* power up pre event */ + if (w->power && w->event && + (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { + pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n", + w->name); + ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) - return ret; - } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, - NULL, SND_SOC_DAPM_PRE_PMD); + pr_err("%s: pre event failed: %d\n", + w->name, ret); + } + + /* power down pre event */ + if (!w->power && w->event && + (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { + pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n", + w->name); + ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) - return ret; + pr_err("%s: pre event failed: %d\n", + w->name, ret); } - return 0; - case snd_soc_dapm_post: - if (!w->event) - return 0; + /* Lower PGA volume to reduce pops */ + if (w->id == snd_soc_dapm_pga && !w->power) + dapm_set_pga(w, w->power); + } - if (event == SND_SOC_DAPM_STREAM_START) { + if (reg >= 0) { + pop_dbg(codec->pop_time, + "pop test : Applying 0x%x/0x%x to %x in %dms\n", + value, mask, reg, codec->pop_time); + pop_wait(codec->pop_time); + snd_soc_update_bits(codec, reg, mask, value); + } + + list_for_each_entry(w, pending, power_list) { + /* Raise PGA volume to reduce pops */ + if (w->id == snd_soc_dapm_pga && w->power) + dapm_set_pga(w, w->power); + + /* power up post event */ + if (w->power && w->event && + (w->event_flags & SND_SOC_DAPM_POST_PMU)) { + pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n", + w->name); ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) - return ret; - } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, - NULL, SND_SOC_DAPM_POST_PMD); + pr_err("%s: post event failed: %d\n", + w->name, ret); + } + + /* power down post event */ + if (!w->power && w->event && + (w->event_flags & SND_SOC_DAPM_POST_PMD)) { + pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n", + w->name); + ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) - return ret; + pr_err("%s: post event failed: %d\n", + w->name, ret); } - return 0; + } +} - default: - return dapm_generic_apply_power(w); +/* Apply a DAPM power sequence. + * + * We walk over a pre-sorted list of widgets to apply power to. In + * order to minimise the number of writes to the device required + * multiple widgets will be updated in a single write where possible. + * Currently anything that requires more than a single write is not + * handled. + */ +static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, + int event, int sort[]) +{ + struct snd_soc_dapm_widget *w, *n; + LIST_HEAD(pending); + int cur_sort = -1; + int cur_reg = SND_SOC_NOPM; + int ret; + + list_for_each_entry_safe(w, n, list, power_list) { + ret = 0; + + /* Do we need to apply any queued changes? */ + if (sort[w->id] != cur_sort || w->reg != cur_reg) { + if (!list_empty(&pending)) + dapm_seq_run_coalesced(codec, &pending); + + INIT_LIST_HEAD(&pending); + cur_sort = -1; + cur_reg = SND_SOC_NOPM; + } + + switch (w->id) { + case snd_soc_dapm_pre: + if (!w->event) + list_for_each_entry_safe_continue(w, n, list, + power_list); + + if (event == SND_SOC_DAPM_STREAM_START) + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMU); + else if (event == SND_SOC_DAPM_STREAM_STOP) + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMD); + break; + + case snd_soc_dapm_post: + if (!w->event) + list_for_each_entry_safe_continue(w, n, list, + power_list); + + if (event == SND_SOC_DAPM_STREAM_START) + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); + else if (event == SND_SOC_DAPM_STREAM_STOP) + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMD); + break; + + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_line: + case snd_soc_dapm_spk: + /* No register support currently */ + ret = dapm_generic_apply_power(w); + break; + + default: + /* Queue it up for application */ + cur_sort = sort[w->id]; + cur_reg = w->reg; + list_move(&w->power_list, &pending); + break; + } + + if (ret < 0) + pr_err("Failed to apply widget power: %d\n", + ret); } + + if (!list_empty(&pending)) + dapm_seq_run_coalesced(codec, &pending); } /* @@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) { struct snd_soc_device *socdev = codec->socdev; struct snd_soc_dapm_widget *w; + LIST_HEAD(up_list); + LIST_HEAD(down_list); int ret = 0; - int i, power; + int power; int sys_power = 0; - INIT_LIST_HEAD(&codec->up_list); - INIT_LIST_HEAD(&codec->down_list); - /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ list_for_each_entry(w, &codec->dapm_widgets, list) { switch (w->id) { case snd_soc_dapm_pre: - list_add_tail(&codec->down_list, &w->power_list); + dapm_seq_insert(w, &down_list, dapm_down_seq); break; case snd_soc_dapm_post: - list_add_tail(&codec->up_list, &w->power_list); + dapm_seq_insert(w, &up_list, dapm_up_seq); break; default: @@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue; if (power) - list_add_tail(&w->power_list, &codec->up_list); + dapm_seq_insert(w, &up_list, dapm_up_seq); else - list_add_tail(&w->power_list, - &codec->down_list); + dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = power; break; @@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) } /* Power down widgets first; try to avoid amplifying pops. */ - for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { - list_for_each_entry(w, &codec->down_list, power_list) { - /* is widget in stream order */ - if (w->id != dapm_down_seq[i]) - continue; - - ret = dapm_power_widget(codec, event, w); - if (ret != 0) - pr_err("Failed to power down %s: %d\n", - w->name, ret); - } - } + dapm_seq_run(codec, &down_list, event, dapm_down_seq); /* Now power up. */ - for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { - list_for_each_entry(w, &codec->up_list, power_list) { - /* is widget in stream order */ - if (w->id != dapm_up_seq[i]) - continue; - - ret = dapm_power_widget(codec, event, w); - if (ret != 0) - pr_err("Failed to power up %s: %d\n", - w->name, ret); - } - } + dapm_seq_run(codec, &up_list, event, dapm_up_seq); /* If we just powered the last thing off drop to standby bias */ if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { @@ -845,6 +997,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) pr_err("Failed to apply active bias: %d\n", ret); } + pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n", + codec->pop_time); + return 0; } @@ -1138,8 +1293,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, if (wsink->id == snd_soc_dapm_input) { if (wsource->id == snd_soc_dapm_micbias || wsource->id == snd_soc_dapm_mic || - wsink->id == snd_soc_dapm_line || - wsink->id == snd_soc_dapm_output) + wsource->id == snd_soc_dapm_line || + wsource->id == snd_soc_dapm_output) wsink->ext = 1; } if (wsource->id == snd_soc_dapm_output) { @@ -1372,7 +1527,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned short val, val2, val_mask; + unsigned int val, val2, val_mask; int ret; val = (ucontrol->value.integer.value[0] & mask); @@ -1436,7 +1591,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val, bitmask; + unsigned int val, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1464,8 +1619,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val, mux; - unsigned short mask, bitmask; + unsigned int val, mux; + unsigned int mask, bitmask; int ret = 0; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) @@ -1523,7 +1678,7 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short reg_val, val, mux; + unsigned int reg_val, val, mux; reg_val = snd_soc_read(widget->codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; @@ -1563,8 +1718,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val, mux; - unsigned short mask; + unsigned int val, mux; + unsigned int mask; int ret = 0; if (ucontrol->value.enumerated.item[0] > e->max - 1) @@ -1880,6 +2035,36 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) } EXPORT_SYMBOL_GPL(snd_soc_dapm_free); +/* + * snd_soc_dapm_shutdown - callback for system shutdown + */ +void snd_soc_dapm_shutdown(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->card->codec; + struct snd_soc_dapm_widget *w; + LIST_HEAD(down_list); + int powerdown = 0; + + list_for_each_entry(w, &codec->dapm_widgets, list) { + if (w->power) { + dapm_seq_insert(w, &down_list, dapm_down_seq); + w->power = 0; + powerdown = 1; + } + } + + /* If there were no widgets to power down we're already in + * standby. + */ + if (powerdown) { + snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE); + dapm_seq_run(codec, &down_list, 0, dapm_down_seq); + snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY); + } + + snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF); +} + /* Module information */ MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 28346fb2e70..4aa7d8f8ce7 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -73,14 +73,15 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) oldstatus = jack->status; jack->status &= ~mask; - jack->status |= status; + jack->status |= status & mask; - /* The DAPM sync is expensive enough to be worth skipping */ - if (jack->status == oldstatus) + /* The DAPM sync is expensive enough to be worth skipping. + * However, empty mask means pin synchronization is desired. */ + if (mask && (jack->status == oldstatus)) goto out; list_for_each_entry(pin, &jack->pins, list) { - enable = pin->mask & status; + enable = pin->mask & jack->status; if (pin->invert) enable = !enable; @@ -228,8 +229,16 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto err; +#ifdef CONFIG_GPIO_SYSFS + /* Expose GPIO value over sysfs for diagnostic purposes */ + gpio_export(gpios[i].gpio, false); +#endif + INIT_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; + + /* Update initial jack status */ + snd_soc_jack_gpio_detect(&gpios[i]); } return 0; @@ -258,6 +267,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, int i; for (i = 0; i < count; i++) { +#ifdef CONFIG_GPIO_SYSFS + gpio_unexport(gpios[i].gpio); +#endif free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); gpio_free(gpios[i].gpio); gpios[i].jack = NULL; diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 938a58a5a24..efed64b8b02 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -297,15 +297,17 @@ static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, static bool filter(struct dma_chan *chan, void *param) { struct txx9aclc_dmadata *dmadata = param; - char devname[20 + 2]; /* FIXME: old BUS_ID_SIZE + 2 */ + char *devname; + bool found = false; - snprintf(devname, sizeof(devname), "%s.%d", dmadata->dma_res->name, + devname = kasprintf(GFP_KERNEL, "%s.%d", dmadata->dma_res->name, (int)dmadata->dma_res->start); if (strcmp(dev_name(chan->device->dev), devname) == 0) { chan->private = &dmadata->dma_slave; - return true; + found = true; } - return false; + kfree(devname); + return found; } static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, |