diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/Kconfig linux-2.6.28-karo/arch/arm/Kconfig --- linux-2.6.28/arch/arm/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -1268,6 +1268,8 @@ source "drivers/rtc/Kconfig" source "drivers/dma/Kconfig" +source "drivers/mxc/Kconfig" + source "drivers/dca/Kconfig" source "drivers/auxdisplay/Kconfig" diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/configs/karo_tx27_defconfig linux-2.6.28-karo/arch/arm/configs/karo_tx27_defconfig --- linux-2.6.28/arch/arm/configs/karo_tx27_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/configs/karo_tx27_defconfig 2009-03-12 16:52:26.000000000 +0100 @@ -0,0 +1,1799 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.28 +# Thu Mar 12 15:05:31 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_CGROUPS is not set +# CONFIG_GROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_COMPAT_BRK=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_AIO is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_CLASSIC_RCU=y +CONFIG_FREEZER=y + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +CONFIG_ARCH_MXC=y +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_MSM is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Freescale MXC Implementations +# +CONFIG_ARCH_MX2=y +# CONFIG_ARCH_MX3 is not set + +# +# MX2 family CPU support +# +CONFIG_MACH_MX27=y + +# +# MX2 Platforms +# +# CONFIG_MACH_MX27ADS is not set +# CONFIG_MACH_PCM038 is not set +CONFIG_MACH_TX27=y +CONFIG_BASE_CLK_26MHz=y +# CONFIG_KARO_DEBUG is not set +CONFIG_MXC_EMMA=y +# CONFIG_MXC_IRQ_PRIOR is not set +CONFIG_MXC_ULPI=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_NOIFAR=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_OUTER_CACHE is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_FLATMEM_HAS_HOLES=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_UNEVICTABLE_LRU=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/mtdblock1 rootfstype=jffs2 console=ttymxc0,115200 ro panic=5" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_APM_EMULATION=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_PHONET is not set +CONFIG_WIRELESS=y +CONFIG_CFG80211=m +CONFIG_NL80211=y +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_MAC80211=m + +# +# Rate control algorithm selection +# +CONFIG_MAC80211_RC_PID=y +# CONFIG_MAC80211_RC_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT_PID=y +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT="pid" +# CONFIG_MAC80211_MESH is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT_WEP=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-5 +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_REDBOOT_PARTS_READONLY=y +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_MXC_FLASH_BBT=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +CONFIG_MTD_NAND_MXC=y +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=m +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +CONFIG_SMSC_PHY=y +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_SMC911X is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +CONFIG_FEC=y +# CONFIG_FEC2 is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +# CONFIG_LIBERTAS_SDIO is not set +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_P54_COMMON is not set +# CONFIG_IWLWIFI_LEDS is not set +# CONFIG_HOSTAP is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_ZD1211RW is not set +# CONFIG_RT2X00 is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=m +# CONFIG_USB_NET_AX8817X is not set +CONFIG_USB_NET_CDCETHER=m +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET=m +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +# CONFIG_USB_BELKIN is not set +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_INPUT_APMPOWER is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_GPIO=m +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_USB_EGALAX=y +# CONFIG_TOUCHSCREEN_USB_PANJIT is not set +# CONFIG_TOUCHSCREEN_USB_3M is not set +# CONFIG_TOUCHSCREEN_USB_ITM is not set +# CONFIG_TOUCHSCREEN_USB_ETURBO is not set +# CONFIG_TOUCHSCREEN_USB_GUNZE is not set +# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set +CONFIG_TOUCHSCREEN_USB_IRTOUCH=y +CONFIG_TOUCHSCREEN_USB_IDEALTEK=y +CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH=y +CONFIG_TOUCHSCREEN_USB_GOTOP=y +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +# CONFIG_CONSOLE_TRANSLATIONS is not set +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=m + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +CONFIG_I2C_GPIO=m +CONFIG_I2C_MXC=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +CONFIG_AT24=m +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_TPS65010 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_BITBANG=m +CONFIG_SPI_MXC=m +# CONFIG_SPI_MXC_TEST_LOOPBACK is not set +CONFIG_SPI_MXC_SELECT1=y +# CONFIG_SPI_MXC_SELECT2 is not set +# CONFIG_SPI_MXC_SELECT3 is not set +CONFIG_SPI_MXC_REV0=y +# CONFIG_SPI_MXC_REV4 is not set +# CONFIG_SPI_MXC_REV5 is not set +# CONFIG_SPI_MXC_REV7 is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_AT25=m +CONFIG_SPI_SPIDEV=m +# CONFIG_SPI_TLE62X0 is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +CONFIG_W1=m +CONFIG_W1_CON=y + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +CONFIG_W1_MASTER_MXC=m +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=m +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_APM_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_BQ27x00 is not set +CONFIG_LP3972=m +CONFIG_HWMON=m +# CONFIG_HWMON_VID 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_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +CONFIG_SENSORS_LM77=m +# 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_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_VT1211 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_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2_COMMON=m +# CONFIG_VIDEO_ALLOW_V4L1 is not set +# CONFIG_VIDEO_V4L1_COMPAT is not set +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=m + +# +# Multimedia drivers +# +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=m +CONFIG_MEDIA_TUNER_CUSTOMIZE=y +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +CONFIG_VIDEO_V4L2=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_DMA_CONTIG=m +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders/decoders and other helper chips +# + +# +# Audio decoders +# +# CONFIG_VIDEO_TVAUDIO 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_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# Video decoders +# +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_TVP5150 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_VIVI is not set +CONFIG_VIDEO_MXC_CAMERA=m + +# +# MXC Camera/V4L2 PRP Features support +# +# CONFIG_MXC_CAMERA_MICRON111 is not set +# CONFIG_MXC_CAMERA_MC521DA is not set +# CONFIG_MXC_CAMERA_OV2640 is not set +CONFIG_VIDEO_MXC_OUTPUT=m +# CONFIG_VIDEO_MXC_OUTPUT_DEBUG is not set +CONFIG_VIDEO_MXC_EMMA_OUTPUT=m +# CONFIG_VIDEO_MXC_OPL is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +CONFIG_SOC_CAMERA=m +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +CONFIG_SOC_CAMERA_PLATFORM=m +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_V4L_USB_DRIVERS is not set +CONFIG_VIDEO_MX27=m +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +CONFIG_FB_IMX=y +# CONFIG_FB_UVESA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=m +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=m +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set + +# +# Special HID drivers +# +# CONFIG_HID_COMPAT is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_BRIGHT is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DELL is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MXC=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set + +# +# Enable Host or Gadget support to see Inventra options +# + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_EZUSB is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +CONFIG_USB_SERIAL_BELKIN=m +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 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_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# 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_PL2303=m +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +CONFIG_USB_TEST=m +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +# CONFIG_USB_GADGET is not set +CONFIG_MMC=m +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=m +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MXC=m +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_PCA955X is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +CONFIG_RTC_DRV_DS13XX=y +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_MXC=y +# CONFIG_DMADEVICES is not set +CONFIG_DRIVERS_MXC=y + +# +# MXC VPU(Video Processing Unit) support +# +CONFIG_MXC_VPU=m +# CONFIG_MXC_VPU_DEBUG is not set +# CONFIG_REGULATOR is not set +# CONFIG_UIO is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=m +CONFIG_FS_MBCACHE=m +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_REGISTER_V4 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=m +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=m +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=m +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=m +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DETECT_SOFTLOCKUP is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_HAVE_FUNCTION_TRACER=y + +# +# Tracers +# +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=m + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_ALGAPI2=m +CONFIG_CRYPTO_AEAD2=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLKCIPHER2=m +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_HASH2=m +CONFIG_CRYPTO_RNG2=m +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MANAGER2=m +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_MICHAEL_MIC=m +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=m +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=m +CONFIG_CRC16=m +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-imx/include/mach/imx-regs.h linux-2.6.28-karo/arch/arm/mach-imx/include/mach/imx-regs.h --- linux-2.6.28/arch/arm/mach-imx/include/mach/imx-regs.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-imx/include/mach/imx-regs.h 2009-03-11 13:16:24.000000000 +0100 @@ -416,7 +416,8 @@ #define PCR_BPIX_4 (2<<25) #define PCR_BPIX_8 (3<<25) #define PCR_BPIX_12 (4<<25) -#define PCR_BPIX_16 (4<<25) +#define PCR_BPIX_16 (5<<25) +#define PCR_BPIX_18 (6<<25) #define PCR_PIXPOL (1<<24) #define PCR_FLMPOL (1<<23) #define PCR_LPPOL (1<<22) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-imx/include/mach/imxfb.h linux-2.6.28-karo/arch/arm/mach-imx/include/mach/imxfb.h --- linux-2.6.28/arch/arm/mach-imx/include/mach/imxfb.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-imx/include/mach/imxfb.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,37 +0,0 @@ -/* - * This structure describes the machine which we are running on. - */ -struct imxfb_mach_info { - u_long pixclock; - - u_short xres; - u_short yres; - - u_int nonstd; - u_char bpp; - u_char hsync_len; - u_char left_margin; - u_char right_margin; - - u_char vsync_len; - u_char upper_margin; - u_char lower_margin; - u_char sync; - - u_int cmap_greyscale:1, - cmap_inverse:1, - cmap_static:1, - unused:29; - - u_int pcr; - u_int pwmr; - u_int lscr1; - u_int dmacr; - - u_char * fixed_screen_cpu; - dma_addr_t fixed_screen_dma; - - void (*lcd_power)(int); - void (*backlight_power)(int); -}; -void set_imx_fb_info(struct imxfb_mach_info *hard_imx_fb_info); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/Kconfig linux-2.6.28-karo/arch/arm/mach-mx2/Kconfig --- linux-2.6.28/arch/arm/mach-mx2/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -24,6 +24,28 @@ config MACH_PCM038 Include support for phyCORE-i.MX27 (aka pcm038) platform. This includes specific configurations for the module and its peripherals. +config MACH_TX27 + bool "Support Ka-Ro electronics TX27 module" + depends on MACH_MX27 + select MXC_ULPI + help + Include support for Ka-Ro TX27 processor module + +config BASE_CLK_26MHz + bool "Use external 26MHz oscillator" + depends on MACH_TX27 + default yes + help + Unselect this option to switch off the external 26MHz oscillator + and use the 32.768kHz reference and i.MX27 internal FPM + +config KARO_DEBUG + bool "Enable Ka-Ro specific debug messages" + depends on MACH_TX27 + help + Compile the architecture specific files with -DDEBUG to enable + additional debug messages + choice prompt "Baseboard" depends on MACH_PCM038 @@ -32,6 +54,7 @@ choice config MACH_PCM970_BASEBOARD prompt "PHYTEC PCM970 development board" bool + select MXC_ULPI help This adds board specific devices that can be found on Phytec's PCM970 evaluation board. diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/Makefile linux-2.6.28-karo/arch/arm/mach-mx2/Makefile --- linux-2.6.28/arch/arm/mach-mx2/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -2,13 +2,19 @@ # Makefile for the linux kernel. # +ifeq ($(CONFIG_KARO_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + # Object file lists. obj-y := system.o generic.o devices.o serial.o obj-$(CONFIG_MACH_MX27) += cpu_imx27.o obj-$(CONFIG_MACH_MX27) += clock_imx27.o +obj-$(CONFIG_PM) += pm.o mxc_pm.o -obj-$(CONFIG_MACH_MX27ADS) += mx27ads.o -obj-$(CONFIG_MACH_PCM038) += pcm038.o -obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o +obj-$(CONFIG_MACH_MX27ADS) += mx27ads.o +obj-$(CONFIG_MACH_PCM038) += pcm038.o +obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o +obj-$(CONFIG_MACH_TX27) += karo-tx27.o tx27_gpio.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/clock_imx27.c linux-2.6.28-karo/arch/arm/mach-mx2/clock_imx27.c --- linux-2.6.28/arch/arm/mach-mx2/clock_imx27.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/clock_imx27.c 2009-03-11 13:16:24.000000000 +0100 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -233,9 +234,23 @@ static void _clk_mstick1_disable(struct _clk_pccr10_disable(CCM_PCCR1_MSHC_BAUD_MASK, CCM_PCCR0_MSHC_MASK); } -#define CSCR() (__raw_readl(CCM_CSCR)) -#define PCDR0() (__raw_readl(CCM_PCDR0)) -#define PCDR1() (__raw_readl(CCM_PCDR1)) +static int _clk_wdog_enable(struct clk *clk) +{ + u32 cscr; + + cscr = __raw_readl(CCM_CSCR); + if (!(cscr & CCM_CSCR_FPM)) { + cscr |= CCM_CSCR_FPM; + __raw_writel(cscr, CCM_CSCR); + /* wait for FPM startup */ + udelay(90); + } + return _clk_enable(clk); +} + +#define CSCR() __raw_readl(CCM_CSCR) +#define PCDR0() __raw_readl(CCM_PCDR0) +#define PCDR1() __raw_readl(CCM_PCDR1) static int _clk_cpu_set_parent(struct clk *clk, struct clk *parent) { @@ -288,7 +303,7 @@ static int _clk_cpu_set_rate(struct clk div = parent_rate / rate; - if (div > 4 || div < 1 || ((parent_rate / div) != rate)) + if (div > 4 || div < 1 || ((parent_rate / div / 100) != rate / 100)) return -EINVAL; div--; @@ -297,10 +312,13 @@ static int _clk_cpu_set_rate(struct clk if (mx27_revision() >= CHIP_REV_2_0) { reg &= ~CCM_CSCR_ARM_MASK; reg |= div << CCM_CSCR_ARM_OFFSET; +#if 0 + // FIXME: What's this? This breaks _clk_spll_enable()! reg &= ~0x06; +#endif __raw_writel(reg | 0x80000000, CCM_CSCR); } else { - printk(KERN_ERR "Cant set CPU frequency!\n"); + printk(KERN_ERR "Can't set CPU frequency!\n"); } return 0; @@ -361,6 +379,24 @@ static unsigned long _clk_usb_recalc(str return parent_rate / (usb_pdf + 1U); } +static int _clk_usb_set_rate(struct clk *clk, unsigned long rate) +{ + u32 div, reg; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + + if (div > 8 || div < 1 || ((parent_rate / div) != rate)) { + return -EINVAL; + } + div--; + + reg = (CSCR() & ~CCM_CSCR_USB_MASK) | (div << CCM_CSCR_USB_OFFSET); + __raw_writel(reg, CCM_CSCR); + return 0; +} + static unsigned long _clk_ssi1_recalc(struct clk *clk) { unsigned long ssi1_pdf; @@ -488,10 +524,13 @@ static unsigned long get_mpll_clk(struct { uint32_t reg; unsigned long ref_clk; - unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0; + int mfi, mfn, mfd, pdf; unsigned long long temp; + int sign = 1; ref_clk = clk_get_rate(clk->parent); + if (clk->parent == &ckil_clk) + ref_clk *= 1024; reg = __raw_readl(CCM_MPCTL0); pdf = (reg & CCM_MPCTL0_PD_MASK) >> CCM_MPCTL0_PD_OFFSET; @@ -500,9 +539,13 @@ static unsigned long get_mpll_clk(struct mfn = (reg & CCM_MPCTL0_MFN_MASK) >> CCM_MPCTL0_MFN_OFFSET; mfi = (mfi <= 5) ? 5 : mfi; + if (mfn >= 512) { + mfn = 1024 - mfn; + sign = -1; + } temp = 2LL * ref_clk * mfn; do_div(temp, mfd + 1); - temp = 2LL * ref_clk * mfi + temp; + temp = temp * sign + 2LL * ref_clk * mfi; do_div(temp, pdf + 1); return (unsigned long)temp; @@ -555,10 +598,13 @@ static unsigned long get_spll_clk(struct { uint32_t reg; unsigned long ref_clk; - unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0; - unsigned long long temp; + int mfi, mfn, mfd, pdf; + u64 temp; + int sign = 1; ref_clk = clk_get_rate(clk->parent); + if (clk->parent == &ckil_clk) + ref_clk *= 1024; reg = __raw_readl(CCM_SPCTL0); /*TODO: This is TO2 Bug */ @@ -571,9 +617,13 @@ static unsigned long get_spll_clk(struct mfn = (reg & CCM_SPCTL0_MFN_MASK) >> CCM_SPCTL0_MFN_OFFSET; mfi = (mfi <= 5) ? 5 : mfi; + if (mfn >= 512) { + sign = -1; + mfn = 1024 - mfn; + } temp = 2LL * ref_clk * mfn; do_div(temp, mfd + 1); - temp = 2LL * ref_clk * mfi + temp; + temp = temp * sign + 2LL * ref_clk * mfi; do_div(temp, pdf + 1); return (unsigned long)temp; @@ -1079,6 +1129,7 @@ static struct clk usb_clk[] = { .name = "usb_clk", .parent = &spll_clk, .get_rate = _clk_usb_recalc, + .set_rate = _clk_usb_set_rate, .enable = _clk_enable, .enable_reg = CCM_PCCR1, .enable_shift = CCM_PCCR1_USBOTG_OFFSET, @@ -1234,7 +1285,7 @@ static struct clk mstick1_clk = { static struct clk wdog_clk = { .name = "wdog_clk", .parent = &ipg_clk, - .enable = _clk_enable, + .enable = _clk_wdog_enable, .enable_reg = CCM_PCCR1, .enable_shift = CCM_PCCR1_WDT_OFFSET, .disable = _clk_disable, @@ -1537,26 +1588,22 @@ void __init change_external_low_referenc external_low_reference = new_ref; } -unsigned long __init clk_early_get_timer_rate(void) -{ - return clk_get_rate(&per_clk[0]); -} - static void __init probe_mxc_clocks(void) { int i; + u32 cscr = CSCR(); if (mx27_revision() >= CHIP_REV_2_0) { - if (CSCR() & 0x8000) + if (cscr & 0x8000) cpu_clk.parent = &mpll_main_clk[0]; - if (!(CSCR() & 0x00800000)) + if (!(cscr & 0x00800000)) ssi2_clk[0].parent = &spll_clk; - if (!(CSCR() & 0x00400000)) + if (!(cscr & 0x00400000)) ssi1_clk[0].parent = &spll_clk; - if (!(CSCR() & 0x00200000)) + if (!(cscr & 0x00200000)) vpu_clk.parent = &spll_clk; } else { cpu_clk.parent = &mpll_clk; @@ -1618,7 +1665,7 @@ int __init mxc_clocks_init(unsigned long clk_enable(&gpio_clk); clk_enable(&iim_clk); clk_enable(&gpt1_clk[0]); -#ifdef CONFIG_DEBUG_LL_CONSOLE +#ifdef CONFIG_DEBUG_LL clk_enable(&uart1_clk[0]); #endif return 0; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/crm_regs.h linux-2.6.28-karo/arch/arm/mach-mx2/crm_regs.h --- linux-2.6.28/arch/arm/mach-mx2/crm_regs.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/crm_regs.h 2009-03-11 13:16:24.000000000 +0100 @@ -22,252 +22,276 @@ #include +#define SYSCTRL_BASE IO_ADDRESS(SYSCTRL_BASE_ADDR) + /* Register offsets */ -#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) -#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) -#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) -#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) -#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) -#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) -#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) -#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) -#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) -#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) -#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) -#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) -#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) -#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) - -#define CCM_CSCR_USB_OFFSET 28 -#define CCM_CSCR_USB_MASK (0x7 << 28) -#define CCM_CSCR_SD_OFFSET 24 -#define CCM_CSCR_SD_MASK (0x3 << 24) -#define CCM_CSCR_SSI2 (1 << 23) -#define CCM_CSCR_SSI2_OFFSET 23 -#define CCM_CSCR_SSI1 (1 << 22) -#define CCM_CSCR_SSI1_OFFSET 22 -#define CCM_CSCR_VPU (1 << 21) -#define CCM_CSCR_VPU_OFFSET 21 -#define CCM_CSCR_MSHC (1 << 20) -#define CCM_CSCR_SPLLRES (1 << 19) -#define CCM_CSCR_MPLLRES (1 << 18) -#define CCM_CSCR_SP (1 << 17) -#define CCM_CSCR_MCU (1 << 16) +#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) +#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) +#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) +#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) +#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) +#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) +#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) +#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) +#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) +#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) +#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) +#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) +#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) +#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) +#define MXC_CCM_PMCR0 (SYSCTRL_BASE + 0x60) +#define MXC_CCM_DCVR0 (SYSCTRL_BASE + 0x64) +#define MXC_CCM_DCVR1 (SYSCTRL_BASE + 0x68) +#define MXC_CCM_DCVR2 (SYSCTRL_BASE + 0x72) +#define MXC_CCM_DCVR3 (SYSCTRL_BASE + 0x76) +#define MXC_CCM_PMCR0_DPTEN 0x00000001 +#define MXC_CCM_DIE 0x00000002 +#define MXC_CCM_DIM 0x0000000C +#define MXC_CCM_DCR 0x00000200 +#define MXC_CCM_PMCR0_DRCE0 0x00000010 +#define MXC_CCM_PMCR0_DRCE1 0x00000020 +#define MXC_CCM_PMCR0_DRCE2 0x00000040 +#define MXC_CCM_PMCR0_DRCE3 0x00000080 +#define MXC_CCM_PMCR0_PTVAIM MXC_CCM_DIM + +#define CCM_CSCR_USB_OFFSET 28 +#define CCM_CSCR_USB_MASK (0x7 << CCM_CSCR_USB_OFFSET) +#define CCM_CSCR_SD_OFFSET 24 +#define CCM_CSCR_SD_MASK (0x3 << CCM_CSCR_SD_OFFSET) +#define CCM_CSCR_SSI2_OFFSET 23 +#define CCM_CSCR_SSI2 (1 << CCM_CSCR_SSI2_OFFSET) +#define CCM_CSCR_SSI1_OFFSET 22 +#define CCM_CSCR_SSI1 (1 << CCM_CSCR_SSI1_OFFSET) +#define CCM_CSCR_VPU_OFFSET 21 +#define CCM_CSCR_VPU (1 << CCM_CSCR_VPU_OFFSET) +#define CCM_CSCR_MSHC (1 << 20) +#define CCM_CSCR_SPLLRES (1 << 19) +#define CCM_CSCR_MPLLRES (1 << 18) +#define CCM_CSCR_SP (1 << 17) +#define CCM_CSCR_MCU (1 << 16) /* CCM_CSCR_ARM_xxx just be avaliable on i.MX27 TO2*/ -#define CCM_CSCR_ARM_SRC (1 << 15) -#define CCM_CSCR_ARM_OFFSET 12 -#define CCM_CSCR_ARM_MASK (0x3 << 12) +#define CCM_CSCR_ARM_SRC (1 << 15) +#define CCM_CSCR_ARM_OFFSET 12 +#define CCM_CSCR_ARM_MASK (0x3 << CCM_CSCR_ARM_OFFSET) /* CCM_CSCR_ARM_xxx just be avaliable on i.MX27 TO2*/ -#define CCM_CSCR_PRESC_OFFSET 13 -#define CCM_CSCR_PRESC_MASK (0x7 << 13) -#define CCM_CSCR_BCLK_OFFSET 9 -#define CCM_CSCR_BCLK_MASK (0xf << 9) -#define CCM_CSCR_IPDIV_OFFSET 8 -#define CCM_CSCR_IPDIV (1 << 8) +#define CCM_CSCR_PRESC_OFFSET 13 +#define CCM_CSCR_PRESC_MASK (0x7 << CCM_CSCR_PRESC_OFFSET) +#define CCM_CSCR_BCLK_OFFSET 9 +#define CCM_CSCR_BCLK_MASK (0xf << CCM_CSCR_BCLK_OFFSET) +#define CCM_CSCR_IPDIV_OFFSET 8 +#define CCM_CSCR_IPDIV (1 << CCM_CSCR_IPDIV_OFFSET) /* CCM_CSCR_AHB_xxx just be avaliable on i.MX27 TO2*/ -#define CCM_CSCR_AHB_OFFSET 8 -#define CCM_CSCR_AHB_MASK (0x3 << 8) +#define CCM_CSCR_AHB_OFFSET 8 +#define CCM_CSCR_AHB_MASK (0x3 << CCM_CSCR_AHB_OFFSET) /* CCM_CSCR_AHB_xxx just be avaliable on i.MX27 TO2*/ -#define CCM_CSCR_OSC26MDIV (1 << 4) -#define CCM_CSCR_OSC26M (1 << 3) -#define CCM_CSCR_FPM (1 << 2) -#define CCM_CSCR_SPEN (1 << 1) -#define CCM_CSCR_MPEN 1 - -#define CCM_MPCTL0_CPLM (1 << 31) -#define CCM_MPCTL0_PD_OFFSET 26 -#define CCM_MPCTL0_PD_MASK (0xf << 26) -#define CCM_MPCTL0_MFD_OFFSET 16 -#define CCM_MPCTL0_MFD_MASK (0x3ff << 16) -#define CCM_MPCTL0_MFI_OFFSET 10 -#define CCM_MPCTL0_MFI_MASK (0xf << 10) -#define CCM_MPCTL0_MFN_OFFSET 0 -#define CCM_MPCTL0_MFN_MASK 0x3ff - -#define CCM_MPCTL1_LF (1 << 15) -#define CCM_MPCTL1_BRMO (1 << 6) - -#define CCM_SPCTL0_CPLM (1 << 31) -#define CCM_SPCTL0_PD_OFFSET 26 -#define CCM_SPCTL0_PD_MASK (0xf << 26) -#define CCM_SPCTL0_MFD_OFFSET 16 -#define CCM_SPCTL0_MFD_MASK (0x3ff << 16) -#define CCM_SPCTL0_MFI_OFFSET 10 -#define CCM_SPCTL0_MFI_MASK (0xf << 10) -#define CCM_SPCTL0_MFN_OFFSET 0 -#define CCM_SPCTL0_MFN_MASK 0x3ff - -#define CCM_SPCTL1_LF (1 << 15) -#define CCM_SPCTL1_BRMO (1 << 6) - -#define CCM_OSC26MCTL_PEAK_OFFSET 16 -#define CCM_OSC26MCTL_PEAK_MASK (0x3 << 16) -#define CCM_OSC26MCTL_AGC_OFFSET 8 -#define CCM_OSC26MCTL_AGC_MASK (0x3f << 8) -#define CCM_OSC26MCTL_ANATEST_OFFSET 0 -#define CCM_OSC26MCTL_ANATEST_MASK 0x3f - -#define CCM_PCDR0_SSI2BAUDDIV_OFFSET 26 -#define CCM_PCDR0_SSI2BAUDDIV_MASK (0x3f << 26) -#define CCM_PCDR0_CLKO_EN 25 -#define CCM_PCDR0_CLKODIV_OFFSET 22 -#define CCM_PCDR0_CLKODIV_MASK (0x7 << 22) -#define CCM_PCDR0_SSI1BAUDDIV_OFFSET 16 -#define CCM_PCDR0_SSI1BAUDDIV_MASK (0x3f << 16) +#define CCM_CSCR_OSC26MDIV (1 << 4) +#define CCM_CSCR_OSC26M (1 << 3) +#define CCM_CSCR_FPM (1 << 2) +#define CCM_CSCR_SPEN (1 << 1) +#define CCM_CSCR_MPEN 1 + +#define CCM_MPCTL0_CPLM (1 << 31) +#define CCM_MPCTL0_PD_OFFSET 26 +#define CCM_MPCTL0_PD_MASK (0xf << CCM_MPCTL0_PD_OFFSET) +#define CCM_MPCTL0_PD_VAL(n) (((n) << CCM_MPCTL0_PD_OFFSET) & CCM_MPCTL0_PD_MASK) +#define CCM_MPCTL0_MFD_OFFSET 16 +#define CCM_MPCTL0_MFD_MASK (0x3ff << CCM_MPCTL0_MFD_OFFSET) +#define CCM_MPCTL0_MFD_VAL(n) (((n) << CCM_MPCTL0_MFD_OFFSET) & CCM_MPCTL0_MFD_MASK) +#define CCM_MPCTL0_MFI_OFFSET 10 +#define CCM_MPCTL0_MFI_MASK (0xf << CCM_MPCTL0_MFI_OFFSET) +#define CCM_MPCTL0_MFI_VAL(n) (((n) << CCM_MPCTL0_MFI_OFFSET) & CCM_MPCTL0_MFI_MASK) +#define CCM_MPCTL0_MFN_OFFSET 0 +#define CCM_MPCTL0_MFN_MASK (0x3ff << CCM_MPCTL0_MFN_OFFSET) +#define CCM_MPCTL0_MFN_VAL(n) (((n) << CCM_MPCTL0_MFN_OFFSET) & CCM_MPCTL0_MFN_MASK) + +#define CCM_MPCTL1_LF (1 << 15) +#define CCM_MPCTL1_BRMO (1 << 6) + +#define CCM_SPCTL0_CPLM (1 << 31) +#define CCM_SPCTL0_PD_OFFSET 26 +#define CCM_SPCTL0_PD_MASK (0xf << CCM_SPCTL0_PD_OFFSET) +#define CCM_SPCTL0_PD_VAL(n) (((n) << CCM_SPCTL0_PD_OFFSET) & CCM_SPCTL0_PD_MASK) +#define CCM_SPCTL0_MFD_OFFSET 16 +#define CCM_SPCTL0_MFD_MASK (0x3ff << CCM_SPCTL0_MFD_OFFSET) +#define CCM_SPCTL0_MFD_VAL(n) (((n) << CCM_SPCTL0_MFD_OFFSET) & CCM_SPCTL0_MFD_MASK) +#define CCM_SPCTL0_MFI_OFFSET 10 +#define CCM_SPCTL0_MFI_MASK (0xf << CCM_SPCTL0_MFI_OFFSET) +#define CCM_SPCTL0_MFI_VAL(n) (((n) << CCM_SPCTL0_MFI_OFFSET) & CCM_SPCTL0_MFI_MASK) +#define CCM_SPCTL0_MFN_OFFSET 0 +#define CCM_SPCTL0_MFN_MASK (0x3ff << CCM_SPCTL0_MFN_OFFSET) +#define CCM_SPCTL0_MFN_VAL(n) (((n) << CCM_SPCTL0_MFN_OFFSET) & CCM_SPCTL0_MFN_MASK) + +#define CCM_SPCTL1_LF (1 << 15) +#define CCM_SPCTL1_BRMO (1 << 6) + +#define CCM_OSC26MCTL_PEAK_OFFSET 16 +#define CCM_OSC26MCTL_PEAK_MASK (0x3 << CCM_OSC26MCTL_PEAK_OFFSET) +#define CCM_OSC26MCTL_AGC_OFFSET 8 +#define CCM_OSC26MCTL_AGC_MASK (0x3f << CCM_OSC26MCTL_AGC_OFFSET) +#define CCM_OSC26MCTL_ANATEST_OFFSET 0 +#define CCM_OSC26MCTL_ANATEST_MASK 0x3f + +#define CCM_PCDR0_SSI2BAUDDIV_OFFSET 26 +#define CCM_PCDR0_SSI2BAUDDIV_MASK (0x3f << CCM_PCDR0_SSI2BAUDDIV_OFFSET) +#define CCM_PCDR0_CLKO_EN 25 +#define CCM_PCDR0_CLKODIV_OFFSET 22 +#define CCM_PCDR0_CLKODIV_MASK (0x7 << CCM_PCDR0_CLKODIV_OFFSET) +#define CCM_PCDR0_SSI1BAUDDIV_OFFSET 16 +#define CCM_PCDR0_SSI1BAUDDIV_MASK (0x3f << CCM_PCDR0_SSI1BAUDDIV_OFFSET) /*The difinition for i.MX27 TO2*/ -#define CCM_PCDR0_VPUDIV2_OFFSET 10 -#define CCM_PCDR0_VPUDIV2_MASK (0x3f << 10) -#define CCM_PCDR0_NFCDIV2_OFFSET 6 -#define CCM_PCDR0_NFCDIV2_MASK (0xf << 6) -#define CCM_PCDR0_MSHCDIV2_MASK 0x3f +#define CCM_PCDR0_VPUDIV2_OFFSET 10 +#define CCM_PCDR0_VPUDIV2_MASK (0x3f << CCM_PCDR0_VPUDIV2_OFFSET) +#define CCM_PCDR0_NFCDIV2_OFFSET 6 +#define CCM_PCDR0_NFCDIV2_MASK (0xf << CCM_PCDR0_NFCDIV2_OFFSET) +#define CCM_PCDR0_MSHCDIV2_MASK 0x3f /*The difinition for i.MX27 TO2*/ -#define CCM_PCDR0_NFCDIV_OFFSET 12 -#define CCM_PCDR0_NFCDIV_MASK (0xf << 12) -#define CCM_PCDR0_VPUDIV_OFFSET 8 -#define CCM_PCDR0_VPUDIV_MASK (0xf << 8) -#define CCM_PCDR0_MSHCDIV_OFFSET 0 -#define CCM_PCDR0_MSHCDIV_MASK 0x1f - -#define CCM_PCDR1_PERDIV4_OFFSET 24 -#define CCM_PCDR1_PERDIV4_MASK (0x3f << 24) -#define CCM_PCDR1_PERDIV3_OFFSET 16 -#define CCM_PCDR1_PERDIV3_MASK (0x3f << 16) -#define CCM_PCDR1_PERDIV2_OFFSET 8 -#define CCM_PCDR1_PERDIV2_MASK (0x3f << 8) -#define CCM_PCDR1_PERDIV1_OFFSET 0 -#define CCM_PCDR1_PERDIV1_MASK 0x3f - -#define CCM_PCCR0_CSPI1_OFFSET 31 -#define CCM_PCCR0_CSPI1_MASK (1 << 31) -#define CCM_PCCR0_CSPI2_OFFSET 30 -#define CCM_PCCR0_CSPI2_MASK (1 << 30) -#define CCM_PCCR0_CSPI3_OFFSET 29 -#define CCM_PCCR0_CSPI3_MASK (1 << 29) -#define CCM_PCCR0_DMA_OFFSET 28 -#define CCM_PCCR0_DMA_MASK (1 << 28) -#define CCM_PCCR0_EMMA_OFFSET 27 -#define CCM_PCCR0_EMMA_MASK (1 << 27) -#define CCM_PCCR0_FEC_OFFSET 26 -#define CCM_PCCR0_FEC_MASK (1 << 26) -#define CCM_PCCR0_GPIO_OFFSET 25 -#define CCM_PCCR0_GPIO_MASK (1 << 25) -#define CCM_PCCR0_GPT1_OFFSET 24 -#define CCM_PCCR0_GPT1_MASK (1 << 24) -#define CCM_PCCR0_GPT2_OFFSET 23 -#define CCM_PCCR0_GPT2_MASK (1 << 23) -#define CCM_PCCR0_GPT3_OFFSET 22 -#define CCM_PCCR0_GPT3_MASK (1 << 22) -#define CCM_PCCR0_GPT4_OFFSET 21 -#define CCM_PCCR0_GPT4_MASK (1 << 21) -#define CCM_PCCR0_GPT5_OFFSET 20 -#define CCM_PCCR0_GPT5_MASK (1 << 20) -#define CCM_PCCR0_GPT6_OFFSET 19 -#define CCM_PCCR0_GPT6_MASK (1 << 19) -#define CCM_PCCR0_I2C1_OFFSET 18 -#define CCM_PCCR0_I2C1_MASK (1 << 18) -#define CCM_PCCR0_I2C2_OFFSET 17 -#define CCM_PCCR0_I2C2_MASK (1 << 17) -#define CCM_PCCR0_IIM_OFFSET 16 -#define CCM_PCCR0_IIM_MASK (1 << 16) -#define CCM_PCCR0_KPP_OFFSET 15 -#define CCM_PCCR0_KPP_MASK (1 << 15) -#define CCM_PCCR0_LCDC_OFFSET 14 -#define CCM_PCCR0_LCDC_MASK (1 << 14) -#define CCM_PCCR0_MSHC_OFFSET 13 -#define CCM_PCCR0_MSHC_MASK (1 << 13) -#define CCM_PCCR0_OWIRE_OFFSET 12 -#define CCM_PCCR0_OWIRE_MASK (1 << 12) -#define CCM_PCCR0_PWM_OFFSET 11 -#define CCM_PCCR0_PWM_MASK (1 << 11) -#define CCM_PCCR0_RTC_OFFSET 9 -#define CCM_PCCR0_RTC_MASK (1 << 9) -#define CCM_PCCR0_RTIC_OFFSET 8 -#define CCM_PCCR0_RTIC_MASK (1 << 8) -#define CCM_PCCR0_SAHARA_OFFSET 7 -#define CCM_PCCR0_SAHARA_MASK (1 << 7) -#define CCM_PCCR0_SCC_OFFSET 6 -#define CCM_PCCR0_SCC_MASK (1 << 6) -#define CCM_PCCR0_SDHC1_OFFSET 5 -#define CCM_PCCR0_SDHC1_MASK (1 << 5) -#define CCM_PCCR0_SDHC2_OFFSET 4 -#define CCM_PCCR0_SDHC2_MASK (1 << 4) -#define CCM_PCCR0_SDHC3_OFFSET 3 -#define CCM_PCCR0_SDHC3_MASK (1 << 3) -#define CCM_PCCR0_SLCDC_OFFSET 2 -#define CCM_PCCR0_SLCDC_MASK (1 << 2) -#define CCM_PCCR0_SSI1_IPG_OFFSET 1 -#define CCM_PCCR0_SSI1_IPG_MASK (1 << 1) -#define CCM_PCCR0_SSI2_IPG_OFFSET 0 -#define CCM_PCCR0_SSI2_IPG_MASK (1 << 0) - -#define CCM_PCCR1_UART1_OFFSET 31 -#define CCM_PCCR1_UART1_MASK (1 << 31) -#define CCM_PCCR1_UART2_OFFSET 30 -#define CCM_PCCR1_UART2_MASK (1 << 30) -#define CCM_PCCR1_UART3_OFFSET 29 -#define CCM_PCCR1_UART3_MASK (1 << 29) -#define CCM_PCCR1_UART4_OFFSET 28 -#define CCM_PCCR1_UART4_MASK (1 << 28) -#define CCM_PCCR1_UART5_OFFSET 27 -#define CCM_PCCR1_UART5_MASK (1 << 27) -#define CCM_PCCR1_UART6_OFFSET 26 -#define CCM_PCCR1_UART6_MASK (1 << 26) -#define CCM_PCCR1_USBOTG_OFFSET 25 -#define CCM_PCCR1_USBOTG_MASK (1 << 25) -#define CCM_PCCR1_WDT_OFFSET 24 -#define CCM_PCCR1_WDT_MASK (1 << 24) -#define CCM_PCCR1_HCLK_ATA_OFFSET 23 -#define CCM_PCCR1_HCLK_ATA_MASK (1 << 23) -#define CCM_PCCR1_HCLK_BROM_OFFSET 22 -#define CCM_PCCR1_HCLK_BROM_MASK (1 << 22) -#define CCM_PCCR1_HCLK_CSI_OFFSET 21 -#define CCM_PCCR1_HCLK_CSI_MASK (1 << 21) -#define CCM_PCCR1_HCLK_DMA_OFFSET 20 -#define CCM_PCCR1_HCLK_DMA_MASK (1 << 20) -#define CCM_PCCR1_HCLK_EMI_OFFSET 19 -#define CCM_PCCR1_HCLK_EMI_MASK (1 << 19) -#define CCM_PCCR1_HCLK_EMMA_OFFSET 18 -#define CCM_PCCR1_HCLK_EMMA_MASK (1 << 18) -#define CCM_PCCR1_HCLK_FEC_OFFSET 17 -#define CCM_PCCR1_HCLK_FEC_MASK (1 << 17) -#define CCM_PCCR1_HCLK_VPU_OFFSET 16 -#define CCM_PCCR1_HCLK_VPU_MASK (1 << 16) -#define CCM_PCCR1_HCLK_LCDC_OFFSET 15 -#define CCM_PCCR1_HCLK_LCDC_MASK (1 << 15) -#define CCM_PCCR1_HCLK_RTIC_OFFSET 14 -#define CCM_PCCR1_HCLK_RTIC_MASK (1 << 14) -#define CCM_PCCR1_HCLK_SAHARA_OFFSET 13 -#define CCM_PCCR1_HCLK_SAHARA_MASK (1 << 13) -#define CCM_PCCR1_HCLK_SLCDC_OFFSET 12 -#define CCM_PCCR1_HCLK_SLCDC_MASK (1 << 12) -#define CCM_PCCR1_HCLK_USBOTG_OFFSET 11 -#define CCM_PCCR1_HCLK_USBOTG_MASK (1 << 11) -#define CCM_PCCR1_PERCLK1_OFFSET 10 -#define CCM_PCCR1_PERCLK1_MASK (1 << 10) -#define CCM_PCCR1_PERCLK2_OFFSET 9 -#define CCM_PCCR1_PERCLK2_MASK (1 << 9) -#define CCM_PCCR1_PERCLK3_OFFSET 8 -#define CCM_PCCR1_PERCLK3_MASK (1 << 8) -#define CCM_PCCR1_PERCLK4_OFFSET 7 -#define CCM_PCCR1_PERCLK4_MASK (1 << 7) -#define CCM_PCCR1_VPU_BAUD_OFFSET 6 -#define CCM_PCCR1_VPU_BAUD_MASK (1 << 6) -#define CCM_PCCR1_SSI1_BAUD_OFFSET 5 -#define CCM_PCCR1_SSI1_BAUD_MASK (1 << 5) -#define CCM_PCCR1_SSI2_BAUD_OFFSET 4 -#define CCM_PCCR1_SSI2_BAUD_MASK (1 << 4) -#define CCM_PCCR1_NFC_BAUD_OFFSET 3 -#define CCM_PCCR1_NFC_BAUD_MASK (1 << 3) -#define CCM_PCCR1_MSHC_BAUD_OFFSET 2 -#define CCM_PCCR1_MSHC_BAUD_MASK (1 << 2) - -#define CCM_CCSR_32KSR (1 << 15) -#define CCM_CCSR_CLKMODE1 (1 << 9) -#define CCM_CCSR_CLKMODE0 (1 << 8) -#define CCM_CCSR_CLKOSEL_OFFSET 0 -#define CCM_CCSR_CLKOSEL_MASK 0x1f +#define CCM_PCDR0_NFCDIV_OFFSET 12 +#define CCM_PCDR0_NFCDIV_MASK (0xf << CCM_PCDR0_NFCDIV_OFFSET) +#define CCM_PCDR0_VPUDIV_OFFSET 8 +#define CCM_PCDR0_VPUDIV_MASK (0xf << CCM_PCDR0_VPUDIV_OFFSET) +#define CCM_PCDR0_MSHCDIV_OFFSET 0 +#define CCM_PCDR0_MSHCDIV_MASK (0x1f << CCM_PCDR0_MSHCDIV_OFFSET) + +#define CCM_PCDR1_PERDIV4_OFFSET 24 +#define CCM_PCDR1_PERDIV4_MASK (0x3f << CCM_PCDR1_PERDIV4_OFFSET) +#define CCM_PCDR1_PERDIV3_OFFSET 16 +#define CCM_PCDR1_PERDIV3_MASK (0x3f << CCM_PCDR1_PERDIV3_OFFSET) +#define CCM_PCDR1_PERDIV2_OFFSET 8 +#define CCM_PCDR1_PERDIV2_MASK (0x3f << CCM_PCDR1_PERDIV2_OFFSET) +#define CCM_PCDR1_PERDIV1_OFFSET 0 +#define CCM_PCDR1_PERDIV1_MASK (0x3f << CCM_PCDR1_PERDIV1_OFFSET) + +#define CCM_PCCR0_CSPI1_OFFSET 31 +#define CCM_PCCR0_CSPI1_MASK (1 << CCM_PCCR0_CSPI1_OFFSET) +#define CCM_PCCR0_CSPI2_OFFSET 30 +#define CCM_PCCR0_CSPI2_MASK (1 << CCM_PCCR0_CSPI2_OFFSET) +#define CCM_PCCR0_CSPI3_OFFSET 29 +#define CCM_PCCR0_CSPI3_MASK (1 << CCM_PCCR0_CSPI3_OFFSET) +#define CCM_PCCR0_DMA_OFFSET 28 +#define CCM_PCCR0_DMA_MASK (1 << CCM_PCCR0_DMA_OFFSET) +#define CCM_PCCR0_EMMA_OFFSET 27 +#define CCM_PCCR0_EMMA_MASK (1 << CCM_PCCR0_EMMA_OFFSET) +#define CCM_PCCR0_FEC_OFFSET 26 +#define CCM_PCCR0_FEC_MASK (1 << CCM_PCCR0_FEC_OFFSET) +#define CCM_PCCR0_GPIO_OFFSET 25 +#define CCM_PCCR0_GPIO_MASK (1 << CCM_PCCR0_GPIO_OFFSET) +#define CCM_PCCR0_GPT1_OFFSET 24 +#define CCM_PCCR0_GPT1_MASK (1 << CCM_PCCR0_GPT1_OFFSET) +#define CCM_PCCR0_GPT2_OFFSET 23 +#define CCM_PCCR0_GPT2_MASK (1 << CCM_PCCR0_GPT2_OFFSET) +#define CCM_PCCR0_GPT3_OFFSET 22 +#define CCM_PCCR0_GPT3_MASK (1 << CCM_PCCR0_GPT3_OFFSET) +#define CCM_PCCR0_GPT4_OFFSET 21 +#define CCM_PCCR0_GPT4_MASK (1 << CCM_PCCR0_GPT4_OFFSET) +#define CCM_PCCR0_GPT5_OFFSET 20 +#define CCM_PCCR0_GPT5_MASK (1 << CCM_PCCR0_GPT5_OFFSET) +#define CCM_PCCR0_GPT6_OFFSET 19 +#define CCM_PCCR0_GPT6_MASK (1 << CCM_PCCR0_GPT6_OFFSET) +#define CCM_PCCR0_I2C1_OFFSET 18 +#define CCM_PCCR0_I2C1_MASK (1 << CCM_PCCR0_I2C1_OFFSET) +#define CCM_PCCR0_I2C2_OFFSET 17 +#define CCM_PCCR0_I2C2_MASK (1 << CCM_PCCR0_I2C2_OFFSET) +#define CCM_PCCR0_IIM_OFFSET 16 +#define CCM_PCCR0_IIM_MASK (1 << CCM_PCCR0_IIM_OFFSET) +#define CCM_PCCR0_KPP_OFFSET 15 +#define CCM_PCCR0_KPP_MASK (1 << CCM_PCCR0_KPP_OFFSET) +#define CCM_PCCR0_LCDC_OFFSET 14 +#define CCM_PCCR0_LCDC_MASK (1 << CCM_PCCR0_LCDC_OFFSET) +#define CCM_PCCR0_MSHC_OFFSET 13 +#define CCM_PCCR0_MSHC_MASK (1 << CCM_PCCR0_MSHC_OFFSET) +#define CCM_PCCR0_OWIRE_OFFSET 12 +#define CCM_PCCR0_OWIRE_MASK (1 << CCM_PCCR0_OWIRE_OFFSET) +#define CCM_PCCR0_PWM_OFFSET 11 +#define CCM_PCCR0_PWM_MASK (1 << CCM_PCCR0_PWM_OFFSET) +#define CCM_PCCR0_RTC_OFFSET 9 +#define CCM_PCCR0_RTC_MASK (1 << CCM_PCCR0_RTC_OFFSET) +#define CCM_PCCR0_RTIC_OFFSET 8 +#define CCM_PCCR0_RTIC_MASK (1 << CCM_PCCR0_RTIC_OFFSET) +#define CCM_PCCR0_SAHARA_OFFSET 7 +#define CCM_PCCR0_SAHARA_MASK (1 << CCM_PCCR0_SAHARA_OFFSET) +#define CCM_PCCR0_SCC_OFFSET 6 +#define CCM_PCCR0_SCC_MASK (1 << CCM_PCCR0_SCC_OFFSET) +#define CCM_PCCR0_SDHC1_OFFSET 5 +#define CCM_PCCR0_SDHC1_MASK (1 << CCM_PCCR0_SDHC1_OFFSET) +#define CCM_PCCR0_SDHC2_OFFSET 4 +#define CCM_PCCR0_SDHC2_MASK (1 << CCM_PCCR0_SDHC2_OFFSET) +#define CCM_PCCR0_SDHC3_OFFSET 3 +#define CCM_PCCR0_SDHC3_MASK (1 << CCM_PCCR0_SDHC3_OFFSET) +#define CCM_PCCR0_SLCDC_OFFSET 2 +#define CCM_PCCR0_SLCDC_MASK (1 << CCM_PCCR0_SLCDC_OFFSET) +#define CCM_PCCR0_SSI1_IPG_OFFSET 1 +#define CCM_PCCR0_SSI1_IPG_MASK (1 << CCM_PCCR0_SSI1_IPG_OFFSET) +#define CCM_PCCR0_SSI2_IPG_OFFSET 0 +#define CCM_PCCR0_SSI2_IPG_MASK (1 << CCM_PCCR0_SSI2_IPG_OFFSET) + +#define CCM_PCCR1_UART1_OFFSET 31 +#define CCM_PCCR1_UART1_MASK (1 << CCM_PCCR1_UART1_OFFSET) +#define CCM_PCCR1_UART2_OFFSET 30 +#define CCM_PCCR1_UART2_MASK (1 << CCM_PCCR1_UART2_OFFSET) +#define CCM_PCCR1_UART3_OFFSET 29 +#define CCM_PCCR1_UART3_MASK (1 << CCM_PCCR1_UART3_OFFSET) +#define CCM_PCCR1_UART4_OFFSET 28 +#define CCM_PCCR1_UART4_MASK (1 << CCM_PCCR1_UART4_OFFSET) +#define CCM_PCCR1_UART5_OFFSET 27 +#define CCM_PCCR1_UART5_MASK (1 << CCM_PCCR1_UART5_OFFSET) +#define CCM_PCCR1_UART6_OFFSET 26 +#define CCM_PCCR1_UART6_MASK (1 << CCM_PCCR1_UART6_OFFSET) +#define CCM_PCCR1_USBOTG_OFFSET 25 +#define CCM_PCCR1_USBOTG_MASK (1 << CCM_PCCR1_USBOTG_OFFSET) +#define CCM_PCCR1_WDT_OFFSET 24 +#define CCM_PCCR1_WDT_MASK (1 << CCM_PCCR1_WDT_OFFSET) +#define CCM_PCCR1_HCLK_ATA_OFFSET 23 +#define CCM_PCCR1_HCLK_ATA_MASK (1 << CCM_PCCR1_HCLK_ATA_OFFSET) +#define CCM_PCCR1_HCLK_BROM_OFFSET 22 +#define CCM_PCCR1_HCLK_BROM_MASK (1 << CCM_PCCR1_HCLK_BROM_OFFSET) +#define CCM_PCCR1_HCLK_CSI_OFFSET 21 +#define CCM_PCCR1_HCLK_CSI_MASK (1 << CCM_PCCR1_HCLK_CSI_OFFSET) +#define CCM_PCCR1_HCLK_DMA_OFFSET 20 +#define CCM_PCCR1_HCLK_DMA_MASK (1 << CCM_PCCR1_HCLK_DMA_OFFSET) +#define CCM_PCCR1_HCLK_EMI_OFFSET 19 +#define CCM_PCCR1_HCLK_EMI_MASK (1 << CCM_PCCR1_HCLK_EMI_OFFSET) +#define CCM_PCCR1_HCLK_EMMA_OFFSET 18 +#define CCM_PCCR1_HCLK_EMMA_MASK (1 << CCM_PCCR1_HCLK_EMMA_OFFSET) +#define CCM_PCCR1_HCLK_FEC_OFFSET 17 +#define CCM_PCCR1_HCLK_FEC_MASK (1 << CCM_PCCR1_HCLK_FEC_OFFSET) +#define CCM_PCCR1_HCLK_VPU_OFFSET 16 +#define CCM_PCCR1_HCLK_VPU_MASK (1 << CCM_PCCR1_HCLK_VPU_OFFSET) +#define CCM_PCCR1_HCLK_LCDC_OFFSET 15 +#define CCM_PCCR1_HCLK_LCDC_MASK (1 << CCM_PCCR1_HCLK_LCDC_OFFSET) +#define CCM_PCCR1_HCLK_RTIC_OFFSET 14 +#define CCM_PCCR1_HCLK_RTIC_MASK (1 << CCM_PCCR1_HCLK_RTIC_OFFSET) +#define CCM_PCCR1_HCLK_SAHARA_OFFSET 13 +#define CCM_PCCR1_HCLK_SAHARA_MASK (1 << CCM_PCCR1_HCLK_SAHARA_OFFSET) +#define CCM_PCCR1_HCLK_SLCDC_OFFSET 12 +#define CCM_PCCR1_HCLK_SLCDC_MASK (1 << CCM_PCCR1_HCLK_SLCDC_OFFSET) +#define CCM_PCCR1_HCLK_USBOTG_OFFSET 11 +#define CCM_PCCR1_HCLK_USBOTG_MASK (1 << CCM_PCCR1_HCLK_USBOTG_OFFSET) +#define CCM_PCCR1_PERCLK1_OFFSET 10 +#define CCM_PCCR1_PERCLK1_MASK (1 << CCM_PCCR1_PERCLK1_OFFSET) +#define CCM_PCCR1_PERCLK2_OFFSET 9 +#define CCM_PCCR1_PERCLK2_MASK (1 << CCM_PCCR1_PERCLK2_OFFSET) +#define CCM_PCCR1_PERCLK3_OFFSET 8 +#define CCM_PCCR1_PERCLK3_MASK (1 << CCM_PCCR1_PERCLK3_OFFSET) +#define CCM_PCCR1_PERCLK4_OFFSET 7 +#define CCM_PCCR1_PERCLK4_MASK (1 << CCM_PCCR1_PERCLK4_OFFSET) +#define CCM_PCCR1_VPU_BAUD_OFFSET 6 +#define CCM_PCCR1_VPU_BAUD_MASK (1 << CCM_PCCR1_VPU_BAUD_OFFSET) +#define CCM_PCCR1_SSI1_BAUD_OFFSET 5 +#define CCM_PCCR1_SSI1_BAUD_MASK (1 << CCM_PCCR1_SSI1_BAUD_OFFSET) +#define CCM_PCCR1_SSI2_BAUD_OFFSET 4 +#define CCM_PCCR1_SSI2_BAUD_MASK (1 << CCM_PCCR1_SSI2_BAUD_OFFSET) +#define CCM_PCCR1_NFC_BAUD_OFFSET 3 +#define CCM_PCCR1_NFC_BAUD_MASK (1 << CCM_PCCR1_NFC_BAUD_OFFSET) +#define CCM_PCCR1_MSHC_BAUD_OFFSET 2 +#define CCM_PCCR1_MSHC_BAUD_MASK (1 << CCM_PCCR1_MSHC_BAUD_OFFSET) + +#define CCM_CCSR_32KSR (1 << 15) +#define CCM_CCSR_CLKMODE1 (1 << 9) +#define CCM_CCSR_CLKMODE0 (1 << 8) +#define CCM_CCSR_CLKOSEL_OFFSET 0 +#define CCM_CCSR_CLKOSEL_MASK (0x1f << CCM_CCSR_CLKOSEL_OFFSET) -#define SYS_FMCR 0x14 /* Functional Muxing Control Reg */ -#define SYS_CHIP_ID 0x00 /* The offset of CHIP ID register */ +#define SYS_FMCR 0x14 /* Functional Muxing Control Reg */ +#define SYS_CHIP_ID 0x00 /* The offset of CHIP ID register */ #endif /* __ARCH_ARM_MACH_MX2_CRM_REGS_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/devices.c linux-2.6.28-karo/arch/arm/mach-mx2/devices.c --- linux-2.6.28/arch/arm/mach-mx2/devices.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/devices.c 2009-03-11 13:16:24.000000000 +0100 @@ -33,6 +33,7 @@ #include #include +#include /* * Resource definition for the MXC IrDA @@ -58,6 +59,118 @@ struct platform_device mxc_irda_device = .resource = mxc_irda_resources, }; +static u64 mxc_vpu_dmamask = 0xffffffffUL; + +/* Platform Data for MXC VPU */ +struct platform_device mxc_vpu_device = { + .name = "mxc_vpu", + .id = 0, + .dev = { + .dma_mask = &mxc_vpu_dmamask, + .coherent_dma_mask = ~0UL, + }, +}; + +#ifdef CONFIG_MACH_MX27 +static struct resource mx27_camera_resources[] = { + { + .start = CSI_BASE_ADDR, + .end = CSI_BASE_ADDR + 0x1f, + .flags = IORESOURCE_MEM, + }, { + .start = EMMA_PRP_BASE_ADDR, + .end = EMMA_PRP_BASE_ADDR + 0x1f, + .flags = IORESOURCE_MEM, + }, { + .start = MXC_INT_CSI, + .end = MXC_INT_CSI, + .flags = IORESOURCE_IRQ, + }, { + .start = MXC_INT_EMMAPRP, + .end = MXC_INT_EMMAPRP, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device mx27_camera_device = { + .name = "mx27-camera", + .id = 0, + .num_resources = ARRAY_SIZE(mx27_camera_resources), + .resource = mx27_camera_resources, +}; +#endif + +/* + * SPI master controller + * + * - i.MX1: 2 channel (slighly different register setting) + * - i.MX21: 2 channel + * - i.MX27: 3 channel + */ +static struct resource mxc_spi_resources0[] = { + [0] = { + .start = CSPI1_BASE_ADDR, + .end = CSPI1_BASE_ADDR + 0x1F, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_CSPI1, + .end = MXC_INT_CSPI1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource mxc_spi_resources1[] = { + [0] = { + .start = CSPI2_BASE_ADDR, + .end = CSPI2_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_CSPI2, + .end = MXC_INT_CSPI2, + .flags = IORESOURCE_IRQ, + }, +}; + +#ifdef CONFIG_MACH_MX27 +static struct resource mxc_spi_resources2[] = { + [0] = { + .start = CSPI3_BASE_ADDR, + .end = CSPI3_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_CSPI3, + .end = MXC_INT_CSPI3, + .flags = IORESOURCE_IRQ, + }, +}; +#endif + +struct platform_device mxc_spi_device0 = { + .name = "mxc_spi", + .id = 0, + .num_resources = ARRAY_SIZE(mxc_spi_resources0), + .resource = mxc_spi_resources0, +}; + +struct platform_device mxc_spi_device1 = { + .name = "mxc_spi", + .id = 1, + .num_resources = ARRAY_SIZE(mxc_spi_resources1), + .resource = mxc_spi_resources1, +}; + +#ifdef CONFIG_MACH_MX27 +struct platform_device mxc_spi_device2 = { + .name = "mxc_spi", + .id = 2, + .num_resources = ARRAY_SIZE(mxc_spi_resources2), + .resource = mxc_spi_resources2, +}; +#endif + /* * General Purpose Timer * - i.MX1: 2 timer (slighly different register handling) @@ -169,6 +282,72 @@ struct platform_device mxc_gpt5 = { }; #endif +/* I2C channel #1 */ +static struct resource imx_i2c_1_resources[] = { + [0] = { + .start = I2C_BASE_ADDR, + .end = I2C_BASE_ADDR + 0x0F, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MXC_INT_I2C, + .end = MXC_INT_I2C, + .flags = IORESOURCE_IRQ + } +}; + +#ifdef CONFIG_MACH_MX27 +static struct resource imx_i2c_2_resources[] = { + [0] = { + .start = I2C2_BASE_ADDR, + .end = I2C2_BASE_ADDR + 0x0F, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MXC_INT_I2C2, + .end = MXC_INT_I2C2, + .flags = IORESOURCE_IRQ + } +}; +#endif + +struct platform_device imx_i2c_device0 = { + .name = "imx_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(imx_i2c_1_resources), + .resource = imx_i2c_1_resources +}; + +#ifdef CONFIG_MACH_MX27 +struct platform_device imx_i2c_device1 = { + .name = "imx_i2c", + .id = 1, + .num_resources = ARRAY_SIZE(imx_i2c_2_resources), + .resource = imx_i2c_2_resources +}; +#endif + +#if defined(CONFIG_RTC_MXC) || defined(CONFIG_RTC_MXC_MODULE) +static struct resource rtc_resources[] = { + { + .start = RTC_BASE_ADDR, + .end = RTC_BASE_ADDR + 0x30, + .flags = IORESOURCE_MEM, + }, + { + .start = MXC_INT_RTC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device mxc_rtc_device = { + .name = "mxc_rtc", + .id = 0, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; +#endif + /* * Watchdog: * - i.MX1 @@ -190,6 +369,291 @@ struct platform_device mxc_wdt = { .resource = mxc_wdt_resources, }; +static struct resource mxc_nand_resources[] = { + { + .start = NFC_BASE_ADDR, + .end = NFC_BASE_ADDR + 0xfff, + .flags = IORESOURCE_MEM + }, { + .start = MXC_INT_NANDFC, + .end = MXC_INT_NANDFC, + .flags = IORESOURCE_IRQ + }, +}; + +struct platform_device mxc_nand_device = { + .name = "mxc_nand", + .id = 0, + .num_resources = ARRAY_SIZE(mxc_nand_resources), + .resource = mxc_nand_resources, +}; + +static struct resource mxc_w1_master_resources[] = { + { + .start = OWIRE_BASE_ADDR, + .end = OWIRE_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device mxc_w1_master_device = { + .name = "mxc_w1", + .id = 0, + .num_resources = ARRAY_SIZE(mxc_w1_master_resources), + .resource = mxc_w1_master_resources, +}; + +/* + * Resource definition for the MXC SDHC + */ +static struct resource mxc_sdhc1_resources[] = { + [0] = { + .start = SDHC1_BASE_ADDR, + .end = SDHC1_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_SDHC1, + .end = MXC_INT_SDHC1, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "sdhc1", + .start = DMA_REQ_SDHC1, + .end = DMA_REQ_SDHC1, + .flags = IORESOURCE_DMA + }, +}; + +static u64 mxc_sdhc1_dmamask = 0xffffffffUL; + +struct platform_device mxc_sdhc_device0 = { + .name = "imx-mmc", + .id = 0, + .dev = { + .dma_mask = &mxc_sdhc1_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(mxc_sdhc1_resources), + .resource = mxc_sdhc1_resources, +}; + +static struct resource mxc_sdhc2_resources[] = { + [0] = { + .start = SDHC2_BASE_ADDR, + .end = SDHC2_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_SDHC2, + .end = MXC_INT_SDHC2, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "sdhc2", + .start = DMA_REQ_SDHC2, + .end = DMA_REQ_SDHC2, + .flags = IORESOURCE_DMA + }, +}; + +static u64 mxc_sdhc2_dmamask = 0xffffffffUL; + +struct platform_device mxc_sdhc_device1 = { + .name = "imx-mmc", + .id = 1, + .dev = { + .dma_mask = &mxc_sdhc2_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(mxc_sdhc2_resources), + .resource = mxc_sdhc2_resources, +}; + +static u64 otg_dmamask = ~(u32) 0; + +static struct resource mxc_otg_resources[] = { + { + .start = OTG_BASE_ADDR + 0x000, + .end = OTG_BASE_ADDR + 0x1ff, + .flags = IORESOURCE_MEM, + }, { + .start = MXC_INT_USB1, + .flags = IORESOURCE_IRQ, + }, +}; + +/* + * lcdc: + * - i.MX1: the basic controller + * - i.MX21: to be checked + * - i.MX27: like i.MX1, with slightly variations + */ +static struct resource mxc_fb[] = { + { + .start = LCDC_BASE_ADDR, + .end = LCDC_BASE_ADDR + 0xFFF, + .flags = IORESOURCE_MEM, + }, { + .start = MXC_INT_LCDC, + .end = MXC_INT_LCDC, + .flags = IORESOURCE_IRQ, + }, +}; + +/* mxc lcd driver */ +struct platform_device mxc_fb_device = { + .name = "imx-fb", + .id = 0, + .num_resources = ARRAY_SIZE(mxc_fb), + .resource = mxc_fb, + .dev = { + .coherent_dma_mask = 0xFFFFFFFF, + }, +}; + +struct platform_device mxc_otg = { + .name = "mxc-ehci", + .id = 0, + .dev = { + .coherent_dma_mask = 0xffffffff, + .dma_mask = &otg_dmamask, + }, + .resource = mxc_otg_resources, + .num_resources = ARRAY_SIZE(mxc_otg_resources), +}; + +static struct resource mxc_ehci2_resources[] = { + { + .start = OTG_BASE_ADDR + 0x400, + .end = OTG_BASE_ADDR + 0x5ff, + .flags = IORESOURCE_MEM, + }, { + .start = MXC_INT_USB2, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 ehci2_dmamask = ~(u32) 0; + +struct platform_device mxc_ehci2 = { + .name = "mxc-ehci", + .id = 1, + .dev = { + .coherent_dma_mask = 0xffffffff, + .dma_mask = &ehci2_dmamask, + }, + .num_resources = ARRAY_SIZE(mxc_ehci2_resources), + .resource = mxc_ehci2_resources, +}; + +/* + * SSI bus: + * - i.MX1: 2 channels + * - i.MX21: 2 channels + * - i.MX27: 2 channels + */ +static struct resource imx_ssi_resources0[] = { + [0] = { + .start = SSI1_BASE_ADDR, + .end = SSI1_BASE_ADDR + 0x6F, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MXC_INT_SSI1, + .end = MXC_INT_SSI1, + .flags = IORESOURCE_IRQ + }, + [2] = { + .name = "tx0", + .start = DMA_REQ_SSI1_TX0, + .end = DMA_REQ_SSI1_TX0, + .flags = IORESOURCE_DMA + }, + [3] = { + .name = "rx0", + .start = DMA_REQ_SSI1_RX0, + .end = DMA_REQ_SSI1_RX0, + .flags = IORESOURCE_DMA + }, + [4] = { + .name = "tx1", + .start = DMA_REQ_SSI1_TX1, + .end = DMA_REQ_SSI1_TX1, + .flags = IORESOURCE_DMA + }, + [5] = { + .name = "rx1", + .start = DMA_REQ_SSI1_RX1, + .end = DMA_REQ_SSI1_RX1, + .flags = IORESOURCE_DMA + } +}; + +static struct resource imx_ssi_resources1[] = { + [0] = { + .start = SSI2_BASE_ADDR, + .end = SSI2_BASE_ADDR + 0x6F, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MXC_INT_SSI2, + .end = MXC_INT_SSI2, + .flags = IORESOURCE_IRQ + }, + [2] = { + .name = "tx0", + .start = DMA_REQ_SSI2_TX0, + .end = DMA_REQ_SSI2_TX0, + .flags = IORESOURCE_DMA + }, + [3] = { + .name = "rx0", + .start = DMA_REQ_SSI2_RX0, + .end = DMA_REQ_SSI2_RX0, + .flags = IORESOURCE_DMA + }, + [4] = { + .name = "tx1", + .start = DMA_REQ_SSI2_TX1, + .end = DMA_REQ_SSI2_TX1, + .flags = IORESOURCE_DMA + }, + [5] = { + .name = "rx1", + .start = DMA_REQ_SSI2_RX1, + .end = DMA_REQ_SSI2_RX1, + .flags = IORESOURCE_DMA + } +}; + +struct platform_device imx_ssi_device0 = { + .name = "mxc-ssi", + .id = 0, + .num_resources = ARRAY_SIZE(imx_ssi_resources0), + .resource = imx_ssi_resources0 +}; + +struct platform_device imx_ssi_device1 = { + .name = "mxc-ssi", + .id = 1, + .num_resources = ARRAY_SIZE(imx_ssi_resources1), + .resource = imx_ssi_resources1 +}; + +static struct resource mxc_dam_resources = { + .start = AUDMUX_BASE_ADDR, + .end = AUDMUX_BASE_ADDR + 0x1F, + .flags = IORESOURCE_MEM +}; + +struct platform_device mxc_dam_device = { + .name = "mxc-dam", + .id = 0, + .num_resources = 1, + .resource = &mxc_dam_resources +}; + /* GPIO port description */ static struct mxc_gpio_port imx_gpio_ports[] = { [0] = { @@ -229,3 +693,4 @@ int __init mxc_register_gpios(void) { return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports)); } +//arch_initcall(mxc_register_gpios); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/devices.h linux-2.6.28-karo/arch/arm/mach-mx2/devices.h --- linux-2.6.28/arch/arm/mach-mx2/devices.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/devices.h 2009-03-11 13:16:24.000000000 +0100 @@ -4,6 +4,7 @@ extern struct platform_device mxc_gpt2; extern struct platform_device mxc_gpt3; extern struct platform_device mxc_gpt4; extern struct platform_device mxc_gpt5; +extern struct platform_device mxc_rtc_device; extern struct platform_device mxc_wdt; extern struct platform_device mxc_irda_device; extern struct platform_device mxc_uart_device0; @@ -12,4 +13,20 @@ extern struct platform_device mxc_uart_d extern struct platform_device mxc_uart_device3; extern struct platform_device mxc_uart_device4; extern struct platform_device mxc_uart_device5; - +extern struct platform_device mxc_nand_device; +extern struct platform_device mxc_w1_master_device; +extern struct platform_device mxc_sdhc_device0; +extern struct platform_device mxc_sdhc_device1; +extern struct platform_device mxc_otg; +extern struct platform_device mxc_ehci2; +extern struct platform_device mxc_spi_device0; +extern struct platform_device mxc_spi_device1; +extern struct platform_device mxc_spi_device2; +extern struct platform_device imx_ssi_device0; +extern struct platform_device imx_ssi_device1; +extern struct platform_device mxc_dam_device; +extern struct platform_device imx_i2c_device0; +extern struct platform_device imx_i2c_device1; +extern struct platform_device mx27_camera_device; +extern struct platform_device mxc_fb_device; +extern struct platform_device mxc_vpu_device; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/generic.c linux-2.6.28-karo/arch/arm/mach-mx2/generic.c --- linux-2.6.28/arch/arm/mach-mx2/generic.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/generic.c 2009-03-11 13:16:24.000000000 +0100 @@ -35,7 +35,7 @@ static struct map_desc mxc_io_desc[] __i * - and some reserved space */ { - .virtual = AIPI_BASE_ADDR_VIRT, + .virtual = (unsigned long)AIPI_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(AIPI_BASE_ADDR), .length = AIPI_SIZE, .type = MT_DEVICE @@ -46,7 +46,7 @@ static struct map_desc mxc_io_desc[] __i * - ATA */ { - .virtual = SAHB1_BASE_ADDR_VIRT, + .virtual = (unsigned long)SAHB1_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(SAHB1_BASE_ADDR), .length = SAHB1_SIZE, .type = MT_DEVICE @@ -56,7 +56,7 @@ static struct map_desc mxc_io_desc[] __i * - EMI */ { - .virtual = X_MEMC_BASE_ADDR_VIRT, + .virtual = (unsigned long)X_MEMC_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR), .length = X_MEMC_SIZE, .type = MT_DEVICE diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/include/mach/board-tx27.h linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/board-tx27.h --- linux-2.6.28/arch/arm/mach-mx2/include/mach/board-tx27.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/board-tx27.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,94 @@ +/* + * arch/arm/mach-mx27/board-tx27.h + * + * Copyright 2008 + * based on arch/arm/mach-mx27/board-mx27ads.h by eescale Semiconductor, Inc. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +//#define FEC_MII_IRQ + +#define MXC_LL_UART_PADDR UART1_BASE_ADDR +#define MXC_LL_UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR) + +/*! + * These defines are used to enable or disable a particular UART port. If + * disabled, the UART will not be registered in the file system and the user + * will not be able to access it. + * Specify a value of 1 to enable the UART and 0 to disable it. + */ +#define UART1_ENABLED 1 +/* UART 2 configuration */ +#define UART2_ENABLED 1 +/* UART 3 configuration */ +#define UART3_ENABLED 1 +/* UART 4 configuration */ +#define UART4_ENABLED 0 /* Disable UART 4 as its pins are shared with ATA */ +#define UART5_ENABLED 0 +#define UART6_ENABLED 0 + +#ifndef __ASSEMBLY__ +#include + +#define MXC_BD_LED1 0 +#define MXC_BD_LED2 (1 << 5) +static inline void MXC_BD_LED_ON(unsigned int led) +{ + if (led & MXC_BD_LED2) { + __gpio_set_value(GPIO_PORTF | 13, 1); + } +} +static inline void MXC_BD_LED_OFF(unsigned int led) +{ + if (led & MXC_BD_LED2) { + __gpio_set_value(GPIO_PORTF | 13, 0); + } +} + +extern int gpio_fec_active(void); +extern void gpio_fec_inactive(void); +extern int gpio_sdhc_active(int module); +extern int gpio_sdhc_inactive(int module); +extern int gpio_usbh2_active(void); +extern void gpio_usbh2_inactive(void); +#if 0 +extern int gpio_uart_active(int port, int irda); +extern int gpio_uart_inactive(int port); +extern int config_uartdma_event(int port); +extern int gpio_usbh1_active(void); +extern void gpio_usbh1_inactive(void); +extern int gpio_usbotg_fs_active(void); +extern void gpio_usbotg_fs_inactive(void); +extern int gpio_i2c_active(int i2c_num); +extern int gpio_i2c_inactive(int i2c_num); +extern int gpio_spi_active(int cspi_mod); +extern int gpio_spi_inactive(int cspi_mod); +extern int gpio_nand_active(void); +extern void gpio_nand_inactive(void); +extern int gpio_sensor_active(void); +extern void gpio_sensor_inactive(void); +extern int gpio_keypad_active(void); +extern void gpio_keypad_inactive(void); +extern int gpio_ata_active(void); +extern void gpio_ata_inactive(void); +extern int gpio_slcdc_active(int type); +extern int gpio_slcdc_inactive(int type); +extern int gpio_ssi_active(int ssi_num); +extern int gpio_ssi_inactive(int ssi_num); +extern int gpio_owire_active(void); +extern void gpio_owire_inactive(void); +#endif +extern int gpio_usbotg_hs_active(void); +extern void gpio_usbotg_hs_inactive(void); +extern int gpio_ac97_active(void); +extern void gpio_ac97_inactive(void); + +#endif // __ASSEMBLY__ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/include/mach/iomux.h linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/iomux.h --- linux-2.6.28/arch/arm/mach-mx2/include/mach/iomux.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/iomux.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,460 @@ +/* + * arch/arm/mach-mx1/include/mach/iomux.h + * + * Copyright (C) 2008 by Lothar Wassmann + * derived from: arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h + * Copyright (C) 2008 by Sascha Hauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Pin definitions for the Freescale MX2 architecture + */ + +#ifndef _MXC_GPIO_MX2_H +#define _MXC_GPIO_MX2_H + +#define GPIO_PORT_MAX 5 + +#include + +enum { + MXC_DEFINE_PIN(A, 0, PF, USBH2_CLK, 0), + MXC_DEFINE_PIN(A, 1, PF, USBH2_DIR, 0), + MXC_DEFINE_PIN(A, 2, PF, USBH2_DATA7, 0), + MXC_DEFINE_PIN(A, 3, PF, USBH2_NXT, 0), + MXC_DEFINE_PIN(A, 4, PF, USBH2_STP, 0), + + MXC_DEFINE_PIN(A, 5, PF, LSCLK, GPIO_OUT), + MXC_DEFINE_PIN(A, 6, PF, LD0, GPIO_OUT), + MXC_DEFINE_PIN(A, 7, PF, LD1, GPIO_OUT), + MXC_DEFINE_PIN(A, 8, PF, LD2, GPIO_OUT), + MXC_DEFINE_PIN(A, 9, PF, LD3, GPIO_OUT), + MXC_DEFINE_PIN(A, 10, PF, LD4, GPIO_OUT), + MXC_DEFINE_PIN(A, 11, PF, LD5, GPIO_OUT), + MXC_DEFINE_PIN(A, 12, PF, LD6, GPIO_OUT), + MXC_DEFINE_PIN(A, 13, PF, LD7, GPIO_OUT), + MXC_DEFINE_PIN(A, 14, PF, LD8, GPIO_OUT), + MXC_DEFINE_PIN(A, 15, PF, LD9, GPIO_OUT), + MXC_DEFINE_PIN(A, 16, PF, LD10, GPIO_OUT), + MXC_DEFINE_PIN(A, 17, PF, LD11, GPIO_OUT), + MXC_DEFINE_PIN(A, 18, PF, LD12, GPIO_OUT), + MXC_DEFINE_PIN(A, 19, PF, LD13, GPIO_OUT), + MXC_DEFINE_PIN(A, 20, PF, LD14, GPIO_OUT), + MXC_DEFINE_PIN(A, 21, PF, LD15, GPIO_OUT), + MXC_DEFINE_PIN(A, 22, PF, LD16, GPIO_OUT), + MXC_DEFINE_PIN(A, 23, PF, LD17, GPIO_OUT), + MXC_DEFINE_PIN(A, 24, PF, REV, GPIO_OUT), + MXC_DEFINE_PIN(A, 25, PF, CLS, GPIO_OUT), + MXC_DEFINE_PIN(A, 26, PF, PS, GPIO_OUT), + MXC_DEFINE_PIN(A, 27, PF, SPL_SPR, GPIO_OUT), + MXC_DEFINE_PIN(A, 28, PF, HSYNC, GPIO_OUT), + MXC_DEFINE_PIN(A, 29, PF, VSYNC, GPIO_OUT), + MXC_DEFINE_PIN(A, 30, PF, CONTRAST, GPIO_OUT), + MXC_DEFINE_PIN(A, 31, PF, OE_ACD, GPIO_OUT), + + MXC_DEFINE_PIN(B, 10, PF, CSI_D0, GPIO_OUT), + MXC_DEFINE_PIN(B, 11, PF, CSI_D1, GPIO_OUT), + MXC_DEFINE_PIN(B, 12, PF, CSI_D2, GPIO_OUT), + MXC_DEFINE_PIN(B, 13, PF, CSI_D3, GPIO_OUT), + MXC_DEFINE_PIN(B, 14, PF, CSI_D4, GPIO_OUT), + MXC_DEFINE_PIN(B, 15, PF, CSI_MCLK, GPIO_OUT), + MXC_DEFINE_PIN(B, 16, PF, CSI_PIXCLK, GPIO_OUT), + MXC_DEFINE_PIN(B, 17, PF, CSI_D5, GPIO_OUT), + MXC_DEFINE_PIN(B, 18, PF, CSI_D6, GPIO_OUT), + + MXC_DEFINE_PIN(B, 10, AF, UART6_TXD, GPIO_OUT), + MXC_DEFINE_PIN(B, 11, AF, UART6_RXD, GPIO_IN), + MXC_DEFINE_PIN(B, 12, AF, UART6_CTS, GPIO_OUT), + MXC_DEFINE_PIN(B, 13, AF, UART6_RTS, GPIO_IN), + + MXC_DEFINE_PIN(B, 18, AF, UART5_TXD, GPIO_OUT), + MXC_DEFINE_PIN(B, 19, AF, UART5_RXD, GPIO_IN), + MXC_DEFINE_PIN(B, 20, AF, UART5_CTS, GPIO_OUT), + MXC_DEFINE_PIN(B, 21, AF, UART5_RTS, GPIO_IN), + + MXC_DEFINE_PIN(B, 19, PF, CSI_D7, GPIO_OUT), + MXC_DEFINE_PIN(B, 20, PF, CSI_VSYNC, GPIO_OUT), + MXC_DEFINE_PIN(B, 21, PF, CSI_HSYNC, GPIO_OUT), + + MXC_DEFINE_PIN(B, 22, PF, USBH1_SUSP, 0), + MXC_DEFINE_PIN(B, 23, PF, USB_PWR, 0), + MXC_DEFINE_PIN(B, 24, PF, USB_OC_B, 0), + MXC_DEFINE_PIN(B, 25, PF, USBH1_RCV, 0), + MXC_DEFINE_PIN(B, 26, PF, USBH1_FS, 0), + MXC_DEFINE_PIN(B, 27, PF, USBH1_OE_B, 0), + MXC_DEFINE_PIN(B, 28, PF, USBH1_TXDM, 0), + MXC_DEFINE_PIN(B, 29, PF, USBH1_TXDP, 0), + MXC_DEFINE_PIN(B, 30, PF, USBH1_RXDM, 0), + MXC_DEFINE_PIN(B, 31, PF, USBH1_RXDP, 0), + + MXC_DEFINE_PIN(B, 26, AF, UART4_RTS, GPIO_IN), + MXC_DEFINE_PIN(B, 28, AF, UART4_TXD, GPIO_OUT), + MXC_DEFINE_PIN(B, 29, AF, UART4_CTS, GPIO_OUT), + MXC_DEFINE_PIN(B, 31, AF, UART4_RXD, GPIO_IN), + + MXC_DEFINE_PIN(C, 14, PF, TOUT, GPIO_OUT), + MXC_DEFINE_PIN(C, 15, PF, TIN, GPIO_IN), + + MXC_DEFINE_PIN(C, 5, PF, I2C2_SDA, GPIO_IN), + MXC_DEFINE_PIN(C, 6, PF, I2C2_SCL, GPIO_IN), + + MXC_DEFINE_PIN(C, 7, PF, USBOTG_DATA5, GPIO_OUT), + MXC_DEFINE_PIN(C, 8, PF, USBOTG_DATA6, GPIO_OUT), + MXC_DEFINE_PIN(C, 9, PF, USBOTG_DATA0, GPIO_OUT), + MXC_DEFINE_PIN(C, 10, PF, USBOTG_DATA2, GPIO_OUT), + MXC_DEFINE_PIN(C, 11, PF, USBOTG_DATA1, GPIO_OUT), + MXC_DEFINE_PIN(C, 12, PF, USBOTG_DATA4, GPIO_OUT), + MXC_DEFINE_PIN(C, 13, PF, USBOTG_DATA3, GPIO_OUT), + MXC_DEFINE_PIN(E, 0, PF, USBOTG_NXT, GPIO_OUT), + MXC_DEFINE_PIN(E, 1, PF, USBOTG_STP, GPIO_OUT), + MXC_DEFINE_PIN(E, 2, PF, USBOTG_DIR, GPIO_OUT), + MXC_DEFINE_PIN(E, 24, PF, USBOTG_CLK, GPIO_OUT), + MXC_DEFINE_PIN(E, 25, PF, USBOTG_DATA7, GPIO_OUT), + + MXC_DEFINE_PIN(C, 20, PF, SSI1_FS, GPIO_IN), + MXC_DEFINE_PIN(C, 21, PF, SSI1_RXD, GPIO_IN), + MXC_DEFINE_PIN(C, 22, PF, SSI1_TXD, GPIO_IN), + MXC_DEFINE_PIN(C, 23, PF, SSI1_CLK, GPIO_IN), + + MXC_DEFINE_PIN(C, 24, PF, SSI2_FS, GPIO_IN), + MXC_DEFINE_PIN(C, 25, PF, SSI2_RXD, GPIO_IN), + MXC_DEFINE_PIN(C, 26, PF, SSI2_TXD, GPIO_IN), + MXC_DEFINE_PIN(C, 27, PF, SSI2_CLK, GPIO_IN), + + MXC_DEFINE_PIN(C, 28, PF, SSI3_FS, GPIO_IN), + MXC_DEFINE_PIN(C, 29, PF, SSI3_RXD, GPIO_IN), + MXC_DEFINE_PIN(C, 30, PF, SSI3_TXD, GPIO_IN), + MXC_DEFINE_PIN(C, 31, PF, SSI3_CLK, GPIO_IN), + + MXC_DEFINE_PIN(C, 16, PF, SSI4_FS, GPIO_IN), + MXC_DEFINE_PIN(C, 17, PF, SSI4_RXD, GPIO_IN), + MXC_DEFINE_PIN(C, 18, PF, SSI4_TXD, GPIO_IN), + MXC_DEFINE_PIN(C, 19, PF, SSI4_CLK, GPIO_IN), + + MXC_DEFINE_PIN(B, 5, AIN, SLCDC1_CLK, 0), + MXC_DEFINE_PIN(B, 6, AIN, SLCDC1_D0, 0), + MXC_DEFINE_PIN(B, 7, AIN, SLCDC1_RS, 0), + MXC_DEFINE_PIN(B, 8, AIN, SLCDC1_CS, 0), + + MXC_DEFINE_PIN(C, 28, AIN, SLCDC2_D0, GPIO_OUT), + MXC_DEFINE_PIN(C, 29, AIN, SLCDC2_RS, GPIO_OUT), + MXC_DEFINE_PIN(C, 30, AIN, SLCDC2_CS, GPIO_OUT), + MXC_DEFINE_PIN(C, 31, AIN, SLCDC2_CLK, GPIO_OUT), + + MXC_DEFINE_PIN(D, 2, PF, ATA_DATA0, 0), + MXC_DEFINE_PIN(D, 3, PF, ATA_DATA1, 0), + MXC_DEFINE_PIN(D, 4, PF, ATA_DATA2, 0), + MXC_DEFINE_PIN(D, 5, PF, ATA_DATA3, 0), + MXC_DEFINE_PIN(D, 6, PF, ATA_DATA4, 0), + MXC_DEFINE_PIN(D, 7, PF, ATA_DATA5, 0), + MXC_DEFINE_PIN(D, 8, PF, ATA_DATA6, 0), + MXC_DEFINE_PIN(D, 9, PF, ATA_DATA7, 0), + MXC_DEFINE_PIN(D, 10, PF, ATA_DATA8, 0), + MXC_DEFINE_PIN(D, 11, PF, ATA_DATA9, 0), + MXC_DEFINE_PIN(D, 12, PF, ATA_DATA10, 0), + MXC_DEFINE_PIN(D, 13, PF, ATA_DATA11, 0), + MXC_DEFINE_PIN(D, 14, PF, ATA_DATA12, 0), + MXC_DEFINE_PIN(D, 15, PF, ATA_DATA13, 0), + MXC_DEFINE_PIN(D, 16, PF, ATA_DATA14, 0), + MXC_DEFINE_PIN(F, 23, PF, ATA_DATA15, 0), + + MXC_DEFINE_PIN(D, 0, AIN, FEC_TXD0, GPIO_OUT), + MXC_DEFINE_PIN(D, 1, AIN, FEC_TXD1, GPIO_OUT), + MXC_DEFINE_PIN(D, 2, AIN, FEC_TXD2, GPIO_OUT), + MXC_DEFINE_PIN(D, 3, AIN, FEC_TXD3, GPIO_OUT), + MXC_DEFINE_PIN(D, 4, AOUT, FEC_RX_ER, GPIO_IN), + MXC_DEFINE_PIN(D, 5, AOUT, FEC_RXD1, GPIO_IN), + MXC_DEFINE_PIN(D, 6, AOUT, FEC_RXD2, GPIO_IN), + MXC_DEFINE_PIN(D, 7, AOUT, FEC_RXD3, GPIO_IN), + MXC_DEFINE_PIN(D, 8, AF, FEC_MDIO, GPIO_IN), + MXC_DEFINE_PIN(D, 9, AIN, FEC_MDC, GPIO_OUT), + MXC_DEFINE_PIN(D, 10, AOUT, FEC_CRS, GPIO_IN), + MXC_DEFINE_PIN(D, 11, AOUT, FEC_TX_CLK, GPIO_IN), + MXC_DEFINE_PIN(D, 12, AOUT, FEC_RXD0, GPIO_IN), + MXC_DEFINE_PIN(D, 13, AOUT, FEC_RX_DV, GPIO_IN), + MXC_DEFINE_PIN(D, 14, AOUT, FEC_RX_CLK, GPIO_IN), + MXC_DEFINE_PIN(D, 15, AOUT, FEC_COL, GPIO_IN), + MXC_DEFINE_PIN(D, 16, AIN, FEC_TX_ER, GPIO_OUT), + + MXC_DEFINE_PIN(D, 17, PF, I2C_DATA, GPIO_OUT), + MXC_DEFINE_PIN(D, 18, PF, I2C_CLK, GPIO_OUT), + + MXC_DEFINE_PIN(D, 19, PF, CSPI2_SS2, 0), + MXC_DEFINE_PIN(D, 20, PF, CSPI2_SS1, 0), + MXC_DEFINE_PIN(D, 21, PF, CSPI2_SS0, 0), + MXC_DEFINE_PIN(D, 22, PF, CSPI2_SCLK, 0), + MXC_DEFINE_PIN(D, 23, PF, CSPI2_MISO, 0), + MXC_DEFINE_PIN(D, 24, PF, CSPI2_MOSI, 0), + + MXC_DEFINE_PIN(D, 19, AF, USBH2_DATA4, 0), + MXC_DEFINE_PIN(D, 20, AF, USBH2_DATA3, 0), + MXC_DEFINE_PIN(D, 21, AF, USBH2_DATA6, 0), + MXC_DEFINE_PIN(D, 22, AF, USBH2_DATA0, 0), + MXC_DEFINE_PIN(D, 23, AF, USBH2_DATA2, 0), + MXC_DEFINE_PIN(D, 24, AF, USBH2_DATA1, 0), + MXC_DEFINE_PIN(D, 26, AF, USBH2_DATA5, 0), + + MXC_DEFINE_PIN(D, 25, PF, CSPI1_RDY, GPIO_OUT), + MXC_DEFINE_PIN(D, 26, PF, CSPI1_SS2, GPIO_OUT), + MXC_DEFINE_PIN(D, 27, PF, CSPI1_SS1, GPIO_OUT), + MXC_DEFINE_PIN(D, 28, PF, CSPI1_SS0, GPIO_OUT), + MXC_DEFINE_PIN(D, 29, PF, CSPI1_SCLK, GPIO_OUT), + MXC_DEFINE_PIN(D, 30, PF, CSPI1_MISO, GPIO_IN), + MXC_DEFINE_PIN(D, 31, PF, CSPI1_MOSI, GPIO_OUT), + + MXC_DEFINE_PIN(E, 3, PF, UART2_CTS, GPIO_OUT), + MXC_DEFINE_PIN(E, 4, PF, UART2_RTS, GPIO_IN), + MXC_DEFINE_PIN(E, 6, PF, UART2_TXD, GPIO_OUT), + MXC_DEFINE_PIN(E, 7, PF, UART2_RXD, GPIO_IN), + + MXC_DEFINE_PIN(E, 8, PF, UART3_TXD, GPIO_OUT), + MXC_DEFINE_PIN(E, 9, PF, UART3_RXD, GPIO_IN), + MXC_DEFINE_PIN(E, 10, PF, UART3_CTS, GPIO_OUT), + MXC_DEFINE_PIN(E, 11, PF, UART3_RTS, GPIO_IN), + + MXC_DEFINE_PIN(E, 12, PF, UART1_TXD, GPIO_OUT), + MXC_DEFINE_PIN(E, 13, PF, UART1_RXD, GPIO_IN), + MXC_DEFINE_PIN(E, 14, PF, UART1_CTS, GPIO_OUT), + MXC_DEFINE_PIN(E, 15, PF, UART1_RTS, GPIO_IN), + + MXC_DEFINE_PIN(E, 16, AF, OWIRE, GPIO_OUT), + MXC_DEFINE_PIN(E, 16, PF, RTCK, GPIO_OUT), + + MXC_DEFINE_PIN(E, 18, PF, SD1_D0, 0), + MXC_DEFINE_PIN(E, 19, PF, SD1_D1, 0), + MXC_DEFINE_PIN(E, 20, PF, SD1_D2, 0), + MXC_DEFINE_PIN(E, 21, PF, SD1_D3, 0), + MXC_DEFINE_PIN(E, 22, PF, SD1_CMD, 0), + MXC_DEFINE_PIN(E, 23, PF, SD1_CLK, 0), + + MXC_DEFINE_PIN(B, 4, PF, SD2_D0, 0), + MXC_DEFINE_PIN(B, 5, PF, SD2_D1, 0), + MXC_DEFINE_PIN(B, 6, PF, SD2_D2, 0), + MXC_DEFINE_PIN(B, 7, PF, SD2_D3, 0), + MXC_DEFINE_PIN(B, 8, PF, SD2_CMD, 0), + MXC_DEFINE_PIN(B, 9, PF, SD2_CLK, 0), + + MXC_DEFINE_PIN(D, 0, PF, SD3_CMD, GPIO_OUT), + MXC_DEFINE_PIN(D, 1, PF, SD3_CLK, GPIO_OUT), + MXC_DEFINE_PIN(D, 2, AF, SD3_D0, GPIO_OUT), + MXC_DEFINE_PIN(D, 3, AF, SD3_D1, GPIO_OUT), + MXC_DEFINE_PIN(D, 4, AF, SD3_D2, GPIO_OUT), + MXC_DEFINE_PIN(D, 5, AF, SD3_D3, GPIO_OUT), + + MXC_DEFINE_PIN(E, 18, AF, CSPI3_MISO, GPIO_IN), + MXC_DEFINE_PIN(E, 21, AF, CSPI3_SS, GPIO_OUT), + MXC_DEFINE_PIN(E, 22, AF, CSPI3_MOSI, GPIO_OUT), + MXC_DEFINE_PIN(E, 23, AF, CSPI3_SCLK, GPIO_OUT), + + MXC_DEFINE_PIN(F, 0, PF, NFRB, GPIO_IN), + MXC_DEFINE_PIN(F, 1, PF, NFCLE, GPIO_OUT), + MXC_DEFINE_PIN(F, 2, PF, NFWP_B, GPIO_OUT), + MXC_DEFINE_PIN(F, 3, PF, NFCE_B, GPIO_OUT), + MXC_DEFINE_PIN(F, 4, PF, NFALE, GPIO_OUT), + MXC_DEFINE_PIN(F, 5, PF, NFRE_B, GPIO_OUT), + MXC_DEFINE_PIN(F, 6, PF, NFWE_B, GPIO_OUT), + + MXC_DEFINE_PIN(F, 7, AF, PC_POE, GPIO_OUT), + MXC_DEFINE_PIN(F, 8, AF, PC_RW_B, GPIO_OUT), + MXC_DEFINE_PIN(F, 9, AF, IOIS16, GPIO_IN), + MXC_DEFINE_PIN(F, 10, AF, PC_RST, GPIO_OUT), + MXC_DEFINE_PIN(F, 11, AF, PC_BVD2, GPIO_IN), + MXC_DEFINE_PIN(F, 12, AF, PC_BVD1, GPIO_IN), + MXC_DEFINE_PIN(F, 13, AF, PC_VS2, GPIO_IN), + MXC_DEFINE_PIN(F, 14, AF, PC_VS1, GPIO_IN), + MXC_DEFINE_PIN(F, 15, PF, CLKO, GPIO_OUT), + MXC_DEFINE_PIN(F, 16, AF, PC_PWRON, GPIO_IN), + MXC_DEFINE_PIN(F, 17, AF, PC_READY, GPIO_IN), + MXC_DEFINE_PIN(F, 18, AF, PC_WAIT_B, GPIO_IN), + MXC_DEFINE_PIN(F, 19, AF, PC_CD2_B, GPIO_IN), + MXC_DEFINE_PIN(F, 20, AF, PC_CD1_B, GPIO_IN), + + MXC_DEFINE_PIN(F, 23, AIN, FEC_TX_EN, GPIO_OUT), +}; + +#define _PIN_NAME(v) \ + case v: \ + name = #v; \ + break + +static inline const char *MX27_PIN_NAME(int iomux) +{ + const char *name = ""; + switch (iomux) { + _PIN_NAME(PA0_PF_USBH2_CLK); + _PIN_NAME(PA1_PF_USBH2_DIR); + _PIN_NAME(PA2_PF_USBH2_DATA7); + _PIN_NAME(PA3_PF_USBH2_NXT); + _PIN_NAME(PA4_PF_USBH2_STP); + _PIN_NAME(PA5_PF_LSCLK); + _PIN_NAME(PA6_PF_LD0); + _PIN_NAME(PA7_PF_LD1); + _PIN_NAME(PA8_PF_LD2); + _PIN_NAME(PA9_PF_LD3); + _PIN_NAME(PA10_PF_LD4); + _PIN_NAME(PA11_PF_LD5); + _PIN_NAME(PA12_PF_LD6); + _PIN_NAME(PA13_PF_LD7); + _PIN_NAME(PA14_PF_LD8); + _PIN_NAME(PA15_PF_LD9); + _PIN_NAME(PA16_PF_LD10); + _PIN_NAME(PA17_PF_LD11); + _PIN_NAME(PA18_PF_LD12); + _PIN_NAME(PA19_PF_LD13); + _PIN_NAME(PA20_PF_LD14); + _PIN_NAME(PA21_PF_LD15); + _PIN_NAME(PA22_PF_LD16); + _PIN_NAME(PA23_PF_LD17); + _PIN_NAME(PA24_PF_REV); + _PIN_NAME(PA25_PF_CLS); + _PIN_NAME(PA26_PF_PS); + _PIN_NAME(PA27_PF_SPL_SPR); + _PIN_NAME(PA28_PF_HSYNC); + _PIN_NAME(PA29_PF_VSYNC); + _PIN_NAME(PA30_PF_CONTRAST); + _PIN_NAME(PA31_PF_OE_ACD); + _PIN_NAME(PB4_PF_SD2_D0); + _PIN_NAME(PB5_PF_SD2_D1); + _PIN_NAME(PB6_PF_SD2_D2); + _PIN_NAME(PB7_PF_SD2_D3); + _PIN_NAME(PB8_PF_SD2_CMD); + _PIN_NAME(PB9_PF_SD2_CLK); + _PIN_NAME(PB10_PF_CSI_D0); + _PIN_NAME(PB10_AF_UART6_TXD); + _PIN_NAME(PB11_PF_CSI_D1); + _PIN_NAME(PB11_AF_UART6_RXD); + _PIN_NAME(PB12_PF_CSI_D2); + _PIN_NAME(PB12_AF_UART6_CTS); + _PIN_NAME(PB13_PF_CSI_D3); + _PIN_NAME(PB13_AF_UART6_RTS); + _PIN_NAME(PB14_PF_CSI_D4); + _PIN_NAME(PB15_PF_CSI_MCLK); + _PIN_NAME(PB16_PF_CSI_PIXCLK); + _PIN_NAME(PB17_PF_CSI_D5); + _PIN_NAME(PB18_PF_CSI_D6); + _PIN_NAME(PB18_AF_UART5_TXD); + _PIN_NAME(PB19_PF_CSI_D7); + _PIN_NAME(PB19_AF_UART5_RXD); + _PIN_NAME(PB20_PF_CSI_VSYNC); + _PIN_NAME(PB20_AF_UART5_CTS); + _PIN_NAME(PB21_PF_CSI_HSYNC); + _PIN_NAME(PB21_AF_UART5_RTS); + _PIN_NAME(PB22_PF_USBH1_SUSP); + _PIN_NAME(PB23_PF_USB_PWR); + _PIN_NAME(PB24_PF_USB_OC_B); + _PIN_NAME(PB25_PF_USBH1_RCV); + _PIN_NAME(PB26_PF_USBH1_FS); + _PIN_NAME(PB27_PF_USBH1_OE_B); + _PIN_NAME(PB28_PF_USBH1_TXDM); + _PIN_NAME(PB29_PF_USBH1_TXDP); + _PIN_NAME(PB30_PF_USBH1_RXDM); + _PIN_NAME(PB31_PF_USBH1_RXDP); + _PIN_NAME(PB26_AF_UART4_RTS); + _PIN_NAME(PB28_AF_UART4_TXD); + _PIN_NAME(PB29_AF_UART4_CTS); + _PIN_NAME(PB31_AF_UART4_RXD); + _PIN_NAME(PC5_PF_I2C2_SDA); + _PIN_NAME(PC6_PF_I2C2_SCL); + _PIN_NAME(PC7_PF_USBOTG_DATA5); + _PIN_NAME(PC8_PF_USBOTG_DATA6); + _PIN_NAME(PC9_PF_USBOTG_DATA0); + _PIN_NAME(PC10_PF_USBOTG_DATA2); + _PIN_NAME(PC11_PF_USBOTG_DATA1); + _PIN_NAME(PC12_PF_USBOTG_DATA4); + _PIN_NAME(PC13_PF_USBOTG_DATA3); + _PIN_NAME(PC16_PF_SSI4_FS); + _PIN_NAME(PC17_PF_SSI4_RXD); + _PIN_NAME(PC18_PF_SSI4_TXD); + _PIN_NAME(PC19_PF_SSI4_CLK); + _PIN_NAME(PC20_PF_SSI1_FS); + _PIN_NAME(PC21_PF_SSI1_RXD); + _PIN_NAME(PC22_PF_SSI1_TXD); + _PIN_NAME(PC23_PF_SSI1_CLK); + _PIN_NAME(PC24_PF_SSI2_FS); + _PIN_NAME(PC25_PF_SSI2_RXD); + _PIN_NAME(PC26_PF_SSI2_TXD); + _PIN_NAME(PC27_PF_SSI2_CLK); + _PIN_NAME(PC28_PF_SSI3_FS); + _PIN_NAME(PC29_PF_SSI3_RXD); + _PIN_NAME(PC30_PF_SSI3_TXD); + _PIN_NAME(PC31_PF_SSI3_CLK); + _PIN_NAME(PD0_AIN_FEC_TXD0); + _PIN_NAME(PD1_AIN_FEC_TXD1); + _PIN_NAME(PD2_AIN_FEC_TXD2); + _PIN_NAME(PD3_AIN_FEC_TXD3); + _PIN_NAME(PD4_AOUT_FEC_RX_ER); + _PIN_NAME(PD5_AOUT_FEC_RXD1); + _PIN_NAME(PD6_AOUT_FEC_RXD2); + _PIN_NAME(PD7_AOUT_FEC_RXD3); + _PIN_NAME(PD8_AF_FEC_MDIO); + _PIN_NAME(PD9_AIN_FEC_MDC); + _PIN_NAME(PD10_AOUT_FEC_CRS); + _PIN_NAME(PD11_AOUT_FEC_TX_CLK); + _PIN_NAME(PD12_AOUT_FEC_RXD0); + _PIN_NAME(PD13_AOUT_FEC_RX_DV); + _PIN_NAME(PD14_AOUT_FEC_RX_CLK); + _PIN_NAME(PD15_AOUT_FEC_COL); + _PIN_NAME(PD16_AIN_FEC_TX_ER); + _PIN_NAME(PD17_PF_I2C_DATA); + _PIN_NAME(PD18_PF_I2C_CLK); + _PIN_NAME(PD19_AF_USBH2_DATA4); + _PIN_NAME(PD20_AF_USBH2_DATA3); + _PIN_NAME(PD21_AF_USBH2_DATA6); + _PIN_NAME(PD22_AF_USBH2_DATA0); + _PIN_NAME(PD23_AF_USBH2_DATA2); + _PIN_NAME(PD24_AF_USBH2_DATA1); + _PIN_NAME(PD25_PF_CSPI1_RDY); + _PIN_NAME(PD26_PF_CSPI1_SS2); + _PIN_NAME(PD26_AF_USBH2_DATA5); + _PIN_NAME(PD27_PF_CSPI1_SS1); + _PIN_NAME(PD28_PF_CSPI1_SS0); + _PIN_NAME(PD29_PF_CSPI1_SCLK); + _PIN_NAME(PD30_PF_CSPI1_MISO); + _PIN_NAME(PD31_PF_CSPI1_MOSI); + _PIN_NAME(PF23_AIN_FEC_TX_EN); + _PIN_NAME(PE0_PF_USBOTG_NXT); + _PIN_NAME(PE1_PF_USBOTG_STP); + _PIN_NAME(PE2_PF_USBOTG_DIR); + _PIN_NAME(PE3_PF_UART2_CTS); + _PIN_NAME(PE4_PF_UART2_RTS); + _PIN_NAME(PE6_PF_UART2_TXD); + _PIN_NAME(PE7_PF_UART2_RXD); + _PIN_NAME(PE8_PF_UART3_TXD); + _PIN_NAME(PE9_PF_UART3_RXD); + _PIN_NAME(PE10_PF_UART3_CTS); + _PIN_NAME(PE11_PF_UART3_RTS); + _PIN_NAME(PE12_PF_UART1_TXD); + _PIN_NAME(PE13_PF_UART1_RXD); + _PIN_NAME(PE14_PF_UART1_CTS); + _PIN_NAME(PE15_PF_UART1_RTS); + _PIN_NAME(PE16_AF_OWIRE); + _PIN_NAME(PE16_PF_RTCK); + _PIN_NAME(PE18_PF_SD1_D0); + _PIN_NAME(PE18_AF_CSPI3_MISO); + _PIN_NAME(PE19_PF_SD1_D1); + _PIN_NAME(PE20_PF_SD1_D2); + _PIN_NAME(PE21_PF_SD1_D3); + _PIN_NAME(PE21_AF_CSPI3_SS); + _PIN_NAME(PE22_PF_SD1_CMD); + _PIN_NAME(PE22_AF_CSPI3_MOSI); + _PIN_NAME(PE23_PF_SD1_CLK); + _PIN_NAME(PE23_AF_CSPI3_SCLK); + _PIN_NAME(PE24_PF_USBOTG_CLK); + _PIN_NAME(PE25_PF_USBOTG_DATA7); + } + return name; +} + +#endif /* _MXC_GPIO_MX2_H */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/include/mach/mxc_pm.h linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/mxc_pm.h --- linux-2.6.28/arch/arm/mach-mx2/include/mach/mxc_pm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/mxc_pm.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,229 @@ + +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup LPMD Low-Level Power Management Driver + */ + +/*! + * @file arch-mxc/mxc_pm.h + * + * @brief This file contains the chip level configuration details and + * public API declarations for CRM_AP module + * + * @ingroup LPMD + */ + +#ifndef __ASM_ARCH_MXC_PM_H__ +#define __ASM_ARCH_MXC_PM_H__ + +#define WAIT_MODE 111 +#define DOZE_MODE 112 +#define STOP_MODE 113 +#define DSM_MODE 114 + +#define GATE_STOP_WAIT 9 +#define GATE_STOP 10 + +/* + * Used for MHz conversion + */ +#define MEGA_HERTZ 1000000 + +/* + * If invalid frequency value other than the following + * CORE_133 - ARM desired to run @133MHz, LoV (1.2V) + * CORE_266 - ARM desired to run @266MHz, LoV (1.2V) + * CORE_399 - ARM desired to run @399MHz, LoV (1.2V) + * CORE_532 - ARM desired to run @133MHz, HiV (1.6V) + * are passed then this error is returned, + */ +#define ERR_FREQ_INVALID 1 + +/* + * If PLL freq is less than desired ARM frequency during Integer + * DVFS, then return this error + */ +#define PLL_LESS_ARM_ERR 2 + +/* + * Frequency change within the same-lo voltage is not approved. + * Inorder to do Integer DFS, move to the high voltage range and + * then set LFDF and move to the low voltage range + */ +#define INT_DFS_LOW_NOT_ALLOW 3 + +/* + * If the desired AHB or IPG exceeds 133MHz or 66.5MHz respectively, + * then return this error + */ +#define AHB_IPG_EXCEED_LIMIT 4 + +/* + * If the desired ARM frequency is too low to get by PLL scaling + * and the mxc_pm_pllscale API is called, return this error: + */ +#define PLL_DVFS_FREQ_TOO_LOW 5 + +/* + * Invalid frequencies requested + */ +#define MXC_PM_INVALID_PARAM 6 + +/* + * If AHB and/or IPG frequencies are greater than maximum allowed + */ +#define FREQ_OUT_OF_RANGE 2 + +/* + * If AHB and/or IPG frequencies are other than 100 or 50Mhz + */ +#define BUS_FREQ_INVALID 2 + +/* + * If MAX_PDF is greater than max value (8) then return this error + */ +#define AHB_MAX_DIV_ERR 3 + +/* + * If IPG_PDF is greater than max value (2) then return this error + */ +#define IPG_MAX_DIV_ERR 4 + +/* + * If ARM freq is out of range i.e., less than 133 or greater than + * 399 then return this error + */ +#define INVALID_ARM_FREQ 5 + +/* + * This file includes all platform APIs. Some of the APIs are not + * appicable to some platforms. So, this error is used to indicate + * that a particular API is not available + */ +#define MXC_PM_API_NOT_SUPPORTED 6 + +/*! + * Additional define for stop mode + */ +#define PM_SUSPEND_STOP ((__force suspend_state_t) 2) + +/*! + * CKOH pins configuration + */ +#define CKOH_AP_SEL 1 +#define CKOH_AHB_SEL 2 +#define CKOH_IP_SEL 3 + +/*! + * Defines for Stop and DSM mode acknowledgements + */ +#define MXC_PM_LOWPWR_ACK_SDMA 0x01 +#define MXC_PM_LOWPWR_ACK_IPU 0x02 +#define MXC_PM_LOWPWR_ACK_MAX 0x04 +#define MXC_PM_LOWPWR_ACK_MQSPI 0x08 +#define MXC_PM_LOWPWR_ACK_USB 0x10 +#define MXC_PM_LOWPWR_ACK_RTIC 0x20 + +/* + * PMIC configuration + */ +#define MXC_PMIC_1_2_VOLT 0xC +#define MXC_PMIC_1_6_VOLT 0x1C +#define MXC_PMIC_1_0_VOLT 0x4 +#define MXC_PMIC_DVS_SPEED 0x3 + +/*! + * Implementing Level 1 CRM Gate Control. Level 2 gate control + * is provided at module level using LPMD registers + * + * @param group The desired clock gate control register bits. + * Possible values are 0 through 6 + * @param opt The desired option requesting clock to run during stop + * and wait modes or just during the stop mode. Possible + * values are GATE_STOP_WAIT and GATE_STOP. + * + */ +void mxc_pm_clockgate(int group, int opt); + +/*! + * Implementing steps required to transition to low-power modes + * + * @param mode The desired low-power mode. Possible values are, + * WAIT_MODE, STOP_MODE or DSM_MODE + * + */ +void mxc_pm_lowpower(int mode); + +/*! + * Enables acknowledgement from module when entering stop or DSM mode. + * + * @param ack The desired module acknowledgement to enable. + * + */ +void mxc_pm_lp_ack_enable(int ack); + +/*! + * Disables acknowledgement from module when entering stop or DSM mode. + * + * @param ack The desired module acknowledgement to disable. + * + */ +void mxc_pm_lp_ack_disable(int ack); + +/*! + * Implementing steps required to set Integer Scaling + * + * @param armfreq The desired ARM frequency. AHB and IP + * frequency are changed depending on ARM + * frequency and the divider values. + * @param ahbfreq The desired AHB frequency + * @param ipfreq The desired IP frequency + * + * @return Returns 0 on success or + * Returns -PLL_LESS_ARM_ERR if pllfreq is less than + * desired core freq + */ +int mxc_pm_intscale(long armfreq, long ahbfreq, long ipfreq); + +/*! + * To calculate MFI, MFN, MFD values. Using this the output frequency + * whose value is calculated using, + * 2 * REF_FREQ * (MF / PDF), where + * REF_FREQ is 26 Mhz + * MF = MFI + (MFN + MFD) + * PDF is assumed to be 1 + * + * @param armfreq The desired ARM frequency + * @param ahbfreq The desired AHB frequency + * @param ipfreq The desired IP frequency + * + * @return Returns 0 on success or + * Returns -1 on error + */ +int mxc_pm_pllscale(long armfreq, long ahbfreq, long ipfreq); + +/*! + * To change AP core frequency and/or voltage suitably + * + * @param armfreq The desired ARM frequency + * @param ahbfreq The desired AHB frequency + * @param ipfreq The desired IP frequency + * + * @return Returns -ERR_FREQ_INVALID on failure + * Returns 0 on success + */ +int mxc_pm_dvfs(unsigned long armfreq, long ahbfreq, long ipfreq); + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/include/mach/mxc_v4l2.h linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/mxc_v4l2.h --- linux-2.6.28/arch/arm/mach-mx2/include/mach/mxc_v4l2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/include/mach/mxc_v4l2.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,56 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU Lesser General + * Public License. You may obtain a copy of the GNU Lesser General + * Public License Version 2.1 or later at the following locations: + * + * http://www.opensource.org/licenses/lgpl-license.html + * http://www.gnu.org/copyleft/lgpl.html + */ + +/*! + * @file arch-mxc/mxc_v4l2.h + * + * @brief mxc V4L2 private structures + * + * @ingroup MXC_V4L2_CAPTURE + */ + +#ifndef __ASM_ARCH_MXC_V4L2_H__ +#define __ASM_ARCH_MXC_V4L2_H__ + +#define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_MXC_FLICKER (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_MXC_TEAR_PROTECT (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_MXC_GAIN_LIMIT (V4L2_CID_PRIVATE_BASE + 4) + +/* V4L2_CID_MXC_ROT values */ + +#define V4L2_MXC_ROTATE_NONE 0 +#define V4L2_MXC_ROTATE_VERT_FLIP 1 +#define V4L2_MXC_ROTATE_HORIZ_FLIP 2 +#define V4L2_MXC_ROTATE_180 3 +#define V4L2_MXC_ROTATE_90_RIGHT 4 +#define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5 +#define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6 +#define V4L2_MXC_ROTATE_90_LEFT 7 + +/* V4L2_CID_MXC_FLICKER values */ + +#define V4L2_MXC_FLICKER_DISABLE 0 +#define V4L2_MXC_FLICKER_60HZ 1 +#define V4L2_MXC_FLICKER_50HZ 2 +#define V4L2_MXC_FLICKER_AUTO 3 + +struct v4l2_mxc_offset { + unsigned int y_offset; + unsigned int u_offset; + unsigned int v_offset; + unsigned int qp_offset; +}; + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/karo-tx27.c linux-2.6.28-karo/arch/arm/mach-mx2/karo-tx27.c --- linux-2.6.28/arch/arm/mach-mx2/karo-tx27.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/karo-tx27.c 2009-03-11 13:31:40.000000000 +0100 @@ -0,0 +1,1556 @@ +/* + * arch/arm/mach-mx27/karo-tx27.c + * + * Copyright (C) 2008 Lothar Wassmann + * + * based on: arch/arm/mach-mx27ads.c (C) Freescale Semiconductor, 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crm_regs.h" +#include "devices.h" + +#ifdef DEBUG +int tx27_debug = 1; +#define dbg_lvl(n) ((n) < tx27_debug) +module_param(tx27_debug, int, S_IRUGO | S_IWUSR); + +#define DBG(lvl, fmt...) do { if (dbg_lvl(lvl)) printk(KERN_DEBUG fmt); } while (0) +#else +int tx27_debug; +#define dbg_lvl(n) 0 +#define DBG(lvl, fmt...) do { } while (0) +#endif + +#include "karo.h" + +#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) +static int tx27_uart_pins[][4] = { + { + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + },{ + PE6_PF_UART2_TXD, + PE7_PF_UART2_RXD, + PE3_PF_UART2_CTS, + PE4_PF_UART2_RTS, + },{ + PE8_PF_UART3_TXD, + PE9_PF_UART3_RXD, + PE10_PF_UART3_CTS, + PE11_PF_UART3_RTS, + },{ + PB28_AF_UART4_TXD, + PB31_AF_UART4_RXD, + PB29_AF_UART4_CTS, + PB26_AF_UART4_RTS, + },{ + PB18_AF_UART5_TXD, + PB19_AF_UART5_RXD, + PB20_AF_UART5_CTS, + PB21_AF_UART5_RTS, + },{ + PB10_AF_UART6_TXD, + PB11_AF_UART6_RXD, + PB12_AF_UART6_CTS, + PB13_AF_UART6_RTS, + }, +}; + +static int tx27_uart_init(struct platform_device *pdev) +{ + DBG(0, "%s: \n", __FUNCTION__); + return mxc_gpio_setup_multiple_pins(tx27_uart_pins[pdev->id], + ARRAY_SIZE(tx27_uart_pins[pdev->id]), "UART"); +} + +static int tx27_uart_exit(struct platform_device *pdev) +{ + DBG(0, "%s: \n", __FUNCTION__); + mxc_gpio_release_multiple_pins(tx27_uart_pins[pdev->id], + ARRAY_SIZE(tx27_uart_pins[pdev->id])); + return 0; +} + +static struct imxuart_platform_data tx27_uart_ports[] = { + { + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + },{ + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + },{ + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + },{ + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + },{ + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + },{ + .init = tx27_uart_init, + .exit = tx27_uart_exit, + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +static struct platform_device *tx27_uart_devices[] = { +#if UART1_ENABLED + &mxc_uart_device0, +#endif +#if UART2_ENABLED + &mxc_uart_device1, +#endif +#if UART3_ENABLED + &mxc_uart_device2, +#endif +#if UART4_ENABLED + &mxc_uart_device3, +#endif +#if UART5_ENABLED + &mxc_uart_device4, +#endif +#if UART6_ENABLED + &mxc_uart_device5, +#endif +}; +#endif + +#ifdef CONFIG_USB_EHCI_MXC + +#define SMSC_VENDOR_ID 0x0424 +#define USB3317_PROD_ID 0x0006 + +static int usb3317_set_vbus_power(void __iomem *view, int on) +{ + int vid, pid, ret = 0; + + ret = ulpi_read(ISP1504_VID_HIGH, view); + if (ret < 0) { + goto err; + } + vid = ret << 8; + + ret = ulpi_read(ISP1504_VID_LOW, view); + if (ret < 0) { + goto err; + } + vid |= ret; + + ret = ulpi_read(ISP1504_PID_HIGH, view); + if (ret < 0) { + goto err; + } + pid = ret << 8; + + ret = ulpi_read(ISP1504_PID_LOW, view); + if (ret < 0) { + goto err; + } + pid |= ret; + + pr_info("ULPI Vendor ID 0x%x Product ID 0x%x\n", vid, pid); + if (vid != SMSC_VENDOR_ID || pid != USB3317_PROD_ID) { + pr_err("No USB3317 found\n"); + return -1; + } + + if (on) { + ret = ulpi_set(DRV_VBUS_EXT | /* enable external Vbus */ + DRV_VBUS | /* enable internal Vbus */ + USE_EXT_VBUS_IND | /* use external indicator */ + CHRG_VBUS, /* charge Vbus */ + ISP1504_OTGCTL, view); + } else { + ret = ulpi_clear(DRV_VBUS_EXT | /* disable external Vbus */ + DRV_VBUS, /* disable internal Vbus */ + ISP1504_OTGCTL, view); + + ret |= ulpi_set(USE_EXT_VBUS_IND | /* use external indicator */ + DISCHRG_VBUS, /* discharge Vbus */ + ISP1504_OTGCTL, view); + } + return 0; + + err: + printk(KERN_ERR "ULPI read failed with error %d\n", ret); + return ret; +} + +static int tx27_usb_host_init(struct platform_device *pdev) +{ + int ret; + u32 temp; + unsigned long freq; + unsigned long flags; + struct clk *usb_clk = clk_get(NULL, "usb_clk"); + + if (IS_ERR(usb_clk)) { + ret = PTR_ERR(usb_clk); + printk(KERN_ERR "Failed to get usb_clk: %d\n", ret); + goto err; + } + + ret = gpio_usbh2_active(); + if (ret != 0) { + return ret; + } + + freq = clk_get_rate(usb_clk); + if ((freq < 59999000) || (freq > 60001000)) { + printk(KERN_ERR "USB_CLK=%lu.%03luMHz, should be 60MHz\n", + freq / 1000000, freq / 1000 % 1000); + ret = clk_set_rate(usb_clk, 60000000); + if (ret != 0) { + printk(KERN_ERR "Failed to set usb_clk rate: %d\n", ret); + } + } + clk_put(usb_clk); + if (ret != 0) { + goto err; + } + + local_irq_save(flags); + temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x600); + temp &= ~(3 << 21); + temp |= (1 << 5) | (1 << 16) | (1 << 19) | (1 << 20); + writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x600); + local_irq_restore(flags); + + temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x584); + temp &= ~(3 << 30); + temp |= 2 << 30; + temp = 0x88001215; + /* select ULPI transceiver */ + DBG(0, "%s: Changing USBH2_PORTSC1[%08lx] from %08x to %08x\n", __FUNCTION__, + OTG_BASE_ADDR + 0x584, readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x584), temp); + writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x584); + + mdelay(10); + + ret = usb3317_set_vbus_power(IO_ADDRESS(OTG_BASE_ADDR + 0x570), 1); + if (ret != 0) { +// goto err; + } + return 0; + + err: + gpio_usbh2_inactive(); + return ret; +} + +static int tx27_usb_host_exit(struct platform_device *pdev) +{ + gpio_usbh2_inactive(); + return 0; +} + +static struct mxc_usbh_platform_data tx27_usbh2_data = { + .init = tx27_usb_host_init, + .exit = tx27_usb_host_exit, +}; + +int tx27_usbh2_init(void) +{ + int ret; + + ret = mxc_register_device(&mxc_ehci2, &tx27_usbh2_data); + return ret; +} +device_initcall(tx27_usbh2_init); + +static int tx27_usb_otg_init(struct platform_device *pdev) +{ + int ret; + u32 temp; + unsigned long freq; + unsigned long flags; + struct clk *usb_clk = clk_get(NULL, "usb_clk"); + + if (IS_ERR(usb_clk)) { + ret = PTR_ERR(usb_clk); + printk(KERN_ERR "Failed to get usb_clk: %d\n", ret); + goto err; + } + + ret = gpio_usbotg_hs_active(); + if (ret != 0) { + return ret; + } + + freq = clk_get_rate(usb_clk); + if ((freq < 59999000) || (freq > 60001000)) { + printk(KERN_ERR "USB_CLK=%lu.%03luMHz, should be 60MHz\n", + freq / 1000000, freq / 1000 % 1000); + ret = clk_set_rate(usb_clk, 60000000); + if (ret != 0) { + printk(KERN_ERR "Failed to set usb_clk rate: %d\n", ret); + } + } + clk_put(usb_clk); + if (ret != 0) { + goto err; + } + + local_irq_save(flags); + temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x600); + temp &= ~(3 << 21); + temp |= (1 << 5) | (1 << 16) | (1 << 19) | (1 << 20); + writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x600); + local_irq_restore(flags); + + temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x184); + temp &= ~(3 << 30); + temp |= 2 << 30; + temp = 0x88001215; + /* select ULPI transceiver */ + DBG(0, "%s: Changing USBOTG_PORTSC1[%08lx] from %08x to %08x\n", __FUNCTION__, + OTG_BASE_ADDR + 0x184, readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x184), temp); + writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x184); + + mdelay(10); + + ret = usb3317_set_vbus_power(IO_ADDRESS(OTG_BASE_ADDR + 0x170), 1); + if (ret != 0) { +// goto err; + } + return 0; + + err: + gpio_usbotg_hs_active(); + return ret; +} + +static int tx27_usb_otg_exit(struct platform_device *pdev) +{ + gpio_usbotg_hs_inactive(); + return 0; +} + +static struct mxc_usbh_platform_data tx27_otg_data = { + .init = tx27_usb_otg_init, + .exit = tx27_usb_otg_exit, +}; + +int tx27_otg_init(void) +{ + int ret; + + ret = mxc_register_device(&mxc_otg, &tx27_otg_data); + return ret; +} +device_initcall(tx27_otg_init); +#endif // CONFIG_USB_EHCI_MXC + +#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE) +static struct resource fec_resources[] = { + { + .start = FEC_BASE_ADDR, + .end = FEC_BASE_ADDR + 0x18f, + .flags = IORESOURCE_MEM, + }, + { + .start = FEC_BASE_ADDR + 0x200, + .end = FEC_BASE_ADDR + 0x2e3, + .flags = IORESOURCE_MEM, + }, + { + .start = MXC_INT_FEC, + .end = MXC_INT_FEC, + .flags = IORESOURCE_IRQ, + }, +#ifdef FEC_MII_IRQ + { + .start = IRQ_GPIOD(16), + .end = IRQ_GPIOD(16), + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct clk *fec_clk; +static int tx27_fec_suspend(struct platform_device *pdev) +{ + BUG_ON(fec_clk == NULL); + DBG(1, "%s: Switching FEC PHY off\n", __FUNCTION__); + gpio_fec_inactive(); + clk_disable(fec_clk); + return 0; +} + +static int tx27_fec_resume(struct platform_device *pdev) +{ + BUG_ON(fec_clk == NULL); + DBG(1, "%s: Switching FEC PHY on\n", __FUNCTION__); + clk_enable(fec_clk); + gpio_fec_active(); + return 0; +} + +static int fec_arch_init(struct platform_device *pdev) +{ + int ret; + + DBG(0, "%s: Activating FEC GPIOs\n", __FUNCTION__); + dump_regs(); + ret = gpio_fec_active(); + if (ret) { + printk(KERN_ERR "%s: could not enable FEC gpios: %d\n", __FUNCTION__, ret); + return ret; + } + BUG_ON(fec_clk != NULL); + fec_clk = clk_get(&pdev->dev, "fec_clk"); + if (unlikely(IS_ERR(fec_clk))) { + printk(KERN_ERR "Failed to get fec_clk\n"); + return PTR_ERR(fec_clk); + } + DBG(0, "%s: Enabling FEC clock\n", __FUNCTION__); + clk_enable(fec_clk); + dump_regs(); + return 0; +} + +static void fec_arch_exit(struct platform_device *pdev) +{ + BUG_ON(fec_clk == NULL); + if (unlikely(IS_ERR(fec_clk))) { + printk(KERN_ERR "Failed to get fec_clk\n"); + return; + } + DBG(0, "%s: Disabling FEC clock\n", __FUNCTION__); + clk_disable(fec_clk); + clk_put(fec_clk); + fec_clk = NULL; + DBG(0, "%s: Deactivating FEC GPIOs\n", __FUNCTION__); + gpio_fec_inactive(); +} + +static struct fec_enet_platform_data fec_data = { + .arch_init = fec_arch_init, + .arch_exit = fec_arch_exit, + .suspend = tx27_fec_suspend, + .resume = tx27_fec_resume, +}; + +static struct platform_device fec_device = { + .name = "fec_enet", + .id = -1, + .num_resources = ARRAY_SIZE(fec_resources), + .resource = fec_resources, + .dev = { + .platform_data = &fec_data, + .coherent_dma_mask = 0xFFFFFFFF, + }, +}; +#endif + +#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) +/* tx27 gpio keys driver */ +struct gpio_keys_button tx27_gpio_keys[] = { + { + .code = KEY_POWER, + .gpio = GPIO_PORTB + 24, + .active_low = 0, + .desc = "Power Button", + .type = EV_KEY, /* input event type (EV_KEY, EV_SW) */ + .wakeup = 1, /* configure the button as a wake-up source */ + .debounce_interval = 1, /* debounce ticks interval in msecs */ + }, +}; + +struct gpio_keys_platform_data tx27_gpio_keys_pdata = { + .buttons = tx27_gpio_keys, + .nbuttons = ARRAY_SIZE(tx27_gpio_keys), +}; + +static struct platform_device tx27_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &tx27_gpio_keys_pdata, + }, +}; +#endif + +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) +static struct gpio_led tx27_leds[] = { + { + .name = "Power-LED", + .default_trigger = "heartbeat", + .gpio = GPIO_PORTF + 13, + }, +}; + +static struct gpio_led_platform_data tx27_led_data = { + .leds = tx27_leds, + .num_leds = ARRAY_SIZE(tx27_leds), +}; + +static struct platform_device tx27_led_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &tx27_led_data, + }, +}; +#endif + +#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE) +/*! + * This array is used for mapping mx27 ADS keypad scancodes to input keyboard + * keycodes. + */ +static u16 tx27_kpd_keycodes[] = { + KEY_POWER, +}; + +static struct keypad_data tx27_keypad = { + .rowmax = 1, + .colmax = 1, + .irq = MXC_INT_KPP, + .learning = 0, + //.delay = 2, /* unused in the driver! */ + .matrix = tx27_kpd_keycodes, +}; + +static struct resource tx27_kpp_resources[] = { + { + .start = MXC_INT_KPP, + .end = MXC_INT_KPP, + .flags = IORESOURCE_IRQ, + }, +}; + +/* tx27 keypad driver */ +static struct platform_device tx27_keypad_device = { + .name = "mxc_keypad", + .id = 0, + .num_resources = ARRAY_SIZE(tx27_kpp_resources), + .resource = tx27_kpp_resources, + .dev = { + .platform_data = &tx27_keypad, + }, +}; +#endif + +/* MTD NAND flash */ +#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) +#if 0 +static struct mtd_partition tx27_nand_partitions[] = { + { + .name = "RedBoot", + .offset = 0, + .size = 0x00040000, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0x001A0000, + }, + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = 0x07E000000, + }, + { + .name = "FIS directory", + .offset = MTDPART_OFS_APPEND, + .size = 0x00003000, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "RedBoot config", + .offset = MTDPART_OFS_APPEND, + .size = 0x00001000, + .mask_flags = MTD_WRITEABLE, + }, +}; + +static struct flash_platform_data tx27_nand_data = { + .map_name = "nand_probe", + .name = "tx27-nand", + .parts = tx27_nand_partitions, + .nr_parts = ARRAY_SIZE(tx27_nand_partitions), + .width = 1, +}; +#else +static struct mxc_nand_platform_data tx27_nand_data = { + .hw_ecc = 1, + .width = 1, +}; +#endif + +static struct resource tx27_nand_resources[] = { + { + .start = NFC_BASE_ADDR, + .end = NFC_BASE_ADDR + 0xfff, + .flags = IORESOURCE_MEM + }, { + .start = MXC_INT_NANDFC, + .end = MXC_INT_NANDFC, + .flags = IORESOURCE_IRQ + }, +}; + +static struct platform_device tx27_nand_mtd_device = { + .name = "mxc_nand", + .id = 0, + .num_resources = ARRAY_SIZE(tx27_nand_resources), + .resource = tx27_nand_resources, + .dev = { + .platform_data = &tx27_nand_data, + }, +}; +#endif + +#if defined(CONFIG_FB_IMX) || defined(CONFIG_FB_IMX_MODULE) +/* + * Setup GPIO for LCDC device to be active + * + */ +static unsigned int mx27_lcdc_gpios[] = { + MXC_PIN(A, 30, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA30 */ + MXC_PIN(A, 25, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA25 */ + MXC_PIN(A, 26, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA26 */ + MXC_PIN(A, 24, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA24 */ + MXC_PIN(A, 27, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA27 */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA23_PF_LD17, + PA28_PF_HSYNC, + PA29_PF_VSYNC, + PA31_PF_OE_ACD, +}; + +static int tx27_gpio_lcdc_active(struct platform_device *dev) +{ + int ret; + + DBG(0, "%s: Setting up GPIO pins for LCD\n", __FUNCTION__); + ret = mxc_gpio_setup_multiple_pins(mx27_lcdc_gpios, + ARRAY_SIZE(mx27_lcdc_gpios), "LCD"); + if (ret) { + DBG(0, "%s: Failed to setup GPIO pins for LCD: %d\n", + __FUNCTION__, ret); + return ret; + } + return 0; +} + +/* + * Setup GPIO for LCDC device to be inactive + * + */ +static int tx27_gpio_lcdc_inactive(struct platform_device *dev) +{ + mxc_gpio_release_multiple_pins(mx27_lcdc_gpios, + ARRAY_SIZE(mx27_lcdc_gpios)); + return 0; +} + +static struct imx_fb_platform_data tx27_fb_data[] __initdata = { + [0] = { + //.fb_mode = "Xenarc_700_Y-16", + .init = tx27_gpio_lcdc_active, + .exit = tx27_gpio_lcdc_inactive, + .lcd_power = NULL, + .backlight_power = NULL, + + .pixclock = 34576, + .xres = 640, + .yres = 480, + + .bpp = 16, + + .hsync_len = 64, + .right_margin = 138 + 1, + .left_margin = 118 + 3, + + .vsync_len = 7, + .upper_margin = 44, + .lower_margin = 44, +#if 0 + /* currently not used by driver! */ + .sync = ((0*FB_SYNC_HOR_HIGH_ACT) | + (0*FB_SYNC_VERT_HIGH_ACT) | + (1*FB_SYNC_OE_ACT_HIGH)), +#else + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | + PCR_BPIX_16 | PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, + .dmacr = 0x80040060, +#endif + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + + .fixed_screen_cpu = NULL, + }, + [1] = { + //.fb_mode = "SHARP LQ10D42-16", + .init = tx27_gpio_lcdc_active, + .exit = tx27_gpio_lcdc_inactive, + .lcd_power = NULL, + .backlight_power = NULL, + + .pixclock = 34576, + .xres = 640, + .yres = 480, + +#ifdef USE_18BPP + .bpp = 32, +#else + .bpp = 16, +#endif + .hsync_len = 64, + .right_margin = 138 + 1, + .left_margin = 118 + 3, + + .vsync_len = 7, + .upper_margin = 28, + .lower_margin = 60, +#if 0 + /* currently not used by driver! */ + .sync = ((0*FB_SYNC_HOR_HIGH_ACT) | + (0*FB_SYNC_VERT_HIGH_ACT) | + (1*FB_SYNC_OE_ACT_HIGH)), +#else + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | +#ifdef USE_18BPP + PCR_BPIX_18 | PCR_END_SEL | PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, +#else + PCR_BPIX_16 | PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, +#endif + .dmacr = 0x80040060, +#endif + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + + .fixed_screen_cpu = NULL, + }, + [2] = { + //.fb_mode = "SHARP LQ104V1DG61-16", + .init = tx27_gpio_lcdc_active, + .exit = tx27_gpio_lcdc_inactive, + .lcd_power = NULL, + .backlight_power = NULL, + + .pixclock = 40000, + .xres = 640, + .yres = 480, + +#ifdef USE_18BPP + .bpp = 32, +#else + .bpp = 16, +#endif + .hsync_len = 32, + .right_margin = 32 + 1, + .left_margin = 0 + 3, + + .vsync_len = 35, + .upper_margin = 0, + .lower_margin = 0, +#if 0 + /* currently not used by driver! */ + .sync = ((0*FB_SYNC_HOR_HIGH_ACT) | + (0*FB_SYNC_VERT_HIGH_ACT) | + (1*FB_SYNC_OE_ACT_HIGH)), +#else + .pcr = PCR_TFT | PCR_COLOR | PCR_PBSIZ_8 | +#ifdef USE_18BPP + PCR_BPIX_18 | PCR_END_SEL | PCR_FLMPOL | PCR_LPPOL | PCR_SCLK_SEL, +#else + PCR_BPIX_16 | PCR_FLMPOL | PCR_LPPOL | PCR_CLKPOL | PCR_SCLK_SEL, +#endif + .dmacr = 0x80040060, +#endif + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + + .fixed_screen_cpu = NULL, + }, +}; + +int __init karo_tx27_fb_init(void) +{ + int ret; + + ret = mxc_register_device(&mxc_fb_device, &tx27_fb_data[0]); + if (ret != 0) { + DBG(0, "%s: Failed to register FB device: %d\n", __FUNCTION__, ret); + } + return ret; +} +device_initcall(karo_tx27_fb_init); +#endif + +#if defined(CONFIG_MMC_MXC) || defined(CONFIG_MMC_MXC_MODULE) +/*! + * Resource definition for the SDHC1 + */ +static struct resource tx27_sdhc1_resources[] = { + { + .start = SDHC1_BASE_ADDR, + .end = SDHC1_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MXC_INT_SDHC1, + .end = MXC_INT_SDHC1, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_GPIOC(21), + .end = IRQ_GPIOC(21), + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdhc1", + .start = DMA_REQ_SDHC1, + .end = DMA_REQ_SDHC1, + .flags = IORESOURCE_DMA + }, +}; + +/*! + * Resource definition for the SDHC2 + */ +static struct resource tx27_sdhc2_resources[] = { + { + .start = SDHC2_BASE_ADDR, + .end = SDHC2_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MXC_INT_SDHC2, + .end = MXC_INT_SDHC2, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_GPIOC(22), + .end = IRQ_GPIOC(22), + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdhc2", + .start = DMA_REQ_SDHC2, + .end = DMA_REQ_SDHC2, + .flags = IORESOURCE_DMA + }, +}; +static inline int tx27_mmc_get_irq(int id) +{ + int irq; + + switch (id) { + case 0: + irq = tx27_sdhc1_resources[2].start; + break; + case 1: + irq = tx27_sdhc2_resources[2].start; + break; + default: + BUG(); + } + return irq; +} + +static const char *tx27_mmc_irqdesc[] = { + "MMC card 0 detect", + "MMC card 1 detect", +}; + +static int tx27_mmc_init(struct device *dev, irqreturn_t (*mmc_detect_irq)(int, void *), + void *data) +{ + int err; + int id = to_platform_device(dev)->id; + struct mmc_host *host = data; + int irq = tx27_mmc_get_irq(id); + + err = gpio_sdhc_active(id); + if (err) { + return err; + } + + host->caps |= MMC_CAP_4_BIT_DATA; + + err = request_irq(irq, mmc_detect_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + tx27_mmc_irqdesc[id], data); + if (err) { + printk(KERN_ERR "%s: MMC/SD: can't request MMC card detect IRQ %d\n", + __FUNCTION__, irq); + return err; + } + device_set_wakeup_capable(dev, 1); + + return 0; +} + +static void tx27_mmc_exit(struct device *dev, void *data) +{ + int id = to_platform_device(dev)->id; + int irq = tx27_mmc_get_irq(id); + + free_irq(irq, data); + gpio_sdhc_inactive(id); +} + +static int tx27_mmc_suspend(struct device *dev, pm_message_t state) +{ + int id = to_platform_device(dev)->id; + int irq = tx27_mmc_get_irq(id); + + if (device_may_wakeup(dev)) { + DBG(0, "%s: Enabling IRQ %d wakeup\n", __FUNCTION__, irq); + return enable_irq_wake(irq); + } + return 0; +} + +static int tx27_mmc_resume(struct device *dev) +{ + int id = to_platform_device(dev)->id; + int irq = tx27_mmc_get_irq(id); + + if (device_may_wakeup(dev)) { + DBG(0, "%s: Disabling IRQ %d wakeup\n", __FUNCTION__, irq); + return disable_irq_wake(irq); + } + return 0; +} + +static struct imxmmc_platform_data tx27_sdhc1_data = { + //.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + //.min_clk = 150000, + //.max_clk = 25000000, + //.detect_delay = 20, + .init = tx27_mmc_init, + .exit = tx27_mmc_exit, + .suspend = tx27_mmc_suspend, + .resume = tx27_mmc_resume, +}; + +static struct imxmmc_platform_data tx27_sdhc2_data = { + //.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + //.min_clk = 150000, + //.max_clk = 25000000, + //.detect_delay = 20, + .init = tx27_mmc_init, + .exit = tx27_mmc_exit, +}; + +static struct platform_device tx27_sdhc1_device = { + .name = "imx-mmc", + .id = 0, + .dev = { + .platform_data = &tx27_sdhc1_data, + }, + .num_resources = ARRAY_SIZE(tx27_sdhc1_resources), + .resource = tx27_sdhc1_resources, +}; + +/*! Device Definition for MXC SDHC2 */ +static struct platform_device tx27_sdhc2_device = { + .name = "imx-mmc", + .id = 1, + .dev = { + .platform_data = &tx27_sdhc2_data, + }, + .num_resources = ARRAY_SIZE(tx27_sdhc2_resources), + .resource = tx27_sdhc2_resources, +}; +#endif + +#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE) +static struct resource mxcspi1_resources[] = { + [0] = { + .start = CSPI1_BASE_ADDR, + .end = CSPI1_BASE_ADDR + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MXC_INT_CSPI1, + .end = MXC_INT_CSPI1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mxc_spi_master mxcspi1_data = { + .maxchipselect = 2, + .spi_version = 0, +}; + +static struct platform_device mxcspi1_device = { + .name = "mxc_spi", + .id = 0, + .dev = { + .platform_data = &mxcspi1_data, + }, + .num_resources = ARRAY_SIZE(mxcspi1_resources), + .resource = mxcspi1_resources, +}; +#endif // defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE) + +#if defined(CONFIG_AC97_BUS) || defined(CONFIG_AC97_BUS_MODULE) +static u64 tx27_dma_mask = ~0UL; + +static void tx27_ac97_gpio_release(void) +{ + gpio_ac97_inactive(); +} + +static int tx27_ac97_init(struct platform_device *dev) +{ + int ret; + + DBG(0, "%s: \n", __FUNCTION__); + ret = gpio_ac97_active(); + if (ret) { + + return ret; + } + return 0; +} + +static void tx27_ac97_exit(struct platform_device *dev) +{ + DBG(0, "%s: Releasing AC97 GPIO pins\n", __FUNCTION__); + tx27_ac97_gpio_release(); +} + +static struct mxc_ac97_audio_ops tx27_ac97_ops = { + .init = tx27_ac97_init, + .exit = tx27_ac97_exit, + .startup = NULL, + .shutdown = NULL, + .suspend = NULL, + .resume = NULL, + .priv = NULL, +}; + +static struct platform_device ac97_device = { + .name = "mx27-ac97", + .id = -1, + .dev = { + .dma_mask = &tx27_dma_mask, + .coherent_dma_mask = ~0UL, + .platform_data = &tx27_ac97_ops, + }, +}; +#endif + +#if defined(CONFIG_RTC_DRV_DS13XX) || defined(CONFIG_RTC_DRV_DS13XX_MODULE) +static struct ds13xx_platform_data tx27_ds1339_data = { + .type = ds_1339, + .ctrl = 1 << 2, /* set INTCN to disable SQW output */ + .trc = DS1339_TRC_ENABLE | DS1339_DIODE_ENABLE | DS1339_TRC_250R, +}; + +static struct platform_device tx27_ds1339_device = { + .name = "rtc-ds13xx", + .dev = { + .platform_data = &tx27_ds1339_data, + }, +}; +#endif + +#if defined(CONFIG_VIDEO_MXC_EMMA_OUTPUT) || defined(CONFIG_VIDEO_MXC_EMMA_OUTPUT_MODULE) +static u64 mxc_emma_dmamask = 0xffffffffUL; + +static struct platform_device tx27_v4l2out_device = { + .name = "MXC Video Output", + .id = 0, + .dev = { + .dma_mask = &mxc_emma_dmamask, + .coherent_dma_mask = ~0UL, + }, +}; +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int mxc_i2c0_pins[] = { + /* + * it seems the data line misses a pullup, so we must enable + * the internal pullup as a local workaround + */ + PD17_PF_I2C_DATA, + PD18_PF_I2C_CLK, +}; + +static int karo_tx27_i2c_0_init(struct platform_device *pdev) +{ + return mxc_gpio_setup_multiple_pins(mxc_i2c0_pins, + ARRAY_SIZE(mxc_i2c0_pins), "I2C0"); +} + +static int karo_tx27_i2c_0_exit(struct platform_device *pdev) +{ + mxc_gpio_release_multiple_pins(mxc_i2c0_pins, + ARRAY_SIZE(mxc_i2c0_pins)); + + return 0; +} + +static struct imx_i2c_platform_data karo_tx27_i2c_0_data = { + .max_clk = 100000, + .init = karo_tx27_i2c_0_init, + .exit = karo_tx27_i2c_0_exit, +}; + +#if defined(CONFIG_RTC_DRV_DS13XX) || defined(CONFIG_RTC_DRV_DS13XX_MODULE) +static struct ds13xx_platform_data karo_ds1339_data = { + .type = ds_1339, + .ctrl = 0, + .trc = DS1339_TRC_ENABLE | DS1339_DIODE_ENABLE | DS1339_TRC_250R, +}; +#endif + +static struct at24_platform_data karo_tx27_eeprom = { + .byte_len = 2048, + .page_size = 32, + .flags = AT24_FLAG_ADDR16 | AT24_FLAG_TAKE8ADDR, +}; + +static struct i2c_board_info karo_i2c_0_boardinfo[] __initdata = { + { + I2C_BOARD_INFO("24c16", 0x50), + .platform_data = &karo_tx27_eeprom, + .type = "24c16", + }, +#if defined(CONFIG_RTC_DRV_DS13XX) || defined(CONFIG_RTC_DRV_DS13XX_MODULE) + { + I2C_BOARD_INFO("ds1339", 0x68/*DS1339_CHIP_ID*/), + .type = "ds1339", + .platform_data = &karo_ds1339_data, + }, +#endif +#if defined(CONFIG_RTC_DRV_DS1307) || defined(CONFIG_RTC_DRV_DS1307_MODULE) + { + I2C_BOARD_INFO("ds1339", 0x68/*DS1339_CHIP_ID*/), + .type = "ds1339", + //.platform_data = &karo_ds1339_data, + }, +#endif +}; + +static int mxc_i2c_1_pins[] = { + /* + * it seems the data line misses a pullup, so we must enable + * the internal pullup as a local workaround + */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, +}; + +static int karo_tx27_i2c_1_init(struct platform_device *pdev) +{ + return mxc_gpio_setup_multiple_pins(mxc_i2c_1_pins, + ARRAY_SIZE(mxc_i2c_1_pins), "I2C_1"); +} + +static int karo_tx27_i2c_1_exit(struct platform_device *pdev) +{ + mxc_gpio_release_multiple_pins(mxc_i2c_1_pins, + ARRAY_SIZE(mxc_i2c_1_pins)); + + return 0; +} + +static struct imx_i2c_platform_data karo_tx27_i2c_1_data = { + .max_clk = 100000, + .init = karo_tx27_i2c_1_init, + .exit = karo_tx27_i2c_1_exit, +}; + +static struct i2c_board_info karo_i2c_1_boardinfo[] __initdata = { + { + I2C_BOARD_INFO("lp3972", 0x34), + .type = "lp3972", + }, +}; + +int __init karo_i2c_init(void) +{ + int ret; + + DBG(0, "%s: Registering I2C bus 0\n", __FUNCTION__); + ret = mxc_register_device(&imx_i2c_device0, &karo_tx27_i2c_0_data); + if (ret != 0) { + printk(KERN_ERR "Failed to register I2C device: %d\n", ret); + return ret; + } + ret = i2c_register_board_info(0, karo_i2c_0_boardinfo, + ARRAY_SIZE(karo_i2c_0_boardinfo)); + if (ret != 0) { + printk(KERN_ERR "Failed to register I2C board info: %d\n", ret); + platform_device_unregister(&imx_i2c_device0); + } + + DBG(0, "%s: Registering I2C bus 1\n", __FUNCTION__); + ret = mxc_register_device(&imx_i2c_device1, &karo_tx27_i2c_1_data); + if (ret != 0) { + printk(KERN_ERR "Failed to register I2C device: %d\n", ret); + return ret; + } + ret = i2c_register_board_info(1, karo_i2c_1_boardinfo, + ARRAY_SIZE(karo_i2c_1_boardinfo)); + if (ret != 0) { + printk(KERN_ERR "Failed to register I2C board info: %d\n", ret); + platform_device_unregister(&imx_i2c_device1); + } + return ret; +} +device_initcall(karo_i2c_init); +#endif + +struct platform_dev_list { + struct platform_device *pdev; + int flag; +} tx27_devices[] __initdata = { +#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) + { .pdev = &tx27_gpio_keys_device, .flag = -1, }, +#endif +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) + { .pdev = &tx27_led_device, .flag = -1, }, +#endif +#if defined(CONFIG_RTC_MXC) || defined(CONFIG_RTC_MXC_MODULE) + { .pdev = &mxc_rtc_device, .flag = -1, }, +#endif +#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) + { .pdev = &tx27_nand_mtd_device, .flag = 1, }, +#endif +#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE) + { .pdev = &tx27_keypad_device, .flag = 1, }, +#endif +#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE) + { .pdev = &fec_device, .flag = 1, }, +#endif +#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE) + { .pdev = &mxcspi1_device, .flag = 1, }, +#endif +#if defined(CONFIG_AC97_BUS) || defined(CONFIG_AC97_BUS_MODULE) + { .pdev = &ac97_device, .flag = 1, }, +#endif +#if defined(CONFIG_MMC_MXC) || defined(CONFIG_MMC_MXC_MODULE) + { .pdev = &tx27_sdhc1_device, .flag = 1, }, + { .pdev = &tx27_sdhc2_device, .flag = 1, }, +#endif +#if defined(CONFIG_RTC_DRV_DS13XX) || defined(CONFIG_RTC_DRV_DS13XX_MODULE) + { .pdev = &tx27_ds1339_device, .flag = 1, }, +#endif +#if defined(CONFIG_VIDEO_MXC_EMMA_OUTPUT) || defined(CONFIG_VIDEO_MXC_EMMA_OUTPUT_MODULE) + { .pdev = &tx27_v4l2out_device, .flag = 1, }, +#endif +#if defined(CONFIG_MXC_VPU) || defined(CONFIG_MXC_VPU_MODULE) + { .pdev = &mxc_vpu_device, .flag = 1, }, +#endif +}; +#define TX27_NUM_DEVICES ARRAY_SIZE(tx27_devices) + +#define OSC26M_ENABLE_PIN (GPIO_PORTB | 22) + +static int _clk_26m_enable(struct clk *clk) +{ + int ret; + + DBG(0, "%s: Switching 26MHz oscillator on\n", __FUNCTION__); + ret = gpio_request(OSC26M_ENABLE_PIN, "OSC26m"); + if (ret != 0) { + printk(KERN_ERR "Failed to request 26MHz oscillator enable GPIO: %d\n", ret); + return ret; + } + gpio_set_value(OSC26M_ENABLE_PIN, 1); + mxc_gpio_mode(OSC26M_ENABLE_PIN | GPIO_GPIO | GPIO_OUT); + return 0; +} + +static void _clk_26m_disable(struct clk *clk) +{ + DBG(0, "%s: Switching 26MHz oscillator off\n", __FUNCTION__); + gpio_set_value(OSC26M_ENABLE_PIN, 0); + gpio_free(OSC26M_ENABLE_PIN); +} + +static struct clk clk_26m = { + .name = "clk_26m", + .enable = _clk_26m_enable, + .disable = _clk_26m_disable, +}; + +#ifdef CONFIG_BASE_CLK_26MHz +static __init void karo_tx27_clock_switch(struct clk *_26m_clk) +{ + int loops = 0; + u32 cscr = __raw_readl(CCM_CSCR); + u32 ccsr; + + if (_26m_clk != NULL) { + DBG(0, "%s: Enabling 26MHz clock\n", __FUNCTION__); + clk_enable(_26m_clk); + + __raw_writel(CCM_MPCTL0_PD_VAL(0) | + CCM_MPCTL0_MFD_VAL(51) | + CCM_MPCTL0_MFI_VAL(7) | + CCM_MPCTL0_MFN_VAL(35), CCM_MPCTL0); + + __raw_writel(CCM_SPCTL0_PD_VAL(1) | + CCM_SPCTL0_MFD_VAL(12) | + CCM_SPCTL0_MFI_VAL(9) | + CCM_SPCTL0_MFN_VAL(3), CCM_SPCTL0); + + cscr |= CCM_CSCR_MCU | CCM_CSCR_SP; + __raw_writel(cscr, CCM_CSCR); + + cscr |= CCM_CSCR_MPLLRES | CCM_CSCR_SPLLRES; + __raw_writel(cscr, CCM_CSCR); + while (__raw_readl(CCM_CSCR) & (CCM_CSCR_MPLLRES | CCM_CSCR_SPLLRES)) { + udelay(1); + loops++; + } + printk("PLLs locked after %d loops: CSCR=%08x(%08x)\n", loops, + __raw_readl(CCM_CSCR), cscr); + + cscr &= ~CCM_CSCR_FPM; + __raw_writel(cscr, CCM_CSCR); + DBG(9, "%s: Disabling FPM, DPLL and OSC26M\n", __FUNCTION__); + ccsr = __raw_readl(CCM_CCSR); + __raw_writel(ccsr & ~0x300, CCM_CCSR); + DBG(9, "changing CCSR from %08x to %08x(%08x)\n", + ccsr, ccsr & ~0x300, __raw_readl(CCM_CCSR)); + } else { + printk(KERN_INFO "Changing SPCTL0 from %08x to %08x\n", + __raw_readl(CCM_SPCTL0), CCM_SPCTL0_PD_VAL(2) | + CCM_SPCTL0_MFD_VAL(755) | + CCM_SPCTL0_MFI_VAL(11) | + CCM_SPCTL0_MFN_VAL(-205)); + + __raw_writel(CCM_SPCTL0_PD_VAL(2) | + CCM_SPCTL0_MFD_VAL(755) | + CCM_SPCTL0_MFI_VAL(11) | + CCM_SPCTL0_MFN_VAL(-205), CCM_SPCTL0); + } +} +#else +static inline void karo_tx27_clock_switch(struct clk *_26m_clk) +{ + printk(KERN_INFO "Changing SPCTL0 from %08x to %08x\n", + __raw_readl(CCM_SPCTL0), CCM_SPCTL0_PD_VAL(2) | + CCM_SPCTL0_MFD_VAL(755) | + CCM_SPCTL0_MFI_VAL(11) | + CCM_SPCTL0_MFN_VAL(-205)); + + __raw_writel(CCM_SPCTL0_PD_VAL(2) | + CCM_SPCTL0_MFD_VAL(755) | + CCM_SPCTL0_MFI_VAL(11) | + CCM_SPCTL0_MFN_VAL(-205), CCM_SPCTL0); +} +#endif + +static __init void karo_tx27_clock_init(void) +{ + struct clk *cpu_clk; + struct clk *_26m_clk = NULL; + int ret; + + ret = clk_register(&clk_26m); + if (ret != 0) { + printk(KERN_ERR "Failed to register 26MHz clock: %d\n", ret); + goto no_26m; + } + _26m_clk = clk_get(NULL, "clk_26m"); + if (IS_ERR(_26m_clk)) { + printk(KERN_ERR "Cannot request 26MHz clock: %ld\n", PTR_ERR(_26m_clk)); + _26m_clk = NULL; + } + no_26m: + karo_tx27_clock_switch(_26m_clk); + mxc_clocks_init(26000000); + cpu_clk = clk_get(NULL, "cpu_clk"); + if (!IS_ERR(cpu_clk)) { + printk(KERN_DEBUG "%s: Setting CPU clock to 400MHz\n", __FUNCTION__); + if (clk_set_rate(cpu_clk, 399000000) != 0) { + printk(KERN_ERR "Failed to set CPU clock rate\n"); + } + } else { + printk(KERN_ERR "Failed to get CPU clock: %ld\n", + PTR_ERR(cpu_clk)); + } + SHOW_REG(CCM_CSCR); +} + +static __init void karo_tx27_board_init(void) +{ + int i; + + DBG(0, "%s: \n", __FUNCTION__); + SHOW_REG(CCM_CSCR); + + for (i = 0; i < ARRAY_SIZE(tx27_uart_devices); i++) { + int ret; + int port = tx27_uart_devices[i]->id; + + DBG(0, "%s: Registering platform device[%d] @ %p dev %p: %s\n", + __FUNCTION__, i, tx27_uart_devices[i], + &tx27_uart_devices[i]->dev, tx27_uart_devices[i]->name); + ret = mxc_register_device(tx27_uart_devices[i], + &tx27_uart_ports[port]); + if (ret != 0) { + printk(KERN_WARNING "%s: Failed to register platform_device[%d]: %s: %d\n", + __FUNCTION__, i, tx27_uart_devices[i]->name, ret); + } + } + //mxc_cpu_common_init(); + //karo_tx27_clock_init(); + //early_console_setup(saved_command_line); + + /* configure PF13 as output for Starterkit-5 LED */ + //mxc_gpio_mode(MXC_PIN(F, 13, GPIO, GPIO_OUT | GPIO_DFLT_LOW)); + dump_regs(); + + /* enable SSI3_INT (PC23) for IRQ probing */ + set_irq_flags(gpio_to_irq(GPIO_PORTC | 23), IRQF_VALID | IRQF_PROBE); + + for (i = 0; i < TX27_NUM_DEVICES; i++) { + int ret; + + if (tx27_devices[i].pdev == NULL) continue; + if (!tx27_devices[i].flag) { + DBG(0, "%s: Skipping platform device[%d] @ %p dev %p: %s\n", + __FUNCTION__, i, tx27_devices[i].pdev, &tx27_devices[i].pdev->dev, + tx27_devices[i].pdev->name); + continue; + } + DBG(0, "%s: Registering platform device[%d] @ %p dev %p: %s\n", + __FUNCTION__, i, tx27_devices[i].pdev, &tx27_devices[i].pdev->dev, + tx27_devices[i].pdev->name); + ret = platform_device_register(tx27_devices[i].pdev); + if (ret) { + printk(KERN_WARNING "%s: Failed to register platform_device[%d]: %s: %d\n", + __FUNCTION__, i, tx27_devices[i].pdev->name, ret); + } + } + DBG(0, "%s: Done\n", __FUNCTION__); +} + +static void __init karo_tx27_map_io(void) +{ + mxc_map_io(); +} + +static void __init karo_tx27_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ +} + +static void __init karo_tx27_timer_init(void) +{ + DBG(0, "%s: \n", __FUNCTION__); + karo_tx27_clock_init(); + mxc_timer_init("gpt_clk.0"); + DBG(0, "%s: Done\n", __FUNCTION__); +} + +struct sys_timer karo_tx27_timer = { + .init = karo_tx27_timer_init, +}; + +MACHINE_START(TX27, "Ka-Ro electronics TX27 module (Freescale i.MX27)") + /* Maintainer: */ + .phys_io = AIPI_BASE_ADDR, + .io_pg_offst = ((unsigned long)AIPI_BASE_ADDR_VIRT >> 18) & 0xfffc, + .fixup = karo_tx27_fixup, + .map_io = karo_tx27_map_io, + .init_irq = mxc_init_irq, + .init_machine = karo_tx27_board_init, + .timer = &karo_tx27_timer, +MACHINE_END diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/karo.h linux-2.6.28-karo/arch/arm/mach-mx2/karo.h --- linux-2.6.28/arch/arm/mach-mx2/karo.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/karo.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,45 @@ +#ifdef DEBUG +#include + +#define SHOW_REG(reg) DBG(0, "%s[%08lx]=%08x\n", #reg, MXC_PHYS_ADDRESS(reg), __raw_readl(reg)) +#define SHOW_GPIO_REG(reg, port) \ + DBG(0, "PT%c_%s[%08lx]=%08x\n", 'A' + port, #reg, \ + GPIO_BASE_ADDR + MXC_##reg(port), __raw_readl(VA_GPIO_BASE + MXC_##reg(port))) + +static void dump_regs(void) +{ + int i; + + SHOW_REG(CCM_CSCR); + SHOW_REG(CCM_MPCTL0); + SHOW_REG(CCM_MPCTL1); + SHOW_REG(CCM_SPCTL0); + SHOW_REG(CCM_SPCTL1); + SHOW_REG(CCM_OSC26MCTL); + SHOW_REG(CCM_PCDR0); + SHOW_REG(CCM_PCDR1); + SHOW_REG(CCM_PCCR0); + SHOW_REG(CCM_PCCR1); + SHOW_REG(CCM_CCSR); + SHOW_REG(CCM_PMCTL); + SHOW_REG(CCM_PMCOUNT); + SHOW_REG(CCM_WKGDCTL); + for (i = 0; i < 6; i++) { + SHOW_GPIO_REG(GIUS, i); + SHOW_GPIO_REG(DDIR, i); + SHOW_GPIO_REG(SSR, i); + SHOW_GPIO_REG(GPR, i); + SHOW_GPIO_REG(OCR1, i); + SHOW_GPIO_REG(OCR2, i); + SHOW_GPIO_REG(ICONFA1, i); + SHOW_GPIO_REG(ICONFA2, i); + SHOW_GPIO_REG(ICONFB1, i); + SHOW_GPIO_REG(ICONFB2, i); + } +} +#else +static inline void dump_regs(void) +{ +} +#define SHOW_REG(reg) do {} while (0) +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/mxc_pm.c linux-2.6.28-karo/arch/arm/mach-mx2/mxc_pm.c --- linux-2.6.28/arch/arm/mach-mx2/mxc_pm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/mxc_pm.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,460 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup DPM_MX27 Power Management + * @ingroup MSL_MX27 + */ +/*! + * @file mach-mx27/mxc_pm.c + * + * @brief This file contains the implementation of the Low-level power + * management driver. It modifies the registers of the PLL and clock module + * of the i.MX27. + * + * @ingroup DPM_MX27 + */ + +/* + * Include Files + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crm_regs.h" + +/* Local defines */ +#define MAX_ARM_FREQ 400000000 +#define MAX_AHB_FREQ 133000000 +#define MAX_IPG_FREQ 66500000 +#define FREQ_COMP_TOLERANCE 100 /* tolerance percentage times 100 */ +#define MX27_LLPM_DEBUG 0 + +/* + * Global variables + */ +#if 0 +/*! + * These variables hold the various clock values when the module is loaded. + * This is needed because these clocks are derived from MPLL and when MPLL + * output changes, these clocks need to be adjusted. + */ +static u32 perclk1, perclk2, perclk3, perclk4, nfcclk, cpuclk; + +/*! + * Compare two frequences using allowable tolerance + * + * The MX27 PLL can generate many frequencies. This function + * compares the generated frequency to the requested frequency + * and determines it they are within and acceptable tolerance. + * + * @param freq1 desired frequency + * @param freq2 generated frequency + * + * @return Returns 0 is frequencies are within talerance + * and non-zero is they are not. + */ +static s32 freq_equal(u32 freq1, u32 freq2) +{ + if (freq1 > freq2) { + return (freq1 - freq2) <= (freq1 / FREQ_COMP_TOLERANCE); + } + return (freq2 - freq1) <= (freq1 / FREQ_COMP_TOLERANCE); +} + +/*! + * Select the PLL frequency based on the desired ARM frequency. + * + * The MPLL will be configured to output three frequencies, 400/333/266 MHz. + * + * @param armfreq Desired ARM frequency + * + * @return Returns one of the selected PLL frequency (400/333/266 MHz). + * Returns -1 on error. + * + */ +static s32 select_freq_pll(u32 armfreq) +{ + u32 div; + + div = 266000000 / armfreq; + if ((div == 0) || (!freq_equal(armfreq, 266000000 / div))) { + div = 400000000 / armfreq; + if ((div == 0) || (!freq_equal(armfreq, 400000000 / div))) { + return -1; + } + + return 400000000; + } + + return 266000000; +} + +/*! + * Check whether the desired ARM and AHB frequencies are valid. + * + * @param armfreq Desired ARM frequency + * @param ahbfreq Desired AHB frequency + * + * @return Returns 0 on success + * Return -1 on error + */ +static s32 mx27_pm_check_parameters(u32 armfreq, u32 ahbfreq) +{ + u32 ahbdiv; + + /* No idea about minimum frequencies.. just a guess! */ + if ((armfreq < 1000000) || (ahbfreq < 1000000)) { + printk("arm or ahb freq. too low\n"); + return -1; + } + + if ((armfreq > MAX_ARM_FREQ) || (ahbfreq > MAX_AHB_FREQ)) { + printk("arm or ahb freq. too high\n"); + return -1; + } + + /* AHB divider value is restricted to less than 8 */ + ahbdiv = armfreq / ahbfreq; + if ((ahbdiv == 0) || (ahbdiv > 8)) { + printk("Invalid ahb frequency\n"); + return -1; + } + + return 0; +} + +/*! + * Integer clock scaling + * + * Change the main ARM clock frequencies without changing the MPLL. + * The integer dividers (PRESC and BCLKDIV) are changed to obtain the + * desired frequency. Since NFC clock is derived from ARM frequency, + * NFCDIV is also adjusted. + * + * @param arm_freq Desired ARM frequency + * @param ahb_freq Desired AHB frequency + * @param pll_freq Current PLL frequency + * + * @return Returns 0 + */ +static s32 mx27_pm_intscale(u32 arm_freq, u32 ahb_freq, s32 pll_freq) +{ + u32 pre_div, bclk_div, nfc_div; + + /* Calculate ARM divider */ + pre_div = pll_freq / arm_freq; + if (pre_div == 0) + pre_div = 1; + + /* Calculate AHB divider */ + bclk_div = arm_freq / ahb_freq; + if (bclk_div == 0) + bclk_div = 1; + + if ((arm_freq / bclk_div) > ahb_freq) + bclk_div++; + + /* NFC clock is dependent on ARM clock */ + nfc_div = arm_freq / nfcclk; + if ((arm_freq / nfc_div) > nfcclk) + nfc_div++; + + /* Adjust NFC divider */ + mxc_set_clocks_div(NFC_CLK, nfc_div); + +#if MX27_LLPM_DEBUG + printk("DIVIDERS: PreDiv = %d BCLKDIV = %d \n", pre_div, bclk_div); + printk("Integer scaling\n"); + printk("PLL = %d : ARM = %d: AHB = %d\n", pll_freq, arm_freq, ahb_freq); +#endif + + /* + * This part is tricky. What to adjust first (PRESC or BCLKDIV)? + * After trial and error, if current ARM frequency is greater than + * desired ARM frequency, then adjust PRESC first, else if current + * ARM frequency is less than desired ARM frequency, then adjust + * BCLKDIV first. + */ + if (cpuclk > arm_freq) { + mxc_set_clocks_div(CPU_CLK, pre_div); + mxc_set_clocks_div(AHB_CLK, bclk_div); + } else { + mxc_set_clocks_div(AHB_CLK, bclk_div); + mxc_set_clocks_div(CPU_CLK, pre_div); + } + + cpuclk = arm_freq; + mdelay(50); + return 0; +} + +/*! + * Set dividers for various peripheral clocks. + * + * PERCLK1, PERCLK2, PERCLK3 and PERCLK4 are adjusted based on the MPLL + * output frequency. + * + * @param pll_freq Desired MPLL output frequency + */ +static void mx27_set_dividers(u32 pll_freq) +{ + s32 perdiv1, perdiv2, perdiv3, perdiv4; + + perdiv1 = pll_freq / perclk1; + if ((pll_freq / perdiv1) > perclk1) + perdiv1++; + + perdiv2 = pll_freq / perclk2; + if ((pll_freq / perdiv2) > perclk2) + perdiv2++; + + perdiv3 = pll_freq / perclk3; + if ((pll_freq / perdiv3) > perclk3) + perdiv3++; + + perdiv4 = pll_freq / perclk4; + if ((pll_freq / perdiv4) > perclk4) + perdiv4++; + + mxc_set_clocks_div(PERCLK1, perdiv1); + mxc_set_clocks_div(PERCLK2, perdiv2); + mxc_set_clocks_div(PERCLK3, perdiv3); + mxc_set_clocks_div(PERCLK4, perdiv4); +} + +/*! + * Change MPLL output frequency and adjust derived clocks to produce the + * desired frequencies. + * + * @param arm_freq Desired ARM frequency + * @param ahb_freq Desired AHB frequency + * @param org_pll Current PLL frequency + * + * @return Returns 0 on success + * Returns -1 on error + */ +static s32 mx27_pm_pllscale(u32 arm_freq, u32 ahb_freq, s32 org_pll) +{ + u32 mfi, mfn, mfd, pd = 1, cscr; + s32 pll_freq; + + /* Obtain the PLL frequency for the desired ARM frequency */ + pll_freq = select_freq_pll(arm_freq); + if (pll_freq == -1) { + return -1; + } + + /* The MPCTL0 register values are programmed based on the oscillator */ + cscr = __raw_readl(CCM_CSCR); + if ((cscr & CCM_CSCR_OSC26M) == 0) { + /* MPCTL0 register values are programmed for 400/266 MHz */ + switch (pll_freq) { + case 400000000: + mfi = 7; + mfn = 9; + mfd = 12; + pd = 0; + break; + + case 266000000: + mfi = 10; + mfn = 6; + mfd = 25; + break; + + default: + return -1; + } + } else { + /* MPCTL0 register values are programmed for 400/266 MHz */ + switch (pll_freq) { + case 400000000: + mfi = 12; + mfn = 2; + mfd = 3; + break; + + case 266000000: + mfi = 8; + mfn = 10; + mfd = 31; + break; + + default: + return -1; + } + } + +#if MX27_LLPM_DEBUG + printk("PLL scaling\n"); + printk("PLL = %d : ARM = %d: AHB = %d\n", pll_freq, arm_freq, ahb_freq); +#endif + + /* Adjust the peripheral clock dividers for new PLL frequency */ + mx27_set_dividers(pll_freq); + + if (pll_freq > org_pll) { + /* Set the dividers first */ + mx27_pm_intscale(arm_freq, ahb_freq, pll_freq); + + /* Set the PLL */ + mxc_pll_set(MCUPLL, mfi, pd, mfd, mfn); + mdelay(50); + } else { + /* Set the PLL first */ + mxc_pll_set(MCUPLL, mfi, pd, mfd, mfn); + mdelay(50); + + /* Set the dividers later */ + mx27_pm_intscale(arm_freq, ahb_freq, pll_freq); + } + + return 0; +} +#endif +/*! + * Implement steps required to transition to low-power modes. + * + * @param mode The desired low-power mode. Possible values are, + * DOZE_MODE + * WAIT_MODE + * STOP_MODE + * DSM_MODE + */ +void mxc_pm_lowpower(s32 mode) +{ + u32 cscr; + + local_irq_disable(); + + /* WAIT and DOZE execute WFI only */ + switch (mode) { + case STOP_MODE: + case DSM_MODE: + /* Clear MPEN and SPEN to disable MPLL/SPLL */ + cscr = __raw_readl(CCM_CSCR); + cscr &= 0xFFFFFFFC; + __raw_writel(cscr, CCM_CSCR); + break; + } + + /* Executes WFI */ + arch_idle(); + + local_irq_enable(); +} + +#if 0 +/*! + * Called to change the core frequency. This function internally decides + * whether to do integer scaling or pll scaling. + * + * @param arm_freq Desired ARM frequency + * @param ahb_freq Desired AHB frequency + * @param ipg_freq Desired IP frequency, constant AHB / 2 always. + * + * @return Returns 0 on success + * Returns -1 on error + */ +int mxc_pm_dvfs(unsigned long arm_freq, long ahb_freq, long ipg_freq) +{ + u32 divider; + s32 pll_freq, ret; + unsigned long flags; + + if (mx27_pm_check_parameters(arm_freq, ahb_freq) != 0) { + return -1; + } + + local_irq_save(flags); + + /* Get the current PLL frequency */ + pll_freq = mxc_pll_clock(MCUPLL); + +#if MX27_LLPM_DEBUG + printk("MCU PLL frequency is %d\n", pll_freq); +#endif + + /* Decide whether to do integer scaling or pll scaling */ + if (arm_freq > pll_freq) { + /* Do PLL scaling */ + ret = mx27_pm_pllscale(arm_freq, ahb_freq, pll_freq); + } else { + /* We need integer divider values */ + divider = pll_freq / arm_freq; + if (!freq_equal(arm_freq, pll_freq / divider)) { + /* Do PLL scaling */ + ret = mx27_pm_pllscale(arm_freq, ahb_freq, pll_freq); + } else { + /* Do integer scaling */ + ret = mx27_pm_intscale(arm_freq, ahb_freq, pll_freq); + } + } + + local_irq_restore(flags); + return ret; +} +#endif +/* + * This API is not supported on i.MX27 + */ +int mxc_pm_intscale(long armfreq, long ahbfreq, long ipfreq) +{ + return -MXC_PM_API_NOT_SUPPORTED; +} + +/* + * This API is not supported on i.MX27 + */ +int mxc_pm_pllscale(long armfreq, long ahbfreq, long ipfreq) +{ + return -MXC_PM_API_NOT_SUPPORTED; +} + +/*! + * This function is used to load the module. + * + * @return Returns an Integer on success + */ +static int __init mxc_pm_init_module(void) +{ + printk(KERN_INFO "MX27: Power management module initialized\n"); + return 0; +} + +/*! + * This function is used to unload the module + */ +static void __exit mxc_pm_cleanup_module(void) +{ + printk(KERN_INFO "MX27: Power management module exit\n"); +} + +module_init(mxc_pm_init_module); +module_exit(mxc_pm_cleanup_module); + +EXPORT_SYMBOL(mxc_pm_lowpower); +//EXPORT_SYMBOL(mxc_pm_dvfs); +EXPORT_SYMBOL(mxc_pm_pllscale); +EXPORT_SYMBOL(mxc_pm_intscale); + +MODULE_AUTHOR("Freescale Semiconductor"); +MODULE_DESCRIPTION("i.MX27 low level PM driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/pm.c linux-2.6.28-karo/arch/arm/mach-mx2/pm.c --- linux-2.6.28/arch/arm/mach-mx2/pm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/pm.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,88 @@ +/* + * linux/arch/arm/mach-mx27/pm.c + * + * MX27 Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Modified for the OMAP1510 by David Singleton: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Cleanup 2004 for OMAP1510/1610 by Dirk Behme + * + * Modified for the MX27 + * Copyright 2007 Freescale Semiconductor, Inc. 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include +#include + +/* + * TODO: whatta save? + */ + +static int mx27_pm_enter(suspend_state_t state) +{ + pr_debug("%s: Entering state %d\n", __FUNCTION__, state); + switch (state) { + case PM_SUSPEND_MEM: + mxc_pm_lowpower(STOP_MODE); + break; + + case PM_SUSPEND_STANDBY: + mxc_pm_lowpower(WAIT_MODE); + break; + + case PM_SUSPEND_STOP: + mxc_pm_lowpower(DSM_MODE); + break; + + default: + return -1; + } + return 0; +} + +struct platform_suspend_ops mx27_pm_ops = { + .enter = mx27_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static int __init mx27_pm_init(void) +{ + pr_debug("Power Management for Freescale MX27\n"); + suspend_set_ops(&mx27_pm_ops); + + return 0; +} + +late_initcall(mx27_pm_init); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/system.c linux-2.6.28-karo/arch/arm/mach-mx2/system.c --- linux-2.6.28/arch/arm/mach-mx2/system.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/system.c 2009-03-11 13:31:09.000000000 +0100 @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include #include @@ -43,21 +45,33 @@ void arch_idle(void) #define WDOG_WCR_REG IO_ADDRESS(WDOG_BASE_ADDR) #define WDOG_WCR_SRS (1 << 4) +static struct clk *wdog_clk; + /* * Reset the system. It is called by machine_restart(). */ void arch_reset(char mode) { - struct clk *clk; - - clk = clk_get(NULL, "wdog_clk"); - if (!clk) { - printk(KERN_ERR"Cannot activate the watchdog. Giving up\n"); - return; + if (mode == 's') { + cpu_reset(0); + } + if (likely(wdog_clk != NULL)) { + clk_enable(wdog_clk); } - - clk_enable(clk); /* Assert SRS signal */ __raw_writew(__raw_readw(WDOG_WCR_REG) & ~WDOG_WCR_SRS, WDOG_WCR_REG); } + +int __init mxc_wdog_init(void) +{ + wdog_clk = clk_get(NULL, "wdog_clk"); + if (IS_ERR(wdog_clk)) { + int ret = PTR_ERR(wdog_clk); + printk(KERN_CRIT "Cannot get wdog clock; watchdog reset may not work\n"); + wdog_clk = NULL; + return ret; + } + return 0; +} +postcore_initcall(mxc_wdog_init); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/mach-mx2/tx27_gpio.c linux-2.6.28-karo/arch/arm/mach-mx2/tx27_gpio.c --- linux-2.6.28/arch/arm/mach-mx2/tx27_gpio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/mach-mx2/tx27_gpio.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1019 @@ +/* + * arch/arm/mach-mx27/tx27_gpio.c + * + * Copyright (C) 2008 Lothar Wassmann + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crm_regs.h" + +#ifdef DEBUG +extern int tx27_debug; +#define dbg_lvl(n) ((n) < tx27_debug) +#define DBG(lvl, fmt...) do { if (dbg_lvl(lvl)) printk(KERN_DEBUG fmt); } while (0) +#else +#define dbg_lvl(n) 0 + +#define DBG(lvl, fmt...) do { } while (0) +#endif + +#ifdef DEBUG +#define try_request_mux(gmd) _try_request_mux(gmd, ARRAY_SIZE((gmd)), __FUNCTION__) +static int _try_request_mux(const unsigned int *gmd, int num_gpios, const char *func) +#else +#define try_request_mux(gmd) _try_request_mux(gmd, ARRAY_SIZE((gmd))) +static int _try_request_mux(const unsigned int *gmd, int num_gpios) +#endif +{ + int i; + for (i = 0; i < num_gpios; i++) { + unsigned gpio = gmd[i] & (GPIO_PIN_MASK | GPIO_PORT_MASK); + int ret; + + DBG(0, "Requesting GPIO: P%c%d (%s) for %s\n", + GPIO_PORT(gmd[i]) + 'A', GPIO_INDEX(gmd[i]), + MX27_PIN_NAME(gmd[i]), func); + ret = gpio_request(gpio, ""); + if (ret != 0) { + while (--i >= 0) { + gpio_free(gmd[i] & (GPIO_PIN_MASK | GPIO_PORT_MASK)); + } + return ret; + } + DBG(0, "%s: Configuring %s: %08x\n", __FUNCTION__, + MX27_PIN_NAME(gmd[i]), gmd[i]); + mxc_gpio_mode(gmd[i]); + } + return 0; +} + +#ifdef DEBUG +#define release_mux(gmd) _release_mux(gmd, ARRAY_SIZE((gmd)), __FUNCTION__) +static void _release_mux(const unsigned int *gmd, int num_gpios, + const char *func) +#else +#define release_mux(gmd) _release_mux(gmd, ARRAY_SIZE((gmd))) +static void _release_mux(const unsigned int *gmd, int num_gpios) +#endif +{ + int i; + + for (i = 0; i < num_gpios; i++) { + unsigned gpio = gmd[i] & (GPIO_PIN_MASK | GPIO_PORT_MASK); +#ifdef DEBUG + DBG(0, "%s: Releasing GPIO port P%c%d (%s)\n", func, + GPIO_PORT(gpio) + 'A', GPIO_INDEX(gpio), + MX27_PIN_NAME(gmd[i])); +#endif + gpio_free(gpio); + } +} + +#define tx27_config_mux(__gmd, __puen) _tx27_config_mux(__gmd, ARRAY_SIZE((__gmd)), __puen) +static void _tx27_config_mux(const unsigned int *gmd, int num_gpios, int puen) +{ + int i; + + for (i = 0; i < num_gpios; i++) { + switch (puen) { + case 1: + DBG(0, "Enabling Pullup on %s\n", + MX27_PIN_NAME(gmd[i])); + mxc_gpio_mode(gmd[i] | GPIO_PUEN); + break; + case -1: + case 0: + DBG(0, "Disabling Pullup on %s\n", + MX27_PIN_NAME(gmd[i])); + mxc_gpio_mode(gmd[i] & ~GPIO_PUEN); + break; + } + } +} + +#ifdef DEBUG +#define dump_pins(__gmd) _dump_pins(__gmd, ARRAY_SIZE((__gmd)), __FUNCTION__) +static void _dump_pins(const unsigned int *gmd, int num_gpios, const char *func) +{ + int i; + + for (i = 0; i < num_gpios; i++) { + int gpio = IOMUX_TO_GPIO(gmd[i]); + + DBG(0, "%s: %s (P%c%d) is: %d\n", func, + MX27_PIN_NAME(gmd[i]), + GPIO_PORT(gpio) + 'A', GPIO_INDEX(gpio), + gpio_get_value(gpio)); + } +} +#else +#define dump_pins(arg) do { } while (0) +#endif + +/* + Setup GPIO for USB + + PB22: 26MHz oscillator enable + + PIN Configuration for USBOTG: High/Full speed OTG + PE2,PE1,PE0,PE24,PE25 -- PRIMARY + PC7 - PC13 -- PRIMARY + PB23,PB24 -- PRIMARY + + PIN Configuration for USBH2: : High/Full/Low speed host + PA0 - PA4 -- PRIMARY + PD19, PD20,PD21,PD22,PD23,PD24,PD26 --Alternate (SECONDARY) + + USBH1: Full/low speed host + not supported on TX27 + */ + +static unsigned int mx27_usbh2_gpios_active[] = { + PD19_AF_USBH2_DATA4, + PD20_AF_USBH2_DATA3, + PD21_AF_USBH2_DATA6, + PD22_AF_USBH2_DATA0, + PD23_AF_USBH2_DATA2, + PD24_AF_USBH2_DATA1, + PD26_AF_USBH2_DATA5, + + PA0_PF_USBH2_CLK, + PA1_PF_USBH2_DIR, + PA2_PF_USBH2_DATA7, + PA3_PF_USBH2_NXT, + PA4_PF_USBH2_STP, + + MXC_PIN(B, 31, AOUT, GPIO_IN), /* OC detect */ +}; + +static unsigned int mx27_usbh2_gpios_inactive[] = { + MXC_PIN(D, 19, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 20, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 21, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 22, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 23, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 24, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(D, 26, AOUT, GPIO_IN | GPIO_PUEN), + + MXC_PIN(A, 0, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(A, 1, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(A, 2, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(A, 3, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(A, 4, AOUT, GPIO_IN | GPIO_PUEN), + + MXC_PIN(B, 31, AOUT, GPIO_IN), /* OC detect */ +}; + +/* + * conflicts with CSPI1 and CSPI2 + */ +static struct clk *usbh2_clk; + +int gpio_usbh2_active(void) +{ + int ret; + + DBG(0, "%s: \n", __FUNCTION__); + usbh2_clk = clk_get(NULL, "clk_26m"); + if (IS_ERR(usbh2_clk)) { + ret = PTR_ERR(usbh2_clk); + printk(KERN_ERR "Cannot request 26MHz clock: %d\n", ret); + clk_put(usbh2_clk); + usbh2_clk = NULL; + return ret; + } else { + clk_enable(usbh2_clk); + } + + ret = try_request_mux(mx27_usbh2_gpios_active); + return ret; +} + +void gpio_usbh2_inactive(void) +{ + int i; + + DBG(0, "%s: \n", __FUNCTION__); + if (usbh2_clk != NULL) { + clk_disable(usbh2_clk); + clk_put(usbh2_clk); + usbh2_clk = NULL; + } + + for (i = ARRAY_SIZE(mx27_usbh2_gpios_inactive) - 1; i >= 0; i--) { + DBG(0, "Enabling Pullup on %s\n", + MX27_PIN_NAME(mx27_usbh2_gpios_inactive[i])); + mxc_gpio_mode(mx27_usbh2_gpios_inactive[i]); + } + release_mux(mx27_usbh2_gpios_active); +} + +static unsigned int mx27_usbotg_hs_gpios_active[] = { + PC7_PF_USBOTG_DATA5, + PC8_PF_USBOTG_DATA6, + PC9_PF_USBOTG_DATA0, + PC10_PF_USBOTG_DATA2, + PC11_PF_USBOTG_DATA1, + PC13_PF_USBOTG_DATA3, + PC12_PF_USBOTG_DATA4, + + PE24_PF_USBOTG_CLK, + PE2_PF_USBOTG_DIR, + PE25_PF_USBOTG_DATA7, + PE0_PF_USBOTG_NXT, + PE1_PF_USBOTG_STP, + + MXC_PIN(B, 29, AOUT, GPIO_IN), /* OC detect */ +}; + +static unsigned int mx27_usbotg_hs_gpios_inactive[] = { + MXC_PIN(C, 7, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 8, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 9, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 10, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 11, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 13, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(C, 12, AOUT, GPIO_IN | GPIO_PUEN), + + MXC_PIN(E, 24, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(E, 2, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(E, 25, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(E, 0, AOUT, GPIO_IN | GPIO_PUEN), + MXC_PIN(E, 1, AOUT, GPIO_IN | GPIO_PUEN), + + MXC_PIN(B, 29, AOUT, GPIO_IN), /* OC detect */ +}; + +static struct clk *usbotg_clk; + +int gpio_usbotg_hs_active(void) +{ + int ret; + + usbotg_clk = clk_get(NULL, "clk_26m"); + if (IS_ERR(usbotg_clk)) { + ret = PTR_ERR(usbotg_clk); + printk(KERN_ERR "Cannot request 26MHz clock: %d\n", ret); + clk_put(usbotg_clk); + usbotg_clk = NULL; + return ret; + } else { + clk_enable(usbotg_clk); + } + + ret = try_request_mux(mx27_usbotg_hs_gpios_active); + return ret; +} + +void gpio_usbotg_hs_inactive(void) +{ + int i; + + if (usbotg_clk != NULL) { + clk_disable(usbotg_clk); + clk_put(usbotg_clk); + usbotg_clk = NULL; + } + + for (i = 0; i < ARRAY_SIZE(mx27_usbotg_hs_gpios_inactive); i++) { + DBG(0, "Enabling Pullup on %s\n", + MX27_PIN_NAME(mx27_usbotg_hs_gpios_inactive[i])); + mxc_gpio_mode(mx27_usbotg_hs_gpios_inactive[i]); + } + release_mux(mx27_usbotg_hs_gpios_active); +} + +int gpio_usbotg_fs_active(void) +{ + return gpio_usbotg_hs_active(); +} + +void gpio_usbotg_fs_inactive(void) +{ + gpio_usbotg_hs_inactive(); +} + +/************************************************************************/ +/* for i2c gpio */ +/* I2C1: PD17,PD18 -- Primary */ +/* I2C2: PC5,PC6 -- Primary */ +/************************************************************************/ +/* + * Setup GPIO for an I2C device to be active + */ +static unsigned int mx27_i2c_gpios[][2] = { + { + PD18_PF_I2C_CLK, + PD17_PF_I2C_DATA, + },{ + PC6_PF_I2C2_SCL, + PC5_PF_I2C2_SDA, + } +}; + +int gpio_i2c_active(int i2c_num) +{ + if (i2c_num < 0 || i2c_num >= ARRAY_SIZE(mx27_i2c_gpios)) { + printk(KERN_ERR "%s: no compatible I2C adapter\n", __FUNCTION__); + return -EINVAL; + } + return try_request_mux(mx27_i2c_gpios[i2c_num]); +} + +/* + * Setup GPIO for an I2C device to be inactive + */ +int gpio_i2c_inactive(int i2c_num) +{ + if (i2c_num < 0 || i2c_num >= ARRAY_SIZE(mx27_i2c_gpios)) { + return -EINVAL; + } + release_mux(mx27_i2c_gpios[i2c_num]); + return 0; +} + +/* + * Setup GPIO for a CSPI device to be active + */ +static unsigned int mx27_cspi1_gpios[] = { + PD31_PF_CSPI1_MOSI, + PD30_PF_CSPI1_MISO, + PD29_PF_CSPI1_SCLK, + PD25_PF_CSPI1_RDY, + PD28_PF_CSPI1_SS0, + PD27_PF_CSPI1_SS1, + //PD26_PF_CSPI1_SS2, /* already in use by the USB controller */ +}; + +static unsigned int mx27_cspi2_gpios[] = { + PD24_PF_CSPI2_MOSI, + PD23_PF_CSPI2_MISO, + PD22_PF_CSPI2_SCLK, + PD21_PF_CSPI2_SS0, + PD20_PF_CSPI2_SS1, + PD19_PF_CSPI2_SS2, +}; + +static unsigned int mx27_cspi3_gpios[] = { + PE18_AF_CSPI3_MISO, + PE22_AF_CSPI3_MOSI, + PE23_AF_CSPI3_SCLK, + PE21_AF_CSPI3_SS, +}; + +int gpio_spi_active(int cspi_mod) +{ + + switch (cspi_mod) { + case 0: + /* SPI1 */ + return try_request_mux(mx27_cspi1_gpios); + case 1: + /*SPI2 */ + return try_request_mux(mx27_cspi2_gpios); + case 2: + /*SPI3 */ + return try_request_mux(mx27_cspi3_gpios); + default: + return -EINVAL; + } +} + +/* + * Setup GPIO for a CSPI device to be inactive + */ +int gpio_spi_inactive(int cspi_mod) +{ + switch (cspi_mod) { + case 0: + /* SPI1 */ + release_mux(mx27_cspi1_gpios); + break; + case 1: + /*SPI2 */ + release_mux(mx27_cspi2_gpios); + break; + case 2: + /*SPI3 */ + release_mux(mx27_cspi3_gpios); + break; + + default: + return -EINVAL; + } + return 0; +} + +/* + * Setup GPIO for a nand flash device to be active + * + */ +static unsigned int mx27_nand_gpios[] = { + PF0_PF_NFRB, + PF3_PF_NFCE_B, + PF2_PF_NFWP_B, + PF1_PF_NFCLE, + PF4_PF_NFALE, + PF5_PF_NFRE_B, + PF6_PF_NFWE_B, +}; + +int gpio_nand_active(void) +{ + return try_request_mux(mx27_nand_gpios); +} + +/* + * Setup GPIO for a nand flash device to be inactive + * + */ +void gpio_nand_inactive(void) +{ + release_mux(mx27_nand_gpios); +} + +/* + * Setup GPIO for CSI device to be active + * + */ +static unsigned int mx27_csi_gpios[] = { + PB10_PF_CSI_D0, + PB11_PF_CSI_D1, + PB12_PF_CSI_D2, + PB13_PF_CSI_D3, + PB14_PF_CSI_D4, + PB15_PF_CSI_MCLK, + PB16_PF_CSI_PIXCLK, + PB17_PF_CSI_D5, + PB18_PF_CSI_D6, + PB19_PF_CSI_D7, + PB20_PF_CSI_VSYNC, + PB21_PF_CSI_HSYNC, +}; + +int gpio_sensor_active(void) +{ + return try_request_mux(mx27_csi_gpios); +} + +void gpio_sensor_inactive(void) +{ + release_mux(mx27_csi_gpios); +} + +#if 0 +/* + * Setup GPIO for LCDC device to be active + * + */ +static unsigned int mx27_lcdc_gpios[] = { + MXC_PIN(A, 30, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA30 */ + MXC_PIN(A, 25, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA25 */ + MXC_PIN(A, 26, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA26 */ + MXC_PIN(A, 24, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA24 */ + MXC_PIN(A, 27, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* PA27 */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA23_PF_LD17, + PA28_PF_HSYNC, + PA29_PF_VSYNC, + PA31_PF_OE_ACD, +}; + +int tx27_gpio_lcdc_active(void) +{ + int ret; + ret = try_request_mux(mx27_lcdc_gpios); + if (ret) { + return ret; + } + return 0; +} + +/* + * Setup GPIO for LCDC device to be inactive + * + */ +int tx27_gpio_lcdc_inactive(void) +{ + release_mux(mx27_lcdc_gpios); + return 0; +} +#endif + +/* + * GPIO settings not required for keypad + * + */ +int gpio_keypad_active(void) +{ + return 0; +} + +/* + * GPIO settings not required for keypad + * + */ +void gpio_keypad_inactive(void) +{ +} + +/* + * Setup GPIO for ATA device to be active + * + */ +static unsigned int mx27_ata_gpios[] = { + PD2_PF_ATA_DATA0, + PD3_PF_ATA_DATA1, + PD4_PF_ATA_DATA2, + PD5_PF_ATA_DATA3, + PD6_PF_ATA_DATA4, + PD7_PF_ATA_DATA5, + PD8_PF_ATA_DATA6, + PD9_PF_ATA_DATA7, + PD10_PF_ATA_DATA8, + PD11_PF_ATA_DATA9, + PD12_PF_ATA_DATA10, + PD13_PF_ATA_DATA11, + PD14_PF_ATA_DATA12, + PD15_PF_ATA_DATA13, + PD16_PF_ATA_DATA14, + PF23_PF_ATA_DATA15, + + PF20_AF_PC_CD1_B, + PF19_AF_PC_CD2_B, + PF18_AF_PC_WAIT_B, + PF17_AF_PC_READY, + PF16_AF_PC_PWRON, + PF14_AF_PC_VS1, + PF13_AF_PC_VS2, + PF12_AF_PC_BVD1, + PF11_AF_PC_BVD2, + PF10_AF_PC_RST, + PF9_AF_IOIS16, + PF8_AF_PC_RW_B, + PF7_AF_PC_POE, +}; + +int gpio_ata_active(void) +{ + return try_request_mux(mx27_ata_gpios); +} + +/* + * Setup GPIO for ATA device to be inactive + * + */ +void gpio_ata_inactive(void) +{ + release_mux(mx27_ata_gpios); +} + +/* + * Setup GPIO for FEC device to be active + * + */ +static unsigned int mx27_fec_gpios_off[] = { + /* configure the PHY strap pins to the correct values */ + MXC_PIN(D, 0, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 1, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 2, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 3, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 4, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 5, GPIO, GPIO_OUT | GPIO_DFLT_HIGH), + MXC_PIN(D, 6, GPIO, GPIO_OUT | GPIO_DFLT_HIGH), + MXC_PIN(D, 7, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 8, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 9, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 10, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 11, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 12, GPIO, GPIO_OUT | GPIO_DFLT_HIGH), + MXC_PIN(D, 13, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 14, GPIO, GPIO_OUT | GPIO_DFLT_LOW), + MXC_PIN(D, 15, GPIO, GPIO_OUT | GPIO_DFLT_LOW), +}; + +static unsigned int mx27_fec_pwr_gpios[] = { + MXC_PIN(B, 27, GPIO, GPIO_OUT), /* FEC PHY power on pin */ + MXC_PIN(B, 30, GPIO, GPIO_OUT), /* keep FEC reset in original state */ +}; + +static unsigned int mx27_fec_gpios_on[] = { + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, +#ifndef FEC_MII_IRQ + PD16_AIN_FEC_TX_ER, /* TX_ER */ +#else + MXC_PIN(D, 16, AOUT, GPIO_IN), /* #INT */ +#endif + PF23_AIN_FEC_TX_EN, +}; + +#define TX27_FEC_PWR_GPIO (GPIO_PORTB | 27) +#define TX27_FEC_RST_GPIO (GPIO_PORTB | 30) + +int gpio_fec_active(void) +{ + int ret; + + ret = try_request_mux(mx27_fec_pwr_gpios); + if (ret) { + return ret; + } + /* + * If the PHY is already powered on, assume it has been + * correctly configured (by the boot loader) + */ + if (0 && gpio_get_value(TX27_FEC_PWR_GPIO) && + gpio_get_value(TX27_FEC_RST_GPIO)) { + ret = try_request_mux(mx27_fec_gpios_on); + if (ret) { + release_mux(mx27_fec_pwr_gpios); + return ret; + } + } else { + DBG(0, "%s: Switching FEC PHY power on\n", __FUNCTION__); + gpio_set_value(TX27_FEC_PWR_GPIO, 1); + DBG(0, "%s: Asserting FEC PHY reset\n", __FUNCTION__); + gpio_set_value(TX27_FEC_RST_GPIO, 0); + dump_pins(mx27_fec_pwr_gpios); + dump_pins(mx27_fec_gpios_on); + /* switch PHY strap pins into required state */ + ret = try_request_mux(mx27_fec_gpios_off); + if (ret) { + release_mux(mx27_fec_pwr_gpios); + return ret; + } + DBG(0, "%s: Delaying for 22ms\n", __FUNCTION__); + mdelay(22); + dump_pins(mx27_fec_pwr_gpios); + dump_pins(mx27_fec_gpios_on); + DBG(0, "%s: Deasserting FEC PHY reset\n", __FUNCTION__); + gpio_set_value(TX27_FEC_RST_GPIO, 1); + tx27_config_mux(mx27_fec_gpios_on, -1); + } + return 0; +} + +/* + * Setup GPIO for FEC device to be inactive + * + */ +void gpio_fec_inactive(void) +{ + tx27_config_mux(mx27_fec_gpios_off, -1); + DBG(0, "%s: Asserting FEC PHY reset\n", __FUNCTION__); + gpio_set_value(TX27_FEC_RST_GPIO, 0); + DBG(0, "%s: Switching FEC PHY power off\n", __FUNCTION__); + gpio_set_value(TX27_FEC_PWR_GPIO, 0); + release_mux(mx27_fec_gpios_off); + release_mux(mx27_fec_pwr_gpios); +} + +/* + * Setup GPIO for SLCDC device to be active + * + */ +static unsigned int mx27_slcdc0_gpios[] = { + PC31_AIN_SLCDC2_CLK, /* CLK */ + PC30_AIN_SLCDC2_CS, /* CS */ + PC29_AIN_SLCDC2_RS, /* RS */ + PC28_AIN_SLCDC2_D0, /* D0 */ +}; + +static unsigned int mx27_slcdc1_gpios[] = { + PB5_AIN_SLCDC1_CLK, /* CLK */ + PB6_AIN_SLCDC1_D0, /* D0 */ + PB7_AIN_SLCDC1_RS, /* RS */ + PB8_AIN_SLCDC1_CS, /* CS */ +}; + +static unsigned int mx27_slcdc2_gpios[] = { + MXC_PIN(A, 6, GPIO, GPIO_OUT), + MXC_PIN(A, 7, GPIO, GPIO_OUT), + MXC_PIN(A, 8, GPIO, GPIO_OUT), + MXC_PIN(A, 9, GPIO, GPIO_OUT), + MXC_PIN(A, 10, GPIO, GPIO_OUT), + MXC_PIN(A, 11, GPIO, GPIO_OUT), + MXC_PIN(A, 12, GPIO, GPIO_OUT), + MXC_PIN(A, 13, GPIO, GPIO_OUT), + MXC_PIN(A, 14, GPIO, GPIO_OUT), + MXC_PIN(A, 15, GPIO, GPIO_OUT), + MXC_PIN(A, 16, GPIO, GPIO_OUT), + MXC_PIN(A, 17, GPIO, GPIO_OUT), + MXC_PIN(A, 18, GPIO, GPIO_OUT), + MXC_PIN(A, 19, GPIO, GPIO_OUT), + MXC_PIN(A, 20, GPIO, GPIO_OUT), + MXC_PIN(A, 21, GPIO, GPIO_OUT), +}; + +int gpio_slcdc_active(int type) +{ + switch (type) { + case 0: + return try_request_mux(mx27_slcdc0_gpios); + case 1: + return try_request_mux(mx27_slcdc1_gpios); + case 2: + return try_request_mux(mx27_slcdc2_gpios); + } + return -EINVAL; +} + +/* + * Setup GPIO for SLCDC device to be inactive + * + */ +int gpio_slcdc_inactive(int type) +{ + switch (type) { + case 0: + release_mux(mx27_slcdc0_gpios); + break; + case 1: + release_mux(mx27_slcdc1_gpios); + break; + case 2: + release_mux(mx27_slcdc2_gpios); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int mx27_ssi_gpios[][4] = { + { + PC28_PF_SSI3_FS, + PC29_PF_SSI3_RXD, + PC30_PF_SSI3_TXD, + PC31_PF_SSI3_CLK, + }, + { + PC16_PF_SSI4_FS, + PC17_PF_SSI4_RXD, + PC18_PF_SSI4_TXD, + PC19_PF_SSI4_CLK, + }, +}; + +int gpio_ssi_active(int ssi_num) +{ + int ret; + switch (ssi_num) { + case 0: + case 1: + ret = try_request_mux(mx27_ssi_gpios[ssi_num]); + break; + default: + return -EINVAL; + } + return ret; +} + +/* + * Setup GPIO for a SSI port to be inactive + */ +int gpio_ssi_inactive(int ssi_num) +{ + if (ssi_num < 0 || ssi_num >= ARRAY_SIZE(mx27_ssi_gpios)) { + return -EINVAL; + } + release_mux(mx27_ssi_gpios[ssi_num]); + return 0; +} + +static unsigned int mx27_ac97_gpios_off[] = { + MXC_PIN(E, 17, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* RESET_OUT_B */ + PC31_PF_SSI3_CLK, /* AC97_BITCLK */ + PC29_PF_SSI3_RXD, /* AC97_SDATAIN */ + MXC_PIN(C, 23, AOUT, GPIO_IN), /* UCB1400 interrupt */ + /* keep SDATA_OUT and SYNC deasserted to prevent UCB1400 from entering test mode */ + MXC_PIN(C, 30, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* AC97_SDATAOUT */ + MXC_PIN(C, 28, GPIO, GPIO_OUT | GPIO_DFLT_LOW), /* AC97_SYNC */ +}; + +static unsigned int mx27_ac97_gpios_on[] = { + MXC_PIN(E, 17, GPIO, GPIO_OUT | GPIO_DFLT_HIGH), /* RESET_OUT_B */ + PC30_PF_SSI3_TXD, /* AC97_SDATAOUT */ + PC28_PF_SSI3_FS, /* AC97_SYNC */ +}; + +int gpio_ac97_active(void) +{ + int ret; + + ret = try_request_mux(mx27_ac97_gpios_off); + if (ret == 0) { + udelay(1); + dump_pins(mx27_ac97_gpios_off); + /* deassert UCB1400 reset and reconfigure SDATA_OUT and SYNC */ + tx27_config_mux(mx27_ac97_gpios_on, -1); + } + return ret; +} + +void gpio_ac97_inactive(void) +{ + DBG(0, "%s: Releasing AC97 GPIOS\n", __FUNCTION__); + release_mux(mx27_ac97_gpios_off); + DBG(0, "%s: Done\n", __FUNCTION__); +} + +/* + * Setup GPIO for SDHC to be active + */ +static unsigned int mx27_sdhc_gpios[][6] = { + { + PE23_PF_SD1_CLK, + PE22_PF_SD1_CMD, + PE18_PF_SD1_D0, + PE19_PF_SD1_D1, + PE20_PF_SD1_D2, + PE21_PF_SD1_D3, + }, + { + PB9_PF_SD2_CLK, + PB8_PF_SD2_CMD, + PB4_PF_SD2_D0, + PB5_PF_SD2_D1, + PB6_PF_SD2_D2, + PB7_PF_SD2_D3, + }, + { + PD1_PF_SD3_CLK, + PD0_PF_SD3_CMD, + PD2_AF_SD3_D0, + PD3_AF_SD3_D1, + PD4_AF_SD3_D2, + PD5_AF_SD3_D3, + }, +}; + +int gpio_sdhc_active(int module) +{ + int ret; + u16 data; + switch (module) { + case 0: + ret = try_request_mux(mx27_sdhc_gpios[module]); + if (ret == 0) { + /* 22k pull up for sd1 dat3 pins */ + data = __raw_readw(IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54)); + data |= 0x0c; + __raw_writew(data, IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54)); + } + break; + case 1: + ret = try_request_mux(mx27_sdhc_gpios[module]); + if (ret == 0) { + /* 22k pull up for sd2 pins */ + data = __raw_readw(IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54)); + data |= 0xfff0; + __raw_writew(data, IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54)); + } + break; + case 2: + ret = try_request_mux(mx27_sdhc_gpios[module]); + break; + default: + return -EINVAL; + } + return ret; +} + +/* + * Setup GPIO for SDHC to be inactive + */ +int gpio_sdhc_inactive(int module) +{ + switch (module) { + case 0: + case 1: + case 2: + release_mux(mx27_sdhc_gpios[module]); + break; + default: + return -EINVAL; + } + return 0; +} + +int gpio_owire_active(void) +{ + int ret; + + ret = gpio_request(IOMUX_TO_GPIO(PE16_AF_OWIRE), "1Wire"); + if (ret == 0) { + mxc_gpio_mode(PE16_AF_OWIRE); + } + return ret; +} + +void gpio_owire_inactive(void) +{ + gpio_free(IOMUX_TO_GPIO(PE16_AF_OWIRE)); +} + +#define TX27_EXT_WAKEUP_GPIO (GPIO_PORTB | 24) + +#if 0 +static int tx27_gpio_init(void) +{ + int ret; + + DBG(0, "%s: \n", __FUNCTION__); + + /* USB_OC_B => EXT_WAKEUP */ + ret = gpio_request(TX27_EXT_WAKEUP_GPIO, "EXT Wakeup"); + if (ret) { + printk(KERN_WARNING "%s: Failed to request %s\n", __FUNCTION__, + MX27_PIN_NAME(TX27_EXT_WAKEUP_GPIO)); + return ret; + } + mxc_gpio_mode(MXC_PIN(B, 24, AOUT, GPIO_IN)); + return ret; +} +arch_initcall(tx27_gpio_init); +#endif + +EXPORT_SYMBOL(gpio_usbh2_active); +EXPORT_SYMBOL(gpio_usbh2_inactive); +EXPORT_SYMBOL(gpio_usbotg_hs_active); +EXPORT_SYMBOL(gpio_usbotg_hs_inactive); +EXPORT_SYMBOL(gpio_usbotg_fs_active); +EXPORT_SYMBOL(gpio_usbotg_fs_inactive); +EXPORT_SYMBOL(gpio_i2c_active); +EXPORT_SYMBOL(gpio_i2c_inactive); +EXPORT_SYMBOL(gpio_spi_active); +EXPORT_SYMBOL(gpio_spi_inactive); +EXPORT_SYMBOL(gpio_nand_active); +EXPORT_SYMBOL(gpio_nand_inactive); +EXPORT_SYMBOL(gpio_sensor_active); +EXPORT_SYMBOL(gpio_sensor_inactive); +EXPORT_SYMBOL(gpio_keypad_active); +EXPORT_SYMBOL(gpio_keypad_inactive); +EXPORT_SYMBOL(gpio_ata_active); +EXPORT_SYMBOL(gpio_ata_inactive); +EXPORT_SYMBOL(gpio_fec_active); +EXPORT_SYMBOL(gpio_fec_inactive); +EXPORT_SYMBOL(gpio_slcdc_active); +EXPORT_SYMBOL(gpio_slcdc_inactive); +EXPORT_SYMBOL(gpio_ssi_active); +EXPORT_SYMBOL(gpio_ssi_inactive); +EXPORT_SYMBOL(gpio_sdhc_active); +EXPORT_SYMBOL(gpio_sdhc_inactive); +EXPORT_SYMBOL(gpio_owire_active); +EXPORT_SYMBOL(gpio_owire_inactive); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/Kconfig linux-2.6.28-karo/arch/arm/plat-mxc/Kconfig --- linux-2.6.28/arch/arm/plat-mxc/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -8,6 +8,7 @@ choice config ARCH_MX2 bool "MX2-based" + select MXC_EMMA help This enables support for systems based on the Freescale i.MX2 family @@ -23,6 +24,14 @@ source "arch/arm/mach-mx3/Kconfig" endmenu +config MXC_EMMA + bool + depends on ARCH_MXC + +config MXC_FB_IRAM + bool + depends on ARCH_MXC + config MXC_IRQ_PRIOR bool "Use IRQ priority" depends on ARCH_MXC @@ -34,4 +43,6 @@ config MXC_IRQ_PRIOR requirements for timing. Say N here, unless you have a specialized requirement. +config MXC_ULPI + bool endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/Makefile linux-2.6.28-karo/arch/arm/plat-mxc/Makefile --- linux-2.6.28/arch/arm/plat-mxc/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -5,4 +5,6 @@ # Common support obj-y := irq.o clock.o gpio.o time.o devices.o +obj-$(CONFIG_ARCH_MX1) += iomux-mx1-mx2.o dma-mx1-mx2.o obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o +obj-$(CONFIG_MXC_ULPI) += ulpi.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/dma-mx1-mx2.c linux-2.6.28-karo/arch/arm/plat-mxc/dma-mx1-mx2.c --- linux-2.6.28/arch/arm/plat-mxc/dma-mx1-mx2.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/dma-mx1-mx2.c 2009-03-11 13:16:24.000000000 +0100 @@ -151,7 +151,7 @@ static inline int imx_dma_sg_next(int ch unsigned long now; if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", + printk(KERN_CRIT "%s: called for unallocated channel %d\n", __func__, channel); return 0; } @@ -390,20 +390,21 @@ imx_dma_setup_handlers(int channel, { struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; unsigned long flags; + int ret = 0; - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", + local_irq_save(flags); + if (imxdma->name) { + __raw_writel(1 << channel, DMA_BASE + DMA_DISR); + imxdma->irq_handler = irq_handler; + imxdma->err_handler = err_handler; + imxdma->data = data; + } else { + ret = -ENODEV; + printk(KERN_CRIT "%s: called for unallocated channel %d\n", __func__, channel); - return -ENODEV; } - - local_irq_save(flags); - __raw_writel(1 << channel, DMA_BASE + DMA_DISR); - imxdma->irq_handler = irq_handler; - imxdma->err_handler = err_handler; - imxdma->data = data; local_irq_restore(flags); - return 0; + return ret; } EXPORT_SYMBOL(imx_dma_setup_handlers); @@ -419,17 +420,18 @@ imx_dma_setup_progression_handler(int ch { struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; unsigned long flags; + int ret = 0; - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", + local_irq_save(flags); + if (imxdma->name) { + imxdma->prog_handler = prog_handler; + } else { + printk(KERN_CRIT "%s: called for unallocated channel %d\n", __func__, channel); - return -ENODEV; + ret = -ENODEV; } - - local_irq_save(flags); - imxdma->prog_handler = prog_handler; local_irq_restore(flags); - return 0; + return ret; } EXPORT_SYMBOL(imx_dma_setup_progression_handler); @@ -451,16 +453,16 @@ void imx_dma_enable(int channel) pr_debug("imxdma%d: imx_dma_enable\n", channel); + local_irq_save(flags); if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", + printk(KERN_CRIT "%s: called for unallocated channel %d\n", __func__, channel); - return; + goto out; } - if (imxdma->in_use) - return; - - local_irq_save(flags); + if (imxdma->in_use) { + goto out; + } __raw_writel(1 << channel, DMA_BASE + DMA_DISR); __raw_writel(__raw_readl(DMA_BASE + DMA_DIMR) & ~(1 << channel), @@ -482,7 +484,7 @@ void imx_dma_enable(int channel) } #endif imxdma->in_use = 1; - + out: local_irq_restore(flags); } EXPORT_SYMBOL(imx_dma_enable); @@ -512,6 +514,7 @@ void imx_dma_disable(int channel) } EXPORT_SYMBOL(imx_dma_disable); +#ifdef CONFIG_ARCH_MX2 static void imx_dma_watchdog(unsigned long chno) { struct imx_dma_channel *imxdma = &imx_dma_channels[chno]; @@ -523,6 +526,7 @@ static void imx_dma_watchdog(unsigned lo if (imxdma->err_handler) imxdma->err_handler(chno, imxdma->data, IMX_DMA_ERR_TIMEOUT); } +#endif static irqreturn_t dma_err_handler(int irq, void *dev_id) { @@ -675,16 +679,16 @@ int imx_dma_request(int channel, const c { struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; unsigned long flags; - int ret; + int ret = -EINVAL; /* basic sanity checks */ if (!name) - return -EINVAL; + return ret; if (channel >= IMX_DMA_CHANNELS) { printk(KERN_CRIT "%s: called for non-existed channel %d\n", __func__, channel); - return -EINVAL; + return ret; } local_irq_save(flags); @@ -692,6 +696,8 @@ int imx_dma_request(int channel, const c local_irq_restore(flags); return -EBUSY; } + imxdma->name = name; + local_irq_restore(flags); #ifdef CONFIG_ARCH_MX2 ret = request_irq(MXC_INT_DMACH0 + channel, dma_irq_handler, 0, "DMA", @@ -706,13 +712,11 @@ int imx_dma_request(int channel, const c imxdma->watchdog.data = channel; #endif - imxdma->name = name; imxdma->irq_handler = NULL; imxdma->err_handler = NULL; imxdma->data = NULL; imxdma->sg = NULL; - local_irq_restore(flags); return 0; } EXPORT_SYMBOL(imx_dma_request); @@ -726,14 +730,14 @@ void imx_dma_free(int channel) unsigned long flags; struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; + local_irq_save(flags); if (!imxdma->name) { printk(KERN_CRIT "%s: trying to free free channel %d\n", __func__, channel); - return; + goto out; } - local_irq_save(flags); /* Disable interrupts */ __raw_writel(__raw_readl(DMA_BASE + DMA_DIMR) | (1 << channel), DMA_BASE + DMA_DIMR); @@ -744,7 +748,7 @@ void imx_dma_free(int channel) #ifdef CONFIG_ARCH_MX2 free_irq(MXC_INT_DMACH0 + channel, NULL); #endif - + out: local_irq_restore(flags); } EXPORT_SYMBOL(imx_dma_free); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/gpio.c linux-2.6.28-karo/arch/arm/plat-mxc/gpio.c --- linux-2.6.28/arch/arm/plat-mxc/gpio.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/gpio.c 2009-03-11 18:50:00.000000000 +0100 @@ -22,18 +22,42 @@ #include #include #include +#include #include +#include +#include #include #include static struct mxc_gpio_port *mxc_gpio_ports; static int gpio_table_size; +#ifdef DEBUG +static inline void __mxc_gpio_writel(unsigned int val, + volatile unsigned int __force *addr, + const char *fn) +{ + printk(KERN_DEBUG "%s: Writing %08x to %p\n", fn, val, addr); + __raw_writel(val addr); +} + +static inline unsigned int __mxc_gpio_readl(volatile unsigned int __force *addr) +{ + printk(KERN_DEBUG "Writing %08x to %p\n", val, addr); + __raw_readl(val addr); +} +#define mxc_gpio_writel(v,a) __mxc_gpio_writel(v, a, __func__) +#define mxc_gpio_readl(a) __mxc_gpio_readl(a, __func__) +#else +#define mxc_gpio_writel __raw_writel +#define mxc_gpio_readl __raw_readl +#endif + /* Note: This driver assumes 32 GPIOs are handled in one register */ static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) { - __raw_writel(1 << index, port->base + GPIO_ISR); + mxc_gpio_writel(1 << index, port->base + GPIO_ISR); } static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, @@ -41,15 +65,21 @@ static void _set_gpio_irqenable(struct m { u32 l; - l = __raw_readl(port->base + GPIO_IMR); + l = mxc_gpio_readl(port->base + GPIO_IMR); l = (l & (~(1 << index))) | (!!enable << index); - __raw_writel(l, port->base + GPIO_IMR); + mxc_gpio_writel(l, port->base + GPIO_IMR); } -static void gpio_ack_irq(u32 irq) +static void _set_gpio_edge_ctrl(struct mxc_gpio_port *port, u32 index, + int edge) { - u32 gpio = irq_to_gpio(irq); - _clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); + void __iomem *reg = port->base; + u32 bit, val; + + reg += (index >> 4) ? GPIO_ICR2 : GPIO_ICR1; + bit = (index & 0xf) << 1; + val = mxc_gpio_readl(reg) & ~(0x3 << bit); + mxc_gpio_writel(val | (edge << bit), reg); } static void gpio_mask_irq(u32 irq) @@ -64,35 +94,73 @@ static void gpio_unmask_irq(u32 irq) _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); } +static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); + +static void gpio_ack_irq(u32 irq) +{ + u32 gpio = irq_to_gpio(irq); + struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + int bit = 1 << (gpio & 0x1f); + + if (port->level_irq & bit) { + gpio_mask_irq(irq); + } else if (port->both_edge_irq & bit) { + if (mxc_gpio_get(&port->chip, gpio & 0x1f)) { + _set_gpio_edge_ctrl(port, gpio, GPIO_INT_FALL_EDGE); + } else { + _set_gpio_edge_ctrl(port, gpio, GPIO_INT_RISE_EDGE); + } + } + _clear_gpio_irqstatus(port, gpio & 0x1f); +} + static int gpio_set_irq_type(u32 irq, u32 type) { u32 gpio = irq_to_gpio(irq); struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; - u32 bit, val; + u32 bitmask = 1 << (gpio & 0x1f); int edge; - void __iomem *reg = port->base; switch (type) { case IRQ_TYPE_EDGE_RISING: edge = GPIO_INT_RISE_EDGE; + port->level_irq &= ~bitmask; + port->both_edge_irq &= ~bitmask; + __set_irq_handler_unlocked(irq, handle_edge_irq); break; case IRQ_TYPE_EDGE_FALLING: edge = GPIO_INT_FALL_EDGE; + port->level_irq &= ~bitmask; + port->both_edge_irq &= ~bitmask; + __set_irq_handler_unlocked(irq, handle_edge_irq); break; case IRQ_TYPE_LEVEL_LOW: + port->level_irq |= bitmask; + port->both_edge_irq &= ~bitmask; edge = GPIO_INT_LOW_LEV; + __set_irq_handler_unlocked(irq, handle_level_irq); break; case IRQ_TYPE_LEVEL_HIGH: + port->level_irq |= bitmask; + port->both_edge_irq &= ~bitmask; edge = GPIO_INT_HIGH_LEV; + __set_irq_handler_unlocked(irq, handle_level_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + port->level_irq &= ~bitmask; + port->both_edge_irq |= bitmask; + if (mxc_gpio_get(&port->chip, gpio & 0x1f)) { + edge = GPIO_INT_FALL_EDGE; + } else { + edge = GPIO_INT_RISE_EDGE; + } + __set_irq_handler_unlocked(irq, handle_edge_irq); break; - default: /* this includes IRQ_TYPE_EDGE_BOTH */ + default: return -EINVAL; } - reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ - bit = gpio & 0xf; - val = __raw_readl(reg) & ~(0x3 << (bit << 1)); - __raw_writel(val | (edge << (bit << 1)), reg); + _set_gpio_edge_ctrl(port, gpio, edge); _clear_gpio_irqstatus(port, gpio & 0x1f); return 0; @@ -115,15 +183,15 @@ static void mxc_gpio_irq_handler(struct } } -#ifdef CONFIG_ARCH_MX3 -/* MX3 has one interrupt *per* gpio port */ +#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1) +/* MX1 and MX3 has one interrupt *per* gpio port */ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) { u32 irq_stat; - struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); + struct mxc_gpio_port *port = get_irq_data(irq); - irq_stat = __raw_readl(port->base + GPIO_ISR) & - __raw_readl(port->base + GPIO_IMR); + irq_stat = mxc_gpio_readl(port->base + GPIO_ISR) & + mxc_gpio_readl(port->base + GPIO_IMR); BUG_ON(!irq_stat); mxc_gpio_irq_handler(port, irq_stat); } @@ -135,26 +203,52 @@ static void mx2_gpio_irq_handler(u32 irq { int i; u32 irq_msk, irq_stat; - struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); + struct mxc_gpio_port *port = get_irq_data(irq); /* walk through all interrupt status registers */ for (i = 0; i < gpio_table_size; i++) { - irq_msk = __raw_readl(port[i].base + GPIO_IMR); + irq_msk = mxc_gpio_readl(port[i].base + GPIO_IMR); if (!irq_msk) continue; - irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; + irq_stat = mxc_gpio_readl(port[i].base + GPIO_ISR) & irq_msk; if (irq_stat) mxc_gpio_irq_handler(&port[i], irq_stat); } } #endif +/* + * Set interrupt number "irq" in the GPIO as a wakeup source. + * While system is running all registered GPIO interrupts need to have + * wakeup enabled. When system is suspended, only selected GPIO interrupts + * need to have wakeup enabled. + * @param irq interrupt source number + * @param enable enable as wakeup if equal to non-zero + * @return This function returns 0 on success. + */ +static int gpio_set_wake_irq(u32 irq, u32 enable) +{ + u32 gpio = irq_to_gpio(irq); + u32 gpio_idx = gpio & 0x1f; + struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + + if ((gpio / 32) >= gpio_table_size) + return -EINVAL; + + if (enable) + port->suspend_wakeup |= (1 << gpio_idx); + else + port->suspend_wakeup &= ~(1 << gpio_idx); + return 0; +} + static struct irq_chip gpio_irq_chip = { .ack = gpio_ack_irq, .mask = gpio_mask_irq, .unmask = gpio_unmask_irq, .set_type = gpio_set_irq_type, + .set_wake = gpio_set_wake_irq, }; static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, @@ -164,12 +258,12 @@ static void _set_gpio_direction(struct g container_of(chip, struct mxc_gpio_port, chip); u32 l; - l = __raw_readl(port->base + GPIO_GDIR); + l = mxc_gpio_readl(port->base + GPIO_GDIR); if (dir) l |= 1 << offset; else l &= ~(1 << offset); - __raw_writel(l, port->base + GPIO_GDIR); + mxc_gpio_writel(l, port->base + GPIO_GDIR); } static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -178,9 +272,12 @@ static void mxc_gpio_set(struct gpio_chi container_of(chip, struct mxc_gpio_port, chip); void __iomem *reg = port->base + GPIO_DR; u32 l; + unsigned long flags; - l = (__raw_readl(reg) & (~(1 << offset))) | (value << offset); - __raw_writel(l, reg); + local_irq_save(flags); + l = (mxc_gpio_readl(reg) & (~(1 << offset))) | (value << offset); + mxc_gpio_writel(l, reg); + local_irq_restore(flags); } static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -188,20 +285,32 @@ static int mxc_gpio_get(struct gpio_chip struct mxc_gpio_port *port = container_of(chip, struct mxc_gpio_port, chip); - return (__raw_readl(port->base + GPIO_PSR) >> offset) & 1; + return (mxc_gpio_readl(port->base + GPIO_PSR) >> offset) & 1; } static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { + unsigned long flags; + + local_irq_save(flags); _set_gpio_direction(chip, offset, 0); + local_irq_restore(flags); return 0; } static int mxc_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - _set_gpio_direction(chip, offset, 1); + unsigned long flags; + + local_irq_save(flags); + /* + * set the output data register _before_ potentially changing + * the pin direction to prevent glitches on the output + */ mxc_gpio_set(chip, offset, value); + _set_gpio_direction(chip, offset, 1); + local_irq_restore(flags); return 0; } @@ -217,10 +326,10 @@ int __init mxc_gpio_init(struct mxc_gpio for (i = 0; i < cnt; i++) { /* disable the interrupt and clear the status */ - __raw_writel(0, port[i].base + GPIO_IMR); - __raw_writel(~0, port[i].base + GPIO_ISR); + mxc_gpio_writel(0, port[i].base + GPIO_IMR); + mxc_gpio_writel(~0, port[i].base + GPIO_ISR); for (j = port[i].virtual_irq_start; - j < port[i].virtual_irq_start + 32; j++) { + j < port[i].virtual_irq_start + 32; j++) { set_irq_chip(j, &gpio_irq_chip); set_irq_handler(j, handle_edge_irq); set_irq_flags(j, IRQF_VALID); @@ -235,9 +344,9 @@ int __init mxc_gpio_init(struct mxc_gpio port[i].chip.ngpio = 32; /* its a serious configuration bug when it fails */ - BUG_ON( gpiochip_add(&port[i].chip) < 0 ); + BUG_ON(gpiochip_add(&port[i].chip) < 0); -#ifdef CONFIG_ARCH_MX3 +#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1) /* setup one handler for each entry */ set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler); set_irq_data(port[i].irq, &port[i]); @@ -251,3 +360,126 @@ int __init mxc_gpio_init(struct mxc_gpio #endif return 0; } + +#ifdef CONFIG_PM +/* + * This function puts the GPIO in low-power mode/state. + * All the interrupts that are enabled are first saved. + * Only those interrupts which registers as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + * + * @param dev the system device structure used to give information + * on GPIO to suspend + * @param mesg the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_gpio_suspend(struct sys_device *dev, pm_message_t mesg) +{ + int ret = 0; + int i; + int wakeup = 0; + + for (i = 0; i < gpio_table_size; i++) { + struct mxc_gpio_port *port = &mxc_gpio_ports[i]; + + if (__raw_readl(port->base + GPIO_ISR) & port->suspend_wakeup) + return -EBUSY; + + port->saved_wakeup = __raw_readl(port->base + GPIO_IMR); + __raw_writel(port->suspend_wakeup, port->base + GPIO_IMR); + + if (port->suspend_wakeup) { + wakeup = 1; + } + if (ret != 0) { + while (--i >= 0) { + port = &mxc_gpio_ports[i]; + __raw_writel(port->saved_wakeup, + port->base + GPIO_IMR); + } + return ret; + } + } + if (wakeup) + ret = enable_irq_wake(MXC_INT_GPIO); + + return ret; +} + +/* + * This function brings the GPIO back from low-power state. + * All the interrupts enabled before suspension are re-enabled from + * the saved information. + * + * @param dev the system device structure used to give information + * on GPIO to resume + * + * @return The function always returns 0. + */ +static int mxc_gpio_resume(struct sys_device *dev) +{ + int i; + int wakeup = 0; + + for (i = 0; i < gpio_table_size; i++) { + struct mxc_gpio_port *port = &mxc_gpio_ports[i]; + + __raw_writel(port->saved_wakeup, port->base + GPIO_IMR); + if (port->suspend_wakeup) { + wakeup = 1; + } + } + if (wakeup) + disable_irq_wake(MXC_INT_GPIO); + + return 0; +} +#else +#define mxc_gpio_suspend NULL +#define mxc_gpio_resume NULL +#endif /* CONFIG_PM */ + +/* + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_gpio_sysclass = { + .name = "mxc_gpio", + .suspend = mxc_gpio_suspend, + .resume = mxc_gpio_resume, +}; + +/* + * This structure represents GPIO as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +static struct sys_device mxc_gpio_device = { + .cls = &mxc_gpio_sysclass, +}; + +/* + * This function registers GPIO hardware as a system device and + * intializes all the GPIO ports if not already done. + * System devices will only be suspended with interrupts disabled, and + * after all other devices have been suspended. On resume, they will be + * resumed before any other devices, and also with interrupts disabled. + * This may get called early from board specific init + * + * @return This function returns 0 on success. + */ +int __init mxc_gpio_sys_init(void) +{ + int ret = 0; + + ret = sysdev_class_register(&mxc_gpio_sysclass); + if (ret) + return ret; + ret = sysdev_register(&mxc_gpio_device); + if (ret != 0) + sysdev_class_unregister(&mxc_gpio_sysclass); + + return ret; +} diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/debug-macro.S linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/debug-macro.S --- linux-2.6.28/arch/arm/plat-mxc/include/mach/debug-macro.S 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/debug-macro.S 2009-03-11 13:16:24.000000000 +0100 @@ -28,6 +28,9 @@ #ifdef CONFIG_MACH_PCM038 #include #endif +#ifdef CONFIG_MACH_TX27 +#include +#endif .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/gpio.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/gpio.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/gpio.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/gpio.h 2009-03-11 13:16:24.000000000 +0100 @@ -35,6 +35,12 @@ struct mxc_gpio_port { int irq; int virtual_irq_start; struct gpio_chip chip; + u32 level_irq; + u32 both_edge_irq; +#ifdef CONFIG_PM + u32 suspend_wakeup; + u32 saved_wakeup; +#endif }; int mxc_gpio_init(struct mxc_gpio_port*, int); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_cam.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_cam.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_cam.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_cam.h 2009-01-05 17:47:15.000000000 +0100 @@ -0,0 +1,47 @@ +/* + imx-cam.h - i.MX27 camera driver header file + + Copyright (C) 2003, Intel Corporation + Copyright (C) 2008, Sascha Hauer + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ASM_ARCH_CAMERA_H_ +#define __ASM_ARCH_CAMERA_H_ + +#define MX27_CAMERA_SWAP16 (1 << 0) +#define MX27_CAMERA_EXT_VSYNC (1 << 1) +#define MX27_CAMERA_CCIR (1 << 2) +#define MX27_CAMERA_CCIR_INTERLACE (1 << 3) +#define MX27_CAMERA_HSYNC_HIGH (1 << 4) +#define MX27_CAMERA_GATED_CLOCK (1 << 5) +#define MX27_CAMERA_INV_DATA (1 << 6) +#define MX27_CAMERA_PCLK_SAMPLE_RISING (1 << 7) +#define MX27_CAMERA_PACK_DIR_MSB (1 << 8) + +struct mx27_camera_platform_data { + int (*init)(struct platform_device *); + int (*exit)(struct platform_device *); + + unsigned long flags; + + unsigned long clk; +}; + +extern int mx27_init_camera(struct mx27_camera_platform_data *); + +#endif /* __ASM_ARCH_CAMERA_H_ */ + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_i2c.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_i2c.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_i2c.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_i2c.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,81 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ASM_ARCH_MXC_I2C_H__ +#define __ASM_ARCH_MXC_I2C_H__ + +struct imx_i2c_platform_data { + u32 max_clk; + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); +}; + +/* + * This file contains the I2C chip level configuration details. + * + * It also contains the API function that other drivers can use to read/write + * to the I2C device registers. + */ + +/* + * This defines the string used to identify MXC I2C Bus drivers + */ +#define MXC_ADAPTER_NAME "MXC I2C Adapter" + +#define MXC_I2C_FLAG_READ 0x01 /* if set, is read; else is write */ +#define MXC_I2C_FLAG_POLLING 0x02 /* if set, is polling mode; else is interrupt mode */ + +int mxc_i2c_read(int bus_id, unsigned int addr, char *reg, int reg_len, + char *buf, int num); + +int mxc_i2c_write(int bus_id, unsigned int addr, char *reg, int reg_len, + char *buf, int num); + +int mxc_i2c_polling_read(int bus_id, unsigned int addr, char *reg, int reg_len, + char *buf, int num); + +int mxc_i2c_polling_write(int bus_id, unsigned int addr, char *reg, int reg_len, + char *buf, int num); + +/* FIXME: This should be in a generic register file */ + +/* Address offsets of the I2C registers */ +#define MXC_IADR 0x00 /* Address Register */ +#define MXC_IFDR 0x04 /* Freq div register */ +#define MXC_I2CR 0x08 /* Control regsiter */ +#define MXC_I2SR 0x0C /* Status register */ +#define MXC_I2DR 0x10 /* Data I/O register */ + +/* Bit definitions of I2CR */ +#define MXC_I2CR_IEN 0x0080 +#define MXC_I2CR_IIEN 0x0040 +#define MXC_I2CR_MSTA 0x0020 +#define MXC_I2CR_MTX 0x0010 +#define MXC_I2CR_TXAK 0x0008 +#define MXC_I2CR_RSTA 0x0004 + +/* Bit definitions of I2SR */ +#define MXC_I2SR_ICF 0x0080 +#define MXC_I2SR_IAAS 0x0040 +#define MXC_I2SR_IBB 0x0020 +#define MXC_I2SR_IAL 0x0010 +#define MXC_I2SR_SRW 0x0004 +#define MXC_I2SR_IIF 0x0002 +#define MXC_I2SR_RXAK 0x0001 + +#endif /* __ASM_ARCH_MXC_I2C_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_spi.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_spi.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_spi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_spi.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * Copyright 2008 Luotao Fu, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MXC_SPI_MX27_H__ +#define __MXC_SPI_MX27_H__ + +#include +#include + +/* + * This structure is used to define the SPI master controller's platform + * data. It includes the SPI bus number and the maximum number of + * slaves/chips it supports. + */ +struct mxc_spi_master { + unsigned int bus_num; /* bus number. */ + unsigned int maxchipselect; /* number of chip selects. */ + unsigned int spi_version; /* CSPI Hardware Version. */ + + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); +}; + +/* Register definitions. + * XXX: The ifdef CONFIG_ARCH_MX2 segments are merged from the + * freescale mxc_spi_mx27.h. This is actually highly strange, since according + * to the processor reference manuals the spi controllers on mx27 and mx31 are + * identical. We might want to clearify this after chatting with freescale on + * this issue */ + +#define MXC_CSPIRXDATA 0x00 +#define MXC_CSPITXDATA 0x04 +#define MXC_CSPICTRL 0x08 +#define MXC_CSPIINT 0x0C + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPIDMA 0x18 +#define MXC_CSPISTAT 0x0C +#define MXC_CSPIPERIOD 0x14 +#define MXC_CSPITEST 0x10 +#define MXC_CSPIRESET 0x1C +#define MXC_CSPICTRL_ENABLE (1 << 10) +#else +#define MXC_CSPIDMA 0x10 +#define MXC_CSPISTAT 0x14 +#define MXC_CSPIPERIOD 0x18 +#define MXC_CSPITEST 0x1C +#define MXC_CSPIRESET 0x00 +#define MXC_CSPICTRL_ENABLE 0x1 +#endif + +#define MXC_CSPICTRL_DISABLE 0x0 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPICTRL_MASTER (1 << 11) +#else +#define MXC_CSPICTRL_MASTER (1 << 1) +#endif + +#define MXC_CSPICTRL_SLAVE 0x0 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPICTRL_XCH (1 << 9) +#define MXC_CSPICTRL_LOWPOL (1 << 5) +#else +#define MXC_CSPICTRL_XCH (1 << 2) +#define MXC_CSPICTRL_SMC (1 << 3) +#define MXC_CSPICTRL_LOWPOL (1 << 4) +#endif + +#define MXC_CSPICTRL_HIGHPOL 0x0 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPICTRL_PHA (1 << 6) +#else +#define MXC_CSPICTRL_PHA (1 << 5) +#endif + +#define MXC_CSPICTRL_NOPHA 0x0 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPICTRL_SSCTL (1 << 7) +#define MXC_CSPICTRL_HIGHSSPOL (1 << 8) +#else +#define MXC_CSPICTRL_SSCTL (1 << 6) +#define MXC_CSPICTRL_HIGHSSPOL (1 << 7) +#endif + +#define MXC_CSPICTRL_LOWSSPOL 0x0 +#define MXC_CSPICTRL_CSMASK 0x3 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPICTRL_MAXDATRATE 0x10 +#define MXC_CSPICTRL_DATAMASK 0x1F +#define MXC_CSPICTRL_DATASHIFT 14 +/* This adjustment in the shift is valid only for even states only(i.e. divide + ratio of 2). SDHC_SPIEN is not set by default. If SDHC_SPIEN bit is set in + MXC_CSPICTRL, then divide ratio is 3, this shift adjustment is invalid. */ +#define MXC_CSPICTRL_ADJUST_SHIFT(x) ((x) = ((x) - 1) * 2) +#else +#define MXC_CSPICTRL_MAXDATRATE 0x7 +#define MXC_CSPICTRL_DATAMASK 0x7 +#define MXC_CSPICTRL_DATASHIFT 16 +#define MXC_CSPICTRL_ADJUST_SHIFT(x) ((x) -= 2) +#endif + +#define MXC_CSPICTRL_CSSHIFT_0_7 12 +#define MXC_CSPICTRL_BCSHIFT_0_7 20 +#define MXC_CSPICTRL_BCMASK_0_7 0xFFF +#define MXC_CSPICTRL_DRCTRLSHIFT_0_7 8 + +#define MXC_CSPICTRL_CSSHIFT_0_5 12 +#define MXC_CSPICTRL_BCSHIFT_0_5 20 +#define MXC_CSPICTRL_BCMASK_0_5 0xFFF +#define MXC_CSPICTRL_DRCTRLSHIFT_0_5 8 + +#define MXC_CSPICTRL_CSSHIFT_0_4 24 +#define MXC_CSPICTRL_BCSHIFT_0_4 8 +#define MXC_CSPICTRL_BCMASK_0_4 0x1F +#define MXC_CSPICTRL_DRCTRLSHIFT_0_4 20 + +#define MXC_CSPICTRL_CSSHIFT_0_0 19 +#define MXC_CSPICTRL_BCSHIFT_0_0 0 +#define MXC_CSPICTRL_BCMASK_0_0 0x1F +#define MXC_CSPICTRL_DRCTRLSHIFT_0_0 12 + +#define MXC_CSPIINT_IRQSHIFT_0_7 8 +#define MXC_CSPIINT_IRQSHIFT_0_5 9 +#define MXC_CSPIINT_IRQSHIFT_0_4 9 +#define MXC_CSPIINT_IRQSHIFT_0_0 18 + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPIINT_TEEN (1 << 9) +#define MXC_CSPIINT_THEN (1 << 10) +#define MXC_CSPIINT_TFEN (1 << 11) +#define MXC_CSPIINT_RREN (1 << 13) +#define MXC_CSPIINT_RHEN (1 << 14) +#define MXC_CSPIINT_RFEN (1 << 15) +#define MXC_CSPIINT_ROEN (1 << 16) +#else +#define MXC_CSPIINT_TEEN (1 << 0) +#define MXC_CSPIINT_THEN (1 << 1) +#define MXC_CSPIINT_TFEN (1 << 2) +#define MXC_CSPIINT_RREN (1 << 3) +#define MXC_CSPIINT_RHEN (1 << 4) +#define MXC_CSPIINT_RFEN (1 << 5) +#define MXC_CSPIINT_ROEN (1 << 6) +#endif + +#define MXC_CSPIINT_TCEN_0_7 (1 << 7) +#define MXC_CSPIINT_TCEN_0_5 (1 << 8) +#define MXC_CSPIINT_TCEN_0_4 (1 << 8) + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPIINT_TCEN_0_0 (1 << 12) +#else +#define MXC_CSPIINT_TCEN_0_0 (1 << 3) +#endif + +#define MXC_CSPIINT_BOEN_0_7 0 +#define MXC_CSPIINT_BOEN_0_5 (1 << 7) +#define MXC_CSPIINT_BOEN_0_4 (1 << 7) + +#ifdef CONFIG_ARCH_MX2 +#define MXC_CSPIINT_BOEN_0_0 (1 << 17) +#else +#define MXC_CSPIINT_BOEN_0_0 (1 << 8) +#endif + +#define MXC_CSPISTAT_TE (1 << 0) +#define MXC_CSPISTAT_TH (1 << 1) +#define MXC_CSPISTAT_TF (1 << 2) +#define MXC_CSPISTAT_RR (1 << 3) +#define MXC_CSPISTAT_RH (1 << 4) +#define MXC_CSPISTAT_RF (1 << 5) +#define MXC_CSPISTAT_RO (1 << 6) +#define MXC_CSPISTAT_TC_0_7 (1 << 7) +#define MXC_CSPISTAT_TC_0_5 (1 << 8) +#define MXC_CSPISTAT_TC_0_4 (1 << 8) +#define MXC_CSPISTAT_TC_0_0 (1 << 3) +#define MXC_CSPISTAT_BO_0_7 0 +#define MXC_CSPISTAT_BO_0_5 (1 << 7) +#define MXC_CSPISTAT_BO_0_4 (1 << 7) +#define MXC_CSPISTAT_BO_0_0 (1 << 8) + +#define MXC_CSPIPERIOD_32KHZ (1 << 15) + +#define MXC_CSPITEST_LBC (1 << 14) + +/* + * This structure contains information that differs with + * SPI master controller hardware version + */ +struct mxc_spi_unique_def { + unsigned int intr_bit_shift; /* Width of valid bits in MXC_CSPIINT. */ + unsigned int cs_shift; /* Chip Select shift. */ + unsigned int bc_shift; /* Bit count shift. */ + unsigned int bc_mask; /* Bit count mask. */ + unsigned int drctrl_shift; /* Data Control shift. */ + unsigned int xfer_complete; /* Transfer Complete shift. */ + unsigned int bc_overflow; /* Bit counnter overflow shift. */ +}; + +#endif /*__MXC_SPI_MX27_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_ssi.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_ssi.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/imx_ssi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imx_ssi.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,191 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ASM_ARCH_MXC_SSI_H +#define __ASM_ARCH_MXC_SSI_H + +#include + +struct imx_ssi_platform_data { + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); +}; + +#define SSI_STX0 0x00 +#define SSI_STX1 0x04 +#define SSI_SRX0 0x08 +#define SSI_SRX1 0x0c + +#define SSI_SCR 0x10 +# define SSI_SCR_CLK_IST (1 << 9) +# define SSI_SCR_CLK_IST_SHIFT 9 +# define SSI_SCR_TCH_EN (1 << 8) +# define SSI_SCR_SYS_CLK_EN (1 << 7) +# define SSI_SCR_I2S_MODE_NORM (0 << 5) +# define SSI_SCR_I2S_MODE_MSTR (1 << 5) +# define SSI_SCR_I2S_MODE_SLAVE (2 << 5) +# define SSI_SCR_SYN (1 << 4) +# define SSI_SCR_NET (1 << 3) +# define SSI_SCR_RE (1 << 2) +# define SSI_SCR_TE (1 << 1) +# define SSI_SCR_SSIEN (1 << 0) +# define SSI_I2S_MODE_SHIFT 5 + +#define SSI_SISR 0x14 +# define SSI_SISR_MASK ((1 << 19) - 1) +# define SSI_SISR_CMDAU (1 << 18) +# define SSI_SISR_CMDDU (1 << 17) +# define SSI_SISR_RXT (1 << 16) +# define SSI_SISR_RDR1 (1 << 15) +# define SSI_SISR_RDR0 (1 << 14) +# define SSI_SISR_TDE1 (1 << 13) +# define SSI_SISR_TDE0 (1 << 12) +# define SSI_SISR_ROE1 (1 << 11) +# define SSI_SISR_ROE0 (1 << 10) +# define SSI_SISR_TUE1 (1 << 9) +# define SSI_SISR_TUE0 (1 << 8) +# define SSI_SISR_TFS (1 << 7) +# define SSI_SISR_RFS (1 << 6) +# define SSI_SISR_TLS (1 << 5) +# define SSI_SISR_RLS (1 << 4) +# define SSI_SISR_RFF1 (1 << 3) +# define SSI_SISR_RFF0 (1 << 2) +# define SSI_SISR_TFE1 (1 << 1) +# define SSI_SISR_TFE0 (1 << 0) + +#define SSI_SIER 0x18 +# define SSI_SIER_RDMAE (1 << 22) +# define SSI_SIER_RIE (1 << 21) +# define SSI_SIER_TDMAE (1 << 20) +# define SSI_SIER_TIE (1 << 19) +# define SSI_SIER_CMDAU_EN (1 << 18) +# define SSI_SIER_CMDDU_EN (1 << 17) +# define SSI_SIER_RXT_EN (1 << 16) +# define SSI_SIER_RDR1_EN (1 << 15) +# define SSI_SIER_RDR0_EN (1 << 14) +# define SSI_SIER_TDE1_EN (1 << 13) +# define SSI_SIER_TDE0_EN (1 << 12) +# define SSI_SIER_ROE1_EN (1 << 11) +# define SSI_SIER_ROE0_EN (1 << 10) +# define SSI_SIER_TUE1_EN (1 << 9) +# define SSI_SIER_TUE0_EN (1 << 8) +# define SSI_SIER_TFS_EN (1 << 7) +# define SSI_SIER_RFS_EN (1 << 6) +# define SSI_SIER_TLS_EN (1 << 5) +# define SSI_SIER_RLS_EN (1 << 4) +# define SSI_SIER_RFF1_EN (1 << 3) +# define SSI_SIER_RFF0_EN (1 << 2) +# define SSI_SIER_TFE1_EN (1 << 1) +# define SSI_SIER_TFE0_EN (1 << 0) + +#define SSI_STCR 0x1c +# define SSI_STCR_TXBIT0 (1 << 9) +# define SSI_STCR_TFEN1 (1 << 8) +# define SSI_STCR_TFEN0 (1 << 7) +# define SSI_FIFO_ENABLE_0_SHIFT 7 +# define SSI_STCR_TFDIR (1 << 6) +# define SSI_STCR_TXDIR (1 << 5) +# define SSI_STCR_TSHFD (1 << 4) +# define SSI_STCR_TSCKP (1 << 3) +# define SSI_STCR_TFSI (1 << 2) +# define SSI_STCR_TFSL (1 << 1) +# define SSI_STCR_TEFS (1 << 0) + +#define SSI_SRCR 0x20 +# define SSI_SRCR_RXBIT0 (1 << 9) +# define SSI_SRCR_RFEN1 (1 << 8) +# define SSI_SRCR_RFEN0 (1 << 7) +# define SSI_FIFO_ENABLE_0_SHIFT 7 +# define SSI_SRCR_RFDIR (1 << 6) +# define SSI_SRCR_RXDIR (1 << 5) +# define SSI_SRCR_RSHFD (1 << 4) +# define SSI_SRCR_RSCKP (1 << 3) +# define SSI_SRCR_RFSI (1 << 2) +# define SSI_SRCR_RFSL (1 << 1) +# define SSI_SRCR_REFS (1 << 0) + +#define SSI_SRCCR 0x28 +# define SSI_SRCCR_DIV2 (1 << 18) +# define SSI_SRCCR_PSR (1 << 17) +# define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) +# define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) +# define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) +# define SSI_SRCCR_WL_MASK (0xf << 13) +# define SSI_SRCCR_DC_MASK (0x1f << 8) +# define SSI_SRCCR_PM_MASK (0xff << 0) + +#define SSI_STCCR 0x24 +# define SSI_STCCR_DIV2 (1 << 18) +# define SSI_STCCR_PSR (1 << 17) +# define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) +# define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) +# define SSI_STCCR_PM(x) (((x) & 0xff) << 0) +# define SSI_STCCR_WL_MASK (0xf << 13) +# define SSI_STCCR_DC_MASK (0x1f << 8) +# define SSI_STCCR_PM_MASK (0xff << 0) + +#define SSI_SFCSR 0x2c +# define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) +# define SSI_RX_FIFO_1_COUNT_SHIFT 28 +# define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) +# define SSI_TX_FIFO_1_COUNT_SHIFT 24 +# define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) +# define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) +# define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) +# define SSI_RX_FIFO_0_COUNT_SHIFT 12 +# define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) +# define SSI_TX_FIFO_0_COUNT_SHIFT 8 +# define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) +# define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) +# define SSI_SFCSR_RFWM0_MASK (0xf << 4) +# define SSI_SFCSR_TFWM0_MASK (0xf << 0) + +#define SSI_STR 0x30 +# define SSI_STR_TEST (1 << 15) +# define SSI_STR_RCK2TCK (1 << 14) +# define SSI_STR_RFS2TFS (1 << 13) +# define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) +# define SSI_STR_TXD2RXD (1 << 7) +# define SSI_STR_TCK2RCK (1 << 6) +# define SSI_STR_TFS2RFS (1 << 5) +# define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) + +#define SSI_SOR 0x34 +# define SSI_SOR_CLKOFF (1 << 6) +# define SSI_SOR_RX_CLR (1 << 5) +# define SSI_SOR_TX_CLR (1 << 4) +# define SSI_SOR_INIT (1 << 3) +# define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) +# define SSI_SOR_WAIT_MASK (0x3 << 1) +# define SSI_SOR_SYNRST (1 << 0) + +#define SSI_SACNT 0x38 +# define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) +# define SSI_SACNT_WR (x << 4) +# define SSI_SACNT_RD (x << 3) +# define SSI_SACNT_TIF (x << 2) +# define SSI_SACNT_FV (x << 1) +# define SSI_SACNT_AC97EN (x << 0) + +#define SSI_SACADD 0x3c +#define SSI_SRMSK 0x4c +#define SSI_SACDAT 0x40 +#define SSI_SATAG 0x44 +#define SSI_STMSK 0x48 + +#endif /* __ASM_ARCH_MXC_SSI_H */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/imxfb.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imxfb.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/imxfb.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/imxfb.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,88 @@ +/* + * This structure describes the machine which we are running on. + */ + +#define PCR_TFT (1 << 31) +#define PCR_COLOR (1 << 30) +#define PCR_PBSIZ_1 (0 << 28) +#define PCR_PBSIZ_2 (1 << 28) +#define PCR_PBSIZ_4 (2 << 28) +#define PCR_PBSIZ_8 (3 << 28) +#define PCR_BPIX_1 (0 << 25) +#define PCR_BPIX_2 (1 << 25) +#define PCR_BPIX_4 (2 << 25) +#define PCR_BPIX_8 (3 << 25) +#define PCR_BPIX_12 (4 << 25) +#define PCR_BPIX_16 (5 << 25) +#define PCR_BPIX_18 (6 << 25) +#define PCR_PIXPOL (1 << 24) +#define PCR_FLMPOL (1 << 23) +#define PCR_LPPOL (1 << 22) +#define PCR_CLKPOL (1 << 21) +#define PCR_OEPOL (1 << 20) +#define PCR_SCLKIDLE (1 << 19) +#define PCR_END_SEL (1 << 18) +#define PCR_END_BYTE_SWAP (1 << 17) +#define PCR_REV_VS (1 << 16) +#define PCR_ACD_SEL (1 << 15) +#define PCR_ACD(x) (((x) & 0x7f) << 8) +#define PCR_SCLK_SEL (1 << 7) +#define PCR_SHARP (1 << 6) +#define PCR_PCD(x) ((x) & 0x3f) + +#define PWMR_CLS(x) (((x) & 0x1ff) << 16) +#define PWMR_LDMSK (1 << 15) +#define PWMR_SCR1 (1 << 10) +#define PWMR_SCR0 (1 << 9) +#define PWMR_CC_EN (1 << 8) +#define PWMR_PW(x) ((x) & 0xff) + +#define LSCR1_PS_RISE_DELAY(x) (((x) & 0x7f) << 26) +#define LSCR1_CLS_RISE_DELAY(x) (((x) & 0x3f) << 16) +#define LSCR1_REV_TOGGLE_DELAY(x) (((x) & 0xf) << 8) +#define LSCR1_GRAY2(x) (((x) & 0xf) << 4) +#define LSCR1_GRAY1(x) (((x) & 0xf)) + +#define DMACR_BURST (1 << 31) +#define DMACR_HM(x) (((x) & 0xf) << 16) +#define DMACR_TM(x) ((x) & 0xf) + +struct imx_fb_platform_data { + u_long pixclock; + + u_short xres; + u_short yres; + + u_int nonstd; + u_char bpp; + u_char hsync_len; + u_char left_margin; + u_char right_margin; + + u_char vsync_len; + u_char upper_margin; + u_char lower_margin; + u_char sync; + + u_int cmap_greyscale:1, + cmap_inverse:1, + cmap_static:1, + unused:29; + + u_int pcr; + u_int pwmr; + u_int lscr1; + u_int dmacr; + + u_char * fixed_screen_cpu; + dma_addr_t fixed_screen_dma; + + int (*init)(struct platform_device*); + int (*exit)(struct platform_device*); + void (*lcd_power)(int); + void (*backlight_power)(int); +}; + +#ifdef ARCH_IMX +void set_imx_fb_info(struct imx_fb_platform_data *); +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h 2009-03-11 13:16:24.000000000 +0100 @@ -19,14 +19,12 @@ #ifndef _MXC_GPIO_MX1_MX2_H #define _MXC_GPIO_MX1_MX2_H +#if !defined(_MXC_GPIO_MX1_H) && !defined(_MXC_GPIO_MX2_H) +#error "Please don't include mach/iomux-mx1-mx2.h directly, include instead" +#endif + #include -#define MXC_GPIO_ALLOC_MODE_NORMAL 0 -#define MXC_GPIO_ALLOC_MODE_NO_ALLOC 1 -#define MXC_GPIO_ALLOC_MODE_TRY_ALLOC 2 -#define MXC_GPIO_ALLOC_MODE_ALLOC_ONLY 4 -#define MXC_GPIO_ALLOC_MODE_RELEASE 8 - /* * GPIO Module and I/O Multiplexer * x = 0..3 for reg_A, reg_B, reg_C, reg_D @@ -50,13 +48,6 @@ #define MXC_SWR(x) (0x3c + ((x) << 8)) #define MXC_PUEN(x) (0x40 + ((x) << 8)) -#ifdef CONFIG_ARCH_MX1 -# define GPIO_PORT_MAX 3 -#endif -#ifdef CONFIG_ARCH_MX2 -# define GPIO_PORT_MAX 5 -#endif - #ifndef GPIO_PORT_MAX # error "GPIO config port count unknown!" #endif @@ -66,331 +57,67 @@ #define GPIO_PORT_SHIFT 5 #define GPIO_PORT_MASK (0x7 << GPIO_PORT_SHIFT) -#define GPIO_PORTA (0 << GPIO_PORT_SHIFT) -#define GPIO_PORTB (1 << GPIO_PORT_SHIFT) -#define GPIO_PORTC (2 << GPIO_PORT_SHIFT) -#define GPIO_PORTD (3 << GPIO_PORT_SHIFT) -#define GPIO_PORTE (4 << GPIO_PORT_SHIFT) -#define GPIO_PORTF (5 << GPIO_PORT_SHIFT) - -#define GPIO_OUT (1 << 8) -#define GPIO_IN (0 << 8) -#define GPIO_PUEN (1 << 9) - -#define GPIO_PF (1 << 10) -#define GPIO_AF (1 << 11) - -#define GPIO_OCR_SHIFT 12 -#define GPIO_OCR_MASK (3 << GPIO_OCR_SHIFT) -#define GPIO_AIN (0 << GPIO_OCR_SHIFT) -#define GPIO_BIN (1 << GPIO_OCR_SHIFT) -#define GPIO_CIN (2 << GPIO_OCR_SHIFT) -#define GPIO_GPIO (3 << GPIO_OCR_SHIFT) - -#define GPIO_AOUT_SHIFT 14 -#define GPIO_AOUT_MASK (3 << GPIO_AOUT_SHIFT) -#define GPIO_AOUT (0 << GPIO_AOUT_SHIFT) -#define GPIO_AOUT_ISR (1 << GPIO_AOUT_SHIFT) -#define GPIO_AOUT_0 (2 << GPIO_AOUT_SHIFT) -#define GPIO_AOUT_1 (3 << GPIO_AOUT_SHIFT) - -#define GPIO_BOUT_SHIFT 16 -#define GPIO_BOUT_MASK (3 << GPIO_BOUT_SHIFT) -#define GPIO_BOUT (0 << GPIO_BOUT_SHIFT) -#define GPIO_BOUT_ISR (1 << GPIO_BOUT_SHIFT) -#define GPIO_BOUT_0 (2 << GPIO_BOUT_SHIFT) -#define GPIO_BOUT_1 (3 << GPIO_BOUT_SHIFT) +#define GPIO_INDEX(gpio_cookie) ((gpio_cookie) & GPIO_PIN_MASK) +#define GPIO_PORT(gpio_cookie) (((gpio_cookie) & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT) +#define IOMUX_TO_GPIO(gpio_cookie) ((gpio_cookie) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) + +#define GPIO_PORTA (0 << GPIO_PORT_SHIFT) +#define GPIO_PORTB (1 << GPIO_PORT_SHIFT) +#define GPIO_PORTC (2 << GPIO_PORT_SHIFT) +#define GPIO_PORTD (3 << GPIO_PORT_SHIFT) +#define GPIO_PORTE (4 << GPIO_PORT_SHIFT) +#define GPIO_PORTF (5 << GPIO_PORT_SHIFT) + +#define GPIO_OUT (1 << 8) +#define GPIO_IN (0 << 8) +#define GPIO_PUEN (1 << 9) + +#define GPIO_PF (1 << 10) +#define GPIO_AF (1 << 11) + +#define GPIO_OCR_SHIFT 12 +#define GPIO_OCR_MASK (3 << GPIO_OCR_SHIFT) +#define GPIO_AIN (0 << GPIO_OCR_SHIFT) +#define GPIO_BIN (1 << GPIO_OCR_SHIFT) +#define GPIO_CIN (2 << GPIO_OCR_SHIFT) +#define GPIO_GPIO (3 << GPIO_OCR_SHIFT) + +#define GPIO_AOUT_SHIFT 14 +#define GPIO_AOUT_MASK (3 << GPIO_AOUT_SHIFT) +#define GPIO_AOUT (0 << GPIO_AOUT_SHIFT) +#define GPIO_AOUT_ISR (1 << GPIO_AOUT_SHIFT) +#define GPIO_AOUT_0 (2 << GPIO_AOUT_SHIFT) +#define GPIO_AOUT_1 (3 << GPIO_AOUT_SHIFT) + +#define GPIO_BOUT_SHIFT 16 +#define GPIO_BOUT_MASK (3 << GPIO_BOUT_SHIFT) +#define GPIO_BOUT (0 << GPIO_BOUT_SHIFT) +#define GPIO_BOUT_ISR (1 << GPIO_BOUT_SHIFT) +#define GPIO_BOUT_0 (2 << GPIO_BOUT_SHIFT) +#define GPIO_BOUT_1 (3 << GPIO_BOUT_SHIFT) + +#define GPIO_DFLT_LOW (1 << 18) +#define GPIO_DFLT_HIGH (1 << 19) extern void mxc_gpio_mode(int gpio_mode); extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count, - int alloc_mode, const char *label); + const char *label); +extern void mxc_gpio_release_multiple_pins(const int *pin_list, int count); /*-------------------------------------------------------------------------*/ -/* assignements for GPIO alternate/primary functions */ - -/* FIXME: This list is not completed. The correct directions are - * missing on some (many) pins - */ -#ifdef CONFIG_ARCH_MX1 -#define PA0_AIN_SPI2_CLK (GPIO_GIUS | GPIO_PORTA | GPIO_OUT | 0) -#define PA0_AF_ETMTRACESYNC (GPIO_PORTA | GPIO_AF | 0) -#define PA1_AOUT_SPI2_RXD (GPIO_GIUS | GPIO_PORTA | GPIO_IN | 1) -#define PA1_PF_TIN (GPIO_PORTA | GPIO_PF | 1) -#define PA2_PF_PWM0 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 2) -#define PA3_PF_CSI_MCLK (GPIO_PORTA | GPIO_PF | 3) -#define PA4_PF_CSI_D0 (GPIO_PORTA | GPIO_PF | 4) -#define PA5_PF_CSI_D1 (GPIO_PORTA | GPIO_PF | 5) -#define PA6_PF_CSI_D2 (GPIO_PORTA | GPIO_PF | 6) -#define PA7_PF_CSI_D3 (GPIO_PORTA | GPIO_PF | 7) -#define PA8_PF_CSI_D4 (GPIO_PORTA | GPIO_PF | 8) -#define PA9_PF_CSI_D5 (GPIO_PORTA | GPIO_PF | 9) -#define PA10_PF_CSI_D6 (GPIO_PORTA | GPIO_PF | 10) -#define PA11_PF_CSI_D7 (GPIO_PORTA | GPIO_PF | 11) -#define PA12_PF_CSI_VSYNC (GPIO_PORTA | GPIO_PF | 12) -#define PA13_PF_CSI_HSYNC (GPIO_PORTA | GPIO_PF | 13) -#define PA14_PF_CSI_PIXCLK (GPIO_PORTA | GPIO_PF | 14) -#define PA15_PF_I2C_SDA (GPIO_PORTA | GPIO_OUT | GPIO_PF | 15) -#define PA16_PF_I2C_SCL (GPIO_PORTA | GPIO_OUT | GPIO_PF | 16) -#define PA17_AF_ETMTRACEPKT4 (GPIO_PORTA | GPIO_AF | 17) -#define PA17_AIN_SPI2_SS (GPIO_GIUS | GPIO_PORTA | GPIO_OUT | 17) -#define PA18_AF_ETMTRACEPKT5 (GPIO_PORTA | GPIO_AF | 18) -#define PA19_AF_ETMTRACEPKT6 (GPIO_PORTA | GPIO_AF | 19) -#define PA20_AF_ETMTRACEPKT7 (GPIO_PORTA | GPIO_AF | 20) -#define PA21_PF_A0 (GPIO_PORTA | GPIO_PF | 21) -#define PA22_PF_CS4 (GPIO_PORTA | GPIO_PF | 22) -#define PA23_PF_CS5 (GPIO_PORTA | GPIO_PF | 23) -#define PA24_PF_A16 (GPIO_PORTA | GPIO_PF | 24) -#define PA24_AF_ETMTRACEPKT0 (GPIO_PORTA | GPIO_AF | 24) -#define PA25_PF_A17 (GPIO_PORTA | GPIO_PF | 25) -#define PA25_AF_ETMTRACEPKT1 (GPIO_PORTA | GPIO_AF | 25) -#define PA26_PF_A18 (GPIO_PORTA | GPIO_PF | 26) -#define PA26_AF_ETMTRACEPKT2 (GPIO_PORTA | GPIO_AF | 26) -#define PA27_PF_A19 (GPIO_PORTA | GPIO_PF | 27) -#define PA27_AF_ETMTRACEPKT3 (GPIO_PORTA | GPIO_AF | 27) -#define PA28_PF_A20 (GPIO_PORTA | GPIO_PF | 28) -#define PA28_AF_ETMPIPESTAT0 (GPIO_PORTA | GPIO_AF | 28) -#define PA29_PF_A21 (GPIO_PORTA | GPIO_PF | 29) -#define PA29_AF_ETMPIPESTAT1 (GPIO_PORTA | GPIO_AF | 29) -#define PA30_PF_A22 (GPIO_PORTA | GPIO_PF | 30) -#define PA30_AF_ETMPIPESTAT2 (GPIO_PORTA | GPIO_AF | 30) -#define PA31_PF_A23 (GPIO_PORTA | GPIO_PF | 31) -#define PA31_AF_ETMTRACECLK (GPIO_PORTA | GPIO_AF | 31) -#define PB8_PF_SD_DAT0 (GPIO_PORTB | GPIO_PF | GPIO_PUEN | 8) -#define PB8_AF_MS_PIO (GPIO_PORTB | GPIO_AF | 8) -#define PB9_PF_SD_DAT1 (GPIO_PORTB | GPIO_PF | GPIO_PUEN | 9) -#define PB9_AF_MS_PI1 (GPIO_PORTB | GPIO_AF | 9) -#define PB10_PF_SD_DAT2 (GPIO_PORTB | GPIO_PF | GPIO_PUEN | 10) -#define PB10_AF_MS_SCLKI (GPIO_PORTB | GPIO_AF | 10) -#define PB11_PF_SD_DAT3 (GPIO_PORTB | GPIO_PF | 11) -#define PB11_AF_MS_SDIO (GPIO_PORTB | GPIO_AF | 11) -#define PB12_PF_SD_CLK (GPIO_PORTB | GPIO_PF | 12) -#define PB12_AF_MS_SCLK0 (GPIO_PORTB | GPIO_AF | 12) -#define PB13_PF_SD_CMD (GPIO_PORTB | GPIO_PF | GPIO_PUEN | 13) -#define PB13_AF_MS_BS (GPIO_PORTB | GPIO_AF | 13) -#define PB14_AF_SSI_RXFS (GPIO_PORTB | GPIO_AF | 14) -#define PB15_AF_SSI_RXCLK (GPIO_PORTB | GPIO_AF | 15) -#define PB16_AF_SSI_RXDAT (GPIO_PORTB | GPIO_IN | GPIO_AF | 16) -#define PB17_AF_SSI_TXDAT (GPIO_PORTB | GPIO_OUT | GPIO_AF | 17) -#define PB18_AF_SSI_TXFS (GPIO_PORTB | GPIO_AF | 18) -#define PB19_AF_SSI_TXCLK (GPIO_PORTB | GPIO_AF | 19) -#define PB20_PF_USBD_AFE (GPIO_PORTB | GPIO_PF | 20) -#define PB21_PF_USBD_OE (GPIO_PORTB | GPIO_PF | 21) -#define PB22_PFUSBD_RCV (GPIO_PORTB | GPIO_PF | 22) -#define PB23_PF_USBD_SUSPND (GPIO_PORTB | GPIO_PF | 23) -#define PB24_PF_USBD_VP (GPIO_PORTB | GPIO_PF | 24) -#define PB25_PF_USBD_VM (GPIO_PORTB | GPIO_PF | 25) -#define PB26_PF_USBD_VPO (GPIO_PORTB | GPIO_PF | 26) -#define PB27_PF_USBD_VMO (GPIO_PORTB | GPIO_PF | 27) -#define PB28_PF_UART2_CTS (GPIO_PORTB | GPIO_OUT | GPIO_PF | 28) -#define PB29_PF_UART2_RTS (GPIO_PORTB | GPIO_IN | GPIO_PF | 29) -#define PB30_PF_UART2_TXD (GPIO_PORTB | GPIO_OUT | GPIO_PF | 30) -#define PB31_PF_UART2_RXD (GPIO_PORTB | GPIO_IN | GPIO_PF | 31) -#define PC3_PF_SSI_RXFS (GPIO_PORTC | GPIO_PF | 3) -#define PC4_PF_SSI_RXCLK (GPIO_PORTC | GPIO_PF | 4) -#define PC5_PF_SSI_RXDAT (GPIO_PORTC | GPIO_IN | GPIO_PF | 5) -#define PC6_PF_SSI_TXDAT (GPIO_PORTC | GPIO_OUT | GPIO_PF | 6) -#define PC7_PF_SSI_TXFS (GPIO_PORTC | GPIO_PF | 7) -#define PC8_PF_SSI_TXCLK (GPIO_PORTC | GPIO_PF | 8) -#define PC9_PF_UART1_CTS (GPIO_PORTC | GPIO_OUT | GPIO_PF | 9) -#define PC10_PF_UART1_RTS (GPIO_PORTC | GPIO_IN | GPIO_PF | 10) -#define PC11_PF_UART1_TXD (GPIO_PORTC | GPIO_OUT | GPIO_PF | 11) -#define PC12_PF_UART1_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 12) -#define PC13_PF_SPI1_SPI_RDY (GPIO_PORTC | GPIO_PF | 13) -#define PC14_PF_SPI1_SCLK (GPIO_PORTC | GPIO_PF | 14) -#define PC15_PF_SPI1_SS (GPIO_PORTC | GPIO_PF | 15) -#define PC16_PF_SPI1_MISO (GPIO_PORTC | GPIO_PF | 16) -#define PC17_PF_SPI1_MOSI (GPIO_PORTC | GPIO_PF | 17) -#define PC24_BIN_UART3_RI (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 24) -#define PC25_BIN_UART3_DSR (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 25) -#define PC26_AOUT_UART3_DTR (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 26) -#define PC27_BIN_UART3_DCD (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 27) -#define PC28_BIN_UART3_CTS (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 28) -#define PC29_AOUT_UART3_RTS (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 29) -#define PC30_BIN_UART3_TX (GPIO_GIUS | GPIO_PORTC | GPIO_BIN | 30) -#define PC31_AOUT_UART3_RX (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 31) -#define PD6_PF_LSCLK (GPIO_PORTD | GPIO_OUT | GPIO_PF | 6) -#define PD7_PF_REV (GPIO_PORTD | GPIO_PF | 7) -#define PD7_AF_UART2_DTR (GPIO_GIUS | GPIO_PORTD | GPIO_IN | GPIO_AF | 7) -#define PD7_AIN_SPI2_SCLK (GPIO_GIUS | GPIO_PORTD | GPIO_AIN | 7) -#define PD8_PF_CLS (GPIO_PORTD | GPIO_PF | 8) -#define PD8_AF_UART2_DCD (GPIO_PORTD | GPIO_OUT | GPIO_AF | 8) -#define PD8_AIN_SPI2_SS (GPIO_GIUS | GPIO_PORTD | GPIO_AIN | 8) -#define PD9_PF_PS (GPIO_PORTD | GPIO_PF | 9) -#define PD9_AF_UART2_RI (GPIO_PORTD | GPIO_OUT | GPIO_AF | 9) -#define PD9_AOUT_SPI2_RXD (GPIO_GIUS | GPIO_PORTD | GPIO_IN | 9) -#define PD10_PF_SPL_SPR (GPIO_PORTD | GPIO_OUT | GPIO_PF | 10) -#define PD10_AF_UART2_DSR (GPIO_PORTD | GPIO_OUT | GPIO_AF | 10) -#define PD10_AIN_SPI2_TXD (GPIO_GIUS | GPIO_PORTD | GPIO_OUT | 10) -#define PD11_PF_CONTRAST (GPIO_PORTD | GPIO_OUT | GPIO_PF | 11) -#define PD12_PF_ACD_OE (GPIO_PORTD | GPIO_OUT | GPIO_PF | 12) -#define PD13_PF_LP_HSYNC (GPIO_PORTD | GPIO_OUT | GPIO_PF | 13) -#define PD14_PF_FLM_VSYNC (GPIO_PORTD | GPIO_OUT | GPIO_PF | 14) -#define PD15_PF_LD0 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 15) -#define PD16_PF_LD1 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 16) -#define PD17_PF_LD2 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 17) -#define PD18_PF_LD3 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 18) -#define PD19_PF_LD4 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 19) -#define PD20_PF_LD5 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 20) -#define PD21_PF_LD6 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 21) -#define PD22_PF_LD7 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 22) -#define PD23_PF_LD8 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 23) -#define PD24_PF_LD9 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 24) -#define PD25_PF_LD10 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 25) -#define PD26_PF_LD11 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 26) -#define PD27_PF_LD12 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 27) -#define PD28_PF_LD13 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 28) -#define PD29_PF_LD14 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 29) -#define PD30_PF_LD15 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 30) -#define PD31_PF_TMR2OUT (GPIO_PORTD | GPIO_PF | 31) -#define PD31_BIN_SPI2_TXD (GPIO_GIUS | GPIO_PORTD | GPIO_BIN | 31) -#endif - -#ifdef CONFIG_ARCH_MX2 -#define PA0_PF_USBH2_CLK (GPIO_PORTA | GPIO_PF | 0) -#define PA1_PF_USBH2_DIR (GPIO_PORTA | GPIO_PF | 1) -#define PA2_PF_USBH2_DATA7 (GPIO_PORTA | GPIO_PF | 2) -#define PA3_PF_USBH2_NXT (GPIO_PORTA | GPIO_PF | 3) -#define PA4_PF_USBH2_STP (GPIO_PORTA | GPIO_PF | 4) -#define PA5_PF_LSCLK (GPIO_PORTA | GPIO_OUT | GPIO_PF | 5) -#define PA6_PF_LD0 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 6) -#define PA7_PF_LD1 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 7) -#define PA8_PF_LD2 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 8) -#define PA9_PF_LD3 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 9) -#define PA10_PF_LD4 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 10) -#define PA11_PF_LD5 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 11) -#define PA12_PF_LD6 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 12) -#define PA13_PF_LD7 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 13) -#define PA14_PF_LD8 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 14) -#define PA15_PF_LD9 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 15) -#define PA16_PF_LD10 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 16) -#define PA17_PF_LD11 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 17) -#define PA18_PF_LD12 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 18) -#define PA19_PF_LD13 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 19) -#define PA20_PF_LD14 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 20) -#define PA21_PF_LD15 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 21) -#define PA22_PF_LD16 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 22) -#define PA23_PF_LD17 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 23) -#define PA24_PF_REV (GPIO_PORTA | GPIO_OUT | GPIO_PF | 24) -#define PA25_PF_CLS (GPIO_PORTA | GPIO_OUT | GPIO_PF | 25) -#define PA26_PF_PS (GPIO_PORTA | GPIO_OUT | GPIO_PF | 26) -#define PA27_PF_SPL_SPR (GPIO_PORTA | GPIO_OUT | GPIO_PF | 27) -#define PA28_PF_HSYNC (GPIO_PORTA | GPIO_OUT | GPIO_PF | 28) -#define PA29_PF_VSYNC (GPIO_PORTA | GPIO_OUT | GPIO_PF | 29) -#define PA30_PF_CONTRAST (GPIO_PORTA | GPIO_OUT | GPIO_PF | 30) -#define PA31_PF_OE_ACD (GPIO_PORTA | GPIO_OUT | GPIO_PF | 31) -#define PB10_PF_CSI_D0 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 10) -#define PB10_AF_UART6_TXD (GPIO_PORTB | GPIO_OUT | GPIO_AF | 10) -#define PB11_PF_CSI_D1 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 11) -#define PB11_AF_UART6_RXD (GPIO_PORTB | GPIO_IN | GPIO_AF | 11) -#define PB12_PF_CSI_D2 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 12) -#define PB12_AF_UART6_CTS (GPIO_PORTB | GPIO_OUT | GPIO_AF | 12) -#define PB13_PF_CSI_D3 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 13) -#define PB13_AF_UART6_RTS (GPIO_PORTB | GPIO_IN | GPIO_AF | 13) -#define PB14_PF_CSI_D4 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 14) -#define PB15_PF_CSI_MCLK (GPIO_PORTB | GPIO_OUT | GPIO_PF | 15) -#define PB16_PF_CSI_PIXCLK (GPIO_PORTB | GPIO_OUT | GPIO_PF | 16) -#define PB17_PF_CSI_D5 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 17) -#define PB18_PF_CSI_D6 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 18) -#define PB18_AF_UART5_TXD (GPIO_PORTB | GPIO_OUT | GPIO_AF | 18) -#define PB19_PF_CSI_D7 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 19) -#define PB19_AF_UART5_RXD (GPIO_PORTB | GPIO_IN | GPIO_AF | 19) -#define PB20_PF_CSI_VSYNC (GPIO_PORTB | GPIO_OUT | GPIO_PF | 20) -#define PB20_AF_UART5_CTS (GPIO_PORTB | GPIO_OUT | GPIO_AF | 20) -#define PB21_PF_CSI_HSYNC (GPIO_PORTB | GPIO_OUT | GPIO_PF | 21) -#define PB21_AF_UART5_RTS (GPIO_PORTB | GPIO_IN | GPIO_AF | 21) -#define PB22_PF_USBH1_SUSP (GPIO_PORTB | GPIO_PF | 22) -#define PB23_PF_USB_PWR (GPIO_PORTB | GPIO_PF | 23) -#define PB24_PF_USB_OC_B (GPIO_PORTB | GPIO_PF | 24) -#define PB25_PF_USBH1_RCV (GPIO_PORTB | GPIO_PF | 25) -#define PB26_PF_USBH1_FS (GPIO_PORTB | GPIO_PF | 26) -#define PB27_PF_USBH1_OE_B (GPIO_PORTB | GPIO_PF | 27) -#define PB28_PF_USBH1_TXDM (GPIO_PORTB | GPIO_PF | 28) -#define PB29_PF_USBH1_TXDP (GPIO_PORTB | GPIO_PF | 29) -#define PB30_PF_USBH1_RXDM (GPIO_PORTB | GPIO_PF | 30) -#define PB31_PF_USBH1_RXDP (GPIO_PORTB | GPIO_PF | 31) -#define PB26_AF_UART4_RTS (GPIO_PORTB | GPIO_IN | GPIO_PF | 26) -#define PB28_AF_UART4_TXD (GPIO_PORTB | GPIO_OUT | GPIO_AF | 28) -#define PB29_AF_UART4_CTS (GPIO_PORTB | GPIO_OUT | GPIO_AF | 29) -#define PB31_AF_UART4_RXD (GPIO_PORTB | GPIO_IN | GPIO_AF | 31) -#define PC5_PF_I2C2_SDA (GPIO_PORTC | GPIO_IN | GPIO_PF | 5) -#define PC6_PF_I2C2_SCL (GPIO_PORTC | GPIO_IN | GPIO_PF | 6) -#define PC16_PF_SSI4_FS (GPIO_PORTC | GPIO_IN | GPIO_PF | 16) -#define PC17_PF_SSI4_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 17) -#define PC18_PF_SSI4_TXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 18) -#define PC19_PF_SSI4_CLK (GPIO_PORTC | GPIO_IN | GPIO_PF | 19) -#define PC20_PF_SSI1_FS (GPIO_PORTC | GPIO_IN | GPIO_PF | 20) -#define PC21_PF_SSI1_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 21) -#define PC22_PF_SSI1_TXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 22) -#define PC23_PF_SSI1_CLK (GPIO_PORTC | GPIO_IN | GPIO_PF | 23) -#define PC24_PF_SSI2_FS (GPIO_PORTC | GPIO_IN | GPIO_PF | 24) -#define PC25_PF_SSI2_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 25) -#define PC26_PF_SSI2_TXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 26) -#define PC27_PF_SSI2_CLK (GPIO_PORTC | GPIO_IN | GPIO_PF | 27) -#define PC28_PF_SSI3_FS (GPIO_PORTC | GPIO_IN | GPIO_PF | 28) -#define PC29_PF_SSI3_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 29) -#define PC30_PF_SSI3_TXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 30) -#define PC31_PF_SSI3_CLK (GPIO_PORTC | GPIO_IN | GPIO_PF | 31) -#define PD0_AIN_FEC_TXD0 (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 0) -#define PD1_AIN_FEC_TXD1 (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 1) -#define PD2_AIN_FEC_TXD2 (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 2) -#define PD3_AIN_FEC_TXD3 (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 3) -#define PD4_AOUT_FEC_RX_ER (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 4) -#define PD5_AOUT_FEC_RXD1 (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 5) -#define PD6_AOUT_FEC_RXD2 (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 6) -#define PD7_AOUT_FEC_RXD3 (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 7) -#define PD8_AF_FEC_MDIO (GPIO_PORTD | GPIO_IN | GPIO_AF | 8) -#define PD9_AIN_FEC_MDC (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 9) -#define PD10_AOUT_FEC_CRS (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 10) -#define PD11_AOUT_FEC_TX_CLK (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 11) -#define PD12_AOUT_FEC_RXD0 (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 12) -#define PD13_AOUT_FEC_RX_DV (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 13) -#define PD14_AOUT_FEC_CLR (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 14) -#define PD15_AOUT_FEC_COL (GPIO_PORTD | GPIO_IN | GPIO_AOUT | 15) -#define PD16_AIN_FEC_TX_ER (GPIO_PORTD | GPIO_OUT | GPIO_AIN | 16) -#define PD17_PF_I2C_DATA (GPIO_PORTD | GPIO_OUT | GPIO_PF | 17) -#define PD18_PF_I2C_CLK (GPIO_PORTD | GPIO_OUT | GPIO_PF | 18) -#define PD19_AF_USBH2_DATA4 (GPIO_PORTD | GPIO_AF | 19) -#define PD20_AF_USBH2_DATA3 (GPIO_PORTD | GPIO_AF | 20) -#define PD21_AF_USBH2_DATA6 (GPIO_PORTD | GPIO_AF | 21) -#define PD22_AF_USBH2_DATA0 (GPIO_PORTD | GPIO_AF | 22) -#define PD23_AF_USBH2_DATA2 (GPIO_PORTD | GPIO_AF | 23) -#define PD24_AF_USBH2_DATA1 (GPIO_PORTD | GPIO_AF | 24) -#define PD25_PF_CSPI1_RDY (GPIO_PORTD | GPIO_OUT | GPIO_PF | 25) -#define PD26_PF_CSPI1_SS2 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 26) -#define PD26_AF_USBH2_DATA5 (GPIO_PORTD | GPIO_AF | 26) -#define PD27_PF_CSPI1_SS1 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 27) -#define PD28_PF_CSPI1_SS0 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 28) -#define PD29_PF_CSPI1_SCLK (GPIO_PORTD | GPIO_OUT | GPIO_PF | 29) -#define PD30_PF_CSPI1_MISO (GPIO_PORTD | GPIO_IN | GPIO_PF | 30) -#define PD31_PF_CSPI1_MOSI (GPIO_PORTD | GPIO_OUT | GPIO_PF | 31) -#define PF23_AIN_FEC_TX_EN (GPIO_PORTF | GPIO_OUT | GPIO_AIN | 23) -#define PE3_PF_UART2_CTS (GPIO_PORTE | GPIO_OUT | GPIO_PF | 3) -#define PE4_PF_UART2_RTS (GPIO_PORTE | GPIO_IN | GPIO_PF | 4) -#define PE6_PF_UART2_TXD (GPIO_PORTE | GPIO_OUT | GPIO_PF | 6) -#define PE7_PF_UART2_RXD (GPIO_PORTE | GPIO_IN | GPIO_PF | 7) -#define PE8_PF_UART3_TXD (GPIO_PORTE | GPIO_OUT | GPIO_PF | 8) -#define PE9_PF_UART3_RXD (GPIO_PORTE | GPIO_IN | GPIO_PF | 9) -#define PE10_PF_UART3_CTS (GPIO_PORTE | GPIO_OUT | GPIO_PF | 10) -#define PE11_PF_UART3_RTS (GPIO_PORTE | GPIO_IN | GPIO_PF | 11) -#define PE12_PF_UART1_TXD (GPIO_PORTE | GPIO_OUT | GPIO_PF | 12) -#define PE13_PF_UART1_RXD (GPIO_PORTE | GPIO_IN | GPIO_PF | 13) -#define PE14_PF_UART1_CTS (GPIO_PORTE | GPIO_OUT | GPIO_PF | 14) -#define PE15_PF_UART1_RTS (GPIO_PORTE | GPIO_IN | GPIO_PF | 15) -#define PE16_AF_RTCK (GPIO_PORTE | GPIO_OUT | GPIO_AF | 16) -#define PE16_PF_RTCK (GPIO_PORTE | GPIO_OUT | GPIO_PF | 16) -#define PE18_AF_CSPI3_MISO (GPIO_PORTE | GPIO_IN | GPIO_AF | 18) -#define PE21_AF_CSPI3_SS (GPIO_PORTE | GPIO_OUT | GPIO_AF | 21) -#define PE22_AF_CSPI3_MOSI (GPIO_PORTE | GPIO_OUT | GPIO_AF | 22) -#define PE23_AF_CSPI3_SCLK (GPIO_PORTE | GPIO_OUT | GPIO_AF | 23) -#endif +#define MXC_PIN(port,gpio,fn,flags) \ + (GPIO_PORT##port | GPIO_##fn | (flags) | (gpio)) +#define MXC_DEFINE_PIN(port,gpio,fn,name,flags) \ + P##port##gpio##_##fn##_##name = MXC_PIN(port,gpio,fn,flags) /* decode irq number to use with IMR(x), ISR(x) and friends */ -#define IRQ_TO_REG(irq) ((irq - MXC_MAX_INT_LINES) >> 5) +#define IRQ_TO_REG(irq) ((irq - MXC_MAX_INT_LINES) >> 5) -#define IRQ_GPIOA(x) (MXC_MAX_INT_LINES + x) -#define IRQ_GPIOB(x) (IRQ_GPIOA(32) + x) -#define IRQ_GPIOC(x) (IRQ_GPIOB(32) + x) -#define IRQ_GPIOD(x) (IRQ_GPIOC(32) + x) +#define IRQ_GPIOA(x) (MXC_MAX_INT_LINES + x) +#define IRQ_GPIOB(x) (IRQ_GPIOA(32) + x) +#define IRQ_GPIOC(x) (IRQ_GPIOB(32) + x) +#define IRQ_GPIOD(x) (IRQ_GPIOC(32) + x) +#define IRQ_GPIOE(x) (IRQ_GPIOD(32) + x) #endif /* _MXC_GPIO_MX1_MX2_H */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/ipu.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/ipu.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/ipu.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/ipu.h 2009-01-05 17:47:15.000000000 +0100 @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * 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 _IPU_H_ +#define _IPU_H_ + +#include +#include + +/* IPU DMA Controller channel definitions. */ +enum ipu_channel { + IDMAC_IC_0 = 0, /* IC (encoding task) to memory */ + IDMAC_IC_1 = 1, /* IC (viewfinder task) to memory */ + IDMAC_ADC_0 = 1, + IDMAC_IC_2 = 2, + IDMAC_ADC_1 = 2, + IDMAC_IC_3 = 3, + IDMAC_IC_4 = 4, + IDMAC_IC_5 = 5, + IDMAC_IC_6 = 6, + IDMAC_IC_7 = 7, /* IC (sensor data) to memory */ + IDMAC_IC_8 = 8, + IDMAC_IC_9 = 9, + IDMAC_IC_10 = 10, + IDMAC_IC_11 = 11, + IDMAC_IC_12 = 12, + IDMAC_IC_13 = 13, + IDMAC_SDC_0 = 14, /* Background synchronous display data */ + IDMAC_SDC_1 = 15, /* Foreground data (overlay) */ + IDMAC_SDC_2 = 16, + IDMAC_SDC_3 = 17, + IDMAC_ADC_2 = 18, + IDMAC_ADC_3 = 19, + IDMAC_ADC_4 = 20, + IDMAC_ADC_5 = 21, + IDMAC_ADC_6 = 22, + IDMAC_ADC_7 = 23, + IDMAC_PF_0 = 24, + IDMAC_PF_1 = 25, + IDMAC_PF_2 = 26, + IDMAC_PF_3 = 27, + IDMAC_PF_4 = 28, + IDMAC_PF_5 = 29, + IDMAC_PF_6 = 30, + IDMAC_PF_7 = 31, +}; + +/* Order significant! */ +enum ipu_channel_status { + IPU_CHANNEL_FREE, + IPU_CHANNEL_GRANTED, + IPU_CHANNEL_INITIALIZED, + IPU_CHANNEL_READY, + IPU_CHANNEL_ENABLED, +}; + +#define IPU_CHANNELS_NUM 32 + +enum pixel_fmt { + /* 1 byte */ + IPU_PIX_FMT_GENERIC, + IPU_PIX_FMT_RGB332, + IPU_PIX_FMT_YUV420P, + IPU_PIX_FMT_YUV422P, + IPU_PIX_FMT_YUV420P2, + IPU_PIX_FMT_YVU422P, + /* 2 bytes */ + IPU_PIX_FMT_RGB565, + IPU_PIX_FMT_RGB666, + IPU_PIX_FMT_BGR666, + IPU_PIX_FMT_YUYV, + IPU_PIX_FMT_UYVY, + /* 3 bytes */ + IPU_PIX_FMT_RGB24, + IPU_PIX_FMT_BGR24, + /* 4 bytes */ + IPU_PIX_FMT_GENERIC_32, + IPU_PIX_FMT_RGB32, + IPU_PIX_FMT_BGR32, + IPU_PIX_FMT_ABGR32, + IPU_PIX_FMT_BGRA32, + IPU_PIX_FMT_RGBA32, +}; + +enum ipu_color_space { + IPU_COLORSPACE_RGB, + IPU_COLORSPACE_YCBCR, + IPU_COLORSPACE_YUV +}; + +/** + * Enumeration of IPU rotation modes + */ +enum ipu_rotate_mode { + /* Note the enum values correspond to BAM value */ + IPU_ROTATE_NONE = 0, + IPU_ROTATE_VERT_FLIP = 1, + IPU_ROTATE_HORIZ_FLIP = 2, + IPU_ROTATE_180 = 3, + IPU_ROTATE_90_RIGHT = 4, + IPU_ROTATE_90_RIGHT_VFLIP = 5, + IPU_ROTATE_90_RIGHT_HFLIP = 6, + IPU_ROTATE_90_LEFT = 7, +}; + +struct ipu_platform_data { + unsigned int irq_base; +}; + +/** + * Enumeration of DI ports for ADC. + */ +typedef enum { + DISP0, + DISP1, + DISP2, + DISP3 +} display_port_t; + +struct idmac_video_param { + unsigned short in_width; + unsigned short in_height; + uint32_t in_pixel_fmt; + unsigned short out_width; + unsigned short out_height; + uint32_t out_pixel_fmt; + unsigned short out_stride; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + display_port_t disp; + unsigned short out_left; + unsigned short out_top; +}; + +/* + * Union of initialization parameters for a logical channel. So far only video + * parameters are used. + */ +union ipu_channel_param { + struct idmac_video_param video; +}; + +struct idmac_tx_desc { + struct dma_async_tx_descriptor txd; + struct scatterlist *sg; /* scatterlist for this */ + unsigned int sg_len; /* tx-descriptor. */ + struct list_head list; +}; + +struct idmac_channel { + struct dma_chan dma_chan; + dma_cookie_t completed; /* last completed cookie */ + union ipu_channel_param params; + enum ipu_channel link; /* input channel, linked to the output */ + enum ipu_channel_status status; + struct idmac_client *iclient; /* Only one client per channel */ + unsigned int n_tx_desc; + struct idmac_tx_desc *desc; /* allocated tx-descriptors */ + struct scatterlist *sg[2]; /* scatterlist elements in buffer-0 and -1 */ + struct list_head free_list; /* free tx-descriptors */ + struct list_head queue; /* queued tx-descriptors */ + spinlock_t lock; /* protects sg[0,1], queue */ + struct mutex chan_mutex; /* protects status, cookie, free_list */ + unsigned int eof_irq; + bool sec_chan_en; + int active_buffer; +}; + +struct idmac_channel_rq { + enum ipu_channel channel; + struct idmac_channel *ichannel; +}; + +struct idmac_client { + int n_channels; + struct idmac_channel_rq *channels; + struct dma_client dma_client; +}; + +extern unsigned long ipu_clk_get_rate(void); + +#define to_tx_desc(tx) container_of(tx, struct idmac_tx_desc, txd) +#define to_idmac_chan(c) container_of(c, struct idmac_channel, dma_chan) +#define to_idmac_client(i) container_of(i, struct idmac_client, dma_client) + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/irqs.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/irqs.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/irqs.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/irqs.h 2009-03-11 13:16:24.000000000 +0100 @@ -14,4 +14,9 @@ #include extern void imx_irq_set_priority(unsigned char irq, unsigned char prio); +/* all normal IRQs can be FIQs */ +#define FIQ_START 0 +/* switch between IRQ and FIQ */ +extern int mxc_set_irq_fiq(unsigned int irq, unsigned int type); + #endif /* __ASM_ARCH_MXC_IRQS_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/memory.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/memory.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/memory.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/memory.h 2009-03-11 13:16:24.000000000 +0100 @@ -26,4 +26,5 @@ */ #define __bus_to_virt(a) __phys_to_virt(a) +#define CONSISTENT_DMA_SIZE (14 * SZ_1M) #endif /* __ASM_ARCH_MXC_MEMORY_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/mmc.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mmc.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/mmc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mmc.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,19 @@ +#ifndef ASMARM_ARCH_MMC_H +#define ASMARM_ARCH_MMC_H + +#include + +struct device; + +struct imxmmc_platform_data { + int (*get_ro)(struct device *); + int (*init)(struct device *, irq_handler_t, void *); + void (*exit)(struct device *, void *); + void (*setpower)(struct device *, unsigned int vdd); + int (*suspend)(struct device *, pm_message_t state); + int (*resume)(struct device *); +}; + +extern void imx_set_mmc_info(struct imxmmc_platform_data *info); + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/mx27.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mx27.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/mx27.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mx27.h 2009-03-11 13:16:24.000000000 +0100 @@ -24,12 +24,20 @@ #error "Do not include directly." #endif +/* the DMA code supports SG list chaining */ +#define ARCH_HAS_SG_CHAIN + /* IRAM */ #define IRAM_BASE_ADDR 0xFFFF4C00 /* internal ram */ /* Register offests */ +#ifndef __ASSEMBLY__ +#define AIPI_BASE_ADDR 0x10000000UL +#define AIPI_BASE_ADDR_VIRT ((void __iomem *)0xF4000000) +#else #define AIPI_BASE_ADDR 0x10000000 #define AIPI_BASE_ADDR_VIRT 0xF4000000 +#endif #define AIPI_SIZE SZ_1M #define DMA_BASE_ADDR (AIPI_BASE_ADDR + 0x01000) @@ -72,7 +80,8 @@ /* for mx27*/ #define OTG_BASE_ADDR USBOTG_BASE_ADDR #define SAHARA_BASE_ADDR (AIPI_BASE_ADDR + 0x25000) -#define EMMA_BASE_ADDR (AIPI_BASE_ADDR + 0x26400) +#define EMMA_PP_BASE_ADDR (AIPI_BASE_ADDR + 0x26000) +#define EMMA_PRP_BASE_ADDR (AIPI_BASE_ADDR + 0x26400) #define CCM_BASE_ADDR (AIPI_BASE_ADDR + 0x27000) #define SYSCTRL_BASE_ADDR (AIPI_BASE_ADDR + 0x27800) #define IIM_BASE_ADDR (AIPI_BASE_ADDR + 0x28000) @@ -91,16 +100,26 @@ #define AVIC_BASE_ADDR 0x10040000 +#ifndef __ASSEMBLY__ +#define SAHB1_BASE_ADDR 0x80000000UL +#define SAHB1_BASE_ADDR_VIRT ((void __iomem *)0xF4100000UL) +#else #define SAHB1_BASE_ADDR 0x80000000 #define SAHB1_BASE_ADDR_VIRT 0xF4100000 +#endif #define SAHB1_SIZE SZ_1M #define CSI_BASE_ADDR (SAHB1_BASE_ADDR + 0x0000) #define ATA_BASE_ADDR (SAHB1_BASE_ADDR + 0x1000) /* NAND, SDRAM, WEIM, M3IF, EMI controllers */ +#ifndef __ASSEMBLY__ +#define X_MEMC_BASE_ADDR 0xD8000000UL +#define X_MEMC_BASE_ADDR_VIRT ((void __iomem *)0xF4200000UL) +#else #define X_MEMC_BASE_ADDR 0xD8000000 #define X_MEMC_BASE_ADDR_VIRT 0xF4200000 +#endif #define X_MEMC_SIZE SZ_1M #define NFC_BASE_ADDR (X_MEMC_BASE_ADDR) @@ -127,14 +146,24 @@ * and returning the virtual address. If the physical address is not mapped, * it returns 0xDEADBEEF */ -#define IO_ADDRESS(x) \ - (void __iomem *) \ - (((x >= AIPI_BASE_ADDR) && (x < (AIPI_BASE_ADDR + AIPI_SIZE))) ? \ - AIPI_IO_ADDRESS(x) : \ - ((x >= SAHB1_BASE_ADDR) && (x < (SAHB1_BASE_ADDR + SAHB1_SIZE))) ? \ - SAHB1_IO_ADDRESS(x) : \ - ((x >= X_MEMC_BASE_ADDR) && (x < (X_MEMC_BASE_ADDR + X_MEMC_SIZE))) ? \ - X_MEMC_IO_ADDRESS(x) : 0xDEADBEEF) +#define IO_ADDRESS(x) \ + ((((x) >= AIPI_BASE_ADDR) && ((x) < (AIPI_BASE_ADDR + AIPI_SIZE))) ? \ + AIPI_IO_ADDRESS(x) : \ + (((x) >= SAHB1_BASE_ADDR) && ((x) < (SAHB1_BASE_ADDR + SAHB1_SIZE))) ? \ + SAHB1_IO_ADDRESS(x) : \ + (((x) >= X_MEMC_BASE_ADDR) && ((x) < (X_MEMC_BASE_ADDR + X_MEMC_SIZE))) ? \ + X_MEMC_IO_ADDRESS(x) : NULL) + +#define MXC_VADDR_RANGE(v,n) \ + (((v)) >= n##_BASE_ADDR_VIRT) && \ + (((v)) < n##_BASE_ADDR_VIRT + n##_SIZE) ? \ + ((v)-n##_BASE_ADDR_VIRT + n##_BASE_ADDR) : + +#define MXC_PHYS_ADDRESS(v) \ + (unsigned long)(MXC_VADDR_RANGE(v,AIPI) \ + MXC_VADDR_RANGE(v,SAHB1) \ + MXC_VADDR_RANGE(v,X_MEMC) \ + 0UL) /* define the address mapping macros: in physical address order */ #define AIPI_IO_ADDRESS(x) \ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/mxc_ehci.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mxc_ehci.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/mxc_ehci.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mxc_ehci.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,9 @@ +#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H +#define __INCLUDE_ASM_ARCH_MXC_EHCI_H + +struct mxc_usbh_platform_data { + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); +}; +#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/mxc_timer.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mxc_timer.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/mxc_timer.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/mxc_timer.h 2009-03-11 13:16:24.000000000 +0100 @@ -26,7 +26,7 @@ #include #include -#ifdef CONFIG_ARCH_IMX +#ifdef CONFIG_ARCH_MX1 #define TIMER_BASE IO_ADDRESS(TIM1_BASE_ADDR) #define TIMER_INTERRUPT TIM1_INT @@ -65,7 +65,7 @@ static void gpt_irq_acknowledge(void) { __raw_writel(0, TIMER_BASE + MXC_TSTAT); } -#endif /* CONFIG_ARCH_IMX */ +#endif /* CONFIG_ARCH_MX1 */ #ifdef CONFIG_ARCH_MX2 #define TIMER_BASE IO_ADDRESS(GPT1_BASE_ADDR) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/system.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/system.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/system.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/system.h 2009-03-11 13:16:24.000000000 +0100 @@ -21,14 +21,7 @@ #ifndef __ASM_ARCH_MXC_SYSTEM_H__ #define __ASM_ARCH_MXC_SYSTEM_H__ -static inline void arch_idle(void) -{ - cpu_do_idle(); -} - -static inline void arch_reset(char mode) -{ - cpu_reset(0); -} +extern void arch_idle(void); +extern void arch_reset(char mode); #endif /* __ASM_ARCH_MXC_SYSTEM_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/include/mach/ulpi.h linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/ulpi.h --- linux-2.6.28/arch/arm/plat-mxc/include/mach/ulpi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/include/mach/ulpi.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,31 @@ +#ifndef __MACH_ULPI_H +#define __MACH_ULPI_H + +int ulpi_set(u8 bits, int reg, void __iomem *view); +int ulpi_clear(u8 bits, int reg, void __iomem *view); +int ulpi_read(int reg, void __iomem *view); + +/* ISP 1504 register addresses */ +#define ISP1504_VID_LOW 0x00 /* Vendor ID low */ +#define ISP1504_VID_HIGH 0x01 /* Vendor ID high */ +#define ISP1504_PID_LOW 0x02 /* Product ID low */ +#define ISP1504_PID_HIGH 0x03 /* Product ID high */ +#define ISP1504_ITFCTL 0x07 /* Interface Control */ +#define ISP1504_OTGCTL 0x0A /* OTG Control */ + +/* add to above register address to access Set/Clear functions */ +#define ISP1504_REG_SET 0x01 +#define ISP1504_REG_CLEAR 0x02 + +/* 1504 OTG Control Register bits */ +#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */ +#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */ +#define DRV_VBUS (1 << 5) /* Drive Vbus */ +#define CHRG_VBUS (1 << 4) /* Charge Vbus */ +#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */ +#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */ +#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */ +#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */ + +#endif /* __MACH_ULPI_H */ + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/iomux-mx1-mx2.c linux-2.6.28-karo/arch/arm/plat-mxc/iomux-mx1-mx2.c --- linux-2.6.28/arch/arm/plat-mxc/iomux-mx1-mx2.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/iomux-mx1-mx2.c 2009-03-11 13:16:24.000000000 +0100 @@ -1,9 +1,10 @@ /* - * arch/arm/mach-mxc/generic.c + * arch/arm/plat-mxc/iomux-mx1-mx2.c * - * author: Sascha Hauer - * Created: april 20th, 2004 + * Author: Sascha Hauer + * Created: April 20th, 2004 * Copyright: Synertronixx GmbH +// FIXME: This is most likely as incorrect as the filename comment above * * Common code for i.MX machines * @@ -32,7 +33,7 @@ #include #include -#include +#include void mxc_gpio_mode(int gpio_mode) { @@ -40,6 +41,31 @@ void mxc_gpio_mode(int gpio_mode) unsigned int port = (gpio_mode & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT; unsigned int ocr = (gpio_mode & GPIO_OCR_MASK) >> GPIO_OCR_SHIFT; unsigned int tmp; + unsigned long flags; + char buf[64]; + + local_irq_save(flags); + + if (gpio_mode & GPIO_OUT) { + switch (gpio_mode & (GPIO_DFLT_LOW | GPIO_DFLT_HIGH)) { + case 0: + break; + case GPIO_DFLT_LOW: + tmp = __raw_readl(VA_GPIO_BASE + MXC_DR(port)); + tmp &= ~(1 << pin); + __raw_writel(tmp, VA_GPIO_BASE + MXC_DR(port)); + break; + case GPIO_DFLT_HIGH: + tmp = __raw_readl(VA_GPIO_BASE + MXC_DR(port)); + tmp |= (1 << pin); + __raw_writel(tmp, VA_GPIO_BASE + MXC_DR(port)); + break; + default: + printk(KERN_ERR + "GPIO_DFLT_LOW and GPIO_DFLT_HIGH both set for P%c%d\n", + port + 'A', pin); + } + } /* Pullup enable */ tmp = __raw_readl(VA_GPIO_BASE + MXC_PUEN(port)); @@ -106,51 +132,50 @@ void mxc_gpio_mode(int gpio_mode) tmp |= ((gpio_mode >> GPIO_BOUT_SHIFT) & 3) << (pin * 2); __raw_writel(tmp, VA_GPIO_BASE + MXC_ICONFB2(port)); } + local_irq_restore(flags); } EXPORT_SYMBOL(mxc_gpio_mode); int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count, - int alloc_mode, const char *label) + const char *label) { - const int *p = pin_list; + const int *p; int i; - unsigned gpio; - unsigned mode; + int ret = -EINVAL; - for (i = 0; i < count; i++) { - gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); - mode = *p & ~(GPIO_PIN_MASK | GPIO_PORT_MASK); + /* Try to obtain all requested GPIOs */ + for (i = 0, p = pin_list; i < count; i++, p++) { + unsigned gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); if (gpio >= (GPIO_PORT_MAX + 1) * 32) goto setup_error; - if (alloc_mode & MXC_GPIO_ALLOC_MODE_RELEASE) - gpio_free(gpio); - else if (!(alloc_mode & MXC_GPIO_ALLOC_MODE_NO_ALLOC)) - if (gpio_request(gpio, label) - && !(alloc_mode & MXC_GPIO_ALLOC_MODE_TRY_ALLOC)) - goto setup_error; - - if (!(alloc_mode & (MXC_GPIO_ALLOC_MODE_ALLOC_ONLY | - MXC_GPIO_ALLOC_MODE_RELEASE))) - mxc_gpio_mode(gpio | mode); - - p++; + ret = gpio_request(gpio, label); + if (ret) + goto setup_error; + } + /* Reconfigure all requested pins */ + for (i = 0, p = pin_list; i < count; i++, p++) { + mxc_gpio_mode(*p); } return 0; setup_error: - if (alloc_mode & (MXC_GPIO_ALLOC_MODE_NO_ALLOC | - MXC_GPIO_ALLOC_MODE_TRY_ALLOC)) - return -EINVAL; - - while (p != pin_list) { - p--; - gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); + mxc_gpio_release_multiple_pins(pin_list, i); + return ret; +} +EXPORT_SYMBOL(mxc_gpio_setup_multiple_pins); + +void mxc_gpio_release_multiple_pins(const int *pin_list, int count) +{ + const int *p; + int i; + + for (i = 0, p = pin_list; i < count; i++, p++) { + unsigned gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); gpio_free(gpio); } - return -EINVAL; } -EXPORT_SYMBOL(mxc_gpio_setup_multiple_pins); +EXPORT_SYMBOL(mxc_gpio_release_multiple_pins); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/irq.c linux-2.6.28-karo/arch/arm/plat-mxc/irq.c --- linux-2.6.28/arch/arm/plat-mxc/irq.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/irq.c 2009-03-11 18:48:21.000000000 +0100 @@ -17,9 +17,13 @@ * MA 02110-1301, USA. */ +#include +#include #include #include +#include #include +#include #define AVIC_BASE IO_ADDRESS(AVIC_BASE_ADDR) #define AVIC_INTCNTL (AVIC_BASE + 0x00) /* int control reg */ @@ -65,6 +69,28 @@ void imx_irq_set_priority(unsigned char EXPORT_SYMBOL(imx_irq_set_priority); #endif +#ifdef CONFIG_FIQ +int mxc_set_irq_fiq(unsigned int irq, unsigned int type) +{ + unsigned int irqt; + + if (irq >= MXC_MAX_INT_LINES) + return -EINVAL; + + if (irq < MXC_MAX_INT_LINES / 2) { + irqt = __raw_readl(AVIC_INTTYPEL) & ~(1 << irq); + __raw_writel(irqt | (!!type << irq), AVIC_INTTYPEL); + } else { + irq -= MXC_MAX_INT_LINES / 2; + irqt = __raw_readl(AVIC_INTTYPEH) & ~(1 << irq); + __raw_writel(irqt | (!!type << irq), AVIC_INTTYPEH); + } + + return 0; +} +EXPORT_SYMBOL(mxc_set_irq_fiq); +#endif /* CONFIG_FIQ */ + /* Disable interrupt number "irq" in the AVIC */ static void mxc_mask_irq(unsigned int irq) { @@ -77,12 +103,94 @@ static void mxc_unmask_irq(unsigned int __raw_writel(irq, AVIC_INTENNUM); } +static u32 saved_wakeup_low, saved_wakeup_high; +static u32 suspend_wakeup_low, suspend_wakeup_high; + +/* Set interrupt number "irq" in the AVIC as a wake-up source */ +static int mxc_set_wake_irq(unsigned int irq, unsigned int enable) +{ + uint32_t *wakeup_intr; + uint32_t irq_bit; + + if (irq < 32) { + wakeup_intr = &suspend_wakeup_low; + irq_bit = 1 << irq; + } else { + wakeup_intr = &suspend_wakeup_high; + irq_bit = 1 << (irq - 32); + } + + if (enable) { + *wakeup_intr |= irq_bit; + } else { + *wakeup_intr &= ~irq_bit; + } + + return 0; +} + static struct irq_chip mxc_avic_chip = { .ack = mxc_mask_irq, .mask = mxc_mask_irq, .unmask = mxc_unmask_irq, + .set_wake = mxc_set_wake_irq, +}; + +#ifdef CONFIG_PM +/* This function puts the AVIC in low-power mode/state. + * All interrupts that are enabled are first saved. + * Only those interrupts which are registered as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + */ +static int mxc_avic_suspend(struct sys_device *dev, pm_message_t mesg) +{ + saved_wakeup_high = __raw_readl(AVIC_INTENABLEH); + saved_wakeup_low = __raw_readl(AVIC_INTENABLEL); + + __raw_writel(suspend_wakeup_high, AVIC_INTENABLEH); + __raw_writel(suspend_wakeup_low, AVIC_INTENABLEL); + + return 0; +} + +/* This function brings the AVIC back from low-power state. + * All interrupts that were enabled prior to suspend are re-enabled. + */ +static int mxc_avic_resume(struct sys_device *dev) +{ + __raw_writel(saved_wakeup_high, AVIC_INTENABLEH); + __raw_writel(saved_wakeup_low, AVIC_INTENABLEL); + + return 0; +} + +#else +#define mxc_avic_suspend NULL +#define mxc_avic_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_avic_sysclass = { + .name = "mxc_irq", + .suspend = mxc_avic_suspend, + .resume = mxc_avic_resume, +}; + +/*! + * This structure represents AVIC as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +struct sys_device mxc_avic_device = { + .cls = &mxc_avic_sysclass, }; +static int __init mxc_avic_sysinit(void); + /* * This function initializes the AVIC hardware and disables all the * interrupts. It registers the interrupt enable and disable functions @@ -91,7 +199,6 @@ static struct irq_chip mxc_avic_chip = { void __init mxc_init_irq(void) { int i; - u32 reg; /* put the AVIC into the reset value with * all interrupts disabled @@ -119,5 +226,30 @@ void __init mxc_init_irq(void) /* init architectures chained interrupt handler */ mxc_register_gpios(); +#ifdef CONFIG_FIQ + /* Initialize FIQ */ + init_FIQ(); +#endif + printk(KERN_INFO "MXC IRQ initialized\n"); } + +extern int __init mxc_gpio_sys_init(void); + +/* This function registers AVIC hardware as a system device */ +static int __init mxc_avic_sysinit(void) +{ + int ret; + + ret = sysdev_class_register(&mxc_avic_sysclass); + if (ret) + return ret; + ret = sysdev_register(&mxc_avic_device); + if (ret) + return ret; + + ret = mxc_gpio_sys_init(); + + return ret; +} +arch_initcall(mxc_avic_sysinit); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/arch/arm/plat-mxc/ulpi.c linux-2.6.28-karo/arch/arm/plat-mxc/ulpi.c --- linux-2.6.28/arch/arm/plat-mxc/ulpi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/arch/arm/plat-mxc/ulpi.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,120 @@ +/* + * Copyright 2008 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include + +/* ULPIVIEW register bits */ +#define ULPIVW_WU (1 << 31) /* Wakeup */ +#define ULPIVW_RUN (1 << 30) /* read/write run */ +#define ULPIVW_WRITE (1 << 29) /* 0 = read 1 = write */ +#define ULPIVW_SS (1 << 27) /* SyncState */ +#define ULPIVW_PORT_MASK 0x07 /* Port field */ +#define ULPIVW_PORT_SHIFT 24 +#define ULPIVW_ADDR_MASK 0xFF /* data address field */ +#define ULPIVW_ADDR_SHIFT 16 +#define ULPIVW_RDATA_MASK 0xFF /* read data field */ +#define ULPIVW_RDATA_SHIFT 8 +#define ULPIVW_WDATA_MASK 0xFF /* write data field */ +#define ULPIVW_WDATA_SHIFT 0 + +static int ulpi_poll(void __iomem *view, uint32_t bit) +{ + uint32_t data; + int timeout = 10000; + + data = __raw_readl(view); + while (data & bit) { + if (!timeout--) + return -ETIMEDOUT; + + udelay(1); + data = __raw_readl(view); + } + return (data >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK; +} + +int ulpi_read(int reg, void __iomem *view) +{ + int ret; + + /* make sure interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + __raw_writel(ULPIVW_WU, view); + + /* wait for wakeup */ + ret = ulpi_poll(view, ULPIVW_WU); + if (ret < 0) + return ret; + } + + /* read the register */ + __raw_writel((ULPIVW_RUN | (reg << ULPIVW_ADDR_SHIFT)), view); + + /* wait for completion */ + return ulpi_poll(view, ULPIVW_RUN); +} +EXPORT_SYMBOL(ulpi_read); + +int ulpi_set(u8 bits, int reg, void __iomem *view) +{ + int ret; + + /* make sure the interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + __raw_writel(ULPIVW_WU, view); + /* wait for wakeup */ + ret = ulpi_poll(view, ULPIVW_WU); + if (ret < 0) + return ret; + } + + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_SET) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + /* wait for completion */ + ret = ulpi_poll(view, ULPIVW_RUN); + if (ret < 0) + return ret; + return 0; +} +EXPORT_SYMBOL(ulpi_set); + +int ulpi_clear(u8 bits, int reg, void __iomem *view) +{ + int ret; + + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_CLEAR) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + /* wait for completion */ + ret = ulpi_poll(view, ULPIVW_RUN); + if (ret < 0) + return ret; + return 0; +} +EXPORT_SYMBOL(ulpi_clear); + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/Makefile linux-2.6.28-karo/drivers/Makefile --- linux-2.6.28/drivers/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -102,3 +102,4 @@ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_REGULATOR) += regulator/ obj-$(CONFIG_STAGING) += staging/ +obj-$(CONFIG_DRIVERS_MXC) += mxc/ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/dma/dmaengine.c linux-2.6.28-karo/drivers/dma/dmaengine.c --- linux-2.6.28/drivers/dma/dmaengine.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/dma/dmaengine.c 2009-03-11 13:16:24.000000000 +0100 @@ -322,7 +322,12 @@ void dma_async_client_unregister(struct mutex_lock(&dma_list_mutex); /* free all channels the client is holding */ - list_for_each_entry(device, &dma_device_list, global_node) + list_for_each_entry(device, &dma_device_list, global_node) { + /* Client only can have channels from one DMA controller */ + if (client->slave && client->slave->dma_dev && + client->slave->dma_dev != device->dev) + continue; + list_for_each_entry(chan, &device->channels, device_node) { ack = client->event_callback(client, chan, DMA_RESOURCE_REMOVED); @@ -332,6 +337,7 @@ void dma_async_client_unregister(struct chan->client_count--; } } + } list_del(&client->global_node); mutex_unlock(&dma_list_mutex); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/gpio/gpiolib.c linux-2.6.28-karo/drivers/gpio/gpiolib.c --- linux-2.6.28/drivers/gpio/gpiolib.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/gpio/gpiolib.c 2009-03-11 13:16:24.000000000 +0100 @@ -789,6 +789,7 @@ int gpio_request(unsigned gpio, const ch } else { status = -EBUSY; module_put(chip->owner); + goto done; } if (chip->request) { @@ -842,9 +843,14 @@ void gpio_free(unsigned gpio) desc_set_label(desc, NULL); module_put(desc->chip->owner); clear_bit(FLAG_REQUESTED, &desc->flags); - } else + } else { + if (!chip) { + printk(KERN_ERR "%s: No chip for GPIO%d\n", __FUNCTION__, gpio); + } else { + printk(KERN_ERR "%s: GPIO%d not requested\n", __FUNCTION__, gpio); + } WARN_ON(extra_checks); - + } spin_unlock_irqrestore(&gpio_lock, flags); } EXPORT_SYMBOL_GPL(gpio_free); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/i2c/busses/Kconfig linux-2.6.28-karo/drivers/i2c/busses/Kconfig --- linux-2.6.28/drivers/i2c/busses/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/i2c/busses/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -355,6 +355,16 @@ config I2C_IBM_IIC This driver can also be built as a module. If so, the module will be called i2c-ibm_iic. +config I2C_MXC + tristate "MXC I2C support" + depends on I2C && ARCH_MXC + help + Say Y here if you want to use the IIC bus controller on Freescale + i.MX2 family of processors (like i.MX21 and i.MX27). + + This driver can also be built as a module. If so, the module + will be called i2c-mxc. + config I2C_IOP3XX tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface" depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/i2c/busses/Makefile linux-2.6.28-karo/drivers/i2c/busses/Makefile --- linux-2.6.28/drivers/i2c/busses/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/i2c/busses/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -11,6 +11,7 @@ obj-$(CONFIG_I2C_AMD756_S4882) += i2c-am obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o +obj-$(CONFIG_I2C_MXC) += i2c-mxc.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/i2c/busses/i2c-mxc.c linux-2.6.28-karo/drivers/i2c/busses/i2c-mxc.c --- linux-2.6.28/drivers/i2c/busses/i2c-mxc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/i2c/busses/i2c-mxc.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,667 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Driver for the Freescale Semiconductor MXC I2C buses. + * Based on i2c driver algorithm for PCF8584 adapters + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "imx_i2c" + +typedef struct { + struct i2c_adapter adap; + wait_queue_head_t wq; + void __iomem *membase; /* FIXME iomem? */ + int irq; + unsigned int clkdiv; + struct clk *clk; + bool low_power; + struct imx_i2c_platform_data *pdata; + bool transfer_done; + bool tx_success; +} imx_i2c_device; + +struct clk_div_table { + int reg_value; + int div; +}; + +static const struct clk_div_table i2c_clk_table[] = { + {0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28}, + {0, 30}, {1, 32}, {0x24, 32}, {2, 36}, + {0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44}, + {4, 48}, {0x28, 48}, {5, 52}, {0x29, 56}, + {6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72}, + {8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96}, + {0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128}, + {0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192}, + {0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256}, + {0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384}, + {0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512}, + {0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768}, + {0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024}, + {0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536}, + {0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048}, + {0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840}, + {0, 0} +}; + +/* Transmit a STOP signal to the slave device */ +static void imx_i2c_stop(imx_i2c_device * dev) +{ + unsigned int cr; + int retry = 16; + + cr = __raw_readw(dev->membase + MXC_I2CR); + cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX); + __raw_writew(cr, dev->membase + MXC_I2CR); + + /* + * Make sure STOP meets setup requirement. + */ + for (;;) { + unsigned int sr = __raw_readw(dev->membase + MXC_I2SR); + if ((sr & MXC_I2SR_IBB) == 0) break; + if (retry-- <= 0) { + printk(KERN_DEBUG "Bus busy\n"); + break; + } + udelay(3); + } +} + +/* + * Wait for the transmission of the data byte to complete. This function waits + * till we get a signal from the interrupt service routine indicating completion + * of the address cycle or we time out. + * The function returns 0 on success or -1 if an ack was not received + */ +static int imx_i2c_wait_for_tc(imx_i2c_device * dev, int trans_flag) +{ + int retry = 16; + + while (retry-- && !dev->transfer_done) { + wait_event_interruptible_timeout(dev->wq, + dev->transfer_done, + dev->adap.timeout); + } + dev->transfer_done = false; + + if (retry <= 0) { + /* Unable to send data */ + printk(KERN_DEBUG "Data not transmitted\n"); + return -1; + } else if (!(trans_flag & I2C_M_RD)) { + if (!dev->tx_success) { + /* + * An ACK was not received for a transmitted byte. Don't + * print a message here as slow devices might cause this + * condition quite often. This is not an error as + * routines may try to look if a device is ready. + */ + return -1; + } + } + + return 0; +} + +/* Transmit a START signal to the slave device */ +static void imx_i2c_start(imx_i2c_device * dev, struct i2c_msg *msg) +{ + unsigned int cr, sr; + unsigned int addr_trans; + int retry = 16; + + /* + * Set the slave address and the requested transfer mode + * in the data register + */ + addr_trans = msg->addr << 1; + if (msg->flags & I2C_M_RD) { + addr_trans |= 0x01; + } + + /* Set the Master bit */ + cr = __raw_readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_MSTA; + __raw_writew(cr, dev->membase + MXC_I2CR); + + /* Wait till the Bus Busy bit is set */ + sr = __raw_readw(dev->membase + MXC_I2SR); + while (retry-- && (!(sr & MXC_I2SR_IBB))) { + udelay(3); + sr = __raw_readw(dev->membase + MXC_I2SR); + } + if (retry <= 0) { + printk(KERN_DEBUG "Could not grab Bus ownership\n"); + } + + /* Set the Transmit bit */ + cr = __raw_readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_MTX; + __raw_writew(cr, dev->membase + MXC_I2CR); + + __raw_writew(addr_trans, dev->membase + MXC_I2DR); +} + +/* Transmit a REPEAT START to the slave device */ +static void imx_i2c_repstart(imx_i2c_device * dev, struct i2c_msg *msg) +{ + unsigned int cr; + unsigned int addr_trans; + + /* + * Set the slave address and the requested transfer mode + * in the data register + */ + addr_trans = msg->addr << 1; + if (msg->flags & I2C_M_RD) { + addr_trans |= 0x01; + } + cr = __raw_readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_RSTA; + __raw_writew(cr, dev->membase + MXC_I2CR); + udelay(3); + __raw_writew(addr_trans, dev->membase + MXC_I2DR); +} + +/* + * Read the received data. The function waits till data is available or times + * out. Generates a stop signal if this is the last message to be received. + * Sends an ack for all the bytes received except the last byte. + * The function returns the number of bytes read or -1 on time out. + */ +static int imx_i2c_readbytes(imx_i2c_device * dev, struct i2c_msg *msg, + int last, int addr_comp) +{ + int i; + char *buf = msg->buf; + int len = msg->len; + unsigned int cr; + + cr = __raw_readw(dev->membase + MXC_I2CR); + + /* switch to receive mode */ + cr &= ~MXC_I2CR_MTX; + /* + * Clear the TXAK bit to gen an ack when receiving only one byte. + */ + if (len == 1) + cr |= MXC_I2CR_TXAK; + else + cr &= ~MXC_I2CR_TXAK; + + __raw_writew(cr, dev->membase + MXC_I2CR); + /* + * Dummy read only at the end of an address cycle + */ + if (addr_comp > 0) + __raw_readw(dev->membase + MXC_I2DR); + + for (i = 0; i < len; i++) { + /* Wait for data transmission to complete */ + if (imx_i2c_wait_for_tc(dev, msg->flags)) { + imx_i2c_stop(dev); + return -1; + } + /* Do not generate an ACK for the last byte */ + if (i == (len - 2)) { + cr = __raw_readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_TXAK; + __raw_writew(cr, dev->membase + MXC_I2CR); + } else if (i == (len - 1)) { + if (last) + imx_i2c_stop(dev); + } + /* Read the data */ + *buf++ = __raw_readw(dev->membase + MXC_I2DR); + } + + return i; +} + +/* + * Write the data to the data register. Generates a stop signal if this is + * the last message to be sent or if no ack was received for the data sent. + * The function returns the number of bytes written or -1 on time out + * or if no ack was received for the data that was sent. + */ +static int imx_i2c_writebytes(imx_i2c_device * dev, struct i2c_msg *msg, + int last) +{ + int i; + char *buf = msg->buf; + int len = msg->len; + unsigned int cr; + + cr = __raw_readw(dev->membase + MXC_I2CR); + /* switch to transmit mode */ + cr |= MXC_I2CR_MTX; + __raw_writew(cr, dev->membase + MXC_I2CR); + + for (i = 0; i < len; i++) { + /* Write the data */ + __raw_writew(*buf++, dev->membase + MXC_I2DR); + if (imx_i2c_wait_for_tc(dev, msg->flags)) { + imx_i2c_stop(dev); + return -1; + } + } + if (last > 0) { + imx_i2c_stop(dev); + } + + return i; +} + +/* Function enables the I2C module and initializes the registers */ +static void imx_i2c_module_en(imx_i2c_device * dev, int trans_flag) +{ + clk_enable(dev->clk); + /* Set the frequency divider */ + __raw_writew(dev->clkdiv, dev->membase + MXC_IFDR); + /* Clear the status register */ + __raw_writew(0x0, dev->membase + MXC_I2SR); + /* Enable I2C and its interrupts */ + __raw_writew(MXC_I2CR_IEN, dev->membase + MXC_I2CR); + __raw_writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, dev->membase + MXC_I2CR); +} + +/* Disables the I2C module */ +static void imx_i2c_module_dis(imx_i2c_device * dev) +{ + __raw_writew(0x0, dev->membase + MXC_I2CR); + clk_disable(dev->clk); +} + +/* + * The function is registered in the adapter structure. It is called when an MXC + * driver wishes to transfer data to a device connected to the I2C device. + * The function returns the number of messages transferred, -EREMOTEIO on I2C + * failure and a 0 if the num argument is less than 0. + */ +static int imx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + imx_i2c_device *dev = (imx_i2c_device *) (i2c_get_adapdata(adap)); + int i, ret = 0, addr_comp = 0; + unsigned int sr; + + if (dev->low_power) { + printk(KERN_ERR "I2C Device in low power mode\n"); + return -EREMOTEIO; + } + + if (num < 1) + return 0; + + imx_i2c_module_en(dev, msgs[0].flags); + sr = __raw_readw(dev->membase + MXC_I2SR); + + /* Check bus state */ + if (sr & MXC_I2SR_IBB) { + imx_i2c_module_dis(dev); + printk(KERN_DEBUG "Bus busy\n"); + return -EREMOTEIO; + } + + dev->transfer_done = false; + dev->tx_success = false; + for (i = 0; i < num && ret >= 0; i++) { + addr_comp = 0; + /* + * Send the slave address and transfer direction in the + * address cycle + */ + if (i == 0) { + /* Send a start or repeat start signal */ + imx_i2c_start(dev, &msgs[0]); + /* Wait for the address cycle to complete */ + if (imx_i2c_wait_for_tc(dev, msgs[0].flags)) { + imx_i2c_stop(dev); + imx_i2c_module_dis(dev); + return -EREMOTEIO; + } + addr_comp = 1; + } else { + /* + * Generate repeat start only if required i.e the address + * changed or the transfer direction changed + */ + if ((msgs[i].addr != msgs[i - 1].addr) || + ((msgs[i].flags & I2C_M_RD) != + (msgs[i - 1].flags & I2C_M_RD))) { + imx_i2c_repstart(dev, &msgs[i]); + /* Wait for the address cycle to complete */ + if (imx_i2c_wait_for_tc(dev, msgs[i].flags)) { + imx_i2c_stop(dev); + imx_i2c_module_dis(dev); + return -EREMOTEIO; + } + addr_comp = 1; + } + } + + /* Transfer the data */ + if (msgs[i].flags & I2C_M_RD) { + /* Read the data */ + ret = imx_i2c_readbytes(dev, &msgs[i], (i + 1 == num), + addr_comp); + if (ret < 0) { + printk(KERN_ERR "imx_i2c_readbytes: fail.\n"); + break; + } + } else { + /* Write the data */ + ret = imx_i2c_writebytes(dev, &msgs[i], (i + 1 == num)); + if (ret < 0) { + printk(KERN_ERR "imx_i2c_writebytes: fail.\n"); + break; + } + } + } + + imx_i2c_module_dis(dev); + return i; +} + +/* + * Returns the i2c functionality supported by this driver. + * Returns the functionality that is supported. + */ +static u32 imx_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* + * Stores the pointers for the i2c algorithm functions. The algorithm functions + * is used by the i2c bus driver to talk to the i2c bus + */ +static struct i2c_algorithm imx_i2c_algorithm = { + .master_xfer = imx_i2c_xfer, + .functionality = imx_i2c_func +}; + +/* + * Interrupt Service Routine. It signals to the process about the data transfer + * completion. Also sets a flag if bus arbitration is lost. + * The function returns IRQ_HANDLED. + */ +static irqreturn_t imx_i2c_handler(int irq, void *dev_id) +{ + imx_i2c_device *dev = dev_id; + unsigned int sr, cr; + + sr = __raw_readw(dev->membase + MXC_I2SR); + cr = __raw_readw(dev->membase + MXC_I2CR); + + /* + * Clear the interrupt bit + */ + __raw_writew(0x0, dev->membase + MXC_I2SR); + + if (sr & MXC_I2SR_IAL) { + printk(KERN_DEBUG "Bus Arbitration lost\n"); + } else { + /* Interrupt due byte transfer completion */ + dev->tx_success = false; + /* Check if RXAK is received in Transmit mode */ + if ((cr & MXC_I2CR_MTX) && (!(sr & MXC_I2SR_RXAK))) { + dev->tx_success = true; + } + dev->transfer_done = true; + wake_up_interruptible(&dev->wq); + } + + return IRQ_HANDLED; +} + +/* + * This function is called to put the I2C adapter in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * The function returns 0 on success and -1 on failure. + */ +static int mxci2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + imx_i2c_device *mxcdev = platform_get_drvdata(pdev); + unsigned int sr; + + if (mxcdev == NULL) + return -1; + + /* Prevent further calls to be processed */ + mxcdev->low_power = true; + /* Wait till we finish the current transfer */ + sr = __raw_readw(mxcdev->membase + MXC_I2SR); + while (sr & MXC_I2SR_IBB) { + msleep(10); + sr = __raw_readw(mxcdev->membase + MXC_I2SR); + } + + mxcdev->pdata->exit(pdev); + + return 0; +} + +/* + * This function is called to bring the I2C adapter back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * The function returns 0 on success and -1 on failure + */ +static int mxci2c_resume(struct platform_device *pdev) +{ + imx_i2c_device *mxcdev = platform_get_drvdata(pdev); + + if (mxcdev == NULL) + return -1; + + mxcdev->low_power = false; + mxcdev->pdata->init(pdev); + + return 0; +} + +/* + * This function is called during the driver binding process. + * The function always returns 0. + */ +static int mxci2c_probe(struct platform_device *pdev) +{ + imx_i2c_device *imx_i2c; + struct imx_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data; + struct resource *res; + int id = pdev->id; + u32 clk_freq; + int ret; + int i; + + imx_i2c = kzalloc(sizeof(imx_i2c_device), GFP_KERNEL); + if (!imx_i2c) { + return -ENOMEM; + } + + if (i2c_plat_data == NULL) { + dev_err(&pdev->dev, "No platform data for device!\n"); + return -ENODEV; + } + imx_i2c->pdata = i2c_plat_data; + /* claim the region we will work on */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + ret = -ENODEV; + goto err1; + } + if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "request_mem_region failed for IMX I2C %d\n", id); + ret = -EBUSY; + goto err1; + } + + imx_i2c->membase = ioremap(res->start, resource_size(res)); + if (imx_i2c->membase == NULL) { + ret = -ENOMEM; + goto err2; + } + /* Claim the I2C irq line */ + imx_i2c->irq = platform_get_irq(pdev, 0); + if (imx_i2c->irq < 0) { + dev_err(&pdev->dev, "No interrupt defined for IMX I2C %d\n", id); + ret = imx_i2c->irq; + goto err3; + } + ret = request_irq(imx_i2c->irq, imx_i2c_handler, + 0, DRV_NAME, imx_i2c); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot claim interrupt %d for IMX I2C %d\n", + imx_i2c->irq, id); + goto err3; + } + + init_waitqueue_head(&imx_i2c->wq); + + imx_i2c->low_power = false; + imx_i2c->tx_success = false; + imx_i2c->transfer_done = false; + + imx_i2c->pdata->init(pdev); + + imx_i2c->clk = clk_get(&pdev->dev, "i2c_clk"); + if (IS_ERR(imx_i2c->clk)) { + ret = PTR_ERR(imx_i2c->clk); + dev_err(&pdev->dev, "Cannot get clock for for IMX I2C %d: %d\n", id, ret); + goto err4; + } + + clk_freq = clk_get_rate(imx_i2c->clk); + imx_i2c->clkdiv = -1; + if (i2c_plat_data->max_clk) { + /* Calculate divider and round up any fractional part */ + int div = (clk_freq + i2c_plat_data->max_clk - 1) / + i2c_plat_data->max_clk; + for (i = 0; i2c_clk_table[i].div != 0; i++) { + if (i2c_clk_table[i].div >= div) { + imx_i2c->clkdiv = i2c_clk_table[i].reg_value; + break; + } + } + } + if (imx_i2c->clkdiv == -1) { + i = ARRAY_SIZE(i2c_clk_table) - 2; + imx_i2c->clkdiv = i2c_clk_table[i].reg_value; + } + dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n", + clk_freq, i2c_clk_table[i].div, + clk_freq / i2c_clk_table[i].div, imx_i2c->clkdiv); + + /* + * Set the adapter information + */ + strcpy(imx_i2c->adap.name, MXC_ADAPTER_NAME); + imx_i2c->adap.id = id; + imx_i2c->adap.nr = id; + imx_i2c->adap.algo = &imx_i2c_algorithm; + imx_i2c->adap.timeout = 1; + platform_set_drvdata(pdev, imx_i2c); + i2c_set_adapdata(&imx_i2c->adap, imx_i2c); + if ((ret = i2c_add_numbered_adapter(&imx_i2c->adap)) < 0) { + dev_err(&pdev->dev, "Cannot register the IMX I2C %d master\n", id); + goto err5; + } + + return 0; +err5: + clk_put(imx_i2c->clk); +err4: + imx_i2c->pdata->exit(pdev); + free_irq(imx_i2c->irq, imx_i2c); +err3: + iounmap(imx_i2c->membase); +err2: + release_mem_region(res->start, resource_size(res)); +err1: + dev_err(&pdev->dev, "failed to probe i2c adapter\n"); + kfree(imx_i2c); + return ret; +} + +/* + * Dissociates the driver from the I2C device. + */ +static int mxci2c_remove(struct platform_device *pdev) +{ + imx_i2c_device *imx_i2c = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(imx_i2c->irq, imx_i2c); + i2c_del_adapter(&imx_i2c->adap); + imx_i2c->pdata->exit(pdev); + clk_put(imx_i2c->clk); + iounmap(imx_i2c->membase); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + return 0; +} + +/* + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxci2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = mxci2c_probe, + .remove = mxci2c_remove, + .suspend = mxci2c_suspend, + .resume = mxci2c_resume, +}; + +static int __init imx_i2c_init(void) +{ + return platform_driver_register(&mxci2c_driver); +} + +static void __exit imx_i2c_exit(void) +{ + platform_driver_unregister(&mxci2c_driver); +} + +subsys_initcall(imx_i2c_init); +module_exit(imx_i2c_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC I2C driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/Kconfig linux-2.6.28-karo/drivers/media/video/Kconfig --- linux-2.6.28/drivers/media/video/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -531,6 +531,33 @@ config VIDEO_W9966 Check out for more information. +config VIDEO_MXC_CAMERA + tristate "MXC Video For Linux Camera" + depends on VIDEO_DEV && ARCH_MXC + default y + ---help--- + This is the video4linux2 capture driver based on MXC IPU/eMMA module. + +source "drivers/media/video/mxc/capture/Kconfig" + +config VIDEO_MXC_OUTPUT + tristate "MXC Video For Linux Video Output" + depends on VIDEO_DEV && ARCH_MXC + default y + ---help--- + This is the video4linux2 output driver based on MXC IPU/eMMA module. + +source "drivers/media/video/mxc/output/Kconfig" + +config VIDEO_MXC_OPL + tristate + depends on VIDEO_DEV && ARCH_MXC + default n + ---help--- + This is the ARM9-optimized OPL (Open Primitives Library) software + rotation/mirroring implementation. It may be used by eMMA video + capture or output device. + config VIDEO_CPIA tristate "CPiA Video For Linux" depends on VIDEO_V4L1 @@ -893,4 +920,12 @@ config USB_S2255 endif # V4L_USB_DRIVERS +config VIDEO_MX27 + tristate "i.MX27 Quick Capture Interface driver" + depends on VIDEO_DEV && MACH_MX27 + select SOC_CAMERA + select VIDEOBUF_DMA_CONTIG + ---help--- + This is a v4l2 driver for the i.MX27 Capture Interface + endif # VIDEO_CAPTURE_DRIVERS diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/Makefile linux-2.6.28-karo/drivers/media/video/Makefile --- linux-2.6.28/drivers/media/video/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -57,6 +57,11 @@ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o +obj-$(CONFIG_VIDEO_MXC_IPU_CAMERA) += mxc/capture/ +obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mxc/capture/ +obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc/output/ +obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mxc/output/ +obj-$(CONFIG_VIDEO_MXC_OPL) += mxc/opl/ obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o @@ -128,6 +133,7 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_MX27) += mx27_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mx27_camera.c linux-2.6.28-karo/drivers/media/video/mx27_camera.c --- linux-2.6.28/drivers/media/video/mx27_camera.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mx27_camera.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1185 @@ +/* + * V4L2 Driver for MX27 camera host + * + * Copyright (C) 2008, Sascha Hauer, Pengutronix + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define MX27_CAM_DRV_NAME "mx27-camera" +#define MX27_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) /* FIXME: Whats this? */ + +static const char *mx27_cam_driver_description = "i.MX27_Camera"; + +/* reset values */ +#define CSICR1_RESET_VAL 0x40000800 +#define CSICR2_RESET_VAL 0x0 +#define CSICR3_RESET_VAL 0x0 + +/* csi control reg 1 */ +#define CSICR1_SWAP16_EN (1 << 31) +#define CSICR1_EXT_VSYNC (1 << 30) +#define CSICR1_EOF_INTEN (1 << 29) +#define CSICR1_PRP_IF_EN (1 << 28) +#define CSICR1_CCIR_MODE (1 << 27) +#define CSICR1_COF_INTEN (1 << 26) +#define CSICR1_SF_OR_INTEN (1 << 25) +#define CSICR1_RF_OR_INTEN (1 << 24) +#define CSICR1_STATFF_LEVEL (3 << 22) +#define CSICR1_STATFF_INTEN (1 << 21) +#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) +#define CSICR1_RXFF_INTEN (1 << 18) +#define CSICR1_SOF_POL (1 << 17) +#define CSICR1_SOF_INTEN (1 << 16) +#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12) +#define CSICR1_HSYNC_POL (1 << 11) +#define CSICR1_CCIR_EN (1 << 10) +#define CSICR1_MCLKEN (1 << 9) +#define CSICR1_FCC (1 << 8) +#define CSICR1_PACK_DIR (1 << 7) +#define CSICR1_CLR_STATFIFO (1 << 6) +#define CSICR1_CLR_RXFIFO (1 << 5) +#define CSICR1_GCLK_MODE (1 << 4) +#define CSICR1_INV_DATA (1 << 3) +#define CSICR1_INV_PCLK (1 << 2) +#define CSICR1_REDGE (1 << 1) + +#define SHIFT_STATFF_LEVEL 22 +#define SHIFT_RXFF_LEVEL 19 +#define SHIFT_MCLKDIV 12 + +/* control reg 3 */ +#define CSICR3_FRMCNT (0xFFFF << 16) +#define CSICR3_FRMCNT_RST (1 << 15) +#define CSICR3_CSI_SUP (1 << 3) +#define CSICR3_ZERO_PACK_EN (1 << 2) +#define CSICR3_ECC_INT_EN (1 << 1) +#define CSICR3_ECC_AUTO_EN (1 << 0) + +#define SHIFT_FRMCNT 16 + +/* csi status reg */ +#define CSISR_SFF_OR_INT (1 << 25) +#define CSISR_RFF_OR_INT (1 << 24) +#define CSISR_STATFF_INT (1 << 21) +#define CSISR_RXFF_INT (1 << 18) +#define CSISR_EOF_INT (1 << 17) +#define CSISR_SOF_INT (1 << 16) +#define CSISR_F2_INT (1 << 15) +#define CSISR_F1_INT (1 << 14) +#define CSISR_COF_INT (1 << 13) +#define CSISR_ECC_INT (1 << 1) +#define CSISR_DRDY (1 << 0) + +#define CSICR1 0x00 +#define CSICR2 0x04 +#define CSISR 0x08 +#define CSISTATFIFO 0x0c +#define CSIRFIFO 0x10 +#define CSIRXCNT 0x14 +#define CSICR3 0x1C + +/* EMMA PrP */ +#define PRP_CNTL 0x00 +#define PRP_INTR_CNTL 0x04 +#define PRP_INTRSTATUS 0x08 +#define PRP_SOURCE_Y_PTR 0x0c +#define PRP_SOURCE_CB_PTR 0x10 +#define PRP_SOURCE_CR_PTR 0x14 +#define PRP_DEST_RGB1_PTR 0x18 +#define PRP_DEST_RGB2_PTR 0x1c +#define PRP_DEST_Y_PTR 0x20 +#define PRP_DEST_CB_PTR 0x24 +#define PRP_DEST_CR_PTR 0x28 +#define PRP_SRC_FRAME_SIZE 0x2c +#define PRP_DEST_CH1_LINE_STRIDE 0x30 +#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 +#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 +#define PRP_CH1_OUT_IMAGE_SIZE 0x3c +#define PRP_CH2_OUT_IMAGE_SIZE 0x40 +#define PRP_SRC_LINE_STRIDE 0x44 +#define PRP_CSC_COEF_012 0x48 +#define PRP_CSC_COEF_345 0x4c +#define PRP_CSC_COEF_678 0x50 +#define PRP_CH1_RZ_HORI_COEF1 0x54 +#define PRP_CH1_RZ_HORI_COEF2 0x58 +#define PRP_CH1_RZ_HORI_VALID 0x5c +#define PRP_CH1_RZ_VERT_COEF1 0x60 +#define PRP_CH1_RZ_VERT_COEF2 0x64 +#define PRP_CH1_RZ_VERT_VALID 0x68 +#define PRP_CH2_RZ_HORI_COEF1 0x6c +#define PRP_CH2_RZ_HORI_COEF2 0x70 +#define PRP_CH2_RZ_HORI_VALID 0x74 +#define PRP_CH2_RZ_VERT_COEF1 0x78 +#define PRP_CH2_RZ_VERT_COEF2 0x7c +#define PRP_CH2_RZ_VERT_VALID 0x80 + +#define PRP_CNTL_CH1EN (1 << 0) +#define PRP_CNTL_CH2EN (1 << 1) +#define PRP_CNTL_CSIEN (1 << 2) +#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) +#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) +#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) +#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) +#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) +#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) +#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) +#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) +#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) +#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) +#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) +#define PRP_CNTL_CH1_LEN (1 << 9) +#define PRP_CNTL_CH2_LEN (1 << 10) +#define PRP_CNTL_SKIP_FRAME (1 << 11) +#define PRP_CNTL_SWRST (1 << 12) +#define PRP_CNTL_CLKEN (1 << 13) +#define PRP_CNTL_WEN (1 << 14) +#define PRP_CNTL_CH1BYP (1 << 15) +#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) +#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) +#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) +#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) +#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) +#define PRP_CNTL_CH2B1EN (1 << 29) +#define PRP_CNTL_CH2B2EN (1 << 30) +#define PRP_CNTL_CH2FEN (1 << 31) + +/* IRQ Enable and status register */ +#define PRP_INTR_RDERR (1 << 0) +#define PRP_INTR_CH1WERR (1 << 1) +#define PRP_INTR_CH2WERR (1 << 2) +#define PRP_INTR_CH1FC (1 << 3) +#define PRP_INTR_CH2FC (1 << 5) +#define PRP_INTR_LBOVF (1 << 7) +#define PRP_INTR_CH2OVF (1 << 8) + +#define mx27_camera_emma(pcdev) (pcdev->use_emma) + +/* Currently we do not need irqs. All we need is DMA callback + * Leave it here for reference for some time. + */ +#undef MX27_CAMERA_USE_IRQ + +struct mx27_camera_dev { + struct device *dev; + struct soc_camera_device *icd; + struct clk *clk_csi, *clk_emma; + + unsigned int irq_csi, irq_emma; + void __iomem *base_csi, *base_emma; + + struct mx27_camera_platform_data *pdata; + struct resource *res_csi, *res_emma; + unsigned long platform_flags; + + struct list_head capture; + struct list_head active_bufs; + + spinlock_t lock; + + int dma; + struct mx27_buffer *active; + + int use_emma; + + unsigned int csicr1; + + void __iomem *discard_buffer; + dma_addr_t discard_buffer_dma; + size_t discard_size; +}; + +/* buffer for one video frame */ +struct mx27_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + const struct soc_camera_data_format *fmt; + + int bufnum; +}; + +static DEFINE_MUTEX(camera_lock); + +static int mclk_get_divisor(struct mx27_camera_dev *pcdev) +{ + dev_info(pcdev->dev, "%s not implemented. Running at max speed\n", + __func__); + +#if 0 + unsigned int mclk = pcdev->pdata->clk_csi; + unsigned int pclk = clk_get_rate(pcdev->clk_csi); + int i; + + dev_dbg(pcdev->dev, "%s: %ld %ld\n", __func__, mclk, pclk); + + for (i = 0; i < 0xf; i++) + if ((i + 1) * 2 * mclk <= pclk) + break; + return i; +#endif + return 0; +} + +static void mx27_camera_deactivate(struct mx27_camera_dev *pcdev) +{ + clk_disable(pcdev->clk_csi); + writel(0, pcdev->base_csi + CSICR1); + if (mx27_camera_emma(pcdev)) + writel(0, pcdev->base_emma + PRP_CNTL); +} + +/* The following two functions absolutely depend on the fact, that + * there can be only one camera on mx27 quick capture interface */ +static int mx27_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx27_camera_dev *pcdev = ici->priv; + int ret; + u32 csicr1; + + mutex_lock(&camera_lock); + + if (pcdev->icd) { + ret = -EBUSY; + goto ebusy; + } + + dev_info(&icd->dev, "Camera driver attached to camera %d\n", + icd->devnum); + + clk_enable(pcdev->clk_csi); + + csicr1 = CSICR1_MCLKDIV(mclk_get_divisor(pcdev)) | + CSICR1_MCLKEN; + if (mx27_camera_emma(pcdev)) { + csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | + CSICR1_RXFF_LEVEL(0); + } else + csicr1 |= CSICR1_SOF_INTEN | CSICR1_RXFF_LEVEL(2); + + pcdev->csicr1 = csicr1; + writel(pcdev->csicr1, pcdev->base_csi + CSICR1); + + ret = icd->ops->init(icd); + + if (!ret) + pcdev->icd = icd; + +ebusy: + mutex_unlock(&camera_lock); + + return ret; +} + +static void mx27_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx27_camera_dev *pcdev = ici->priv; + + BUG_ON(icd != pcdev->icd); + + dev_info(&icd->dev, "Camera driver detached from camera %d\n", + icd->devnum); + + icd->ops->release(icd); + + mx27_camera_deactivate(pcdev); + + if (pcdev->discard_buffer) { + dma_free_coherent(NULL, pcdev->discard_size, + pcdev->discard_buffer, + pcdev->discard_buffer_dma); + } + pcdev->discard_buffer = 0; + + pcdev->icd = NULL; +} + +static void mx27_camera_dma_enable(struct mx27_camera_dev *pcdev) +{ + u32 tmp; + + imx_dma_enable(pcdev->dma); + + tmp = readl(pcdev->base_csi + CSICR1); + tmp |= CSICR1_RF_OR_INTEN; + writel(tmp, pcdev->base_csi + CSICR1); +} + +static irqreturn_t mx27_camera_irq(int irq_csi, void *data) +{ + struct mx27_camera_dev *pcdev = data; + u32 status = readl(pcdev->base_csi + CSISR); + + if (status & CSISR_SOF_INT && pcdev->active) { + u32 tmp; + + tmp = readl(pcdev->base_csi + CSICR1); + tmp |= CSICR1_CLR_RXFIFO; + writel(tmp, pcdev->base_csi + CSICR1); + mx27_camera_dma_enable(pcdev); + writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, + pcdev->base_csi + CSISR); + status &= ~CSISR_RFF_OR_INT; + } + + writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR); + + return IRQ_HANDLED; +} + +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* + * Videobuf operations + */ +static int mx27_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + *size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + + if (0 == *count) + *count = 32; + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct mx27_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + &buf->vb, buf->vb.baddr, buf->vb.bsize); + + /* This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(&buf->vb, 0, 0); + + videobuf_dma_contig_free(vq, &buf->vb); + dev_dbg(&icd->dev, "%s freed\n", __func__); + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int mx27_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct mx27_buffer *buf = container_of(vb, struct mx27_buffer, vb); + int ret = 0; + +#ifdef DEBUG + /* This can be useful if you want to see if we actually fill + * the buffer with something */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); + if (vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + vb->state = VIDEOBUF_PREPARED; + } + + return 0; + +fail: + free_buffer(vq, buf); +out: + return ret; +} + +static void mx27_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct mx27_camera_dev *pcdev = ici->priv; + struct mx27_buffer *buf = container_of(vb, struct mx27_buffer, vb); + unsigned long flags; + int ret; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + spin_lock_irqsave(&pcdev->lock, flags); + + if (mx27_camera_emma(pcdev)) { + list_add_tail(&vb->queue, &pcdev->capture); + vb->state = VIDEOBUF_QUEUED; + } else { + list_add_tail(&vb->queue, &pcdev->capture); + vb->state = VIDEOBUF_ACTIVE; + + if (!pcdev->active) { + ret = imx_dma_setup_single(pcdev->dma, + videobuf_to_dma_contig(vb), vb->size, + CSI_BASE_ADDR + 0x10, DMA_MODE_READ); + if (ret) { + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + goto out; + } + + pcdev->active = buf; + } + } + +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void mx27_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct mx27_buffer *buf = container_of(vb, struct mx27_buffer, vb); + +#ifdef DEBUG + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + dev_info(&icd->dev, "%s (active)\n", __func__); + break; + case VIDEOBUF_QUEUED: + dev_info(&icd->dev, "%s (queued)\n", __func__); + break; + case VIDEOBUF_PREPARED: + dev_info(&icd->dev, "%s (prepared)\n", __func__); + break; + default: + dev_info(&icd->dev, "%s (unknown) %d\n", __func__, + vb->state); + break; + } +#endif + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops mx27_videobuf_ops = { + .buf_setup = mx27_videobuf_setup, + .buf_prepare = mx27_videobuf_prepare, + .buf_queue = mx27_videobuf_queue, + .buf_release = mx27_videobuf_release, +}; + +static void mx27_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx27_camera_dev *pcdev = ici->priv; + + /* We must pass NULL as dev pointer, then all pci_* dma operations + * transform to normal dma_* ones. */ + videobuf_queue_dma_contig_init(q, &mx27_videobuf_ops, NULL, + &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, sizeof(struct mx27_buffer), icd); +} + +#define MX27_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \ + SOCAM_MASTER | \ + SOCAM_VSYNC_ACTIVE_HIGH | \ + SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_HSYNC_ACTIVE_LOW | \ + SOCAM_PCLK_SAMPLE_RISING | \ + SOCAM_PCLK_SAMPLE_FALLING) + +static int mx27_camera_emma_prp_reset(struct mx27_camera_dev *pcdev) +{ + unsigned int cntl; + + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + while (readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST) + barrier(); + + return 0; +} + +static int mx27_camera_set_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct mx27_camera_dev *pcdev = ici->priv; + unsigned long camera_flags, common_flags; + int ret = 0; + u32 csicr1 = pcdev->csicr1; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, + MX27_BUS_FLAGS); + if (!common_flags) + return -EINVAL; + + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & MX27_CAMERA_HSYNC_HIGH) + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (pcdev->platform_flags & MX27_CAMERA_PCLK_SAMPLE_RISING) + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + } + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + csicr1 |= CSICR1_INV_PCLK; + if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH) + csicr1 |= CSICR1_HSYNC_POL; + if (pcdev->platform_flags & MX27_CAMERA_SWAP16) + csicr1 |= CSICR1_SWAP16_EN; + if (pcdev->platform_flags & MX27_CAMERA_EXT_VSYNC) + csicr1 |= CSICR1_EXT_VSYNC; + if (pcdev->platform_flags & MX27_CAMERA_CCIR) + csicr1 |= CSICR1_CCIR_EN; + if (pcdev->platform_flags & MX27_CAMERA_CCIR_INTERLACE) + csicr1 |= CSICR1_CCIR_MODE; + if (pcdev->platform_flags & MX27_CAMERA_GATED_CLOCK) + csicr1 |= CSICR1_GCLK_MODE; + if (pcdev->platform_flags & MX27_CAMERA_INV_DATA) + csicr1 |= CSICR1_INV_DATA; + if (pcdev->platform_flags & MX27_CAMERA_PACK_DIR_MSB) + csicr1 |= CSICR1_PACK_DIR; + + if (mx27_camera_emma(pcdev)) { + int bytesperline = (icd->width * icd->current_fmt->depth) >> 3; + + if (mx27_camera_emma_prp_reset(pcdev)) + return -ENODEV; + + if (pcdev->discard_buffer) + dma_free_coherent(NULL, pcdev->discard_size, + pcdev->discard_buffer, + pcdev->discard_buffer_dma); + + /* I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(NULL, + pcdev->discard_size, &pcdev->discard_buffer_dma, + GFP_KERNEL); + if (!pcdev->discard_buffer) + return -ENOMEM; + + writel(pcdev->discard_buffer_dma, + pcdev->base_emma + PRP_DEST_RGB1_PTR); + writel(pcdev->discard_buffer_dma, + pcdev->base_emma + PRP_DEST_RGB2_PTR); + + /* We only use the EMMA engine to get rid of the f**king + * DMA Engine. No color space consversion at the moment. + * We adjust incoming and outgoing pixelformat to rgb16 + * and adjust the bytesperline accordingly. + */ + writel(PRP_CNTL_CH1EN | + PRP_CNTL_CSIEN | + PRP_CNTL_DATA_IN_RGB16 | + PRP_CNTL_CH1_OUT_RGB16 | + PRP_CNTL_CH1_LEN | + PRP_CNTL_CH1BYP | + PRP_CNTL_CH1_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + + writel(((bytesperline >> 1) << 16) | icd->height, + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + writel(((bytesperline >> 1) << 16) | icd->height, + pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); + writel(bytesperline, + pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); + writel(0x2ca00565, + pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + writel(0x2ca00565, + pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); + + /* Enable interrupts */ + writel(PRP_INTR_RDERR | + PRP_INTR_CH1WERR | + PRP_INTR_CH2WERR | + PRP_INTR_CH1FC | + PRP_INTR_CH2FC | + PRP_INTR_LBOVF | + PRP_INTR_CH2OVF + , pcdev->base_emma + PRP_INTR_CNTL); + } + + pcdev->csicr1 = csicr1; + + writel(csicr1, pcdev->base_csi + CSICR1); + + return 0; +} + +static int mx27_camera_try_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + unsigned long bus_flags, camera_flags; + + bus_flags = MX27_BUS_FLAGS; + + camera_flags = icd->ops->query_bus_param(icd); + + return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? + 0 : -EINVAL; +} + +static int mx27_camera_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + return icd->ops->set_fmt_cap(icd, pixfmt, rect); +} + +static int mx27_camera_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + return 0; +} + +static int mx27_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the friendly caller:-> */ + strlcpy(cap->card, mx27_cam_driver_description, sizeof(cap->card)); + cap->version = MX27_CAM_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int mx27_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + for (i = 0; i < p->count; i++) { + struct mx27_buffer *buf = container_of(icf->vb_vidq.bufs[i], + struct mx27_buffer, vb); + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +static void mx27_camera_frame_done(struct mx27_camera_dev *pcdev, int state) +{ + struct videobuf_buffer *vb; + struct mx27_buffer *buf; + int ret; + + if (!pcdev->active) { + dev_err(pcdev->dev, "%s called with no active buffer!\n", + __func__); + return; + } + + vb = &pcdev->active->vb; + buf = container_of(vb, struct mx27_buffer, vb); + WARN_ON(list_empty(&vb->queue)); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = state; + do_gettimeofday(&vb->ts); + vb->field_count++; + + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + return; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct mx27_buffer, vb.queue); + + vb = &pcdev->active->vb; + vb->state = VIDEOBUF_ACTIVE; + + ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), + vb->size, CSI_BASE_ADDR + 0x10, DMA_MODE_READ); + if (ret) { + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + return; + } +} + +static void mx27_camera_dma_err_callback(int channel, void *data, int err) +{ + struct mx27_camera_dev *pcdev = data; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR); + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void mx27_camera_dma_callback(int channel, void *data) +{ + struct mx27_camera_dev *pcdev = data; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + mx27_camera_frame_done(pcdev, VIDEOBUF_DONE); + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static unsigned int mx27_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct mx27_buffer *buf; + + buf = list_entry(icf->vb_vidq.stream.next, struct mx27_buffer, + vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +/* Should beallocated dynamically too, but we have only one. */ +static struct soc_camera_host_ops mx27_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = mx27_camera_add_device, + .remove = mx27_camera_remove_device, + .set_fmt_cap = mx27_camera_set_fmt_cap, + .try_fmt_cap = mx27_camera_try_fmt_cap, + .init_videobuf = mx27_camera_init_videobuf, + .reqbufs = mx27_camera_reqbufs, + .poll = mx27_camera_poll, + .querycap = mx27_camera_querycap, + .try_bus_param = mx27_camera_try_bus_param, + .set_bus_param = mx27_camera_set_bus_param, +}; + +static void mx27_camera_frame_done_emma(struct mx27_camera_dev *pcdev, + int bufnum, int state) +{ + struct mx27_buffer *buf; + struct videobuf_buffer *vb; + unsigned long phys; + + if (!list_empty(&pcdev->active_bufs)) { + buf = list_entry(pcdev->active_bufs.next, + struct mx27_buffer, vb.queue); + + if (buf->bufnum == bufnum) { + vb = &buf->vb; +#ifdef DEBUG + phys = videobuf_to_dma_contig(vb); + if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + + 4 * bufnum) != phys) { + dev_err(pcdev->dev, "%p != %p\n", phys, + readl(pcdev->base_emma + + PRP_DEST_RGB1_PTR + + 4 * bufnum)); + } +#endif + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", + __func__, vb, vb->baddr, vb->bsize); + + list_del(&vb->queue); + vb->state = state; + do_gettimeofday(&vb->ts); + vb->field_count++; + + wake_up(&vb->done); + } + } + + if (list_empty(&pcdev->capture)) { + writel(pcdev->discard_buffer_dma, pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum); + return; + } + + buf = list_entry(pcdev->capture.next, + struct mx27_buffer, vb.queue); + + buf->bufnum = bufnum; + + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + vb = &buf->vb; + vb->state = VIDEOBUF_ACTIVE; + + phys = videobuf_to_dma_contig(vb); + writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum); +} + +static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) +{ + struct mx27_camera_dev *pcdev = data; + unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); + + if (status & (1 << 6)) + mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE); + if (status & (1 << 5)) + mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE); + if (status & (1 << 7)) { + uint32_t cntl; + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(cntl & ~PRP_CNTL_CH1EN, pcdev->base_emma + PRP_CNTL); + writel(cntl, pcdev->base_emma + PRP_CNTL); + } + + writel(status, pcdev->base_emma + PRP_INTRSTATUS); + + return IRQ_HANDLED; +} + +/* Should be allocated dynamically too, but we have only one. */ +static struct soc_camera_host mx27_soc_camera_host = { + .drv_name = MX27_CAM_DRV_NAME, + .ops = &mx27_soc_camera_host_ops, +}; + +int mx27_camera_emma_init(struct mx27_camera_dev *pcdev) +{ + struct resource *res_emma = pcdev->res_emma; + int err = 0; + + if (!request_mem_region(res_emma->start, resource_size(res_emma), + MX27_CAM_DRV_NAME)) { + err = -EBUSY; + goto out; + } + + pcdev->base_emma = ioremap(res_emma->start, resource_size(res_emma)); + if (!pcdev->base_emma) { + err = -ENOMEM; + goto exit_release; + } + + err = request_irq(pcdev->irq_emma, mx27_camera_emma_irq, 0, + MX27_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n"); + goto exit_iounmap; + } + + pcdev->clk_emma = clk_get(pcdev->dev, "emma_clk"); + if (IS_ERR(pcdev->clk_emma)) { + err = PTR_ERR(pcdev->clk_emma); + goto exit_free_irq; + } + + clk_enable(pcdev->clk_emma); + + err = mx27_camera_emma_prp_reset(pcdev); + if (err) + goto exit_clk_emma_put; + + return err; + +exit_clk_emma_put: + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); +exit_free_irq: + free_irq(pcdev->irq_emma, pcdev); +exit_iounmap: + iounmap(pcdev->base_emma); +exit_release: + release_mem_region(res_emma->start, resource_size(res_emma)); +out: + return err; +} + +static int mx27_camera_probe(struct platform_device *pdev) +{ + struct mx27_camera_dev *pcdev; + struct resource *res_csi, *res_emma; + void __iomem *base_csi; + unsigned int irq_csi, irq_emma; + int err = 0; + + dev_info(&pdev->dev, "initialising\n"); + + res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_csi = platform_get_irq(pdev, 0); + if (!res_csi || !irq_csi) { + dev_err(&pdev->dev, "No platform irq\n"); + err = -ENODEV; + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit; + } + + pcdev->clk_csi = clk_get(&pdev->dev, "csi_perclk"); + if (IS_ERR(pcdev->clk_csi)) { + err = PTR_ERR(pcdev->clk_csi); + goto exit_kfree; + } + + dev_info(&pdev->dev, "Camera clock frequency: %ld\n", + clk_get_rate(pcdev->clk_csi)); + + /* Initialize DMA */ + pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH); + if (pcdev->dma < 0) { + dev_err(&pdev->dev, + "mxc_v4l_read failed to request DMA channel\n"); + err = -EIO; + goto exit_clk_put; + } + + err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback, + mx27_camera_dma_err_callback, pcdev); + if (err != 0) { + dev_err(&pdev->dev, + "mxc_v4l_read failed to set DMA callback\n"); + err = -EIO; + goto exit_dma_free; + } + + imx_dma_config_channel(pcdev->dma, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, + DMA_REQ_CSI_RX, 1); + + imx_dma_config_burstlen(pcdev->dma, 64); + + dev_set_drvdata(&pdev->dev, pcdev); + pcdev->res_csi = res_csi; + + pcdev->pdata = pdev->dev.platform_data; + pcdev->platform_flags = pcdev->pdata->flags; + + clk_set_rate(pcdev->clk_csi, pcdev->pdata->clk * 2); + + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + spin_lock_init(&pcdev->lock); + + /* + * Request the regions. + */ + if (!request_mem_region(res_csi->start, resource_size(res_csi), + MX27_CAM_DRV_NAME)) { + err = -EBUSY; + goto exit_dma_free; + } + + base_csi = ioremap(res_csi->start, resource_size(res_csi)); + if (!base_csi) { + err = -ENOMEM; + goto exit_release; + } + pcdev->irq_csi = irq_csi; + pcdev->base_csi = base_csi; + pcdev->dev = &pdev->dev; + + pcdev->pdata->init(pdev); + + err = request_irq(pcdev->irq_csi, mx27_camera_irq, 0, MX27_CAM_DRV_NAME, + pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_iounmap; + } + + mx27_soc_camera_host.priv = pcdev; + mx27_soc_camera_host.dev.parent = &pdev->dev; + mx27_soc_camera_host.nr = pdev->id; + + /* EMMA support */ + res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq_emma = platform_get_irq(pdev, 1); + + if (res_emma && irq_emma) { + dev_info(&pdev->dev, "Using EMMA\n"); + pcdev->use_emma = 1; + pcdev->res_emma = res_emma; + pcdev->irq_emma = irq_emma; + if (mx27_camera_emma_init(pcdev)) + goto exit_free_irq; + } + + err = soc_camera_host_register(&mx27_soc_camera_host); + if (err) + goto exit_free_emma; + + return 0; + +exit_free_emma: + free_irq(pcdev->irq_emma, pcdev); + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); + iounmap(pcdev->base_emma); + release_mem_region(res_emma->start, resource_size(res_emma)); +exit_free_irq: + free_irq(pcdev->irq_csi, pcdev); +exit_iounmap: + iounmap(base_csi); +exit_release: + release_mem_region(res_csi->start, resource_size(res_csi)); +exit_dma_free: + imx_dma_free(pcdev->dma); +exit_clk_put: + clk_put(pcdev->clk_csi); +exit_kfree: + kfree(pcdev); +exit: + return err; +} + +static int __devexit mx27_camera_remove(struct platform_device *pdev) +{ + struct mx27_camera_dev *pcdev = platform_get_drvdata(pdev); + struct resource *res; + + clk_put(pcdev->clk_csi); + imx_dma_free(pcdev->dma); + free_irq(pcdev->irq_csi, pcdev); + if (mx27_camera_emma(pcdev)) + free_irq(pcdev->irq_emma, pcdev); + + soc_camera_host_unregister(&mx27_soc_camera_host); + + iounmap(pcdev->base_csi); + + if (mx27_camera_emma(pcdev)) { + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); + iounmap(pcdev->base_emma); + res = pcdev->res_emma; + release_mem_region(res->start, resource_size(res)); + } + + pcdev->pdata->exit(pdev); + + res = pcdev->res_csi; + release_mem_region(res->start, resource_size(res)); + + kfree(pcdev); + + dev_info(&pdev->dev, "MX27 Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver mx27_camera_driver = { + .driver = { + .name = MX27_CAM_DRV_NAME, + }, + .probe = mx27_camera_probe, + .remove = __exit_p(mx27_camera_remove), +}; + + +static int __devinit mx27_camera_init(void) +{ + return platform_driver_register(&mx27_camera_driver); +} + +static void __exit mx27_camera_exit(void) +{ + return platform_driver_unregister(&mx27_camera_driver); +} + +module_init(mx27_camera_init); +module_exit(mx27_camera_exit); + +MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); +MODULE_AUTHOR("Sascha Hauer "); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/Kconfig linux-2.6.28-karo/drivers/media/video/mxc/capture/Kconfig --- linux-2.6.28/drivers/media/video/mxc/capture/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,80 @@ +if VIDEO_MXC_CAMERA + +menu "MXC Camera/V4L2 PRP Features support" +config VIDEO_MXC_IPU_CAMERA + bool + depends on VIDEO_MXC_CAMERA && MXC_IPU + default y + +config VIDEO_MXC_EMMA_CAMERA + bool "MX27 eMMA support" + depends on VIDEO_MXC_CAMERA && MXC_EMMA && FB_MXC_SYNC_PANEL && (MXC_CAMERA_MICRON111 || MXC_CAMERA_MC521DA || MXC_CAMERA_OV2640) + select VIDEO_MXC_OPL + default y + +config VIDEO_MXC_CSI_DMA + bool "CSI-DMA Still Image Capture support" + depends on VIDEO_MXC_EMMA_CAMERA + default n + ---help--- + Use CSI-DMA method instead of CSI-PrP link to capture still image. This allows + to use less physical contiguous memory to capture big resolution still image. But + with this method the CSC (Color Space Conversion) and resize are not supported. + If unsure, say N. + +choice + prompt "Select Camera" + default MXC_CAMERA_MICRON111 + depends on (VIDEO_MXC_CAMERA && I2C_MXC) + +config MXC_CAMERA_MICRON111 + tristate "Micron mt9v111 camera support" + ---help--- + If you plan to use the mt9v111 Camera with your MXC system, say Y here. + +config MXC_CAMERA_MC521DA + tristate "Magnachip mc521da camera support" + ---help--- + If you plan to use the mc521da Camera with your MXC system, say Y here. + +config MXC_CAMERA_OV2640 + tristate "OmniVision ov2640 camera support" + ---help--- + If you plan to use the ov2640 Camera with your MXC system, say Y here. +endchoice + +config MXC_IPU_PRP_VF_SDC + tristate "Pre-Processor VF SDC library" + depends on (VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL && (MXC_CAMERA_MICRON111 || MXC_CAMERA_MC521DA || MXC_CAMERA_OV2640)) + default y + ---help--- + Use case PRP_VF_SDC: + Preprocessing image from smart sensor for viewfinder and + displaying it on synchronous display with SDC use case. + If SDC BG is selected, Rotation will not be supported. + CSI -> IC (PRP VF) -> MEM + MEM -> IC (ROT) -> MEM + MEM -> SDC (FG/BG) + +config MXC_IPU_PRP_VF_ADC + tristate "Pre-Processor VF ADC library" + depends on (VIDEO_MXC_IPU_CAMERA && FB_MXC_ASYNC_PANEL && (MXC_CAMERA_MICRON111 || MXC_CAMERA_MC521DA || MXC_CAMERA_OV2640)) + default y + ---help--- + Use case PRP_VF_ADC: + Preprocessing image from smart sensor for viewfinder and + displaying it on asynchronous display. + CSI -> IC (PRP VF) -> ADC2 + +config MXC_IPU_PRP_ENC + tristate "Pre-processor Encoder library" + depends on (VIDEO_MXC_IPU_CAMERA && (MXC_CAMERA_MICRON111 || MXC_CAMERA_MC521DA || MXC_CAMERA_OV2640)) + default y + ---help--- + Use case PRP_ENC: + Preprocessing image from smart sensor for encoder. + CSI -> IC (PRP ENC) -> MEM + +endmenu + +endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/Makefile linux-2.6.28-karo/drivers/media/video/mxc/capture/Makefile --- linux-2.6.28/drivers/media/video/mxc/capture/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,18 @@ +ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y) + obj-$(CONFIG_VIDEO_MXC_CAMERA) += mxc_v4l2_capture.o + obj-$(CONFIG_MXC_IPU_PRP_VF_ADC) += ipu_prp_vf_adc.o + obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o + obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o +endif + +mx27_capture-objs := mx27_prphw.o mx27_prpsw.o mx27_v4l2_capture.o +obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mx27_csi.o mx27_capture.o + +mc521da_camera-objs := mc521da.o sensor_clock.o +obj-$(CONFIG_MXC_CAMERA_MC521DA) += mc521da_camera.o + +mt9v111_camera-objs := mt9v111.o sensor_clock.o +obj-$(CONFIG_MXC_CAMERA_MICRON111) += mt9v111_camera.o + +ov2640_camera-objs := ov2640.o sensor_clock.o +obj-$(CONFIG_MXC_CAMERA_OV2640) += ov2640_camera.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mc521da.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mc521da.c --- linux-2.6.28/drivers/media/video/mxc/capture/mc521da.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mc521da.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,702 @@ +/* + * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mc521da.c + * + * @brief MC521DA camera driver functions + * + * @ingroup Camera + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define MC521DA_I2C_ADDRESS 0x22 +#define MC521DA_TERM 0xFF + +typedef struct { + u16 width; + u16 height; +} mc521da_image_format; + +struct mc521da_reg { + u8 reg; + u8 val; +}; + +static sensor_interface *interface_param = NULL; + +static mc521da_image_format format[2] = { + { + .width = 1600, + .height = 1200, + }, + { + .width = 640, + .height = 480, + }, +}; + +const static struct mc521da_reg mc521da_initial[] = { + /*---------------------------------------------------------- + * Sensor Setting Start + *---------------------------------------------------------- + */ + {0xff, 0x01}, /* Sensor setting start */ + {0x01, 0x10}, /* Wavetable script, generated by waveman */ + {0x10, 0x64}, + {0x03, 0x00}, {0x04, 0x06}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00}, + {0x03, 0x01}, {0x04, 0x41}, {0x05, 0x70}, {0x06, 0x03}, {0x08, 0x00}, + {0x03, 0x02}, {0x04, 0x55}, {0x05, 0x30}, {0x06, 0x03}, {0x08, 0x00}, + {0x03, 0x03}, {0x04, 0x5A}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00}, + {0x03, 0x04}, {0x04, 0x7A}, {0x05, 0x30}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x05}, {0x04, 0x9C}, {0x05, 0x30}, {0x06, 0x0F}, {0x08, 0x00}, + {0x03, 0x06}, {0x04, 0x73}, {0x05, 0x31}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x07}, {0x04, 0x2D}, {0x05, 0x3B}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x08}, {0x04, 0x32}, {0x05, 0x33}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x09}, {0x04, 0x67}, {0x05, 0x63}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x0a}, {0x04, 0x6C}, {0x05, 0x23}, {0x06, 0x0E}, {0x08, 0x00}, + {0x03, 0x0b}, {0x04, 0x71}, {0x05, 0x23}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x0c}, {0x04, 0x30}, {0x05, 0x2F}, {0x06, 0x06}, {0x08, 0x00}, + {0x03, 0x0d}, {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x06}, {0x08, 0x00}, + {0x07, 0x0e}, + + /* Start Address */ + {0x10, 0x64}, {0x14, 0x10}, {0x15, 0x00}, + + /* SYNC */ + {0x18, 0x40}, {0x19, 0x00}, {0x1A, 0x03}, {0x1B, 0x00}, + + /* X-Y Mirror */ + {0x11, 0x00}, {0xda, 0x00}, /* X mirror OFF, Y Mirror OFF */ + + /* Frame height */ + {0x1c, 0x13}, {0x1d, 0x04}, {0x0e, 0x4b}, {0x0f, 0x05}, + {0x9e, 0x04}, {0x9d, 0xc6}, {0xcc, 0x14}, {0xcd, 0x05}, + + /* Frame width */ + {0x0c, 0x35}, {0x0d, 0x07}, {0x9b, 0x10}, {0x9c, 0x07}, + {0x93, 0x21}, + + {0x01, 0x01}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0xf0}, + {0x43, 0x03}, {0x44, 0x0a}, {0x45, 0x00}, {0x3b, 0x40}, + {0x38, 0x18}, {0x3c, 0x00}, {0x20, 0x00}, {0x21, 0x01}, + {0x22, 0x00}, {0x23, 0x01}, {0x24, 0x00}, {0x25, 0x01}, + {0x26, 0x00}, {0x27, 0x01}, {0xb9, 0x04}, {0xb8, 0xc3}, + {0xbb, 0x04}, {0xba, 0xc3}, {0xbf, 0x04}, {0xbe, 0xc3}, + + /* Ramp */ + {0x57, 0x07}, {0x56, 0xd6}, {0x55, 0x03}, {0x54, 0x74}, + {0x9f, 0x99}, {0x94, 0x80}, {0x91, 0x78}, {0x92, 0x8b}, + + /* Output Mode */ + {0x52, 0x10}, {0x51, 0x00}, + + /* Analog Gain and Output driver */ + {0x28, 0x00}, {0xdd, 0x82}, {0xdb, 0x00}, {0xdc, 0x00}, + + /* Update */ + {0x00, 0x84}, + + /* PLL ADC clock = 75 MHz */ + {0xb5, 0x60}, {0xb4, 0x02}, {0xb5, 0x20}, + + /*----------------------------------------------*/ + /* ISP Setting Start */ + /*----------------------------------------------*/ + {0xff, 0x02}, + {0x01, 0xbd}, {0x02, 0xf8}, {0x03, 0x3a}, {0x04, 0x00}, {0x0e, 0x00}, + + /* Output mode */ + {0x88, 0x00}, {0x87, 0x11}, + + /* Threshold */ + {0xb6, 0x1b}, {0x0d, 0xc0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, + + /* Image Effect */ + {0x3f, 0x80}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x80}, {0x43, 0x00}, + {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x56, 0x80}, {0x57, 0x20}, + {0x58, 0x20}, {0x59, 0x02}, {0x5a, 0x00}, {0x5b, 0x78}, {0x5c, 0x7c}, + {0x5d, 0x84}, {0x5e, 0x85}, {0x5f, 0x78}, {0x60, 0x7e}, {0x61, 0x82}, + {0x62, 0x85}, {0x63, 0x00}, {0x64, 0x80}, {0x65, 0x00}, {0x66, 0x80}, + {0x67, 0x80}, {0x68, 0x80}, + + /* Auto Focus */ + {0x6e, 0x02}, {0x6f, 0xe5}, {0x70, 0x08}, {0x71, 0x01}, {0x72, 0x00}, + + /* Decimator */ + {0x78, 0xff}, {0x79, 0xff}, {0x7a, 0x70}, {0x7b, 0x00}, {0x7c, 0x00}, + {0x7d, 0x00}, {0x7e, 0xc8}, {0x7f, 0xc8}, {0x80, 0x96}, {0x81, 0x96}, + {0x82, 0x00}, {0x83, 0x00}, {0x84, 0x00}, {0x85, 0x00}, {0x86, 0x00}, + + /* Luminance Info */ + {0xf9, 0x20}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08}, + {0xf9, 0xa0}, {0xb7, 0x10}, {0xb9, 0x00}, + {0xf9, 0x40}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08}, + {0xf9, 0xc0}, {0xb7, 0x08}, {0xb9, 0x00}, + {0xf9, 0x60}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08}, + {0xf9, 0xe0}, {0xb7, 0x05}, {0xb9, 0x00}, + {0xf9, 0x00}, {0xb7, 0x03}, {0xb8, 0x2d}, {0xb9, 0xcd}, + {0xf9, 0x80}, {0xb7, 0x02}, {0xb9, 0x00}, + + /* AE */ + {0x8a, 0x00}, {0x89, 0xc0}, {0x8c, 0x32}, {0x8d, 0x96}, {0x8e, 0x25}, + {0x8f, 0x70}, {0x90, 0x12}, {0x91, 0x41}, {0x9e, 0x2e}, {0x9f, 0x2e}, + {0xa0, 0x0b}, {0xa1, 0x71}, {0xa2, 0xb0}, {0xa3, 0x09}, {0xa4, 0x89}, + {0xa5, 0x68}, {0xa6, 0x1a}, {0xa7, 0xb3}, {0xa8, 0xf0}, {0xa9, 0x19}, + {0xaa, 0x6a}, {0xab, 0x6b}, {0xac, 0x01}, {0xad, 0xe8}, {0xae, 0x48}, + {0xaf, 0x01}, {0xb0, 0x96}, {0xb1, 0xe6}, {0xb2, 0x03}, {0xb3, 0x00}, + {0xb4, 0x10}, {0xb5, 0x00}, {0xb6, 0x04}, {0xba, 0x44}, {0xbb, 0x3a}, + {0xbc, 0x01}, {0xbd, 0x08}, {0xbe, 0xa0}, {0xbf, 0x01}, {0xc0, 0x82}, + {0x8a, 0xe1}, {0x8b, 0x8c}, + + /* AWB */ + {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x40}, {0xcb, 0xB0}, {0xcc, 0x40}, + {0xcd, 0xff}, {0xce, 0x19}, {0xcf, 0x40}, {0xd0, 0x01}, {0xd1, 0x43}, + {0xd2, 0x80}, {0xd3, 0x80}, {0xd4, 0xf1}, {0xdf, 0x00}, {0xe0, 0x8f}, + {0xe1, 0x8f}, {0xe2, 0x53}, {0xe3, 0x97}, {0xe4, 0x1f}, {0xe5, 0x3b}, + {0xe6, 0x9c}, {0xe7, 0x2e}, {0xe8, 0x03}, {0xe9, 0x02}, + + /* Neutral CCM */ + {0xfa, 0x00}, {0xd5, 0x3f}, {0xd6, 0x8c}, {0xd7, 0x43}, {0xd8, 0x08}, + {0xd9, 0x27}, {0xda, 0x7e}, {0xdb, 0x17}, {0xdc, 0x1a}, {0xdd, 0x47}, + {0xde, 0xa1}, + + /* Blue CCM */ + {0xfa, 0x01}, {0xd5, 0x3f}, {0xd6, 0x77}, {0xd7, 0x34}, {0xd8, 0x03}, + {0xd9, 0x18}, {0xda, 0x6e}, {0xdb, 0x16}, {0xdc, 0x0f}, {0xdd, 0x29}, + {0xde, 0x77}, + + /* Red CCM */ + {0xfa, 0x02}, {0xd5, 0x3f}, {0xd6, 0x7d}, {0xd7, 0x2f}, {0xd8, 0x0e}, + {0xd9, 0x1e}, {0xda, 0x76}, {0xdb, 0x18}, {0xdc, 0x29}, {0xdd, 0x51}, + {0xde, 0xba}, + + /* AWB */ + {0xea, 0x00}, {0xeb, 0x1a}, {0xc8, 0x33}, {0xc9, 0xc2}, + + {0xed, 0x02}, {0xee, 0x02}, + + /* AFD */ + {0xf0, 0x11}, {0xf1, 0x03}, {0xf2, 0x05}, {0xf5, 0x05}, {0xf6, 0x32}, + {0xf7, 0x32}, + + /* Lens Shading */ + {0xf9, 0x00}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0xf2}, {0x08, 0x00}, + {0x09, 0x00}, {0x0a, 0xf2}, {0x0b, 0xff}, {0x0c, 0xff}, + {0xf9, 0x01}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16}, + {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0}, + {0xf9, 0x02}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16}, + {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0}, + {0xf9, 0x03}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x7c}, {0x08, 0x26}, + {0x09, 0x26}, {0x0a, 0x7c}, {0x0b, 0xd0}, {0x0c, 0xe0}, + {0xf9, 0x04}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00}, + {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xe0}, + {0xf9, 0x05}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00}, + {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0}, + {0xf9, 0x06}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00}, + {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0}, + {0xf9, 0x07}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00}, + {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0}, + + /* Edge setting */ + {0x73, 0x68}, {0x74, 0x40}, {0x75, 0x00}, {0x76, 0xff}, {0x77, 0x80}, + {0x4f, 0x80}, {0x50, 0x82}, {0x51, 0x82}, {0x52, 0x08}, + + /* Interpolation Setting */ + {0x23, 0x7f}, {0x22, 0x08}, {0x18, 0xff}, {0x19, 0x00}, + {0x40, 0x00}, {0x53, 0xff}, {0x54, 0x0a}, {0x55, 0xc2}, + {0x1b, 0x18}, + + {0xfa, 0x00}, {0x15, 0x0c}, {0x22, 0x00}, {0x0e, 0xef}, {0x1f, 0x1d}, + {0x20, 0x2d}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03}, {0x0e, 0xee}, + {0x12, 0x10}, {0x16, 0x10}, {0x17, 0x02}, {0x1a, 0x01}, + {0xfa, 0x04}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03}, + {0x1f, 0x11}, {0x20, 0x11}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10}, + {0x17, 0x02}, {0x1a, 0xee}, + {0xfa, 0x08}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03}, + {0x1f, 0x00}, {0x20, 0x00}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10}, + {0x17, 0x02}, {0x1a, 0x22}, + + /* Gamma Correction */ + {0x27, 0x62}, {0x28, 0x00}, {0x27, 0x62}, {0x28, 0x00}, {0x29, 0x00}, + {0x2a, 0x00}, {0x2f, 0x03}, {0x30, 0x10}, {0x31, 0x2b}, {0x32, 0x50}, + {0x33, 0x70}, {0x34, 0x90}, {0x35, 0xB0}, {0x36, 0xD0}, {0x37, 0x00}, + {0x38, 0x18}, {0x39, 0x57}, {0x3a, 0x89}, {0x3b, 0xac}, {0x3c, 0xc9}, + {0x3d, 0xde}, {0x3e, 0xef}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x40}, + {0x2e, 0xab}, + + /* Contrast */ + {0x47, 0x10}, {0x48, 0x1f}, {0x49, 0xe3}, {0x4a, 0xf0}, {0x4b, 0x08}, + {0x4c, 0x14}, {0x4d, 0xe9}, {0x4e, 0xf5}, {0x98, 0x8a}, + + {0xfa, 0x00}, + {MC521DA_TERM, MC521DA_TERM} +}; + +static int mc521da_attach(struct i2c_adapter *adapter); +static int mc521da_detach(struct i2c_client *client); + +static struct i2c_driver mc521da_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "MC521DA Client", + }, + .attach_adapter = mc521da_attach, + .detach_client = mc521da_detach, +}; + +static struct i2c_client mc521da_i2c_client = { + .name = "MC521DA I2C dev", + .addr = MC521DA_I2C_ADDRESS, + .driver = &mc521da_i2c_driver, +}; + +/* + * Function definitions + */ +static int mc521da_i2c_client_xfer(unsigned int addr, char *reg, + int reg_len, char *buf, int num, + int tran_flag) +{ + struct i2c_msg msg[2]; + int ret; + + msg[0].addr = addr; + msg[0].len = reg_len; + msg[0].buf = reg; + msg[0].flags = tran_flag; + msg[0].flags &= ~I2C_M_RD; + + msg[1].addr = addr; + msg[1].len = num; + msg[1].buf = buf; + msg[1].flags = tran_flag; + + if (tran_flag & MXC_I2C_FLAG_READ) { + msg[1].flags |= I2C_M_RD; + } else { + msg[1].flags &= ~I2C_M_RD; + } + + ret = i2c_transfer(mc521da_i2c_client.adapter, msg, 2); + if (ret >= 0) + return 0; + + return ret; +} + +static int mc521da_read_reg(u8 * reg, u8 * val) +{ + return mc521da_i2c_client_xfer(MC521DA_I2C_ADDRESS, reg, 1, val, 1, + MXC_I2C_FLAG_READ); +} + +static int mc521da_write_reg(u8 reg, u8 val) +{ + u8 temp1, temp2; + temp1 = reg; + temp2 = val; + return mc521da_i2c_client_xfer(MC521DA_I2C_ADDRESS, &temp1, 1, &temp2, + 1, 0); +} + +static int mc521da_write_regs(const struct mc521da_reg reglist[]) +{ + int err; + const struct mc521da_reg *next = reglist; + + while (!((next->reg == MC521DA_TERM) && (next->val == MC521DA_TERM))) { + err = mc521da_write_reg(next->reg, next->val); + if (err) { + return err; + } + next++; + } + return 0; +} + +/*! + * mc521da sensor downscale function + * @param downscale bool + * @return Error code indicating success or failure + */ +static u8 mc521da_sensor_downscale(bool downscale) +{ + u8 reg[1], data; + u32 i = 0; + + if (downscale == true) { + // VGA + mc521da_write_reg(0xff, 0x01); + + mc521da_write_reg(0x52, 0x30); + mc521da_write_reg(0x51, 0x00); + + mc521da_write_reg(0xda, 0x01); + mc521da_write_reg(0x00, 0x8C); + + /* Wait for changes to take effect */ + reg[0] = 0x00; + while (i < 256) { + i++; + mc521da_read_reg(reg, &data); + if ((data & 0x80) == 0) + break; + msleep(5); + } + + /* ISP */ + mc521da_write_reg(0xff, 0x02); + + mc521da_write_reg(0x03, 0x3b); /* Enable Decimator */ + + mc521da_write_reg(0x7a, 0x74); + mc521da_write_reg(0x7b, 0x01); + mc521da_write_reg(0x7e, 0x50); + mc521da_write_reg(0x7f, 0x50); + mc521da_write_reg(0x80, 0x3c); + mc521da_write_reg(0x81, 0x3c); + } else { + //UXGA + mc521da_write_reg(0xff, 0x01); + mc521da_write_reg(0x52, 0x10); + mc521da_write_reg(0x51, 0x00); + mc521da_write_reg(0xda, 0x00); + + /* update */ + mc521da_write_reg(0x00, 0x84); + + /* Wait for changes to take effect */ + reg[0] = 0x00; + while (i < 256) { + i++; + mc521da_read_reg(reg, &data); + if ((data & 0x80) == 0) + break; + msleep(5); + } + + /* ISP */ + mc521da_write_reg(0xff, 0x02); + + mc521da_write_reg(0x03, 0x3a); + + mc521da_write_reg(0x7a, 0x70); + mc521da_write_reg(0x7b, 0x00); + mc521da_write_reg(0x7e, 0xc8); + mc521da_write_reg(0x7f, 0xc8); + mc521da_write_reg(0x80, 0x96); + mc521da_write_reg(0x81, 0x96); + } + + return 0; +} + +/*! + * mc521da sensor interface Initialization + * @param param sensor_interface * + * @param width u32 + * @param height u32 + * @return None + */ +static void mc521da_interface(sensor_interface * param, u32 width, u32 height) +{ + param->clk_mode = 0x0; //gated + param->pixclk_pol = 0x0; + param->data_width = 0x1; + param->data_pol = 0x0; + param->ext_vsync = 0x0; + param->Vsync_pol = 0x0; + param->Hsync_pol = 0x0; + param->width = width - 1; + param->height = height - 1; + param->pixel_fmt = IPU_PIX_FMT_UYVY; +} + +extern void gpio_sensor_reset(bool flag); + +/*! + * mc521da Reset function + * + * @return None + */ +static sensor_interface *mc521da_reset(void) +{ + if (interface_param == NULL) + return NULL; + + mc521da_interface(interface_param, format[1].width, format[1].height); + set_mclk_rate(&interface_param->mclk); + + gpio_sensor_reset(true); + msleep(10); + gpio_sensor_reset(false); + msleep(50); + + return interface_param; +} + +/*! + * mc521da sensor configuration + * + * @param frame_rate int * + * @param high_quality int + * @return sensor_interface * + */ +static sensor_interface *mc521da_config(int *frame_rate, int high_quality) +{ + int num_clock_per_row, err; + int max_rate = 0; + int index = 1; + u16 frame_height; + + if (high_quality == 1) + index = 0; + + err = mc521da_write_regs(mc521da_initial); + if (err) { + /* Reduce the MCLK */ + interface_param->mclk = 20000000; + mc521da_reset(); + + printk(KERN_INFO "mc521da: mclk reduced\n"); + mc521da_write_regs(mc521da_initial); + } + + mc521da_interface(interface_param, format[index].width, + format[index].height); + + if (index == 0) { + mc521da_sensor_downscale(false); + } else { + mc521da_sensor_downscale(true); + } + + num_clock_per_row = 1845; + max_rate = interface_param->mclk * 3 * (index + 1) + / (2 * num_clock_per_row * 1300); + + if ((*frame_rate > max_rate) || (*frame_rate == 0)) { + *frame_rate = max_rate; + } + + frame_height = 1300 * max_rate / (*frame_rate); + + *frame_rate = interface_param->mclk * 3 * (index + 1) + / (2 * num_clock_per_row * frame_height); + + mc521da_write_reg(0xff, 0x01); + mc521da_write_reg(0xE, frame_height & 0xFF); + mc521da_write_reg(0xF, (frame_height & 0xFF00) >> 8); + mc521da_write_reg(0xCC, frame_height & 0xFF); + mc521da_write_reg(0xCD, (frame_height & 0xFF00) >> 8); + + return interface_param; +} + +/*! + * mc521da sensor set color configuration + * + * @param bright int + * @param saturation int + * @param red int + * @param green int + * @param blue int + * @return None + */ +static void +mc521da_set_color(int bright, int saturation, int red, int green, int blue) +{ + /* Select ISP */ + mc521da_write_reg(0xff, 0x02); + + mc521da_write_reg(0x41, bright); + mc521da_write_reg(0xca, red); + mc521da_write_reg(0xcb, green); + mc521da_write_reg(0xcc, blue); +} + +/*! + * mc521da sensor get color configuration + * + * @param bright int * + * @param saturation int * + * @param red int * + * @param green int * + * @param blue int * + * @return None + */ +static void +mc521da_get_color(int *bright, int *saturation, int *red, int *green, int *blue) +{ + u8 reg[1]; + u8 *pdata; + + *saturation = 0; + + /* Select ISP */ + mc521da_write_reg(0xff, 0x02); + + reg[0] = 0x41; + pdata = (u8 *) bright; + mc521da_read_reg(reg, pdata); + + reg[0] = 0xCA; + pdata = (u8 *) red; + mc521da_read_reg(reg, pdata); + + reg[0] = 0xCB; + pdata = (u8 *) green; + mc521da_read_reg(reg, pdata); + + reg[0] = 0xCC; + pdata = (u8 *) blue; + mc521da_read_reg(reg, pdata); +} + +struct camera_sensor camera_sensor_if = { + set_color:mc521da_set_color, + get_color:mc521da_get_color, + config:mc521da_config, + reset:mc521da_reset, +}; + +/*! + * mc521da I2C detect_client function + * + * @param adapter struct i2c_adapter * + * @param address int + * @param kind int + * + * @return Error code indicating success or failure + */ +static int mc521da_detect_client(struct i2c_adapter *adapter, int address, + int kind) +{ + mc521da_i2c_client.adapter = adapter; + if (i2c_attach_client(&mc521da_i2c_client)) { + mc521da_i2c_client.adapter = NULL; + printk(KERN_ERR "mc521da_attach: i2c_attach_client failed\n"); + return -1; + } + + interface_param = (sensor_interface *) + kmalloc(sizeof(sensor_interface), GFP_KERNEL); + if (!interface_param) { + printk(KERN_ERR "mc521da_attach: kmalloc failed \n"); + return -1; + } + + interface_param->mclk = 25000000; + + printk(KERN_INFO "mc521da Detected\n"); + + return 0; +} + +static unsigned short normal_i2c[] = { MC521DA_I2C_ADDRESS, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static int mc521da_attach(struct i2c_adapter *adap) +{ + uint32_t mclk = 25000000; + struct clk *clk; + int err; + + clk = clk_get(NULL, "csi_clk"); + clk_enable(clk); + set_mclk_rate(&mclk); + + gpio_sensor_reset(true); + msleep(10); + gpio_sensor_reset(false); + msleep(100); + + err = i2c_probe(adap, &addr_data, &mc521da_detect_client); + + clk_disable(clk); + clk_put(clk); + + return err; +} + +/*! + * mc521da I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int mc521da_detach(struct i2c_client *client) +{ + int err; + + if (!mc521da_i2c_client.adapter) + return -1; + + err = i2c_detach_client(&mc521da_i2c_client); + mc521da_i2c_client.adapter = NULL; + + if (interface_param) + kfree(interface_param); + interface_param = NULL; + + return err; +} + +extern void gpio_sensor_active(void); +extern void gpio_sensor_inactive(void); + +/*! + * mc521da init function + * + * @return Error code indicating success or failure + */ +static __init int mc521da_init(void) +{ + gpio_sensor_active(); + return i2c_add_driver(&mc521da_i2c_driver); +} + +/*! + * mc521da cleanup function + * + * @return Error code indicating success or failure + */ +static void __exit mc521da_clean(void) +{ + i2c_del_driver(&mc521da_i2c_driver); + gpio_sensor_inactive(); +} + +module_init(mc521da_init); +module_exit(mc521da_clean); + +/* Exported symbols for modules. */ +EXPORT_SYMBOL(camera_sensor_if); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MC521DA Camera Driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mt9v111.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mt9v111.c --- linux-2.6.28/drivers/media/video/mxc/capture/mt9v111.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mt9v111.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,932 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mt9v111.c + * + * @brief mt9v111 camera driver functions + * + * @ingroup Camera + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "mt9v111.h" + +#ifdef MT9V111_DEBUG +static u16 testpattern = 0; +#endif + +static sensor_interface *interface_param = NULL; +static mt9v111_conf mt9v111_device; +static int reset_frame_rate = 30; + +#define MT9V111_FRAME_RATE_NUM 20 + +static mt9v111_image_format format[2] = { + { + .index = 0, + .width = 640, + .height = 480, + }, + { + .index = 1, + .width = 352, + .height = 288, + }, +}; + +static int mt9v111_attach(struct i2c_adapter *adapter); +static int mt9v111_detach(struct i2c_client *client); + +static struct i2c_driver mt9v111_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "MT9V111 Client", + }, + .attach_adapter = mt9v111_attach, + .detach_client = mt9v111_detach, +}; + +static struct i2c_client mt9v111_i2c_client = { + .name = "mt9v111 I2C dev", + .addr = MT9V111_I2C_ADDRESS, + .driver = &mt9v111_i2c_driver, +}; + +/* + * Function definitions + */ + +static u16 mt9v111_endian_swap16(u16 data) +{ + u16 temp; + + temp = data; + temp = ((data >> 8) & 0xff) | ((data << 8) & 0xff00); + + return temp; +} + +static int mt9v111_i2c_client_xfer(unsigned int addr, char *reg, int reg_len, + char *buf, int num, int tran_flag) +{ + struct i2c_msg msg[2]; + int ret; + + msg[0].addr = addr; + msg[0].len = reg_len; + msg[0].buf = reg; + msg[0].flags = tran_flag; + msg[0].flags &= ~I2C_M_RD; + + msg[1].addr = addr; + msg[1].len = num; + msg[1].buf = buf; + msg[1].flags = tran_flag; + + if (tran_flag & MXC_I2C_FLAG_READ) { + msg[1].flags |= I2C_M_RD; + } else { + msg[1].flags &= ~I2C_M_RD; + } + + ret = i2c_transfer(mt9v111_i2c_client.adapter, msg, 2); + if (ret >= 0) + return 0; + + return ret; +} + +static int mt9v111_read_reg(u8 * reg, u16 * val) +{ + return mt9v111_i2c_client_xfer(MT9V111_I2C_ADDRESS, reg, 1, + (u8 *) val, 2, MXC_I2C_FLAG_READ); +} + +static int mt9v111_write_reg(u8 reg, u16 val) +{ + u8 temp1; + u16 temp2; + temp1 = reg; + temp2 = mt9v111_endian_swap16(val); + pr_debug("write reg %x val %x.\n", reg, val); + return mt9v111_i2c_client_xfer(MT9V111_I2C_ADDRESS, &temp1, 1, + (u8 *) & temp2, 2, 0); +} + +/*! + * Initialize mt9v111_sensor_lib + * Libarary for Sensor configuration through I2C + * + * @param coreReg Core Registers + * @param ifpReg IFP Register + * + * @return status + */ +static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg) +{ + u8 reg; + u16 data; + u8 error = 0; + + /* + * setup to IFP registers + */ + reg = MT9V111I_ADDR_SPACE_SEL; + data = ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + // Operation Mode Control + reg = MT9V111I_MODE_CONTROL; + data = ifpReg->modeControl; + mt9v111_write_reg(reg, data); + + // Output format + reg = MT9V111I_FORMAT_CONTROL; + data = ifpReg->formatControl; // Set bit 12 + mt9v111_write_reg(reg, data); + + // Flicker Control + reg = MT9V111I_FLICKER_CONTROL; + data = ifpReg->flickerCtrl; + mt9v111_write_reg(reg, data); + + // AE limit 4 + reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE; + data = ifpReg->gainLimitAE; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_OUTPUT_FORMAT_CTRL2; + data = ifpReg->outputFormatCtrl2; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_AE_SPEED; + data = ifpReg->AESpeed; + mt9v111_write_reg(reg, data); + + /* output image size */ + reg = MT9V111i_H_PAN; + data = 0x8000 | ifpReg->HPan; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_H_ZOOM; + data = 0x8000 | ifpReg->HZoom; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_H_SIZE; + data = 0x8000 | ifpReg->HSize; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_V_PAN; + data = 0x8000 | ifpReg->VPan; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_V_ZOOM; + data = 0x8000 | ifpReg->VZoom; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_V_SIZE; + data = 0x8000 | ifpReg->VSize; + mt9v111_write_reg(reg, data); + + reg = MT9V111i_H_PAN; + data = ~0x8000 & ifpReg->HPan; + mt9v111_write_reg(reg, data); +#if 0 + reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM; + data = ifpReg->upperShutterDelayLi; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_SHUTTER_60; + data = ifpReg->shutter_width_60; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_SEARCH_FLICK_60; + data = ifpReg->search_flicker_60; + mt9v111_write_reg(reg, data); +#endif + + /* + * setup to sensor core registers + */ + reg = MT9V111I_ADDR_SPACE_SEL; + data = coreReg->addressSelect; + mt9v111_write_reg(reg, data); + + // enable changes and put the Sync bit on + reg = MT9V111S_OUTPUT_CTRL; + data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000; + mt9v111_write_reg(reg, data); + + // min PIXCLK - Default + reg = MT9V111S_PIXEL_CLOCK_SPEED; + data = coreReg->pixelClockSpeed; + mt9v111_write_reg(reg, data); + + //Setup image flipping / Dark rows / row/column skip + reg = MT9V111S_READ_MODE; + data = coreReg->readMode; + mt9v111_write_reg(reg, data); + + //zoom 0 + reg = MT9V111S_DIGITAL_ZOOM; + data = coreReg->digitalZoom; + mt9v111_write_reg(reg, data); + + // min H-blank + reg = MT9V111S_HOR_BLANKING; + data = coreReg->horizontalBlanking; + mt9v111_write_reg(reg, data); + + // min V-blank + reg = MT9V111S_VER_BLANKING; + data = coreReg->verticalBlanking; + mt9v111_write_reg(reg, data); + + reg = MT9V111S_SHUTTER_WIDTH; + data = coreReg->shutterWidth; + mt9v111_write_reg(reg, data); + + reg = MT9V111S_SHUTTER_DELAY; + data = ifpReg->upperShutterDelayLi; + mt9v111_write_reg(reg, data); + + // changes become effective + reg = MT9V111S_OUTPUT_CTRL; + data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000; + mt9v111_write_reg(reg, data); + + return error; +} + +/*! + * mt9v111 sensor interface Initialization + * @param param sensor_interface * + * @param width u32 + * @param height u32 + * @return None + */ +static void mt9v111_interface(sensor_interface * param, u32 width, u32 height) +{ + param->Vsync_pol = 0x0; + param->clk_mode = 0x0; //gated + param->pixclk_pol = 0x0; + param->data_width = 0x1; + param->data_pol = 0x0; + param->ext_vsync = 0x0; + param->Vsync_pol = 0x0; + param->Hsync_pol = 0x0; + param->width = width - 1; + param->height = height - 1; + param->pixel_fmt = IPU_PIX_FMT_UYVY; + param->mclk = 27000000; +} + +/*! + * MT9V111 frame rate calculate + * + * @param frame_rate int * + * @param mclk int + * @return None + */ +static void mt9v111_rate_cal(int *frame_rate, int mclk) +{ + int num_clock_per_row; + int max_rate = 0; + + mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN; + + num_clock_per_row = (format[0].width + 114 + MT9V111_HORZBLANK_MIN) * 2; + max_rate = mclk / (num_clock_per_row * + (format[0].height + MT9V111_VERTBLANK_DEFAULT)); + + if ((*frame_rate > max_rate) || (*frame_rate == 0)) { + *frame_rate = max_rate; + } + + mt9v111_device.coreReg->verticalBlanking + = mclk / (*frame_rate * num_clock_per_row) - format[0].height; + + reset_frame_rate = *frame_rate; +} + +/*! + * MT9V111 sensor configuration + * + * @param frame_rate int * + * @param high_quality int + * @return sensor_interface * + */ +sensor_interface *mt9v111_config(int *frame_rate, int high_quality) +{ + u32 out_width, out_height; + + if (interface_param == NULL) + return NULL; + + mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA; + mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP; + + mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT; + mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH; + mt9v111_device.coreReg->zoomColStart = 0; + mt9v111_device.coreReg->zomRowStart = 0; + mt9v111_device.coreReg->digitalZoom = 0x0; + + mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT; + mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN; + mt9v111_device.coreReg->pixelClockSpeed = 0; + mt9v111_device.coreReg->readMode = 0xd0a1; + + mt9v111_device.ifpReg->outputFormatCtrl2 = 0; + mt9v111_device.ifpReg->gainLimitAE = 0x300; + mt9v111_device.ifpReg->AESpeed = 0x80; + + // here is the default value + mt9v111_device.ifpReg->formatControl = 0xc800; + mt9v111_device.ifpReg->modeControl = 0x708e; + mt9v111_device.ifpReg->awbSpeed = 0x4514; + mt9v111_device.ifpReg->flickerCtrl = 0x02; + mt9v111_device.coreReg->shutterWidth = 0xf8; + + out_width = 640; + out_height = 480; + + /*output size */ + mt9v111_device.ifpReg->HPan = 0; + mt9v111_device.ifpReg->HZoom = 640; + mt9v111_device.ifpReg->HSize = out_width; + mt9v111_device.ifpReg->VPan = 0; + mt9v111_device.ifpReg->VZoom = 480; + mt9v111_device.ifpReg->VSize = out_height; + + mt9v111_interface(interface_param, out_width, out_height); + set_mclk_rate(&interface_param->mclk); + mt9v111_rate_cal(frame_rate, interface_param->mclk); + mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg); + + return interface_param; +} + +/*! + * mt9v111 sensor set color configuration + * + * @param bright int + * @param saturation int + * @param red int + * @param green int + * @param blue int + * @return None + */ +static void +mt9v111_set_color(int bright, int saturation, int red, int green, int blue) +{ + u8 reg; + u16 data; + + switch (saturation) { + case 100: + mt9v111_device.ifpReg->awbSpeed = 0x4514; + break; + case 150: + mt9v111_device.ifpReg->awbSpeed = 0x6D14; + break; + case 75: + mt9v111_device.ifpReg->awbSpeed = 0x4D14; + break; + case 50: + mt9v111_device.ifpReg->awbSpeed = 0x5514; + break; + case 37: + mt9v111_device.ifpReg->awbSpeed = 0x5D14; + break; + case 25: + mt9v111_device.ifpReg->awbSpeed = 0x6514; + break; + default: + mt9v111_device.ifpReg->awbSpeed = 0x4514; + break; + } + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + // Operation Mode Control + reg = MT9V111I_AWB_SPEED; + data = mt9v111_device.ifpReg->awbSpeed; + mt9v111_write_reg(reg, data); +} + +/*! + * mt9v111 sensor get color configuration + * + * @param bright int * + * @param saturation int * + * @param red int * + * @param green int * + * @param blue int * + * @return None + */ +static void +mt9v111_get_color(int *bright, int *saturation, int *red, int *green, int *blue) +{ + *saturation = (mt9v111_device.ifpReg->awbSpeed & 0x3800) >> 11; + switch (*saturation) { + case 0: + *saturation = 100; + break; + case 1: + *saturation = 75; + break; + case 2: + *saturation = 50; + break; + case 3: + *saturation = 37; + break; + case 4: + *saturation = 25; + break; + case 5: + *saturation = 150; + break; + case 6: + *saturation = 0; + break; + default: + *saturation = 0; + break; + } +} + +/*! + * mt9v111 sensor set AE measurement window mode configuration + * + * @param ae_mode int + * @return None + */ +static void mt9v111_set_ae_mode(int ae_mode) +{ + u8 reg; + u16 data; + + mt9v111_device.ifpReg->modeControl &= 0xfff3; + mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 2; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_MODE_CONTROL; + data = mt9v111_device.ifpReg->modeControl; + mt9v111_write_reg(reg, data); +} + +/*! + * mt9v111 sensor get AE measurement window mode configuration + * + * @param ae_mode int * + * @return None + */ +static void mt9v111_get_ae_mode(int *ae_mode) +{ + if (ae_mode != NULL) { + *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2; + } +} + +/*! + * mt9v111 sensor enable/disable AE + * + * @param active int + * @return None + */ +static void mt9v111_set_ae(int active) +{ + u8 reg; + u16 data; + + mt9v111_device.ifpReg->modeControl &= 0xBFFF; + mt9v111_device.ifpReg->modeControl |= (active & 0x01) << 14; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_MODE_CONTROL; + data = mt9v111_device.ifpReg->modeControl; + mt9v111_write_reg(reg, data); +} + + /*! + * mt9v111 sensor set AE gain + * + * @param active int + * @return None + */ +static void mt9v111_set_ae_limit(int limit) +{ + u8 reg; + u16 data; + + mt9v111_device.ifpReg->gainLimitAE = (limit & 0x1F) << 5; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE; + data = mt9v111_device.ifpReg->gainLimitAE; + mt9v111_write_reg(reg, data); +} + + +/*! + * mt9v111 sensor enable/disable auto white balance + * + * @param active int + * @return None + */ +static void mt9v111_set_awb(int active) +{ + u8 reg; + u16 data; + + mt9v111_device.ifpReg->modeControl &= 0xFFFD; + mt9v111_device.ifpReg->modeControl |= (active & 0x01) << 1; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + reg = MT9V111I_MODE_CONTROL; + data = mt9v111_device.ifpReg->modeControl; + mt9v111_write_reg(reg, data); +} + +/*! + * mt9v111 sensor set the flicker control + * + * @param control int + * @return None + */ +static void mt9v111_flicker_control(int control) +{ + u8 reg; + u16 data; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(reg, data); + + switch (control) { + case V4L2_MXC_FLICKER_DISABLE: + mt9v111_device.ifpReg->formatControl &= ~(0x01 << 11); + + reg = MT9V111I_FORMAT_CONTROL; + data = mt9v111_device.ifpReg->formatControl; + mt9v111_write_reg(reg, data); + break; + + case V4L2_MXC_FLICKER_50HZ: + if (!(mt9v111_device.ifpReg->formatControl & (0x01 << 11))) { + mt9v111_device.ifpReg->formatControl |= (0x01 << 11); + reg = MT9V111I_FORMAT_CONTROL; + data = mt9v111_device.ifpReg->formatControl; + mt9v111_write_reg(reg, data); + } + mt9v111_device.ifpReg->flickerCtrl = 0x01; + reg = MT9V111I_FLICKER_CONTROL; + data = mt9v111_device.ifpReg->flickerCtrl; + mt9v111_write_reg(reg, data); + break; + + case V4L2_MXC_FLICKER_60HZ: + if (!(mt9v111_device.ifpReg->formatControl & (0x01 << 11))) { + mt9v111_device.ifpReg->formatControl |= (0x01 << 11); + reg = MT9V111I_FORMAT_CONTROL; + data = mt9v111_device.ifpReg->formatControl; + mt9v111_write_reg(reg, data); + } + mt9v111_device.ifpReg->flickerCtrl = 0x03; + reg = MT9V111I_FLICKER_CONTROL; + data = mt9v111_device.ifpReg->flickerCtrl; + mt9v111_write_reg(reg, data); + break; + + case V4L2_MXC_FLICKER_AUTO: + if (!(mt9v111_device.ifpReg->formatControl & (0x01 << 11))) { + mt9v111_device.ifpReg->formatControl |= (0x01 << 11); + reg = MT9V111I_FORMAT_CONTROL; + data = mt9v111_device.ifpReg->formatControl; + mt9v111_write_reg(reg, data); + } + mt9v111_device.ifpReg->flickerCtrl = 0x10; + reg = MT9V111I_FLICKER_CONTROL; + data = mt9v111_device.ifpReg->flickerCtrl; + mt9v111_write_reg(reg, data); + break; + } + return; + +} + +/*! + * mt9v111 Get mode&flicker control parameters + * + * @return None + */ +static void mt9v111_get_control_params(int *ae, int *awb, int *flicker) +{ + if ((ae != NULL) && (awb != NULL) && (flicker != NULL)) { + *ae = (mt9v111_device.ifpReg->modeControl & 0x4000) >> 14; + *awb = (mt9v111_device.ifpReg->modeControl & 0x02) >> 1; + *flicker = (mt9v111_device.ifpReg->formatControl & 0x800) >> 9; + if (*flicker) { + *flicker = (mt9v111_device.ifpReg->flickerCtrl & 0x03); + switch (*flicker) { + case 1: + *flicker = V4L2_MXC_FLICKER_50HZ; + break; + case 3: + *flicker = V4L2_MXC_FLICKER_60HZ; + break; + default: + *flicker = V4L2_MXC_FLICKER_AUTO; + break; + } + } else + *flicker = V4L2_MXC_FLICKER_DISABLE; + } + return; +} + +/*! + * mt9v111 Reset function + * + * @return None + */ +static sensor_interface *mt9v111_reset(void) +{ + return mt9v111_config(&reset_frame_rate, 0); +} + +/*! + * mt9v111 get_status function + * + * @return int + */ +static int mt9v111_get_status(void) +{ + int retval = 0; + u8 reg; + u16 data = 0; + + if (!interface_param) + return -ENODEV; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = MT9V111I_SEL_SCA; + retval = mt9v111_write_reg(reg, data); + + reg = MT9V111S_SENSOR_CORE_VERSION; + retval = mt9v111_read_reg(®, &data); + data = mt9v111_endian_swap16(data); + if (MT9V111_CHIP_VERSION != data) + retval = -ENODEV; + + return retval; +} + +struct camera_sensor camera_sensor_if = { + .set_color = mt9v111_set_color, + .get_color = mt9v111_get_color, + .set_ae_mode = mt9v111_set_ae_mode, + .get_ae_mode = mt9v111_get_ae_mode, + .set_ae = mt9v111_set_ae, + .set_ae_limit = mt9v111_set_ae_limit, + .set_awb = mt9v111_set_awb, + .flicker_control = mt9v111_flicker_control, + .get_control_params = mt9v111_get_control_params, + .config = mt9v111_config, + .reset = mt9v111_reset, + .get_status = mt9v111_get_status, +}; + +#ifdef MT9V111_DEBUG +/*! + * Set sensor to test mode, which will generate test pattern. + * + * @return none + */ +static void mt9v111_test_pattern(bool flag) +{ + u8 reg; + u16 data; + + // switch to sensor registers + reg = MT9V111I_ADDR_SPACE_SEL; + data = MT9V111I_SEL_SCA; + mt9v111_write_reg(reg, data); + + if (flag == true) { + testpattern = MT9V111S_OUTCTRL_TEST_MODE; + + reg = MT9V111S_ROW_NOISE_CTRL; + data = mt9v111_read_reg(®, &data) & 0xBF; + data = mt9v111_endian_swap16(data); + mt9v111_write_reg(reg, data); + + reg = MT9V111S_TEST_DATA; + data = 0; + mt9v111_write_reg(reg, data); + + reg = MT9V111S_OUTPUT_CTRL; + // changes take effect + data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000; + mt9v111_write_reg(reg, data); + } else { + testpattern = 0; + + reg = MT9V111S_ROW_NOISE_CTRL; + data = mt9v111_read_reg(®, &data) | 0x40; + data = mt9v111_endian_swap16(data); + mt9v111_write_reg(reg, data); + + reg = MT9V111S_OUTPUT_CTRL; + // changes take effect + data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000; + mt9v111_write_reg(reg, data); + } +} +#endif + +/*! + * mt9v111 I2C detect_client function + * + * @param adapter struct i2c_adapter * + * @param address int + * @param kind int + * + * @return Error code indicating success or failure + */ +static int mt9v111_detect_client(struct i2c_adapter *adapter, int address, + int kind) +{ + mt9v111_i2c_client.adapter = adapter; + if (i2c_attach_client(&mt9v111_i2c_client)) { + mt9v111_i2c_client.adapter = NULL; + printk(KERN_ERR "mt9v111_attach: i2c_attach_client failed\n"); + return -1; + } + + interface_param = (sensor_interface *) + kmalloc(sizeof(sensor_interface), GFP_KERNEL); + if (!interface_param) { + printk(KERN_ERR "mt9v111_attach: kmalloc failed \n"); + return -1; + } + + printk(KERN_INFO "MT9V111 Detected\n"); + + return 0; +} + +static unsigned short normal_i2c[] = { MT9V111_I2C_ADDRESS, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +/*! + * mt9v111 I2C attach function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int mt9v111_attach(struct i2c_adapter *adap) +{ + uint32_t mclk = 27000000; + struct clk *clk; + int err; + + clk = clk_get(NULL, "csi_clk"); + clk_enable(clk); + set_mclk_rate(&mclk); + + err = i2c_probe(adap, &addr_data, &mt9v111_detect_client); + clk_disable(clk); + clk_put(clk); + + return err; +} + +/*! + * mt9v111 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int mt9v111_detach(struct i2c_client *client) +{ + int err; + + if (!mt9v111_i2c_client.adapter) + return -1; + + err = i2c_detach_client(&mt9v111_i2c_client); + mt9v111_i2c_client.adapter = NULL; + + if (interface_param) + kfree(interface_param); + interface_param = NULL; + + return err; +} + +extern void gpio_sensor_active(void); + +/*! + * MT9V111 init function + * + * @return Error code indicating success or failure + */ +static __init int mt9v111_init(void) +{ + u8 err; + + gpio_sensor_active(); + + mt9v111_device.coreReg = (mt9v111_coreReg *) + kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL); + if (!mt9v111_device.coreReg) + return -1; + + memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg)); + + mt9v111_device.ifpReg = (mt9v111_IFPReg *) + kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL); + if (!mt9v111_device.ifpReg) { + kfree(mt9v111_device.coreReg); + mt9v111_device.coreReg = NULL; + return -1; + } + + memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg)); + + err = i2c_add_driver(&mt9v111_i2c_driver); + return err; +} + +extern void gpio_sensor_inactive(void); +/*! + * MT9V111 cleanup function + * + * @return Error code indicating success or failure + */ +static void __exit mt9v111_clean(void) +{ + if (mt9v111_device.coreReg) { + kfree(mt9v111_device.coreReg); + mt9v111_device.coreReg = NULL; + } + + if (mt9v111_device.ifpReg) { + kfree(mt9v111_device.ifpReg); + mt9v111_device.ifpReg = NULL; + } + + i2c_del_driver(&mt9v111_i2c_driver); + + gpio_sensor_inactive(); +} + +module_init(mt9v111_init); +module_exit(mt9v111_clean); + +/* Exported symbols for modules. */ +EXPORT_SYMBOL(camera_sensor_if); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Mt9v111 Camera Driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mt9v111.h linux-2.6.28-karo/drivers/media/video/mxc/capture/mt9v111.h --- linux-2.6.28/drivers/media/video/mxc/capture/mt9v111.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mt9v111.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,435 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup Camera Sensor Drivers + */ + +/*! + * @file mt9v111.h + * + * @brief MT9V111 Camera Header file + * + * It include all the defines for bitmaps operations, also two main structure + * one for IFP interface structure, other for sensor core registers. + * + * @ingroup Camera + */ + +#ifndef MT9V111_H_ +#define MT9V111_H_ + +/*! + * mt9v111 CHIP VERSION + */ +#define MT9V111_CHIP_VERSION 0x823A + +/*! + * mt9v111 IFP REGISTER BANK MAP + */ +#define MT9V111I_ADDR_SPACE_SEL 0x1 +#define MT9V111I_BASE_MAXTRIX_SIGN 0x2 +#define MT9V111I_BASE_MAXTRIX_SCALE15 0x3 +#define MT9V111I_BASE_MAXTRIX_SCALE69 0x4 +#define MT9V111I_APERTURE_GAIN 0x5 +#define MT9V111I_MODE_CONTROL 0x6 +#define MT9V111I_SOFT_RESET 0x7 +#define MT9V111I_FORMAT_CONTROL 0x8 +#define MT9V111I_BASE_MATRIX_CFK1 0x9 +#define MT9V111I_BASE_MATRIX_CFK2 0xa +#define MT9V111I_BASE_MATRIX_CFK3 0xb +#define MT9V111I_BASE_MATRIX_CFK4 0xc +#define MT9V111I_BASE_MATRIX_CFK5 0xd +#define MT9V111I_BASE_MATRIX_CFK6 0xe +#define MT9V111I_BASE_MATRIX_CFK7 0xf +#define MT9V111I_BASE_MATRIX_CFK8 0x10 +#define MT9V111I_BASE_MATRIX_CFK9 0x11 +#define MT9V111I_AWB_POSITION 0x12 +#define MT9V111I_AWB_RED_GAIN 0x13 +#define MT9V111I_AWB_BLUE_GAIN 0x14 +#define MT9V111I_DELTA_MATRIX_CF_SIGN 0x15 +#define MT9V111I_DELTA_MATRIX_CF_D1 0x16 +#define MT9V111I_DELTA_MATRIX_CF_D2 0x17 +#define MT9V111I_DELTA_MATRIX_CF_D3 0x18 +#define MT9V111I_DELTA_MATRIX_CF_D4 0x19 +#define MT9V111I_DELTA_MATRIX_CF_D5 0x1a +#define MT9V111I_DELTA_MATRIX_CF_D6 0x1b +#define MT9V111I_DELTA_MATRIX_CF_D7 0x1c +#define MT9V111I_DELTA_MATRIX_CF_D8 0x1d +#define MT9V111I_DELTA_MATRIX_CF_D9 0x1e +#define MT9V111I_LUMINANCE_LIMIT_WB 0x20 +#define MT9V111I_RBG_MANUUAL_WB 0x21 +#define MT9V111I_AWB_RED_LIMIT 0x22 +#define MT9V111I_AWB_BLUE_LIMIT 0x23 +#define MT9V111I_MATRIX_ADJUST_LIMIT 0x24 +#define MT9V111I_AWB_SPEED 0x25 +#define MT9V111I_H_BOUND_AE 0x26 +#define MT9V111I_V_BOUND_AE 0x27 +#define MT9V111I_H_BOUND_AE_CEN_WIN 0x2b +#define MT9V111I_V_BOUND_AE_CEN_WIN 0x2c +#define MT9V111I_BOUND_AWB_WIN 0x2d +#define MT9V111I_AE_PRECISION_TARGET 0x2e +#define MT9V111I_AE_SPEED 0x2f +#define MT9V111I_RED_AWB_MEASURE 0x30 +#define MT9V111I_LUMA_AWB_MEASURE 0x31 +#define MT9V111I_BLUE_AWB_MEASURE 0x32 +#define MT9V111I_LIMIT_SHARP_SATU_CTRL 0x33 +#define MT9V111I_LUMA_OFFSET 0x34 +#define MT9V111I_CLIP_LIMIT_OUTPUT_LUMI 0x35 +#define MT9V111I_GAIN_LIMIT_AE 0x36 +#define MT9V111I_SHUTTER_WIDTH_LIMIT_AE 0x37 +#define MT9V111I_UPPER_SHUTTER_DELAY_LIM 0x39 +#define MT9V111I_OUTPUT_FORMAT_CTRL2 0x3a +#define MT9V111I_IPF_BLACK_LEVEL_SUB 0x3b +#define MT9V111I_IPF_BLACK_LEVEL_ADD 0x3c +#define MT9V111I_ADC_LIMIT_AE_ADJ 0x3d +#define MT9V111I_GAIN_THRE_CCAM_ADJ 0x3e +#define MT9V111I_LINEAR_AE 0x3f +#define MT9V111I_THRESHOLD_EDGE_DEFECT 0x47 +#define MT9V111I_LUMA_SUM_MEASURE 0x4c +#define MT9V111I_TIME_ADV_SUM_LUMA 0x4d +#define MT9V111I_MOTION 0x52 +#define MT9V111I_GAMMA_KNEE_Y12 0x53 +#define MT9V111I_GAMMA_KNEE_Y34 0x54 +#define MT9V111I_GAMMA_KNEE_Y56 0x55 +#define MT9V111I_GAMMA_KNEE_Y78 0x56 +#define MT9V111I_GAMMA_KNEE_Y90 0x57 +#define MT9V111I_GAMMA_VALUE_Y0 0x58 +#define MT9V111I_SHUTTER_60 0x59 +#define MT9V111I_FLICKER_CONTROL 0x5B +#define MT9V111I_SEARCH_FLICK_60 0x5c +#define MT9V111I_RATIO_IMAGE_GAIN_BASE 0x5e +#define MT9V111I_RATIO_IMAGE_GAIN_DELTA 0x5f +#define MT9V111I_SIGN_VALUE_REG5F 0x60 +#define MT9V111I_AE_GAIN 0x62 +#define MT9V111I_MAX_GAIN_AE 0x67 +#define MT9V111I_LENS_CORRECT_CTRL 0x80 +#define MT9V111I_SHADING_PARAMETER1 0x81 +#define MT9V111I_SHADING_PARAMETER2 0x82 +#define MT9V111I_SHADING_PARAMETER3 0x83 +#define MT9V111I_SHADING_PARAMETER4 0x84 +#define MT9V111I_SHADING_PARAMETER5 0x85 +#define MT9V111I_SHADING_PARAMETER6 0x86 +#define MT9V111I_SHADING_PARAMETER7 0x87 +#define MT9V111I_SHADING_PARAMETER8 0x88 +#define MT9V111I_SHADING_PARAMETER9 0x89 +#define MT9V111I_SHADING_PARAMETER10 0x8A +#define MT9V111I_SHADING_PARAMETER11 0x8B +#define MT9V111I_SHADING_PARAMETER12 0x8C +#define MT9V111I_SHADING_PARAMETER13 0x8D +#define MT9V111I_SHADING_PARAMETER14 0x8E +#define MT9V111I_SHADING_PARAMETER15 0x8F +#define MT9V111I_SHADING_PARAMETER16 0x90 +#define MT9V111I_SHADING_PARAMETER17 0x91 +#define MT9V111I_SHADING_PARAMETER18 0x92 +#define MT9V111I_SHADING_PARAMETER19 0x93 +#define MT9V111I_SHADING_PARAMETER20 0x94 +#define MT9V111I_SHADING_PARAMETER21 0x95 +#define MT9V111i_FLASH_CTRL 0x98 +#define MT9V111i_LINE_COUNTER 0x99 +#define MT9V111i_FRAME_COUNTER 0x9A +#define MT9V111i_H_PAN 0xA5 +#define MT9V111i_H_ZOOM 0xA6 +#define MT9V111i_H_SIZE 0xA7 +#define MT9V111i_V_PAN 0xA8 +#define MT9V111i_V_ZOOM 0xA9 +#define MT9V111i_V_SIZE 0xAA + +#define MT9V111I_SEL_IFP 0x1 +#define MT9V111I_SEL_SCA 0x4 +#define MT9V111I_FC_RGB_OR_YUV 0x1000 + +/*! + * Mt9v111 SENSOR CORE REGISTER BANK MAP + */ +#define MT9V111S_ADDR_SPACE_SEL 0x1 +#define MT9V111S_COLUMN_START 0x2 +#define MT9V111S_WIN_HEIGHT 0x3 +#define MT9V111S_WIN_WIDTH 0x4 +#define MT9V111S_HOR_BLANKING 0x5 +#define MT9V111S_VER_BLANKING 0x6 +#define MT9V111S_OUTPUT_CTRL 0x7 +#define MT9V111S_ROW_START 0x8 +#define MT9V111S_SHUTTER_WIDTH 0x9 +#define MT9V111S_PIXEL_CLOCK_SPEED 0xa +#define MT9V111S_RESTART 0xb +#define MT9V111S_SHUTTER_DELAY 0xc +#define MT9V111S_RESET 0xd +#define MT9V111S_COLUMN_START_IN_ZOOM 0x12 +#define MT9V111S_ROW_START_IN_ZOOM 0x13 +#define MT9V111S_DIGITAL_ZOOM 0x1e +#define MT9V111S_READ_MODE 0x20 +#define MT9V111S_DAC_CTRL 0x27 +#define MT9V111S_GREEN1_GAIN 0x2b +#define MT9V111S_BLUE_GAIN 0x2c +#define MT9V111S_READ_GAIN 0x2d +#define MT9V111S_GREEN2_GAIN 0x2e +#define MT9V111S_ROW_NOISE_CTRL 0x30 +#define MT9V111S_DARK_TARGET_W 0x31 +#define MT9V111S_TEST_DATA 0x32 +#define MT9V111S_GLOBAL_GAIN 0x35 +#define MT9V111S_SENSOR_CORE_VERSION 0x36 +#define MT9V111S_DARK_TARGET_WO 0x37 +#define MT9V111S_VERF_DAC 0x41 +#define MT9V111S_VCM_VCL 0x42 +#define MT9V111S_DISABLE_BYPASS 0x58 +#define MT9V111S_CALIB_MEAN_TEST 0x59 +#define MT9V111S_DARK_G1_AVE 0x5B +#define MT9V111S_DARK_G2_AVE 0x5C +#define MT9V111S_DARK_R_AVE 0x5D +#define MT9V111S_DARK_B_AVE 0x5E +#define MT9V111S_CAL_THRESHOLD 0x5f +#define MT9V111S_CAL_G1 0x60 +#define MT9V111S_CAL_G2 0x61 +#define MT9V111S_CAL_CTRL 0x62 +#define MT9V111S_CAL_R 0x63 +#define MT9V111S_CAL_B 0x64 +#define MT9V111S_CHIP_ENABLE 0xF1 +#define MT9V111S_CHIP_VERSION 0xFF + +// OUTPUT_CTRL +#define MT9V111S_OUTCTRL_SYNC 0x1 +#define MT9V111S_OUTCTRL_CHIP_ENABLE 0x2 +#define MT9V111S_OUTCTRL_TEST_MODE 0x40 + +// READ_MODE +#define MT9V111S_RM_NOBADFRAME 0x1 +#define MT9V111S_RM_NODESTRUCT 0x2 +#define MT9V111S_RM_COLUMNSKIP 0x4 +#define MT9V111S_RM_ROWSKIP 0x8 +#define MT9V111S_RM_BOOSTEDRESET 0x1000 +#define MT9V111S_RM_COLUMN_LATE 0x10 +#define MT9V111S_RM_ROW_LATE 0x80 +#define MT9V111S_RM_RIGTH_TO_LEFT 0x4000 +#define MT9V111S_RM_BOTTOM_TO_TOP 0x8000 + +/*! I2C Slave Address */ +#define MT9V111_I2C_ADDRESS 0x48 + +/*! + * The image resolution enum for the mt9v111 sensor + */ +typedef enum { + MT9V111_OutputResolution_VGA = 0, /*!< VGA size */ + MT9V111_OutputResolution_QVGA, /*!< QVGA size */ + MT9V111_OutputResolution_CIF, /*!< CIF size */ + MT9V111_OutputResolution_QCIF, /*!< QCIF size */ + MT9V111_OutputResolution_QQVGA, /*!< QQVGA size */ + MT9V111_OutputResolution_SXGA /*!< SXGA size */ +} MT9V111_OutputResolution; + +enum { + MT9V111_WINWIDTH = 0x287, + MT9V111_WINWIDTH_DEFAULT = 0x287, + MT9V111_WINWIDTH_MIN = 0x9, + + MT9V111_WINHEIGHT = 0x1E7, + MT9V111_WINHEIGHT_DEFAULT = 0x1E7, + + MT9V111_HORZBLANK_DEFAULT = 0x26, + MT9V111_HORZBLANK_MIN = 0x9, + MT9V111_HORZBLANK_MAX = 0x3FF, + + MT9V111_VERTBLANK_DEFAULT = 0x4, + MT9V111_VERTBLANK_MIN = 0x3, + MT9V111_VERTBLANK_MAX = 0xFFF, +}; + +/*! + * Mt9v111 Core Register structure. + */ +typedef struct { + u32 addressSelect; /*!< select address bank for Core Register 0x4 */ + u32 columnStart; /*!< Starting Column */ + u32 windowHeight; /*!< Window Height */ + u32 windowWidth; /*!< Window Width */ + u32 horizontalBlanking; /*!< Horizontal Blank time, in pixels */ + u32 verticalBlanking; /*!< Vertical Blank time, in pixels */ + u32 outputControl; /*!< Register to control sensor output */ + u32 rowStart; /*!< Starting Row */ + u32 shutterWidth; + u32 pixelClockSpeed; /*!< pixel date rate */ + u32 restart; /*!< Abandon the readout of current frame */ + u32 shutterDelay; + u32 reset; /*!< reset the sensor to the default mode */ + u32 zoomColStart; /*!< Column start in the Zoom mode */ + u32 zomRowStart; /*!< Row start in the Zoom mode */ + u32 digitalZoom; /*!< 1 means zoom by 2 */ + u32 readMode; /*!< Readmode: aspects of the readout of the sensor */ + u32 dACStandbyControl; + u32 green1Gain; /*!< Gain Settings */ + u32 blueGain; + u32 redGain; + u32 green2Gain; + u32 rowNoiseControl; + u32 darkTargetwNC; + u32 testData; /*!< test mode */ + u32 globalGain; + u32 chipVersion; + u32 darkTargetwoNC; + u32 vREFDACs; + u32 vCMandVCL; + u32 disableBypass; + u32 calibMeanTest; + u32 darkG1average; + u32 darkG2average; + u32 darkRaverage; + u32 darkBaverage; + u32 calibThreshold; + u32 calibGreen1; + u32 calibGreen2; + u32 calibControl; + u32 calibRed; + u32 calibBlue; + u32 chipEnable; /*!< Image core Registers written by image flow processor */ +} mt9v111_coreReg; + +/*! + * Mt9v111 IFP Register structure. + */ +typedef struct { + u32 addrSpaceSel; /*!< select address bank for Core Register 0x1 */ + u32 baseMaxtrixSign; /*!< sign of coefficient for base color correction matrix */ + u32 baseMaxtrixScale15; /*!< scaling of color correction coefficient K1-5 */ + u32 baseMaxtrixScale69; /*!< scaling of color correction coefficient K6-9 */ + u32 apertureGain; /*!< sharpening */ + u32 modeControl; /*!< bit 7 CCIR656 sync codes are embedded in the image */ + u32 softReset; /*!< Image processing mode: 1 reset mode, 0 operational mode */ + u32 formatControl; /*!< bit12 1 for RGB565, 0 for YcrCb */ + u32 baseMatrixCfk1; /*!< K1 Color correction coefficient */ + u32 baseMatrixCfk2; /*!< K2 Color correction coefficient */ + u32 baseMatrixCfk3; /*!< K3 Color correction coefficient */ + u32 baseMatrixCfk4; /*!< K4 Color correction coefficient */ + u32 baseMatrixCfk5; /*!< K5 Color correction coefficient */ + u32 baseMatrixCfk6; /*!< K6 Color correction coefficient */ + u32 baseMatrixCfk7; /*!< K7 Color correction coefficient */ + u32 baseMatrixCfk8; /*!< K8 Color correction coefficient */ + u32 baseMatrixCfk9; /*!< K9 Color correction coefficient */ + u32 awbPosition; /*!< Current position of AWB color correction matrix */ + u32 awbRedGain; /*!< Current value of AWB red channel gain */ + u32 awbBlueGain; /*!< Current value of AWB blue channel gain */ + u32 deltaMatrixCFSign; /*!< Sign of coefficients of delta color correction matrix register */ + u32 deltaMatrixCFD1; /*!< D1 Delta coefficient */ + u32 deltaMatrixCFD2; /*!< D2 Delta coefficient */ + u32 deltaMatrixCFD3; /*!< D3 Delta coefficient */ + u32 deltaMatrixCFD4; /*!< D4 Delta coefficient */ + u32 deltaMatrixCFD5; /*!< D5 Delta coefficient */ + u32 deltaMatrixCFD6; /*!< D6 Delta coefficient */ + u32 deltaMatrixCFD7; /*!< D7 Delta coefficient */ + u32 deltaMatrixCFD8; /*!< D8 Delta coefficient */ + u32 deltaMatrixCFD9; /*!< D9 Delta coefficient */ + u32 lumLimitWB; /*!< Luminance range of pixels considered in WB statistics */ + u32 RBGManualWB; /*!< Red and Blue color channel gains for manual white balance */ + u32 awbRedLimit; /*!< Limits on Red channel gain adjustment through AWB */ + u32 awbBlueLimit; /*!< Limits on Blue channel gain adjustment through AWB */ + u32 matrixAdjLimit; /*!< Limits on color correction matrix adjustment through AWB */ + u32 awbSpeed; /*!< AWB speed and color saturation control */ + u32 HBoundAE; /*!< Horizontal boundaries of AWB measurement window */ + u32 VBoundAE; /*!< Vertical boundaries of AWB measurement window */ + u32 HBoundAECenWin; /*!< Horizontal boundaries of AE measurement window for backlight compensation */ + u32 VBoundAECenWin; /*!< Vertical boundaries of AE measurement window for backlight compensation */ + u32 boundAwbWin; /*!< Boundaries of AWB measurement window */ + u32 AEPrecisionTarget; /*!< Auto exposure target and precision control */ + u32 AESpeed; /*!< AE speed and sensitivity control register */ + u32 redAWBMeasure; /*!< Measure of the red channel value used by AWB */ + u32 lumaAWBMeasure; /*!< Measure of the luminance channel value used by AWB */ + u32 blueAWBMeasure; /*!< Measure of the blue channel value used by AWB */ + u32 limitSharpSatuCtrl; /*!< Automatic control of sharpness and color saturation */ + u32 lumaOffset; /*!< Luminance offset control (brightness control) */ + u32 clipLimitOutputLumi; /*!< Clipping limits for output luminance */ + u32 gainLimitAE; /*!< Imager gain limits for AE adjustment */ + u32 shutterWidthLimitAE; /*!< Shutter width (exposure time) limits for AE adjustment */ + u32 upperShutterDelayLi; /*!< Upper Shutter Delay Limit */ + u32 outputFormatCtrl2; /*!< Output Format Control 2 + 00 = 16-bit RGB565. + 01 = 15-bit RGB555. + 10 = 12-bit RGB444x. + 11 = 12-bit RGBx444. */ + u32 ipfBlackLevelSub; /*!< IFP black level subtraction */ + u32 ipfBlackLevelAdd; /*!< IFP black level addition */ + u32 adcLimitAEAdj; /*!< ADC limits for AE adjustment */ + u32 agimnThreCamAdj; /*!< Gain threshold for CCM adjustment */ + u32 linearAE; + u32 thresholdEdgeDefect; /*!< Edge threshold for interpolation and defect correction */ + u32 lumaSumMeasure; /*!< Luma measured by AE engine */ + u32 timeAdvSumLuma; /*!< Time-averaged luminance value tracked by auto exposure */ + u32 motion; /*!< 1 when motion is detected */ + u32 gammaKneeY12; /*!< Gamma knee points Y1 and Y2 */ + u32 gammaKneeY34; /*!< Gamma knee points Y3 and Y4 */ + u32 gammaKneeY56; /*!< Gamma knee points Y5 and Y6 */ + u32 gammaKneeY78; /*!< Gamma knee points Y7 and Y8 */ + u32 gammaKneeY90; /*!< Gamma knee points Y9 and Y10 */ + u32 gammaKneeY0; /*!< Gamma knee point Y0 */ + u32 shutter_width_60; + u32 flickerCtrl; + u32 search_flicker_60; + u32 ratioImageGainBase; + u32 ratioImageGainDelta; + u32 signValueReg5F; + u32 aeGain; + u32 maxGainAE; + u32 lensCorrectCtrl; + u32 shadingParameter1; /*!< Shade Parameters */ + u32 shadingParameter2; + u32 shadingParameter3; + u32 shadingParameter4; + u32 shadingParameter5; + u32 shadingParameter6; + u32 shadingParameter7; + u32 shadingParameter8; + u32 shadingParameter9; + u32 shadingParameter10; + u32 shadingParameter11; + u32 shadingParameter12; + u32 shadingParameter13; + u32 shadingParameter14; + u32 shadingParameter15; + u32 shadingParameter16; + u32 shadingParameter17; + u32 shadingParameter18; + u32 shadingParameter19; + u32 shadingParameter20; + u32 shadingParameter21; + u32 flashCtrl; /*!< Flash control */ + u32 lineCounter; /*!< Line counter */ + u32 frameCounter; /*!< Frame counter */ + u32 HPan; /*!< Horizontal pan in decimation */ + u32 HZoom; /*!< Horizontal zoom in decimation */ + u32 HSize; /*!< Horizontal output size iIn decimation */ + u32 VPan; /*!< Vertical pan in decimation */ + u32 VZoom; /*!< Vertical zoom in decimation */ + u32 VSize; /*!< Vertical output size in decimation */ +} mt9v111_IFPReg; + +/*! + * mt9v111 Config structure + */ +typedef struct { + mt9v111_coreReg *coreReg; /*!< Sensor Core Register Bank */ + mt9v111_IFPReg *ifpReg; /*!< IFP Register Bank */ +} mt9v111_conf; + +typedef struct { + u8 index; + u16 width; + u16 height; +} mt9v111_image_format; + +typedef struct { + u16 ae; + u16 awb; + u16 flicker; + u16 reserved; +} mt9v111_ctrl_params; + +#endif // MT9V111_H_ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_csi.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_csi.c --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_csi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_csi.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,332 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_csi.c + * + * @brief CMOS Sensor interface functions + * + * @ingroup CSI + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mx27_csi.h" + +static csi_config_t g_csi_cfg; /* csi hardware configuration */ +static bool gcsi_mclk_on = false; +static csi_irq_callback_t g_callback = 0; +static void *g_callback_data = 0; +static struct clk csi_mclk; + +static irqreturn_t csi_irq_handler(int irq, void *data) +{ + unsigned long status = __raw_readl(CSI_CSISR); + + __raw_writel(status, CSI_CSISR); + if (g_callback) + g_callback(g_callback_data, status); + + pr_debug("CSI status = 0x%08lX\n", status); + + return IRQ_HANDLED; +} + +static void csihw_set_config(csi_config_t * cfg) +{ + unsigned val = 0; + + /* control reg 1 */ + val |= cfg->swap16_en ? BIT_SWAP16_EN : 0; + val |= cfg->ext_vsync ? BIT_EXT_VSYNC : 0; + val |= cfg->eof_int_en ? BIT_EOF_INT_EN : 0; + val |= cfg->prp_if_en ? BIT_PRP_IF_EN : 0; + val |= cfg->ccir_mode ? BIT_CCIR_MODE : 0; + val |= cfg->cof_int_en ? BIT_COF_INT_EN : 0; + val |= cfg->sf_or_inten ? BIT_SF_OR_INTEN : 0; + val |= cfg->rf_or_inten ? BIT_RF_OR_INTEN : 0; + val |= cfg->statff_level << SHIFT_STATFF_LEVEL; + val |= cfg->staff_inten ? BIT_STATFF_INTEN : 0; + val |= cfg->rxff_level << SHIFT_RXFF_LEVEL; + val |= cfg->rxff_inten ? BIT_RXFF_INTEN : 0; + val |= cfg->sof_pol ? BIT_SOF_POL : 0; + val |= cfg->sof_inten ? BIT_SOF_INTEN : 0; + val |= cfg->mclkdiv << SHIFT_MCLKDIV; + val |= cfg->hsync_pol ? BIT_HSYNC_POL : 0; + val |= cfg->ccir_en ? BIT_CCIR_EN : 0; + val |= cfg->mclken ? BIT_MCLKEN : 0; + val |= cfg->fcc ? BIT_FCC : 0; + val |= cfg->pack_dir ? BIT_PACK_DIR : 0; + val |= cfg->gclk_mode ? BIT_GCLK_MODE : 0; + val |= cfg->inv_data ? BIT_INV_DATA : 0; + val |= cfg->inv_pclk ? BIT_INV_PCLK : 0; + val |= cfg->redge ? BIT_REDGE : 0; + + __raw_writel(val, CSI_CSICR1); + + /* control reg 3 */ + val = 0x0; + val |= cfg->csi_sup ? BIT_CSI_SUP : 0; + val |= cfg->zero_pack_en ? BIT_ZERO_PACK_EN : 0; + val |= cfg->ecc_int_en ? BIT_ECC_INT_EN : 0; + val |= cfg->ecc_auto_en ? BIT_ECC_AUTO_EN : 0; + + __raw_writel(val, CSI_CSICR3); + + /* rxfifo counter */ + __raw_writel(cfg->rxcnt, CSI_CSIRXCNT); + + /* update global config */ + memcpy(&g_csi_cfg, cfg, sizeof(csi_config_t)); +} + +static void csihw_reset_frame_count(void) +{ + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3); +} + +static void csihw_reset(void) +{ + csihw_reset_frame_count(); + __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1); + __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2); + __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3); +} + +/*! + * csi_init_interface + * Sets initial values for the CSI registers. + * The width and height of the sensor and the actual frame size will be + * set to the same values. + * @param width Sensor width + * @param height Sensor height + * @param pixel_fmt pixel format + * @param sig csi_signal_cfg_t + * + * @return 0 for success, -EINVAL for error + */ +int32_t csi_init_interface(uint16_t width, uint16_t height, + uint32_t pixel_fmt, csi_signal_cfg_t sig) +{ + csi_config_t cfg; + + /* Set the CSI_SENS_CONF register remaining fields */ + cfg.swap16_en = 1; + cfg.ext_vsync = sig.ext_vsync; + cfg.eof_int_en = 0; + cfg.prp_if_en = 1; + cfg.ccir_mode = 0; + cfg.cof_int_en = 0; + cfg.sf_or_inten = 0; + cfg.rf_or_inten = 0; + cfg.statff_level = 0; + cfg.staff_inten = 0; + cfg.rxff_level = 2; + cfg.rxff_inten = 0; + cfg.sof_pol = 1; + cfg.sof_inten = 0; + cfg.mclkdiv = 0; + cfg.hsync_pol = 1; + cfg.ccir_en = 0; + cfg.mclken = gcsi_mclk_on ? 1 : 0; + cfg.fcc = 1; + cfg.pack_dir = 0; + cfg.gclk_mode = 1; + cfg.inv_data = sig.data_pol; + cfg.inv_pclk = sig.pixclk_pol; + cfg.redge = 1; + cfg.csicnt1_rsv = 0; + + /* control reg 3 */ + cfg.frmcnt = 0; + cfg.frame_reset = 0; + cfg.csi_sup = 0; + cfg.zero_pack_en = 0; + cfg.ecc_int_en = 0; + cfg.ecc_auto_en = 0; + + csihw_set_config(&cfg); + + return 0; +} + +/*! + * csi_enable_prpif + * Enable or disable CSI-PrP interface + * @param enable Non-zero to enable, zero to disable + */ +void csi_enable_prpif(uint32_t enable) +{ + if (enable) { + g_csi_cfg.prp_if_en = 1; + g_csi_cfg.sof_inten = 0; + g_csi_cfg.pack_dir = 0; + } else { + g_csi_cfg.prp_if_en = 0; + g_csi_cfg.sof_inten = 1; + g_csi_cfg.pack_dir = 1; + } + + csihw_set_config(&g_csi_cfg); +} + +/*! + * csi_enable_mclk + * + * @param src enum define which source to control the clk + * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C + * @param flag true to enable mclk, false to disable mclk + * @param wait true to wait 100ms make clock stable, false not wait + * + * @return 0 for success + */ +int32_t csi_enable_mclk(int src, bool flag, bool wait) +{ + if (flag == true) { + clk_enable(&csi_mclk); + if (wait == true) + msleep(10); + pr_debug("Enable csi clock from source %d\n", src); + gcsi_mclk_on = true; + } else { + clk_disable(&csi_mclk); + pr_debug("Disable csi clock from source %d\n", src); + gcsi_mclk_on = false; + } + + return 0; +} + +/*! + * csi_read_mclk_flag + * + * @return gcsi_mclk_source + */ +int csi_read_mclk_flag(void) +{ + return 0; +} + +void csi_set_callback(csi_irq_callback_t callback, void *data) +{ + g_callback = callback; + g_callback_data = data; +} + +static void _mclk_recalc(struct clk *clk) +{ + u32 div; + + div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV; + div = (div + 1) * 2; + + clk->rate = clk->parent->rate / div; +} + +static unsigned long _mclk_round_rate(struct clk *clk, unsigned long rate) +{ + /* Keep CSI divider and change parent clock */ + if (clk->parent->round_rate) { + return clk->parent->round_rate(clk->parent, rate * 2); + } + return 0; +} + +static int _mclk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + + /* Keep CSI divider and change parent clock */ + if (clk->parent->set_rate) { + ret = clk->parent->set_rate(clk->parent, rate * 2); + if (ret == 0) { + clk->rate = clk->parent->rate / 2; + } + } + + return ret; +} + +static int _mclk_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1); + return 0; +} + +static void _mclk_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1); +} + +static struct clk csi_mclk = { + .name = "csi_clk", + .recalc = _mclk_recalc, + .round_rate = _mclk_round_rate, + .set_rate = _mclk_set_rate, + .enable = _mclk_enable, + .disable = _mclk_disable, +}; + +int32_t __init csi_init_module(void) +{ + int ret = 0; + struct clk *per_clk; + + per_clk = clk_get(NULL, "csi_perclk"); + if (IS_ERR(per_clk)) + return PTR_ERR(per_clk); + clk_put(per_clk); + csi_mclk.parent = per_clk; + clk_register(&csi_mclk); + clk_enable(per_clk); + csi_mclk.recalc(&csi_mclk); + + csihw_reset(); + + /* interrupt enable */ + ret = request_irq(INT_CSI, csi_irq_handler, 0, "csi", 0); + if (ret) + pr_debug("CSI error: irq request fail\n"); + + return ret; +} + +void __exit csi_cleanup_module(void) +{ + /* free irq */ + free_irq(INT_CSI, 0); + + clk_disable(&csi_mclk); +} + +module_init(csi_init_module); +module_exit(csi_cleanup_module); + +EXPORT_SYMBOL(csi_init_interface); +EXPORT_SYMBOL(csi_enable_mclk); +EXPORT_SYMBOL(csi_read_mclk_flag); +EXPORT_SYMBOL(csi_set_callback); +EXPORT_SYMBOL(csi_enable_prpif); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MX27 CSI driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_csi.h linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_csi.h --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_csi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_csi.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,165 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_csi.h + * + * @brief CMOS Sensor interface functions + * + * @ingroup CSI + */ + +#ifndef MX27_CSI_H +#define MX27_CSI_H + +/* reset values */ +#define CSICR1_RESET_VAL 0x40000800 +#define CSICR2_RESET_VAL 0x0 +#define CSICR3_RESET_VAL 0x0 + +/* csi control reg 1 */ +#define BIT_SWAP16_EN (0x1 << 31) +#define BIT_EXT_VSYNC (0x1 << 30) +#define BIT_EOF_INT_EN (0x1 << 29) +#define BIT_PRP_IF_EN (0x1 << 28) +#define BIT_CCIR_MODE (0x1 << 27) +#define BIT_COF_INT_EN (0x1 << 26) +#define BIT_SF_OR_INTEN (0x1 << 25) +#define BIT_RF_OR_INTEN (0x1 << 24) +#define BIT_STATFF_LEVEL (0x3 << 22) +#define BIT_STATFF_INTEN (0x1 << 21) +#define BIT_RXFF_LEVEL (0x3 << 19) +#define BIT_RXFF_INTEN (0x1 << 18) +#define BIT_SOF_POL (0x1 << 17) +#define BIT_SOF_INTEN (0x1 << 16) +#define BIT_MCLKDIV (0xF << 12) +#define BIT_HSYNC_POL (0x1 << 11) +#define BIT_CCIR_EN (0x1 << 10) +#define BIT_MCLKEN (0x1 << 9) +#define BIT_FCC (0x1 << 8) +#define BIT_PACK_DIR (0x1 << 7) +#define BIT_CLR_STATFIFO (0x1 << 6) +#define BIT_CLR_RXFIFO (0x1 << 5) +#define BIT_GCLK_MODE (0x1 << 4) +#define BIT_INV_DATA (0x1 << 3) +#define BIT_INV_PCLK (0x1 << 2) +#define BIT_REDGE (0x1 << 1) + +#define SHIFT_STATFF_LEVEL 22 +#define SHIFT_RXFF_LEVEL 19 +#define SHIFT_MCLKDIV 12 + +/* control reg 3 */ +#define BIT_FRMCNT (0xFFFF << 16) +#define BIT_FRMCNT_RST (0x1 << 15) +#define BIT_CSI_SUP (0x1 << 3) +#define BIT_ZERO_PACK_EN (0x1 << 2) +#define BIT_ECC_INT_EN (0x1 << 1) +#define BIT_ECC_AUTO_EN (0x1) + +#define SHIFT_FRMCNT 16 + +/* csi status reg */ +#define BIT_SFF_OR_INT (0x1 << 25) +#define BIT_RFF_OR_INT (0x1 << 24) +#define BIT_STATFF_INT (0x1 << 21) +#define BIT_RXFF_INT (0x1 << 18) +#define BIT_EOF_INT (0x1 << 17) +#define BIT_SOF_INT (0x1 << 16) +#define BIT_F2_INT (0x1 << 15) +#define BIT_F1_INT (0x1 << 14) +#define BIT_COF_INT (0x1 << 13) +#define BIT_ECC_INT (0x1 << 1) +#define BIT_DRDY (0x1 << 0) + +#define CSI_MCLK_VF 1 +#define CSI_MCLK_ENC 2 +#define CSI_MCLK_RAW 4 +#define CSI_MCLK_I2C 8 + +#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR)) +#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4)) +#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x8)) +#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC)) +#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10)) +#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14)) +#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x1C)) + +#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10) + +static __inline void csi_clear_status(unsigned long status) +{ + __raw_writel(status, CSI_CSISR); +} + +typedef struct { + unsigned data_width:3; + unsigned clk_mode:2; + unsigned ext_vsync:1; + unsigned Vsync_pol:1; + unsigned Hsync_pol:1; + unsigned pixclk_pol:1; + unsigned data_pol:1; + unsigned sens_clksrc:1; +} csi_signal_cfg_t; + +typedef struct { + /* control reg 1 */ + unsigned int swap16_en:1; + unsigned int ext_vsync:1; + unsigned int eof_int_en:1; + unsigned int prp_if_en:1; + unsigned int ccir_mode:1; + unsigned int cof_int_en:1; + unsigned int sf_or_inten:1; + unsigned int rf_or_inten:1; + unsigned int statff_level:2; + unsigned int staff_inten:1; + unsigned int rxff_level:2; + unsigned int rxff_inten:1; + unsigned int sof_pol:1; + unsigned int sof_inten:1; + unsigned int mclkdiv:4; + unsigned int hsync_pol:1; + unsigned int ccir_en:1; + unsigned int mclken:1; + unsigned int fcc:1; + unsigned int pack_dir:1; + unsigned int gclk_mode:1; + unsigned int inv_data:1; + unsigned int inv_pclk:1; + unsigned int redge:1; + unsigned int csicnt1_rsv:1; + + /* control reg 3 */ + unsigned int frmcnt:16; + unsigned int frame_reset:1; + unsigned int csi_sup:1; + unsigned int zero_pack_en:1; + unsigned int ecc_int_en:1; + unsigned int ecc_auto_en:1; + + /* fifo counter */ + unsigned int rxcnt; +} csi_config_t; + +typedef void (*csi_irq_callback_t) (void *data, unsigned long status); + +int32_t csi_enable_mclk(int src, bool flag, bool wait); +int32_t csi_init_interface(uint16_t width, uint16_t height, + uint32_t pixel_fmt, csi_signal_cfg_t sig); +int csi_read_mclk_flag(void); +void csi_set_callback(csi_irq_callback_t callback, void *data); +void csi_enable_prpif(uint32_t enable); + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_prp.h linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prp.h --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_prp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prp.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,308 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_prp.h + * + * @brief Header file for MX27 V4L2 capture driver + * + * @ingroup MXC_V4L2_CAPTURE + */ +#ifndef __MX27_PRP_H__ +#define __MX27_PRP_H__ + +#define PRP_REG(ofs) (IO_ADDRESS(EMMA_BASE_ADDR) + ofs) + +/* Register definitions of PrP */ +#define PRP_CNTL PRP_REG(0x00) +#define PRP_INTRCNTL PRP_REG(0x04) +#define PRP_INTRSTATUS PRP_REG(0x08) +#define PRP_SOURCE_Y_PTR PRP_REG(0x0C) +#define PRP_SOURCE_CB_PTR PRP_REG(0x10) +#define PRP_SOURCE_CR_PTR PRP_REG(0x14) +#define PRP_DEST_RGB1_PTR PRP_REG(0x18) +#define PRP_DEST_RGB2_PTR PRP_REG(0x1C) +#define PRP_DEST_Y_PTR PRP_REG(0x20) +#define PRP_DEST_CB_PTR PRP_REG(0x24) +#define PRP_DEST_CR_PTR PRP_REG(0x28) +#define PRP_SOURCE_FRAME_SIZE PRP_REG(0x2C) +#define PRP_CH1_LINE_STRIDE PRP_REG(0x30) +#define PRP_SRC_PIXEL_FORMAT_CNTL PRP_REG(0x34) +#define PRP_CH1_PIXEL_FORMAT_CNTL PRP_REG(0x38) +#define PRP_CH1_OUT_IMAGE_SIZE PRP_REG(0x3C) +#define PRP_CH2_OUT_IMAGE_SIZE PRP_REG(0x40) +#define PRP_SOURCE_LINE_STRIDE PRP_REG(0x44) +#define PRP_CSC_COEF_012 PRP_REG(0x48) +#define PRP_CSC_COEF_345 PRP_REG(0x4C) +#define PRP_CSC_COEF_678 PRP_REG(0x50) +#define PRP_CH1_RZ_HORI_COEF1 PRP_REG(0x54) +#define PRP_CH1_RZ_HORI_COEF2 PRP_REG(0x58) +#define PRP_CH1_RZ_HORI_VALID PRP_REG(0x5C) +#define PRP_CH1_RZ_VERT_COEF1 PRP_REG(0x60) +#define PRP_CH1_RZ_VERT_COEF2 PRP_REG(0x64) +#define PRP_CH1_RZ_VERT_VALID PRP_REG(0x68) +#define PRP_CH2_RZ_HORI_COEF1 PRP_REG(0x6C) +#define PRP_CH2_RZ_HORI_COEF2 PRP_REG(0x70) +#define PRP_CH2_RZ_HORI_VALID PRP_REG(0x74) +#define PRP_CH2_RZ_VERT_COEF1 PRP_REG(0x78) +#define PRP_CH2_RZ_VERT_COEF2 PRP_REG(0x7C) +#define PRP_CH2_RZ_VERT_VALID PRP_REG(0x80) + +#define B_SET(b) (1 << (b)) + +/* Bit definitions for PrP control register */ +#define PRP_CNTL_RSTVAL 0x28 +#define PRP_CNTL_CH1EN B_SET(0) +#define PRP_CNTL_CH2EN B_SET(1) +#define PRP_CNTL_CSI B_SET(2) +#define PRP_CNTL_IN_32 B_SET(3) +#define PRP_CNTL_IN_RGB B_SET(4) +#define PRP_CNTL_IN_YUV420 0 +#define PRP_CNTL_IN_YUV422 PRP_CNTL_IN_32 +#define PRP_CNTL_IN_RGB16 PRP_CNTL_IN_RGB +#define PRP_CNTL_IN_RGB32 (PRP_CNTL_IN_RGB | PRP_CNTL_IN_32) +#define PRP_CNTL_CH1_RGB8 0 +#define PRP_CNTL_CH1_RGB16 B_SET(5) +#define PRP_CNTL_CH1_RGB32 B_SET(6) +#define PRP_CNTL_CH1_YUV422 (B_SET(5) | B_SET(6)) +#define PRP_CNTL_CH2_YUV420 0 +#define PRP_CNTL_CH2_YUV422 B_SET(7) +#define PRP_CNTL_CH2_YUV444 B_SET(8) +#define PRP_CNTL_CH1_LOOP B_SET(9) +#define PRP_CNTL_CH2_LOOP B_SET(10) +#define PRP_CNTL_AUTODROP B_SET(11) +#define PRP_CNTL_RST B_SET(12) +#define PRP_CNTL_CNTREN B_SET(13) +#define PRP_CNTL_WINEN B_SET(14) +#define PRP_CNTL_UNCHAIN B_SET(15) +#define PRP_CNTL_IN_SKIP_NONE 0 +#define PRP_CNTL_IN_SKIP_1_2 B_SET(16) +#define PRP_CNTL_IN_SKIP_1_3 B_SET(17) +#define PRP_CNTL_IN_SKIP_2_3 (B_SET(16) | B_SET(17)) +#define PRP_CNTL_IN_SKIP_1_4 B_SET(18) +#define PRP_CNTL_IN_SKIP_3_4 (B_SET(16) | B_SET(18)) +#define PRP_CNTL_IN_SKIP_2_5 (B_SET(17) | B_SET(18)) +#define PRP_CNTL_IN_SKIP_3_5 (B_SET(16) | B_SET(17) | B_SET(18)) +#define PRP_CNTL_CH1_SKIP_NONE 0 +#define PRP_CNTL_CH1_SKIP_1_2 B_SET(19) +#define PRP_CNTL_CH1_SKIP_1_3 B_SET(20) +#define PRP_CNTL_CH1_SKIP_2_3 (B_SET(19) | B_SET(20)) +#define PRP_CNTL_CH1_SKIP_1_4 B_SET(21) +#define PRP_CNTL_CH1_SKIP_3_4 (B_SET(19) | B_SET(21)) +#define PRP_CNTL_CH1_SKIP_2_5 (B_SET(20) | B_SET(21)) +#define PRP_CNTL_CH1_SKIP_3_5 (B_SET(19) | B_SET(20) | B_SET(21)) +#define PRP_CNTL_CH2_SKIP_NONE 0 +#define PRP_CNTL_CH2_SKIP_1_2 B_SET(22) +#define PRP_CNTL_CH2_SKIP_1_3 B_SET(23) +#define PRP_CNTL_CH2_SKIP_2_3 (B_SET(22) | B_SET(23)) +#define PRP_CNTL_CH2_SKIP_1_4 B_SET(24) +#define PRP_CNTL_CH2_SKIP_3_4 (B_SET(22) | B_SET(24)) +#define PRP_CNTL_CH2_SKIP_2_5 (B_SET(23) | B_SET(24)) +#define PRP_CNTL_CH2_SKIP_3_5 (B_SET(22) | B_SET(23) | B_SET(24)) +#define PRP_CNTL_FIFO_I128 0 +#define PRP_CNTL_FIFO_I96 B_SET(25) +#define PRP_CNTL_FIFO_I64 B_SET(26) +#define PRP_CNTL_FIFO_I32 (B_SET(25) | B_SET(26)) +#define PRP_CNTL_FIFO_O64 0 +#define PRP_CNTL_FIFO_O48 B_SET(27) +#define PRP_CNTL_FIFO_O32 B_SET(28) +#define PRP_CNTL_FIFO_O16 (B_SET(27) | B_SET(28)) +#define PRP_CNTL_CH2B1 B_SET(29) +#define PRP_CNTL_CH2B2 B_SET(30) +#define PRP_CNTL_CH2_FLOWEN B_SET(31) + +/* Bit definitions for PrP interrupt control register */ +#define PRP_INTRCNTL_RDERR B_SET(0) +#define PRP_INTRCNTL_CH1WERR B_SET(1) +#define PRP_INTRCNTL_CH2WERR B_SET(2) +#define PRP_INTRCNTL_CH1FC B_SET(3) +#define PRP_INTRCNTL_CH2FC B_SET(5) +#define PRP_INTRCNTL_LBOVF B_SET(7) +#define PRP_INTRCNTL_CH2OVF B_SET(8) + +/* Bit definitions for PrP interrupt status register */ +#define PRP_INTRSTAT_RDERR B_SET(0) +#define PRP_INTRSTAT_CH1WERR B_SET(1) +#define PRP_INTRSTAT_CH2WERR B_SET(2) +#define PRP_INTRSTAT_CH2BUF2 B_SET(3) +#define PRP_INTRSTAT_CH2BUF1 B_SET(4) +#define PRP_INTRSTAT_CH1BUF2 B_SET(5) +#define PRP_INTRSTAT_CH1BUF1 B_SET(6) +#define PRP_INTRSTAT_LBOVF B_SET(7) +#define PRP_INTRSTAT_CH2OVF B_SET(8) + +#define PRP_CHANNEL_1 0x1 +#define PRP_CHANNEL_2 0x2 + +/* PRP-CSI config */ +#define PRP_CSI_EN 0x80 +#define PRP_CSI_LOOP (0x40 | PRP_CSI_EN) +#define PRP_CSI_IRQ_FRM (0x08 | PRP_CSI_LOOP) +#define PRP_CSI_IRQ_CH1ERR (0x10 | PRP_CSI_LOOP) +#define PRP_CSI_IRQ_CH2ERR (0x20 | PRP_CSI_LOOP) +#define PRP_CSI_IRQ_ALL (0x38 | PRP_CSI_LOOP) +#define PRP_CSI_SKIP_NONE 0 +#define PRP_CSI_SKIP_1OF2 1 +#define PRP_CSI_SKIP_1OF3 2 +#define PRP_CSI_SKIP_2OF3 3 +#define PRP_CSI_SKIP_1OF4 4 +#define PRP_CSI_SKIP_3OF4 5 +#define PRP_CSI_SKIP_2OF5 6 +#define PRP_CSI_SKIP_4OF5 7 + +#define PRP_PIXIN_RGB565 0x2CA00565 +#define PRP_PIXIN_RGB888 0x41000888 +#define PRP_PIXIN_YUV420 0 +#define PRP_PIXIN_YUYV 0x22000888 +#define PRP_PIXIN_YVYU 0x20100888 +#define PRP_PIXIN_UYVY 0x03080888 +#define PRP_PIXIN_VYUY 0x01180888 +#define PRP_PIXIN_YUV422 0x62080888 + +#define PRP_PIX1_RGB332 0x14400322 +#define PRP_PIX1_RGB565 0x2CA00565 +#define PRP_PIX1_RGB888 0x41000888 +#define PRP_PIX1_YUYV 0x62000888 +#define PRP_PIX1_YVYU 0x60100888 +#define PRP_PIX1_UYVY 0x43080888 +#define PRP_PIX1_VYUY 0x41180888 +#define PRP_PIX1_UNUSED 0 + +#define PRP_PIX2_YUV420 0 +#define PRP_PIX2_YUV422 1 +#define PRP_PIX2_YUV444 4 +#define PRP_PIX2_UNUSED 8 + +#define PRP_ALGO_WIDTH_ANY 0 +#define PRP_ALGO_HEIGHT_ANY 0 +#define PRP_ALGO_WIDTH_BIL 1 +#define PRP_ALGO_WIDTH_AVG 2 +#define PRP_ALGO_HEIGHT_BIL 4 +#define PRP_ALGO_HEIGHT_AVG 8 +#define PRP_ALGO_BYPASS 0x10 + +typedef struct _emma_prp_ratio { + unsigned short num; + unsigned short den; +} emma_prp_ratio; + +/* + * The following definitions are for resizing. Definition values must not + * be changed otherwise decision logic will be wrong. + */ +#define BC_COEF 3 +#define MAX_TBL 20 +#define SZ_COEF (1 << BC_COEF) + +#define ALGO_AUTO 0 +#define ALGO_BIL 1 +#define ALGO_AVG 2 + +typedef struct { + char tbl[20]; /* table entries */ + char len; /* table length used */ + char algo; /* ALGO_xxx */ + char ratio[20]; /* ratios used */ +} scale_t; + +/* + * structure for prp scaling. + * algorithm - bilinear or averaging for each axis + * PRP_ALGO_WIDTH_x | PRP_ALGO_HEIGHT_x | PRP_ALGO_BYPASS + * PRP_ALGO_BYPASS - Ch1 will not use Ch2 scaling with this flag + */ +typedef struct _emma_prp_scale { + unsigned char algo; + emma_prp_ratio width; + emma_prp_ratio height; +} emma_prp_scale; + +typedef struct emma_prp_cfg { + unsigned int in_pix; /* PRP_PIXIN_xxx */ + unsigned short in_width; /* image width, 32 - 2044 */ + unsigned short in_height; /* image height, 32 - 2044 */ + unsigned char in_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */ + unsigned short in_line_stride; /* in_line_stride and in_line_skip */ + unsigned short in_line_skip; /* allow cropping from CSI */ + unsigned int in_ptr; /* bus address */ + /* + * in_csc[9] = 1 -> Y-16 + * if in_csc[1..9] == 0 + * in_csc[0] represents YUV range 0-3 = A0,A1,B0,B1; + * else + * in_csc[0..9] represents either format + */ + unsigned short in_csc[10]; + + unsigned char ch2_pix; /* PRP_PIX2_xxx */ + emma_prp_scale ch2_scale; /* resizing paramters */ + unsigned short ch2_width; /* 4-2044, 0 = scaled */ + unsigned short ch2_height; /* 4-2044, 0 = scaled */ + unsigned int ch2_ptr; /* bus addr */ + unsigned int ch2_ptr2; /* bus addr for 2nd buf (loop mode) */ + unsigned char ch2_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */ + + unsigned int ch1_pix; /* PRP_PIX1_xxx */ + emma_prp_scale ch1_scale; /* resizing parameters */ + unsigned short ch1_width; /* 4-2044, 0 = scaled */ + unsigned short ch1_height; /* 4-2044, 0 = scaled */ + unsigned short ch1_stride; /* 4-4088, 0 = ch1_width */ + unsigned int ch1_ptr; /* bus addr */ + unsigned int ch1_ptr2; /* bus addr for 2nd buf (loop mode) */ + unsigned char ch1_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */ + + /* + * channel resizing coefficients + * scale[0] for channel 1 width + * scale[1] for channel 1 height + * scale[2] for channel 2 width + * scale[3] for channel 2 height + */ + scale_t scale[4]; +} emma_prp_cfg; + +int prphw_reset(void); +int prphw_enable(int channel); +int prphw_disable(int channel); +int prphw_inptr(emma_prp_cfg *); +int prphw_ch1ptr(emma_prp_cfg *); +int prphw_ch1ptr2(emma_prp_cfg *); +int prphw_ch2ptr(emma_prp_cfg *); +int prphw_ch2ptr2(emma_prp_cfg *); +int prphw_cfg(emma_prp_cfg *); +int prphw_isr(void); +void prphw_init(void); +void prphw_exit(void); + +/* + * scale out coefficient table + * din in scale numerator + * dout in scale denominator + * inv in pre-scale dimension + * vout in/out post-scale output dimension + * pout out post-scale internal dimension [opt] + * retry in retry times (round the output length) when need + */ +int prp_scale(scale_t * pscale, int din, int dout, int inv, + unsigned short *vout, unsigned short *pout, int ch); + +int prp_init(void *dev_id); +void prp_exit(void *dev_id); +int prp_enc_select(void *data); +int prp_enc_deselect(void *data); +int prp_vf_select(void *data); +int prp_vf_deselect(void *data); +int prp_still_select(void *data); +int prp_still_deselect(void *data); + +#endif /* __MX27_PRP_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_prphw.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prphw.c --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_prphw.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prphw.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1230 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_prphw.c + * + * @brief MX27 Video For Linux 2 capture driver + * + * @ingroup MXC_V4L2_CAPTURE + */ +#include +#include +#include +#include +#include +#include + +#include "mx27_prp.h" + +#define PRP_MIN_IN_WIDTH 32 +#define PRP_MAX_IN_WIDTH 2044 +#define PRP_MIN_IN_HEIGHT 32 +#define PRP_MAX_IN_HEIGHT 2044 + +typedef struct _coeff_t { + unsigned long coeff[2]; + unsigned long cntl; +} coeff_t[2][2]; + +static coeff_t *PRP_RSZ_COEFF = (coeff_t *) PRP_CH1_RZ_HORI_COEF1; + +static unsigned char scale_get(scale_t * t, + unsigned char *i, unsigned char *out); +static int gcd(int x, int y); +static int ratio(int x, int y, int *den); +static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt); +static int prp_scale_ave(scale_t * t, unsigned char base); +static int ave_scale(scale_t * t, int inv, int outv); +static int scale(scale_t * t, int inv, int outv); + +/*! + * @param t table + * @param i table index + * @param out bilinear # input pixels to advance + * average whether result is ready for output + * @return coefficient +*/ +static unsigned char scale_get(scale_t * t, unsigned char *i, + unsigned char *out) +{ + unsigned char c; + + c = t->tbl[*i]; + (*i)++; + *i %= t->len; + + if (out) { + if (t->algo == ALGO_BIL) { + for ((*out) = 1; + (*i) && ((*i) < t->len) && !t->tbl[(*i)]; (*i)++) { + (*out)++; + } + if ((*i) == t->len) + (*i) = 0; + } else + *out = c >> BC_COEF; + } + + c &= SZ_COEF - 1; + + if (c == SZ_COEF - 1) + c = SZ_COEF; + + return c; +} + +/*! + * @brief Get maximum common divisor. + * @param x First input value + * @param y Second input value + * @return Maximum common divisor of x and y + */ +static int gcd(int x, int y) +{ + int k; + + if (x < y) { + k = x; + x = y; + y = k; + } + + while ((k = x % y)) { + x = y; + y = k; + } + + return y; +} + +/*! + * @brief Get ratio. + * @param x First input value + * @param y Second input value + * @param den Denominator of the ratio (corresponding to y) + * @return Numerator of the ratio (corresponding to x) + */ +static int ratio(int x, int y, int *den) +{ + int g; + + if (!x || !y) + return 0; + + g = gcd(x, y); + *den = y / g; + + return x / g; +} + +/*! + * @brief Build PrP coefficient entry based on bilinear algorithm + * + * @param t The pointer to scale_t structure + * @param coeff The weighting coefficient + * @param base The base of the coefficient + * @param nxt Number of pixels to be read + * + * @return The length of current coefficient table on success + * -1 on failure + */ +static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt) +{ + int i; + + if (t->len >= sizeof(t->tbl)) + return -1; + + coeff = ((coeff << BC_COEF) + (base >> 1)) / base; + if (coeff >= SZ_COEF - 1) + coeff--; + + coeff |= SZ_COEF; + t->tbl[(int)t->len++] = (unsigned char)coeff; + + for (i = 1; i < nxt; i++) { + if (t->len >= MAX_TBL) + return -1; + + t->tbl[(int)t->len++] = 0; + } + + return t->len; +} + +#define _bary(name) static const unsigned char name[] + +_bary(c1) = { +7}; + +_bary(c2) = { +4, 4}; + +_bary(c3) = { +2, 4, 2}; + +_bary(c4) = { +2, 2, 2, 2}; + +_bary(c5) = { +1, 2, 2, 2, 1}; + +_bary(c6) = { +1, 1, 2, 2, 1, 1}; + +_bary(c7) = { +1, 1, 1, 2, 1, 1, 1}; + +_bary(c8) = { +1, 1, 1, 1, 1, 1, 1, 1}; + +_bary(c9) = { +1, 1, 1, 1, 1, 1, 1, 1, 0}; + +_bary(c10) = { +0, 1, 1, 1, 1, 1, 1, 1, 1, 0}; + +_bary(c11) = { +0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}; + +_bary(c12) = { +0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0}; + +_bary(c13) = { +0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0}; + +_bary(c14) = { +0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; + +_bary(c15) = { +0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; + +_bary(c16) = { +1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; + +_bary(c17) = { +0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; + +_bary(c18) = { +0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}; + +_bary(c19) = { +0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0}; + +_bary(c20) = { +0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0}; + +static const unsigned char *ave_coeff[] = { + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, + c11, c12, c13, c14, c15, c16, c17, c18, c19, c20 +}; + +static const unsigned char coeftab[] = { + 1, 1, + 19, 20, + 18, 19, + 17, 18, + 16, 17, + 15, 16, + 14, 15, + 13, 14, + 12, 13, + 11, 12, + 10, 11, + 9, 10, + 17, 19, + 8, 9, + 15, 17, + 7, 8, + 13, 15, + 6, 7, + 17, 20, + 11, 13, + 16, 19, + 5, 6, + 14, 17, + 9, 11, + 13, 16, + 4, 5, + 15, 19, + 11, 14, + 7, 9, + 10, 13, + 13, 17, + 3, 4, + 14, 19, + 11, 15, + 8, 11, + 13, 18, + 5, 7, + 12, 17, + 7, 10, + 9, 13, + 11, 16, + 13, 19, + 2, 3, + 13, 20, + 11, 17, + 9, 14, + 7, 11, + 12, 19, + 5, 8, + 8, 13, + 11, 18, + 3, 5, + 10, 17, + 7, 12, + 11, 19, + 4, 7, + 9, 16, + 5, 9, + 11, 20, + 6, 11, + 7, 13, + 8, 15, + 9, 17, + 10, 19, + 1, 2, + 9, 19, + 8, 17, + 7, 15, + 6, 13, + 5, 11, + 9, 20, + 4, 9, + 7, 16, + 3, 7, + 8, 19, + 5, 12, + 7, 17, + 2, 5, + 7, 18, + 5, 13, + 3, 8, + 7, 19, + 4, 11, + 5, 14, + 6, 17, + 7, 20, + 1, 3, + 6, 19, + 5, 16, + 4, 13, + 3, 10, + 5, 17, + 2, 7, + 5, 18, + 3, 11, + 4, 15, + 5, 19, + 1, 4, + 4, 17, + 3, 13, + 2, 9, + 3, 14, + 4, 19, + 1, 5, + 3, 16, + 2, 11, + 3, 17, + 1, 6, + 3, 19, + 2, 13, + 3, 20, + 1, 7, + 2, 15, + 1, 8, + 2, 17, + 1, 9, + 2, 19, + 1, 10, + 1, 11, + 1, 12, + 1, 13, + 1, 14, + 1, 15, + 1, 16, + 1, 17, + 1, 18, + 1, 19, + 1, 20 +}; + +/*! + * @brief Build PrP coefficient table based on average algorithm + * + * @param t The pointer to scale_t structure + * @param base The base of the coefficient + * + * @return The length of current coefficient table on success + * -1 on failure + */ +static int prp_scale_ave(scale_t * t, unsigned char base) +{ + if (t->len + base > sizeof(t->tbl)) + return -1; + + memcpy(&t->tbl[(int)t->len], ave_coeff[(int)base - 1], base); + t->len = (unsigned char)(t->len + base); + t->tbl[t->len - 1] |= SZ_COEF; + + return t->len; +} + +/*! + * @brief Build PrP coefficient table based on average algorithm + * + * @param t The pointer to scale_t structure + * @param inv Input resolution + * @param outv Output resolution + * + * @return The length of current coefficient table on success + * -1 on failure + */ +static int ave_scale(scale_t * t, int inv, int outv) +{ + int ratio_count; + + ratio_count = 0; + if (outv != 1) { + unsigned char a[20]; + int v; + + /* split n:m into multiple n[i]:1 */ + for (v = 0; v < outv; v++) + a[v] = (unsigned char)(inv / outv); + + inv %= outv; + if (inv) { + /* find start of next layer */ + v = (outv - inv) >> 1; + inv += v; + for (; v < inv; v++) + a[v]++; + } + + for (v = 0; v < outv; v++) { + if (prp_scale_ave(t, a[v]) < 0) + return -1; + + t->ratio[ratio_count] = a[v]; + ratio_count++; + } + } else if (prp_scale_ave(t, inv) < 0) { + return -1; + } else { + t->ratio[ratio_count++] = (char)inv; + ratio_count++; + } + + return t->len; +} + +/*! + * @brief Build PrP coefficient table + * + * @param t The pointer to scale_t structure + * @param inv input resolution reduced ratio + * @param outv output resolution reduced ratio + * + * @return The length of current coefficient table on success + * -1 on failure + */ +static int scale(scale_t * t, int inv, int outv) +{ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + + t->len = 0; + if (t->algo == ALGO_AUTO) { + /* automatic choice - bilinear for shrinking less than 2:1 */ + t->algo = ((outv != inv) && ((2 * outv) > inv)) ? + ALGO_BIL : ALGO_AVG; + } + + /* 1:1 resize must use averaging, bilinear will hang */ + if ((inv == outv) && (t->algo == ALGO_BIL)) { + pr_debug("Warning: 1:1 resize must use averaging algo\n"); + t->algo = ALGO_AVG; + } + + memset(t->tbl, 0, sizeof(t->tbl)); + if (t->algo == ALGO_BIL) { + t->ratio[0] = (char)inv; + t->ratio[1] = (char)outv; + } else + memset(t->ratio, 0, sizeof(t->ratio)); + + if (inv == outv) { + /* force scaling */ + t->ratio[0] = 1; + if (t->algo == ALGO_BIL) + t->ratio[1] = 1; + + return prp_scale_ave(t, 1); + } + + if (inv < outv) { + pr_debug("Upscaling not supported %d:%d\n", inv, outv); + return -1; + } + + if (t->algo != ALGO_BIL) + return ave_scale(t, inv, outv); + + v = 0; + if (inv >= 2 * outv) { + /* downscale: >=2:1 bilinear approximation */ + coeff = inv - 2 * outv; + v = 0; + nxt = 0; + do { + v += coeff; + nxt = 2; + while (v >= outv) { + v -= outv; + nxt++; + } + + if (prp_scale_bilinear(t, 1, 2, nxt) < 0) + return -1; + } while (v); + } else { + /* downscale: bilinear */ + int in_pos_inc = 2 * outv; + int out_pos = inv; + int out_pos_inc = 2 * inv; + int init_carry = inv - outv; + int carry = init_carry; + + v = outv + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + if (prp_scale_bilinear(t, coeff, in_pos_inc, nxt) < 0) + return -1; + } while (carry != init_carry); + } + return t->len; +} + +/*! + * @brief Get approximate ratio + * + * @param pscale The pointer to scale_t structure which holdes + * coefficient tables + * @param mt Scale ratio numerator + * @param nt Scale ratio denominator + * @param *n denominator of approximate ratio + * @return numerator of approximate ratio + */ + +static int approx_ratio(int mt, int nt, int *n) +{ + int index = sizeof(coeftab) / sizeof(coeftab[0]) / 2; + int left = 0; + int right = index - 1; + int nom, den; + while (index > 0) { + nom = coeftab[(((right + left) >> 1) << 1) + 1]; + den = coeftab[(((right + left) >> 1) << 1)]; + if ((nom * nt - mt * den) < 0) { + left = (right + left) >> 1; + } else { + right = (right + left) >> 1; + } + index = index >> 1; + } + *n = coeftab[left * 2]; + nom = coeftab[left * 2 + 1]; + return nom; +} + +/*! + * @brief Build PrP coefficient table + * + * @param pscale The pointer to scale_t structure which holdes + * coefficient tables + * @param din Scale ratio numerator + * @param dout Scale ratio denominator + * @param inv Input resolution + * @param vout Output resolution + * @param pout Internal output resolution + * @param retry Retry times (round the output length) when need + * + * @return Zero on success, others on failure + */ +int prp_scale(scale_t * pscale, int din, int dout, int inv, + unsigned short *vout, unsigned short *pout, int ch) +{ + int num, new_num; + int den, new_den; + unsigned short outv; + + /* auto-generation of values */ + if (!(dout && din)) { + if (!*vout) + dout = din = 1; + else { + din = inv; + dout = *vout; + } + } + + if (din < dout) { + pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout); + return -1; + } + + num = ratio(din, dout, &den); + if (!num) { + pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout); + return -1; + } + + if (num > MAX_TBL) { + if (num / den <= MAX_TBL) { + new_num = approx_ratio(num, den, &new_den); + num = new_num; + den = new_den; + } else if (ch == PRP_CHANNEL_2) { + pr_debug("Unsupported ch_2 resize ratio %d : %d\n", num, + den); + return -1; + } else if (num / den > MAX_TBL * MAX_TBL) { + pr_debug("Unsupported ch_1 resize ratio %d : %d\n", num, + den); + return -1; + } + } + + if ((num > MAX_TBL * MAX_TBL) || scale(pscale, num, den) < 0) { + pr_debug("Scale err, unsupported ratio %d : %d\n", num, den); + return -1; + } + + if (pscale->algo == ALGO_BIL) { + unsigned char i, j, k; + + outv = + (unsigned short)(inv / pscale->ratio[0] * pscale->ratio[1]); + inv %= pscale->ratio[0]; + for (i = j = 0; inv > 0; j++) { + unsigned char nxt; + + k = scale_get(pscale, &i, &nxt); + if (inv == 1 && k < SZ_COEF) { + /* needs 2 pixels for this output */ + break; + } + inv -= nxt; + } + outv = outv + j; + } else { + unsigned char i, tot; + + for (tot = i = 0; pscale->ratio[i]; i++) + tot = tot + pscale->ratio[i]; + + outv = (unsigned short)(inv / tot) * i; + inv %= tot; + for (i = 0; inv > 0; i++, outv++) + inv -= pscale->ratio[i]; + } + + if (!(*vout) || ((*vout) > outv)) + *vout = outv; + + if (pout) + *pout = outv; + + return 0; +} + +/*! + * @brief Reset PrP block + */ +int prphw_reset(void) +{ + unsigned long val; + unsigned long flag; + int i; + + flag = PRP_CNTL_RST; + val = PRP_CNTL_RSTVAL; + + __raw_writel(flag, PRP_CNTL); + + /* timeout */ + for (i = 0; i < 1000; i++) { + if (!(__raw_readl(PRP_CNTL) & flag)) { + pr_debug("PrP reset over\n"); + break; + } + msleep(1); + } + + /* verify reset value */ + if (__raw_readl(PRP_CNTL) != val) { + pr_info("PrP reset err, val = 0x%08X\n", __raw_readl(PRP_CNTL)); + return -1; + } + + return 0; +} + +/*! + * @brief Enable PrP channel. + * @param channel Channel number to be enabled + * @return Zero on success, others on failure + */ +int prphw_enable(int channel) +{ + unsigned long val; + + val = __raw_readl(PRP_CNTL); + if (channel & PRP_CHANNEL_1) + val |= PRP_CNTL_CH1EN; + if (channel & PRP_CHANNEL_2) + val |= + (PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN | PRP_CNTL_AUTODROP); + + __raw_writel(val, PRP_CNTL); + + return 0; +} + +/*! + * @brief Disable PrP channel. + * @param channel Channel number to be disable + * @return Zero on success, others on failure + */ +int prphw_disable(int channel) +{ + unsigned long val; + + val = __raw_readl(PRP_CNTL); + if (channel & PRP_CHANNEL_1) + val &= ~PRP_CNTL_CH1EN; + if (channel & PRP_CHANNEL_2) + val &= ~(PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN); + + __raw_writel(val, PRP_CNTL); + + return 0; +} + +/*! + * @brief Set PrP input buffer address. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_inptr(emma_prp_cfg * cfg) +{ + if (cfg->in_csi & PRP_CSI_EN) + return -1; + + __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR); + if (cfg->in_pix == PRP_PIXIN_YUV420) { + u32 size; + + size = cfg->in_line_stride * cfg->in_height; + __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR); + __raw_writel(cfg->in_ptr + size + (size >> 2), + PRP_SOURCE_CR_PTR); + } + return 0; +} + +/*! + * @brief Set PrP channel 1 output buffer 1 address. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_ch1ptr(emma_prp_cfg * cfg) +{ + if (cfg->ch1_pix == PRP_PIX1_UNUSED) + return -1; + + __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR); + + /* support double buffer in loop mode only */ + if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) { + if (cfg->ch1_ptr2) + __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR); + else + __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR); + } + + return 0; +} + +/*! + * @brief Set PrP channel 1 output buffer 2 address. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_ch1ptr2(emma_prp_cfg * cfg) +{ + if (cfg->ch1_pix == PRP_PIX1_UNUSED || + (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP) + return -1; + + if (cfg->ch1_ptr2) + __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR); + else + return -1; + + return 0; +} + +/*! + * @brief Set PrP channel 2 output buffer 1 address. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_ch2ptr(emma_prp_cfg * cfg) +{ + u32 size; + + if (cfg->ch2_pix == PRP_PIX2_UNUSED) + return -1; + + __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR); + + if (cfg->ch2_pix == PRP_PIX2_YUV420) { + size = cfg->ch2_width * cfg->ch2_height; + __raw_writel(cfg->ch2_ptr + size, PRP_DEST_CB_PTR); + __raw_writel(cfg->ch2_ptr + size + (size >> 2), + PRP_DEST_CR_PTR); + } + + __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B1, PRP_CNTL); + return 0; +} + +/*! + * @brief Set PrP channel 2 output buffer 2 address. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_ch2ptr2(emma_prp_cfg * cfg) +{ + u32 size; + + if (cfg->ch2_pix == PRP_PIX2_UNUSED || + (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP) + return -1; + + __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR); + if (cfg->ch2_pix == PRP_PIX2_YUV420) { + size = cfg->ch2_width * cfg->ch2_height; + __raw_writel(cfg->ch2_ptr2 + size, PRP_SOURCE_CB_PTR); + __raw_writel(cfg->ch2_ptr2 + size + (size >> 2), + PRP_SOURCE_CR_PTR); + } + + __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B2, PRP_CNTL); + return 0; +} + +/*! + * @brief Build CSC table + * @param csc CSC table + * in csc[0]=index 0..3 : A.1 A.0 B.1 B.0 + * csc[1]=direction 0 : YUV2RGB 1 : RGB2YUV + * out csc[0..4] are coefficients c[9] is offset + * csc[0..8] are coefficients c[9] is offset + */ +void csc_tbl(short csc[10]) +{ + static const unsigned short _r2y[][9] = { + {0x4D, 0x4B, 0x3A, 0x57, 0x55, 0x40, 0x40, 0x6B, 0x29}, + {0x42, 0x41, 0x32, 0x4C, 0x4A, 0x38, 0x38, 0x5E, 0x24}, + {0x36, 0x5C, 0x25, 0x3B, 0x63, 0x40, 0x40, 0x74, 0x18}, + {0x2F, 0x4F, 0x20, 0x34, 0x57, 0x38, 0x38, 0x66, 0x15}, + }; + static const unsigned short _y2r[][5] = { + {0x80, 0xb4, 0x2c, 0x5b, 0x0e4}, + {0x95, 0xcc, 0x32, 0x68, 0x104}, + {0x80, 0xca, 0x18, 0x3c, 0x0ec}, + {0x95, 0xe5, 0x1b, 0x44, 0x1e0}, + }; + unsigned short *_csc; + int _csclen; + + csc[9] = csc[0] & 1; + _csclen = csc[0] & 3; + + if (csc[1]) { + _csc = (unsigned short *)_r2y[_csclen]; + _csclen = sizeof(_r2y[0]); + } else { + _csc = (unsigned short *)_y2r[_csclen]; + _csclen = sizeof(_y2r[0]); + memset(csc + 5, 0, sizeof(short) * 4); + } + memcpy(csc, _csc, _csclen); +} + +/*! + * @brief Setup PrP resize coefficient registers + * + * @param ch PrP channel number + * @param dir Direction, 0 - horizontal, 1 - vertical + * @param scale The pointer to scale_t structure + */ +static void prp_set_scaler(int ch, int dir, scale_t * scale) +{ + int i; + unsigned int coeff[2]; + unsigned int valid; + + for (coeff[0] = coeff[1] = valid = 0, i = 19; i >= 0; i--) { + int j; + + j = i > 9 ? 1 : 0; + coeff[j] = (coeff[j] << BC_COEF) | + (scale->tbl[i] & (SZ_COEF - 1)); + + if (i == 5 || i == 15) + coeff[j] <<= 1; + + valid = (valid << 1) | (scale->tbl[i] >> BC_COEF); + } + + valid |= (scale->len << 24) | ((2 - scale->algo) << 31); + + for (i = 0; i < 2; i++) + (*PRP_RSZ_COEFF)[1 - ch][dir].coeff[i] = coeff[i]; + + (*PRP_RSZ_COEFF)[1 - ch][dir].cntl = valid; +} + +/*! + * @brief Setup PrP registers relevant to input. + * @param cfg Pointer to PrP configuration parameter + * @param prp_cntl Holds the value for PrP control register + * @return Zero on success, others on failure + */ +static int prphw_input_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl) +{ + unsigned long mask; + + switch (cfg->in_pix) { + case PRP_PIXIN_YUV420: + *prp_cntl |= PRP_CNTL_IN_YUV420; + mask = 0x7; + break; + case PRP_PIXIN_YUYV: + case PRP_PIXIN_YVYU: + case PRP_PIXIN_UYVY: + case PRP_PIXIN_VYUY: + *prp_cntl |= PRP_CNTL_IN_YUV422; + mask = 0x1; + break; + case PRP_PIXIN_RGB565: + *prp_cntl |= PRP_CNTL_IN_RGB16; + mask = 0x1; + break; + case PRP_PIXIN_RGB888: + *prp_cntl |= PRP_CNTL_IN_RGB32; + mask = 0; + break; + default: + pr_debug("Unsupported input pix format 0x%08X\n", cfg->in_pix); + return -1; + } + + /* align the input image width */ + if (cfg->in_width & mask) { + pr_debug("in_width misaligned. in_width=%d\n", cfg->in_width); + return -1; + } + + if ((cfg->in_width < PRP_MIN_IN_WIDTH) + || (cfg->in_width > PRP_MAX_IN_WIDTH)) { + pr_debug("Unsupported input width %d\n", cfg->in_width); + return -1; + } + + cfg->in_height &= ~1; /* truncate to make even */ + + if ((cfg->in_height < PRP_MIN_IN_HEIGHT) + || (cfg->in_height > PRP_MAX_IN_HEIGHT)) { + pr_debug("Unsupported input height %d\n", cfg->in_height); + return -1; + } + + if (!(cfg->in_csi & PRP_CSI_EN)) + if (!cfg->in_line_stride) + cfg->in_line_stride = cfg->in_width; + + __raw_writel(cfg->in_pix, PRP_SRC_PIXEL_FORMAT_CNTL); + __raw_writel((cfg->in_width << 16) | cfg->in_height, + PRP_SOURCE_FRAME_SIZE); + __raw_writel((cfg->in_line_skip << 16) | cfg->in_line_stride, + PRP_SOURCE_LINE_STRIDE); + + if (!(cfg->in_csi & PRP_CSI_EN)) { + __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR); + if (cfg->in_pix == PRP_PIXIN_YUV420) { + unsigned int size; + + size = cfg->in_line_stride * cfg->in_height; + __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR); + __raw_writel(cfg->in_ptr + size + (size >> 2), + PRP_SOURCE_CR_PTR); + } + } + + /* always cropping */ + *prp_cntl |= PRP_CNTL_WINEN; + + /* color space conversion */ + if (!cfg->in_csc[1]) { + if (cfg->in_csc[0] > 3) { + pr_debug("in_csc invalid 0x%X\n", cfg->in_csc[0]); + return -1; + } + if ((cfg->in_pix == PRP_PIXIN_RGB565) + || (cfg->in_pix == PRP_PIXIN_RGB888)) + cfg->in_csc[1] = 1; + else + cfg->in_csc[0] = 0; + csc_tbl(cfg->in_csc); + } + + __raw_writel((cfg->in_csc[0] << 21) | (cfg->in_csc[1] << 11) + | cfg->in_csc[2], PRP_CSC_COEF_012); + __raw_writel((cfg->in_csc[3] << 21) | (cfg->in_csc[4] << 11) + | cfg->in_csc[5], PRP_CSC_COEF_345); + __raw_writel((cfg->in_csc[6] << 21) | (cfg->in_csc[7] << 11) + | cfg->in_csc[8] | (cfg->in_csc[9] << 31), + PRP_CSC_COEF_678); + + if (cfg->in_csi & PRP_CSI_EN) { + *prp_cntl |= PRP_CNTL_CSI; + + /* loop mode enable, ch1 ch2 together */ + if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) + *prp_cntl |= (PRP_CNTL_CH1_LOOP | PRP_CNTL_CH2_LOOP); + } + + return 0; +} + +/*! + * @brief Setup PrP registers relevant to channel 2. + * @param cfg Pointer to PrP configuration parameter + * @param prp_cntl Holds the value for PrP control register + * @return Zero on success, others on failure + */ +static int prphw_ch2_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl) +{ + switch (cfg->ch2_pix) { + case PRP_PIX2_YUV420: + *prp_cntl |= PRP_CNTL_CH2_YUV420; + break; + case PRP_PIX2_YUV422: + *prp_cntl |= PRP_CNTL_CH2_YUV422; + break; + case PRP_PIX2_YUV444: + *prp_cntl |= PRP_CNTL_CH2_YUV444; + break; + case PRP_PIX2_UNUSED: + return 0; + default: + pr_debug("Unsupported channel 2 pix format 0x%08X\n", + cfg->ch2_pix); + return -1; + } + + if (cfg->ch2_pix == PRP_PIX2_YUV420) { + cfg->ch2_height &= ~1; /* ensure U/V presence */ + cfg->ch2_width &= ~7; /* ensure U/V word aligned */ + } else if (cfg->ch2_pix == PRP_PIX2_YUV422) { + cfg->ch2_width &= ~1; /* word aligned */ + } + + __raw_writel((cfg->ch2_width << 16) | cfg->ch2_height, + PRP_CH2_OUT_IMAGE_SIZE); + + return 0; +} + +/*! + * @brief Setup PrP registers relevant to channel 1. + * @param cfg Pointer to PrP configuration parameter + * @param prp_cntl Holds the value for PrP control register + * @return Zero on success, others on failure + */ +static int prphw_ch1_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl) +{ + int ch1_bpp = 0; + + switch (cfg->ch1_pix) { + case PRP_PIX1_RGB332: + *prp_cntl |= PRP_CNTL_CH1_RGB8; + ch1_bpp = 1; + break; + case PRP_PIX1_RGB565: + *prp_cntl |= PRP_CNTL_CH1_RGB16; + ch1_bpp = 2; + break; + case PRP_PIX1_RGB888: + *prp_cntl |= PRP_CNTL_CH1_RGB32; + ch1_bpp = 4; + break; + case PRP_PIX1_YUYV: + case PRP_PIX1_YVYU: + case PRP_PIX1_UYVY: + case PRP_PIX1_VYUY: + *prp_cntl |= PRP_CNTL_CH1_YUV422; + ch1_bpp = 2; + break; + case PRP_PIX1_UNUSED: + return 0; + default: + pr_debug("Unsupported channel 1 pix format 0x%08X\n", + cfg->ch1_pix); + return -1; + } + + /* parallel or cascade resize */ + if (cfg->ch1_scale.algo & PRP_ALGO_BYPASS) + *prp_cntl |= PRP_CNTL_UNCHAIN; + + /* word align */ + if (ch1_bpp == 2) + cfg->ch1_width &= ~1; + else if (ch1_bpp == 1) + cfg->ch1_width &= ~3; + + if (!cfg->ch1_stride) + cfg->ch1_stride = cfg->ch1_width; + + __raw_writel(cfg->ch1_pix, PRP_CH1_PIXEL_FORMAT_CNTL); + __raw_writel((cfg->ch1_width << 16) | cfg->ch1_height, + PRP_CH1_OUT_IMAGE_SIZE); + __raw_writel(cfg->ch1_stride * ch1_bpp, PRP_CH1_LINE_STRIDE); + __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR); + + /* double buffer for loop mode */ + if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) { + if (cfg->ch1_ptr2) + __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR); + else + __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR); + } + + return 0; +} + +/*! + * @brief Setup PrP registers. + * @param cfg Pointer to PrP configuration parameter + * @return Zero on success, others on failure + */ +int prphw_cfg(emma_prp_cfg * cfg) +{ + unsigned long prp_cntl = 0; + unsigned long val; + + /* input pixel format checking */ + if (prphw_input_cfg(cfg, &prp_cntl)) + return -1; + + if (prphw_ch2_cfg(cfg, &prp_cntl)) + return -1; + + if (prphw_ch1_cfg(cfg, &prp_cntl)) + return -1; + + /* register setting */ + __raw_writel(prp_cntl, PRP_CNTL); + + /* interrupt configuration */ + val = PRP_INTRCNTL_RDERR | PRP_INTRCNTL_LBOVF; + if (cfg->ch1_pix != PRP_PIX1_UNUSED) + val |= PRP_INTRCNTL_CH1FC | PRP_INTRCNTL_CH1WERR; + if (cfg->ch2_pix != PRP_PIX2_UNUSED) + val |= + PRP_INTRCNTL_CH2FC | PRP_INTRCNTL_CH2WERR | + PRP_INTRCNTL_CH2OVF; + __raw_writel(val, PRP_INTRCNTL); + + prp_set_scaler(1, 0, &cfg->scale[0]); /* Channel 1 width */ + prp_set_scaler(1, 1, &cfg->scale[1]); /* Channel 1 height */ + prp_set_scaler(0, 0, &cfg->scale[2]); /* Channel 2 width */ + prp_set_scaler(0, 1, &cfg->scale[3]); /* Channel 2 height */ + + return 0; +} + +/*! + * @brief Check PrP interrupt status. + * @return PrP interrupt status + */ +int prphw_isr(void) +{ + int status; + + status = __raw_readl(PRP_INTRSTATUS) & 0x1FF; + + if (status & (PRP_INTRSTAT_RDERR | PRP_INTRSTAT_CH1WERR | + PRP_INTRSTAT_CH2WERR)) + pr_debug("isr bus error. status= 0x%08X\n", status); + else if (status & PRP_INTRSTAT_CH2OVF) + pr_debug("isr ch 2 buffer overflow. status= 0x%08X\n", status); + else if (status & PRP_INTRSTAT_LBOVF) + pr_debug("isr line buffer overflow. status= 0x%08X\n", status); + + /* silicon bug?? enable bit does not self clear? */ + if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH1_LOOP)) + __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH1EN), + PRP_CNTL); + if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH2_LOOP)) + __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH2EN), + PRP_CNTL); + + __raw_writel(status, PRP_INTRSTATUS); /* clr irq */ + + return status; +} + +static struct clk *emma_clk; + +/*! + * @brief PrP module clock enable + */ +void prphw_init(void) +{ + emma_clk = clk_get(NULL, "emma_clk"); + clk_enable(emma_clk); +} + +/*! + * @brief PrP module clock disable + */ +void prphw_exit(void) +{ + clk_disable(emma_clk); + clk_put(emma_clk); +} diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_prpsw.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prpsw.c --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_prpsw.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_prpsw.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1050 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_prpsw.c + * + * @brief MX27 Video For Linux 2 capture driver + * + * @ingroup MXC_V4L2_CAPTURE + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mxc_v4l2_capture.h" +#include "mx27_prp.h" +#include "mx27_csi.h" +#include "../drivers/video/mxc/mx2fb.h" +#include "../opl/opl.h" + +#define MEAN_COEF (SZ_COEF >> 1) + +static char prp_dev[] = "emma_prp"; +static int g_still_on = 0; +static emma_prp_cfg g_prp_cfg; +static int g_vfbuf, g_rotbuf; +static struct tasklet_struct prp_vf_tasklet; + +/* + * The following variables represents the virtual address for the cacheable + * buffers accessed by SW rotation/mirroring. The rotation/mirroring in + * cacheable buffers has significant performance improvement than it in + * non-cacheable buffers. + */ +static char *g_vaddr_vfbuf[2] = { 0, 0 }; +static char *g_vaddr_rotbuf[2] = { 0, 0 }; +static char *g_vaddr_fb = 0; + +static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam); +static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam); +static int prp_vf_mem_alloc(cam_data * cam); +static void prp_vf_mem_free(cam_data * cam); +static int prp_rot_mem_alloc(cam_data * cam); +static void prp_rot_mem_free(cam_data * cam); +static int prp_enc_update_eba(u32 eba, int *buffer_num); +static int prp_enc_enable(void *private); +static int prp_enc_disable(void *private); +static int prp_vf_start(void *private); +static int prp_vf_stop(void *private); +static int prp_still_start(void *private); +static int prp_still_stop(void *private); +static irqreturn_t prp_isr(int irq, void *dev_id); +static void rotation(unsigned long private); +static int prp_resize_check_ch1(emma_prp_cfg * cfg); +static int prp_resize_check_ch2(emma_prp_cfg * cfg); + +#define PRP_DUMP(val) pr_debug("%s\t = 0x%08X\t%d\n", #val, val, val) + +/*! + * @brief Dump PrP configuration parameters. + * @param cfg The pointer to PrP configuration parameter + */ +static void prp_cfg_dump(emma_prp_cfg * cfg) +{ + PRP_DUMP(cfg->in_pix); + PRP_DUMP(cfg->in_width); + PRP_DUMP(cfg->in_height); + PRP_DUMP(cfg->in_csi); + PRP_DUMP(cfg->in_line_stride); + PRP_DUMP(cfg->in_line_skip); + PRP_DUMP(cfg->in_ptr); + + PRP_DUMP(cfg->ch1_pix); + PRP_DUMP(cfg->ch1_width); + PRP_DUMP(cfg->ch1_height); + PRP_DUMP(cfg->ch1_scale.algo); + PRP_DUMP(cfg->ch1_scale.width.num); + PRP_DUMP(cfg->ch1_scale.width.den); + PRP_DUMP(cfg->ch1_scale.height.num); + PRP_DUMP(cfg->ch1_scale.height.den); + PRP_DUMP(cfg->ch1_stride); + PRP_DUMP(cfg->ch1_ptr); + PRP_DUMP(cfg->ch1_ptr2); + PRP_DUMP(cfg->ch1_csi); + + PRP_DUMP(cfg->ch2_pix); + PRP_DUMP(cfg->ch2_width); + PRP_DUMP(cfg->ch2_height); + PRP_DUMP(cfg->ch2_scale.algo); + PRP_DUMP(cfg->ch2_scale.width.num); + PRP_DUMP(cfg->ch2_scale.width.den); + PRP_DUMP(cfg->ch2_scale.height.num); + PRP_DUMP(cfg->ch2_scale.height.den); + PRP_DUMP(cfg->ch2_ptr); + PRP_DUMP(cfg->ch2_ptr2); + PRP_DUMP(cfg->ch2_csi); +} + +/*! + * @brief Set PrP channel 1 output address. + * @param cfg Pointer to emma_prp_cfg structure + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam) +{ + if (cam->rotation != V4L2_MXC_ROTATE_NONE) { + cfg->ch1_ptr = (unsigned int)cam->rot_vf_bufs[0]; + cfg->ch1_ptr2 = (unsigned int)cam->rot_vf_bufs[1]; + if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) + cfg->ch1_stride = cam->win.w.height; + else + cfg->ch1_stride = cam->win.w.width; + + if (cam->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY) { + struct fb_info *fb = cam->overlay_fb; + if (!fb) + return -1; + if (g_vaddr_fb) + iounmap(g_vaddr_fb); + g_vaddr_fb = ioremap_cached(fb->fix.smem_start, + fb->fix.smem_len); + if (!g_vaddr_fb) + return -1; + } + } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + cfg->ch1_ptr = (unsigned int)cam->vf_bufs[0]; + cfg->ch1_ptr2 = (unsigned int)cam->vf_bufs[1]; + cfg->ch1_stride = cam->win.w.width; + } else { + struct fb_info *fb = cam->overlay_fb; + + if (!fb) + return -1; + + cfg->ch1_ptr = fb->fix.smem_start; + cfg->ch1_ptr += cam->win.w.top * fb->var.xres_virtual + * (fb->var.bits_per_pixel >> 3) + + cam->win.w.left * (fb->var.bits_per_pixel >> 3); + cfg->ch1_ptr2 = cfg->ch1_ptr; + cfg->ch1_stride = fb->var.xres_virtual; + } + + return 0; +} + +/*! + * @brief Setup PrP configuration parameters. + * @param cfg Pointer to emma_prp_cfg structure + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam) +{ + cfg->in_pix = PRP_PIXIN_YUYV; + cfg->in_width = cam->crop_current.width; + cfg->in_height = cam->crop_current.height; + cfg->in_line_stride = cam->crop_current.left; + cfg->in_line_skip = cam->crop_current.top; + cfg->in_ptr = 0; + cfg->in_csi = PRP_CSI_LOOP; + memset(cfg->in_csc, 0, sizeof(cfg->in_csc)); + + if (cam->overlay_on) { + /* Convert V4L2 pixel format to PrP pixel format */ + switch (cam->v4l2_fb.fmt.pixelformat) { + case V4L2_PIX_FMT_RGB332: + cfg->ch1_pix = PRP_PIX1_RGB332; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + cfg->ch1_pix = PRP_PIX1_RGB888; + break; + case V4L2_PIX_FMT_YUYV: + cfg->ch1_pix = PRP_PIX1_YUYV; + break; + case V4L2_PIX_FMT_UYVY: + cfg->ch1_pix = PRP_PIX1_UYVY; + break; + case V4L2_PIX_FMT_RGB565: + default: + cfg->ch1_pix = PRP_PIX1_RGB565; + break; + } + if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) { + cfg->ch1_width = cam->win.w.height; + cfg->ch1_height = cam->win.w.width; + } else { + cfg->ch1_width = cam->win.w.width; + cfg->ch1_height = cam->win.w.height; + } + + if (set_ch1_addr(cfg, cam)) + return -1; + } else { + cfg->ch1_pix = PRP_PIX1_UNUSED; + cfg->ch1_width = cfg->in_width; + cfg->ch1_height = cfg->in_height; + } + cfg->ch1_scale.algo = 0; + cfg->ch1_scale.width.num = cfg->in_width; + cfg->ch1_scale.width.den = cfg->ch1_width; + cfg->ch1_scale.height.num = cfg->in_height; + cfg->ch1_scale.height.den = cfg->ch1_height; + cfg->ch1_csi = PRP_CSI_EN; + + if (cam->capture_on || g_still_on) { + switch (cam->v2f.fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + cfg->ch2_pix = PRP_PIX2_YUV422; + break; + case V4L2_PIX_FMT_YUV420: + cfg->ch2_pix = PRP_PIX2_YUV420; + break; + /* + * YUV444 is not defined by V4L2. + * We support it in default case. + */ + default: + cfg->ch2_pix = PRP_PIX2_YUV444; + break; + } + cfg->ch2_width = cam->v2f.fmt.pix.width; + cfg->ch2_height = cam->v2f.fmt.pix.height; + } else { + cfg->ch2_pix = PRP_PIX2_UNUSED; + cfg->ch2_width = cfg->in_width; + cfg->ch2_height = cfg->in_height; + } + cfg->ch2_scale.algo = 0; + cfg->ch2_scale.width.num = cfg->in_width; + cfg->ch2_scale.width.den = cfg->ch2_width; + cfg->ch2_scale.height.num = cfg->in_height; + cfg->ch2_scale.height.den = cfg->ch2_height; + cfg->ch2_csi = PRP_CSI_EN; + + memset(cfg->scale, 0, sizeof(cfg->scale)); + cfg->scale[0].algo = cfg->ch1_scale.algo & 3; + cfg->scale[1].algo = (cfg->ch1_scale.algo >> 2) & 3; + cfg->scale[2].algo = cfg->ch2_scale.algo & 3; + cfg->scale[3].algo = (cfg->ch2_scale.algo >> 2) & 3; + + prp_cfg_dump(cfg); + + if (prp_resize_check_ch2(cfg)) + return -1; + + if (prp_resize_check_ch1(cfg)) + return -1; + + return 0; +} + +/*! + * @brief PrP interrupt handler + */ +static irqreturn_t prp_isr(int irq, void *dev_id) +{ + int status; + cam_data *cam = (cam_data *) dev_id; + + status = prphw_isr(); + + if (g_still_on && (status & PRP_INTRSTAT_CH2BUF1)) { + prp_still_stop(cam); + cam->still_counter++; + wake_up_interruptible(&cam->still_queue); + /* + * Still & video capture use the same PrP channel 2. + * They are execlusive. + */ + } else if (cam->capture_on) { + if (status & PRP_INTRSTAT_CH2OVF) { + prphw_disable(PRP_CHANNEL_2); + cam->enc_callback(1, cam); + } else if (status & + (PRP_INTRSTAT_CH2BUF1 | PRP_INTRSTAT_CH2BUF2)) { + if (cam->overflow != 1) + cam->enc_callback(0, cam); + } + } + if (cam->overlay_on + && (status & (PRP_INTRSTAT_CH1BUF1 | PRP_INTRSTAT_CH1BUF2))) { + if (cam->rotation != V4L2_MXC_ROTATE_NONE) { + g_rotbuf = (status & PRP_INTRSTAT_CH1BUF1) ? 0 : 1; + tasklet_schedule(&prp_vf_tasklet); + } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + struct fb_gwinfo gwinfo; + + gwinfo.enabled = 1; + gwinfo.alpha_value = 255; + gwinfo.ck_enabled = 0; + gwinfo.xpos = cam->win.w.left; + gwinfo.ypos = cam->win.w.top; + gwinfo.xres = cam->win.w.width; + gwinfo.yres = cam->win.w.height; + gwinfo.xres_virtual = cam->win.w.width; + gwinfo.vs_reversed = 0; + if (status & PRP_INTRSTAT_CH1BUF1) + gwinfo.base = (unsigned long)cam->vf_bufs[0]; + else + gwinfo.base = (unsigned long)cam->vf_bufs[1]; + + mx2_gw_set(&gwinfo); + } + } + + return IRQ_HANDLED; +} + +/*! + * @brief PrP initialization. + * @param dev_id Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_init(void *dev_id) +{ + enable_irq(INT_EMMAPRP); + if (request_irq(INT_EMMAPRP, prp_isr, 0, prp_dev, dev_id)) + return -1; + prphw_init(); + + return 0; +} + +/*! + * @brief PrP initialization. + * @param dev_id Pointer to cam_data structure + */ +void prp_exit(void *dev_id) +{ + prphw_exit(); + disable_irq(INT_EMMAPRP); + free_irq(INT_EMMAPRP, dev_id); +} + +/*! + * @brief Update PrP channel 2 output buffer address. + * @param eba Physical address for PrP output buffer + * @param buffer_num The PrP channel 2 buffer number to be updated + * @return Zero on success, others on failure + */ +static int prp_enc_update_eba(u32 eba, int *buffer_num) +{ + if (*buffer_num) { + g_prp_cfg.ch2_ptr2 = eba; + prphw_ch2ptr2(&g_prp_cfg); + *buffer_num = 0; + } else { + g_prp_cfg.ch2_ptr = eba; + prphw_ch2ptr(&g_prp_cfg); + *buffer_num = 1; + } + + return 0; +} + +/*! + * @brief Enable PrP for encoding. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_enc_enable(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (prp_v4l2_cfg(&g_prp_cfg, cam)) + return -1; + + csi_enable_mclk(CSI_MCLK_ENC, true, true); + prphw_reset(); + + if (prphw_cfg(&g_prp_cfg)) + return -1; + + prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2) + : PRP_CHANNEL_2); + + return 0; +} + +/*! + * @brief Disable PrP for encoding. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_enc_disable(void *private) +{ + prphw_disable(PRP_CHANNEL_2); + csi_enable_mclk(CSI_MCLK_ENC, false, false); + + return 0; +} + +/*! + * @brief Setup encoding functions. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_enc_select(void *private) +{ + int ret = 0; + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->enc_update_eba = prp_enc_update_eba; + cam->enc_enable = prp_enc_enable; + cam->enc_disable = prp_enc_disable; + } else + ret = -EIO; + + return ret; +} + +/*! + * @brief Uninstall encoding functions. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_enc_deselect(void *private) +{ + int ret = 0; + cam_data *cam = (cam_data *) private; + + ret = prp_enc_disable(private); + + if (cam) { + cam->enc_update_eba = NULL; + cam->enc_enable = NULL; + cam->enc_disable = NULL; + } + + return ret; +} + +/*! + * @brief Allocate memory for overlay. + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_vf_mem_alloc(cam_data * cam) +{ + int i; + + for (i = 0; i < 2; i++) { + cam->vf_bufs_size[i] = cam->win.w.width * cam->win.w.height * 2; + cam->vf_bufs_vaddr[i] = dma_alloc_coherent(0, + cam->vf_bufs_size[i], + &cam->vf_bufs[i], + GFP_DMA | + GFP_KERNEL); + if (!cam->vf_bufs_vaddr[i]) { + pr_debug("Failed to alloc memory for vf.\n"); + prp_vf_mem_free(cam); + return -1; + } + + g_vaddr_vfbuf[i] = + ioremap_cached(cam->vf_bufs[i], cam->vf_bufs_size[i]); + if (!g_vaddr_vfbuf[i]) { + pr_debug("Failed to ioremap_cached() for vf.\n"); + prp_vf_mem_free(cam); + return -1; + } + } + + return 0; +} + +/*! + * @brief Free memory for overlay. + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static void prp_vf_mem_free(cam_data * cam) +{ + int i; + + for (i = 0; i < 2; i++) { + if (cam->vf_bufs_vaddr[i]) { + dma_free_coherent(0, + cam->vf_bufs_size[i], + cam->vf_bufs_vaddr[i], + cam->vf_bufs[i]); + } + cam->vf_bufs[i] = 0; + cam->vf_bufs_vaddr[i] = 0; + cam->vf_bufs_size[i] = 0; + if (g_vaddr_vfbuf[i]) { + iounmap(g_vaddr_vfbuf[i]); + g_vaddr_vfbuf[i] = 0; + } + } +} + +/*! + * @brief Allocate intermediate memory for overlay rotation/mirroring. + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_rot_mem_alloc(cam_data * cam) +{ + int i; + + for (i = 0; i < 2; i++) { + cam->rot_vf_buf_size[i] = + cam->win.w.width * cam->win.w.height * 2; + cam->rot_vf_bufs_vaddr[i] = + dma_alloc_coherent(0, cam->rot_vf_buf_size[i], + &cam->rot_vf_bufs[i], + GFP_DMA | GFP_KERNEL); + if (!cam->rot_vf_bufs_vaddr[i]) { + pr_debug("Failed to alloc memory for vf rotation.\n"); + prp_rot_mem_free(cam); + return -1; + } + + g_vaddr_rotbuf[i] = + ioremap_cached(cam->rot_vf_bufs[i], + cam->rot_vf_buf_size[i]); + if (!g_vaddr_rotbuf[i]) { + pr_debug + ("Failed to ioremap_cached() for rotation buffer.\n"); + prp_rot_mem_free(cam); + return -1; + } + } + + return 0; +} + +/*! + * @brief Free intermedaite memory for overlay rotation/mirroring. + * @param cam Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static void prp_rot_mem_free(cam_data * cam) +{ + int i; + + for (i = 0; i < 2; i++) { + if (cam->rot_vf_bufs_vaddr[i]) { + dma_free_coherent(0, + cam->rot_vf_buf_size[i], + cam->rot_vf_bufs_vaddr[i], + cam->rot_vf_bufs[i]); + } + cam->rot_vf_bufs[i] = 0; + cam->rot_vf_bufs_vaddr[i] = 0; + cam->rot_vf_buf_size[i] = 0; + if (g_vaddr_rotbuf[i]) { + iounmap(g_vaddr_rotbuf[i]); + g_vaddr_rotbuf[i] = 0; + } + } +} + +/*! + * @brief Start overlay (view finder). + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_vf_start(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + prp_vf_mem_free(cam); + if (prp_vf_mem_alloc(cam)) { + pr_info("Error to allocate vf buffer\n"); + return -ENOMEM; + } + } + + if (cam->rotation != V4L2_MXC_ROTATE_NONE) { + prp_rot_mem_free(cam); + if (prp_rot_mem_alloc(cam)) { + pr_info("Error to allocate rotation buffer\n"); + prp_vf_mem_free(cam); + return -ENOMEM; + } + } + + if (prp_v4l2_cfg(&g_prp_cfg, cam)) { + prp_vf_mem_free(cam); + prp_rot_mem_free(cam); + return -1; + } + + csi_enable_mclk(CSI_MCLK_VF, true, true); + prphw_reset(); + + if (prphw_cfg(&g_prp_cfg)) { + prp_vf_mem_free(cam); + prp_rot_mem_free(cam); + return -1; + } + g_vfbuf = g_rotbuf = 0; + tasklet_init(&prp_vf_tasklet, rotation, (unsigned long)private); + + prphw_enable(cam->capture_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2) + : PRP_CHANNEL_1); + + return 0; +} + +/*! + * @brief Stop overlay (view finder). + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_vf_stop(void *private) +{ + cam_data *cam = (cam_data *) private; + + prphw_disable(PRP_CHANNEL_1); + + csi_enable_mclk(CSI_MCLK_VF, false, false); + tasklet_kill(&prp_vf_tasklet); + + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + struct fb_gwinfo gwinfo; + + /* Disable graphic window */ + gwinfo.enabled = 0; + mx2_gw_set(&gwinfo); + + prp_vf_mem_free(cam); + } + prp_rot_mem_free(cam); + if (g_vaddr_fb) { + iounmap(g_vaddr_fb); + g_vaddr_fb = 0; + } + + return 0; +} + +/*! + * @brief Setup overlay functions. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_vf_select(void *private) +{ + int ret = 0; + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->vf_start_sdc = prp_vf_start; + cam->vf_stop_sdc = prp_vf_stop; + cam->overlay_active = false; + } else + ret = -EIO; + + return ret; +} + +/*! + * @brief Uninstall overlay functions. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_vf_deselect(void *private) +{ + int ret = 0; + cam_data *cam = (cam_data *) private; + + ret = prp_vf_stop(private); + + if (cam) { + cam->vf_start_sdc = NULL; + cam->vf_stop_sdc = NULL; + } + + return ret; +} + +/*! + * @brief Start still picture capture. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_still_start(void *private) +{ + cam_data *cam = (cam_data *) private; + + g_still_on = 1; + g_prp_cfg.ch2_ptr = (unsigned int)cam->still_buf; + g_prp_cfg.ch2_ptr2 = 0; + + if (prp_v4l2_cfg(&g_prp_cfg, cam)) + return -1; + + csi_enable_mclk(CSI_MCLK_RAW, true, true); + prphw_reset(); + + if (prphw_cfg(&g_prp_cfg)) { + g_still_on = 0; + return -1; + } + + prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2) + : PRP_CHANNEL_2); + + return 0; +} + +/*! + * @brief Stop still picture capture. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +static int prp_still_stop(void *private) +{ + prphw_disable(PRP_CHANNEL_2); + + csi_enable_mclk(CSI_MCLK_RAW, false, false); + + g_still_on = 0; + + return 0; +} + +/*! + * @brief Setup functions for still picture capture. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_still_select(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->csi_start = prp_still_start; + cam->csi_stop = prp_still_stop; + } + + return 0; +} + +/*! + * @brief Uninstall functions for still picture capture. + * @param private Pointer to cam_data structure + * @return Zero on success, others on failure + */ +int prp_still_deselect(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + err = prp_still_stop(cam); + + if (cam) { + cam->csi_start = NULL; + cam->csi_stop = NULL; + } + + return err; +} + +/*! + * @brief Perform software rotation or mirroring + * @param private Argument passed to the tasklet + */ +static void rotation(unsigned long private) +{ + char *src, *dst; + int width, height, s_stride, d_stride; + int size; + cam_data *cam = (cam_data *) private; + + src = g_vaddr_rotbuf[g_rotbuf]; + size = cam->rot_vf_buf_size[g_rotbuf]; + + if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) { + width = cam->win.w.height; + height = cam->win.w.width; + s_stride = cam->win.w.height << 1; + } else { + width = cam->win.w.width; + height = cam->win.w.height; + s_stride = cam->win.w.width << 1; + } + + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + dst = g_vaddr_vfbuf[g_vfbuf]; + d_stride = cam->win.w.width << 1; + } else { /* The destination is the framebuffer */ + struct fb_info *fb = cam->overlay_fb; + if (!fb) + return; + dst = g_vaddr_fb; + dst += cam->win.w.top * fb->var.xres_virtual + * (fb->var.bits_per_pixel >> 3) + + cam->win.w.left * (fb->var.bits_per_pixel >> 3); + d_stride = fb->var.xres_virtual << 1; + } + + /* + * Invalidate the data in cache before performing the SW rotaion + * or mirroring in case the image size is less than QVGA. For image + * larger than QVGA it is not invalidated becase the invalidation + * will consume much time while we don't see any artifacts on the + * output if we don't perform invalidation for them. + * Similarly we don't flush the data after SW rotation/mirroring. + */ + if (size < 320 * 240 * 2) + dmac_inv_range(src, src + size); + switch (cam->rotation) { + case V4L2_MXC_ROTATE_VERT_FLIP: + opl_vmirror_u16(src, s_stride, width, height, dst, d_stride); + break; + case V4L2_MXC_ROTATE_HORIZ_FLIP: + opl_hmirror_u16(src, s_stride, width, height, dst, d_stride); + break; + case V4L2_MXC_ROTATE_180: + opl_rotate180_u16(src, s_stride, width, height, dst, d_stride); + break; + case V4L2_MXC_ROTATE_90_RIGHT: + opl_rotate90_u16(src, s_stride, width, height, dst, d_stride); + break; + case V4L2_MXC_ROTATE_90_RIGHT_VFLIP: + opl_rotate90_vmirror_u16(src, s_stride, width, height, dst, + d_stride); + break; + case V4L2_MXC_ROTATE_90_RIGHT_HFLIP: + /* ROTATE_90_RIGHT_HFLIP = ROTATE_270_RIGHT_VFLIP */ + opl_rotate270_vmirror_u16(src, s_stride, width, height, dst, + d_stride); + break; + case V4L2_MXC_ROTATE_90_LEFT: + opl_rotate270_u16(src, s_stride, width, height, dst, d_stride); + break; + default: + return; + } + + /* Config and display the graphic window */ + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + struct fb_gwinfo gwinfo; + + gwinfo.enabled = 1; + gwinfo.alpha_value = 255; + gwinfo.ck_enabled = 0; + gwinfo.xpos = cam->win.w.left; + gwinfo.ypos = cam->win.w.top; + gwinfo.xres = cam->win.w.width; + gwinfo.yres = cam->win.w.height; + gwinfo.xres_virtual = cam->win.w.width; + gwinfo.vs_reversed = 0; + gwinfo.base = (unsigned long)cam->vf_bufs[g_vfbuf]; + mx2_gw_set(&gwinfo); + + g_vfbuf = g_vfbuf ? 0 : 1; + } +} + +/* + * @brief Check if the resize ratio is supported based on the input and output + * dimension + * @param input input dimension + * @param output output dimension + * @return output dimension (should equal the parameter *output*) + * -1 on failure + */ +static int check_simple(scale_t * scale, int input, int output, int ch) +{ + unsigned short int_out; /* PrP internel width or height */ + unsigned short orig_out = output; + + if (prp_scale(scale, input, output, input, &orig_out, &int_out, ch)) + return -1; /* resize failed */ + else + return int_out; +} + +/*! + * @brief Check if the resize ratio is supported by PrP channel 1 + * @param cfg Pointer to emma_prp_cfg structure + * @return Zero on success, others on failure + */ +static int prp_resize_check_ch1(emma_prp_cfg * cfg) +{ + int in_w, in_h, ch1_w, ch1_h, ch2_w, ch2_h, w, h; + scale_t *pscale = &cfg->scale[0]; /* Ch1 width resize coeff */ + + if (cfg->ch1_pix == PRP_PIX1_UNUSED) + return 0; + + in_w = cfg->in_width; + in_h = cfg->in_height; + ch1_w = cfg->ch1_width; + ch1_h = cfg->ch1_height; + ch2_w = cfg->ch2_width; + ch2_h = cfg->ch2_height; + + /* + * For channel 1, try parallel resize first. If the resize + * ratio is not exactly supported, try cascade resize. If it + * still fails, use parallel resize but with rounded value. + */ + w = check_simple(pscale, in_w, ch1_w, PRP_CHANNEL_1); + h = check_simple(pscale + 1, in_h, ch1_h, PRP_CHANNEL_1); + + if ((in_w <= ch1_w * MAX_TBL) && (in_h <= MAX_TBL * ch1_h)) + goto exit_parallel; + + if (cfg->ch2_pix != PRP_PIX2_UNUSED) { + /* + * Channel 2 is already used. The pscale is still pointing + * to ch1 resize coeff for temporary use. + */ + if ((ch2_w * MAX_TBL <= ch1_w) && (ch2_h * MAX_TBL <= ch1_h)) { + w = check_simple(pscale, ch2_w, ch1_w, PRP_CHANNEL_1); + h = check_simple(pscale + 1, ch2_h, ch1_h, + PRP_CHANNEL_1); + goto exit_cascade; + } + } else { + /* + * Try cascade resize for width, width is multiple of 2. + * Channel 2 is not used. So we have more values to pick + * for channel 2 resize. + */ + if (in_w * MAX_TBL > ch1_w) { + for (w = in_w / 2; w > ch1_w; w /= 2) { + /* Ch1 width resize */ + if (check_simple + (pscale, w, ch1_w, PRP_CHANNEL_1) < 0) + continue; + /* Ch2 width resize */ + ch2_w = + check_simple(pscale + 2, in_w, w, + PRP_CHANNEL_1); + if (ch2_w < 0) { + w = in_w / MAX_TBL; + continue; + } + check_simple(pscale, ch2_w, ch1_w, + PRP_CHANNEL_1); + break; + } + } else { + w = check_simple(pscale, in_w, ch1_w, PRP_CHANNEL_1); + ch2_w = check_simple(pscale + 2, w, w, PRP_CHANNEL_1); + } + if (ch2_w >= ch1_w) { + if (in_h * MAX_TBL > ch1_h) { + /* try cascade resize for height */ + for (h = in_h / 2; h > ch1_h; h /= 2) { + /* Ch2 height resize */ + if (check_simple + (pscale + 1, h, ch1_h, + PRP_CHANNEL_1) < 0) + continue; + /* Ch1 height resize */ + ch2_h = + check_simple(pscale + 3, in_h, h, + PRP_CHANNEL_1); + if (ch2_w < 0) { + h = in_h / MAX_TBL; + continue; + } + check_simple(pscale + 1, ch2_h, ch1_h, + PRP_CHANNEL_1); + break; + } + } else { + h = check_simple(pscale + 1, in_h, ch1_h, + PRP_CHANNEL_1); + ch2_h = + check_simple(pscale + 3, h, h, + PRP_CHANNEL_1); + } + + goto exit_cascade; + } + } + + pr_debug("Ch1 resize error.\n"); + return -1; + + exit_parallel: + cfg->ch1_scale.algo |= PRP_ALGO_BYPASS; + pr_debug("ch1 parallel resize.\n"); + pr_debug("original width = %d internel width = %d\n", ch1_w, w); + pr_debug("original height = %d internel height = %d\n", ch1_h, h); + return 0; + + exit_cascade: + cfg->ch1_scale.algo &= ~PRP_ALGO_BYPASS; + pr_debug("ch1 cascade resize.\n"); + pr_debug("[width] in : ch2 : ch1=%d : %d : %d\n", in_w, ch2_w, ch1_w); + pr_debug("[height] in : ch2 : ch1=%d : %d : %d\n", in_h, ch2_h, ch1_h); + return 0; +} + +/*! + * @brief Check if the resize ratio is supported by PrP channel 2 + * @param cfg Pointer to emma_prp_cfg structure + * @return Zero on success, others on failure + */ +static int prp_resize_check_ch2(emma_prp_cfg * cfg) +{ + int w, h; + scale_t *pscale = &cfg->scale[2]; /* Ch2 width resize coeff */ + + if (cfg->ch2_pix == PRP_PIX2_UNUSED) + return 0; + + w = check_simple(pscale, cfg->in_width, cfg->ch2_width, PRP_CHANNEL_2); + h = check_simple(pscale + 1, cfg->in_height, cfg->ch2_height, + PRP_CHANNEL_2); + if ((w != -1) && (h != -1)) { + pr_debug("Ch2 resize.\n"); + pr_debug("Original width = %d internel width = %d\n", + cfg->ch2_width, w); + pr_debug("Original height = %d internel height = %d\n", + cfg->ch2_height, h); + return 0; + } else { + pr_debug("Ch2 resize error.\n"); + return -1; + } +} diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mx27_v4l2_capture.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_v4l2_capture.c --- linux-2.6.28/drivers/media/video/mxc/capture/mx27_v4l2_capture.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mx27_v4l2_capture.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,2288 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_v4l2_capture.c + * + * @brief MX27 Video For Linux 2 driver + * + * @ingroup MXC_V4L2_CAPTURE + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mxc_v4l2_capture.h" +#include "mx27_prp.h" +#include "mx27_csi.h" + +static int csi_mclk_flag_backup; +static int video_nr = -1; +cam_data *g_cam; +EXPORT_SYMBOL(g_cam); + +static int dq_intr_cnt = 0; +static int dq_timeout_cnt = 0; +static int empty_wq_cnt = 0; +struct workqueue_struct *v4l2_work; + +static void prp_reset(struct work_struct *unused) +{ + struct mxc_v4l_frame *done_frame, *ready_frame, *temp_frame; + + g_cam->ping_pong_csi = 0; + g_cam->enc_enable(g_cam); + if (!list_empty(&g_cam->working_q)) { + done_frame = + list_entry(g_cam->working_q.next, struct mxc_v4l_frame, + queue); + list_del(g_cam->working_q.next); + if (!list_empty(&g_cam->working_q)) { + temp_frame = + list_entry(g_cam->working_q.next, + struct mxc_v4l_frame, queue); + list_del(g_cam->working_q.next); + list_add_tail(&temp_frame->queue, &g_cam->working_q); + g_cam->enc_update_eba(temp_frame->paddress, + &g_cam->ping_pong_csi); + } + list_add_tail(&done_frame->queue, &g_cam->working_q); + g_cam->enc_update_eba(done_frame->paddress, + &g_cam->ping_pong_csi); + pr_debug("prp_reset - working_q\n"); + } else if (list_empty(&g_cam->ready_q)) { + prphw_disable(PRP_CHANNEL_2); + g_cam->skip_frame++; + } else { + ready_frame = + list_entry(g_cam->ready_q.next, struct mxc_v4l_frame, + queue); + list_del(g_cam->ready_q.next); + list_add_tail(&ready_frame->queue, &g_cam->working_q); + g_cam->enc_update_eba(ready_frame->paddress, + &g_cam->ping_pong_csi); + } + g_cam->overflow = 0; + wake_up_interruptible(&g_cam->overflow_queue); +} + +DECLARE_WORK(prp_reset_work, prp_reset); +/*! + * Free frame buffers + * + * @param cam Structure cam_data * + * + * @return status 0 success. + */ +static int mxc_free_frame_buf(cam_data * cam) +{ + int i; + + for (i = 0; i < FRAME_NUM; i++) { + if (cam->frame[i].vaddress != 0) { + dma_free_coherent(0, + cam->frame[i].buffer.length, + cam->frame[i].vaddress, + cam->frame[i].paddress); + cam->frame[i].vaddress = 0; + } + } + + return 0; +} + +/*! + * Allocate frame buffers + * + * @param cam Structure cam_data * + * + * @param count int number of buffer need to allocated + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int mxc_allocate_frame_buf(cam_data * cam, int count) +{ + int i; + + for (i = 0; i < count; i++) { + cam->frame[i].vaddress = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f. + fmt.pix. + sizeimage), + &cam->frame[i]. + paddress, + GFP_DMA | + GFP_KERNEL); + if (cam->frame[i].vaddress == 0) { + pr_debug("mxc_allocate_frame_buf failed.\n"); + mxc_free_frame_buf(cam); + return -ENOBUFS; + } + cam->frame[i].buffer.index = i; + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buffer.m.offset = cam->frame[i].paddress; + cam->frame[i].index = i; + } + + return 0; +} + +/*! + * Free frame buffers status + * + * @param cam Structure cam_data * + * + * @return none + */ +static void mxc_free_frames(cam_data * cam) +{ + int i; + + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + } + + cam->enc_counter = 0; + cam->skip_frame = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); +} + +/*! + * Return the buffer status + * + * @param cam Structure cam_data * + * @param buf Structure v4l2_buffer * + * + * @return status 0 success, EINVAL failed. + */ +static int mxc_v4l2_buffer_status(cam_data * cam, struct v4l2_buffer *buf) +{ + /* check range */ + if (buf->index < 0 || buf->index >= FRAME_NUM) { + pr_debug("mxc_v4l2_buffer_status buffers not allocated\n"); + return -EINVAL; + } + + memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); + return 0; +} + +/*! + * start the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamon(cam_data * cam) +{ + struct mxc_v4l_frame *frame; + int err = 0; + + if (!cam) + return -EIO; + + if (list_empty(&cam->ready_q)) { + printk(KERN_ERR "mxc_streamon buffer not been queued yet\n"); + return -EINVAL; + } + + cam->capture_pid = current->pid; + + if (cam->enc_enable) { + err = cam->enc_enable(cam); + if (err != 0) { + return err; + } + } + + cam->ping_pong_csi = 0; + cam->overflow = 0; + if (cam->enc_update_eba) { + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + err = cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi); + + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + err |= + cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi); + } else { + return -EINVAL; + } + + return err; +} + +/*! + * Shut down the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamoff(cam_data * cam) +{ + int err = 0; + + if (!cam) + return -EIO; + + if (cam->enc_disable) { + err = cam->enc_disable(cam); + } + mxc_free_frames(cam); + return err; +} + +/*! + * Valid whether the palette is supported + * + * @param palette pixel format + * + * @return 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + /* + * MX27 PrP channel 2 supports YUV444, but YUV444 is not + * defined by V4L2 :( + */ + return ((palette == V4L2_PIX_FMT_YUYV) || + (palette == V4L2_PIX_FMT_YUV420)); +} + +/*! + * Valid and adjust the overlay window size, position + * + * @param cam structure cam_data * + * @param win struct v4l2_window * + * + * @return 0 + */ +static int verify_preview(cam_data * cam, struct v4l2_window *win) +{ + if (cam->output >= num_registered_fb) { + pr_debug("verify_preview No matched.\n"); + return -EINVAL; + } + cam->overlay_fb = (struct fb_info *)registered_fb[cam->output]; + + /* TODO: suppose 16bpp, 4 bytes alignment */ + win->w.left &= ~0x1; + + if (win->w.width + win->w.left > cam->overlay_fb->var.xres) + win->w.width = cam->overlay_fb->var.xres - win->w.left; + if (win->w.height + win->w.top > cam->overlay_fb->var.yres) + win->w.height = cam->overlay_fb->var.yres - win->w.top; + + /* + * TODO: suppose 16bpp. Rounded down to a multiple of 2 pixels for + * width according to PrP limitations. + */ + if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) + || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) + win->w.height &= ~0x1; + else + win->w.width &= ~0x1; + + return 0; +} + +/*! + * start the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int start_preview(cam_data * cam) +{ + int err = 0; + + err = prp_vf_select(cam); + if (err != 0) + return err; + + cam->overlay_pid = current->pid; + err = cam->vf_start_sdc(cam); + + return err; +} + +/*! + * shut down the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int stop_preview(cam_data * cam) +{ + int err = 0; + + err = prp_vf_deselect(cam); + return err; +} + +/*! + * V4L2 - mxc_v4l2_g_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_g_fmt(cam_data * cam, struct v4l2_format *f) +{ + int retval = 0; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->fmt.pix.width = cam->v2f.fmt.pix.width; + f->fmt.pix.height = cam->v2f.fmt.pix.height; + f->fmt.pix.sizeimage = cam->v2f.fmt.pix.sizeimage; + f->fmt.pix.pixelformat = cam->v2f.fmt.pix.pixelformat; + f->fmt.pix.bytesperline = cam->v2f.fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + retval = 0; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + f->fmt.win = cam->win; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/*! + * V4L2 - mxc_v4l2_s_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_fmt(cam_data * cam, struct v4l2_format *f) +{ + int retval = 0; + int size = 0; + int bytesperline = 0; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (!valid_mode(f->fmt.pix.pixelformat)) { + pr_debug("mxc_v4l2_s_fmt: format not supported\n"); + retval = -EINVAL; + } + + if (cam->rotation != V4L2_MXC_ROTATE_NONE) + pr_debug("mxc_v4l2_s_fmt: capture rotation ignored\n"); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + f->fmt.pix.width &= ~0x1; /* Multiple of 2 */ + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_YUV420: + f->fmt.pix.width &= ~0x7; /* Multiple of 8 */ + f->fmt.pix.height &= ~0x1; /* Multiple of 2 */ + size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; + bytesperline = f->fmt.pix.width * 3 / 2; + break; + default: + /* Suppose it's YUV444 or 32bpp */ + size = f->fmt.pix.width * f->fmt.pix.height * 4; + bytesperline = f->fmt.pix.width * 4; + pr_info("mxc_v4l2_s_fmt: default assume" + " to be YUV444 interleaved.\n"); + break; + } + + if (f->fmt.pix.bytesperline < bytesperline) { + f->fmt.pix.bytesperline = bytesperline; + } else { + bytesperline = f->fmt.pix.bytesperline; + } + + if (f->fmt.pix.sizeimage > size) { + pr_debug("mxc_v4l2_s_fmt: sizeimage bigger than" + " needed.\n"); + size = f->fmt.pix.sizeimage; + } + f->fmt.pix.sizeimage = size; + + cam->v2f.fmt.pix.sizeimage = size; + cam->v2f.fmt.pix.bytesperline = bytesperline; + cam->v2f.fmt.pix.width = f->fmt.pix.width; + cam->v2f.fmt.pix.height = f->fmt.pix.height; + cam->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat; + retval = 0; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + retval = verify_preview(cam, &f->fmt.win); + cam->win = f->fmt.win; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/*! + * get control param + * + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_get_v42l_control(cam_data * cam, struct v4l2_control *c) +{ + int status = 0; + + switch (c->id) { + case V4L2_CID_HFLIP: + c->value = cam->rotation; + break; + case V4L2_CID_VFLIP: + c->value = cam->rotation; + break; + case V4L2_CID_MXC_ROT: + c->value = cam->rotation; + break; + case V4L2_CID_BRIGHTNESS: + c->value = cam->bright; + break; + case V4L2_CID_HUE: + c->value = cam->hue; + break; + case V4L2_CID_CONTRAST: + c->value = cam->contrast; + break; + case V4L2_CID_SATURATION: + c->value = cam->saturation; + break; + case V4L2_CID_RED_BALANCE: + c->value = cam->red; + break; + case V4L2_CID_BLUE_BALANCE: + c->value = cam->blue; + break; + case V4L2_CID_BLACK_LEVEL: + c->value = cam->ae_mode; + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + c->value = cam->awb_enable; + break; + case V4L2_CID_AUTOGAIN: + c->value = cam->ae_enable; + break; + case V4L2_CID_MXC_FLICKER: + c->value = cam->flicker_ctrl; + break; + default: + status = -EINVAL; + } + return status; +} + +/*! + * V4L2 - set_control function + * V4L2_CID_MXC_ROT is the extention for rotation/mirroring. + * + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_set_v42l_control(cam_data * cam, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + if (c->value == 1) { + if ((cam->rotation != V4L2_MXC_ROTATE_VERT_FLIP) && + (cam->rotation != V4L2_MXC_ROTATE_180)) + cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP; + else + cam->rotation = V4L2_MXC_ROTATE_180; + } else { + if (cam->rotation == V4L2_MXC_ROTATE_HORIZ_FLIP) + cam->rotation = V4L2_MXC_ROTATE_NONE; + else if (cam->rotation == V4L2_MXC_ROTATE_180) + cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP; + } + break; + case V4L2_CID_VFLIP: + if (c->value == 1) { + if ((cam->rotation != V4L2_MXC_ROTATE_HORIZ_FLIP) && + (cam->rotation != V4L2_MXC_ROTATE_180)) + cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP; + else + cam->rotation = V4L2_MXC_ROTATE_180; + } else { + if (cam->rotation == V4L2_MXC_ROTATE_VERT_FLIP) + cam->rotation = V4L2_MXC_ROTATE_NONE; + if (cam->rotation == V4L2_MXC_ROTATE_180) + cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP; + } + break; + case V4L2_CID_MXC_ROT: + switch (c->value) { + case V4L2_MXC_ROTATE_NONE: + case V4L2_MXC_ROTATE_VERT_FLIP: + case V4L2_MXC_ROTATE_HORIZ_FLIP: + case V4L2_MXC_ROTATE_180: + case V4L2_MXC_ROTATE_90_RIGHT: + case V4L2_MXC_ROTATE_90_RIGHT_VFLIP: + case V4L2_MXC_ROTATE_90_RIGHT_HFLIP: + case V4L2_MXC_ROTATE_90_LEFT: + cam->rotation = c->value; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_HUE: + cam->hue = c->value; + break; + case V4L2_CID_CONTRAST: + cam->contrast = c->value; + break; + case V4L2_CID_BRIGHTNESS: + cam->bright = c->value; + case V4L2_CID_SATURATION: + cam->saturation = c->value; + case V4L2_CID_RED_BALANCE: + cam->red = c->value; + case V4L2_CID_BLUE_BALANCE: + cam->blue = c->value; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + cam->cam_sensor->set_color(cam->bright, cam->saturation, + cam->red, cam->green, cam->blue); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_BLACK_LEVEL: + cam->ae_mode = c->value & 0x03; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->set_ae_mode) + cam->cam_sensor->set_ae_mode(cam->ae_mode); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_MXC_FLASH: + break; + case V4L2_CID_AUTOGAIN: + cam->ae_enable = c->value; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->set_ae) + cam->cam_sensor->set_ae(cam->ae_enable); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_MXC_GAIN_LIMIT: + cam->ae_limit = c->value; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->set_ae_limit) + cam->cam_sensor->set_ae_limit(cam->ae_limit); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + cam->awb_enable = c->value; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->set_awb) + cam->cam_sensor->set_awb(cam->awb_enable); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_MXC_FLICKER: + cam->flicker_ctrl = c->value; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->flicker_control) + cam->cam_sensor->flicker_control(cam->flicker_ctrl); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + default: + return -EINVAL; + } + return 0; +} + +/*! + * V4L2 - mxc_v4l2_s_param function + * + * @param cam structure cam_data * + * + * @param parm structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_param(cam_data * cam, struct v4l2_streamparm *parm) +{ + sensor_interface *param; + csi_signal_cfg_t csi_param; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_debug("mxc_v4l2_s_param invalid type\n"); + return -EINVAL; + } + + if (parm->parm.capture.timeperframe.denominator > + cam->standard.frameperiod.denominator) { + pr_debug("mxc_v4l2_s_param frame rate %d larger " + "than standard supported %d\n", + parm->parm.capture.timeperframe.denominator, + cam->standard.frameperiod.denominator); + return -EINVAL; + } + + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + + csi_enable_mclk(CSI_MCLK_I2C, true, true); + param = cam->cam_sensor->config + (&parm->parm.capture.timeperframe.denominator, + parm->parm.capture.capturemode); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + + cam->streamparm.parm.capture.timeperframe = + parm->parm.capture.timeperframe; + + if ((parm->parm.capture.capturemode != 0) && + (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY)) { + pr_debug("mxc_v4l2_s_param frame un-supported capture mode\n"); + return -EINVAL; + } + + if (parm->parm.capture.capturemode == + cam->streamparm.parm.capture.capturemode) { + return 0; + } + + /* resolution changed, so need to re-program the CSI */ + csi_param.sens_clksrc = 0; + csi_param.clk_mode = param->clk_mode; + csi_param.pixclk_pol = param->pixclk_pol; + csi_param.data_width = param->data_width; + csi_param.data_pol = param->data_pol; + csi_param.ext_vsync = param->ext_vsync; + csi_param.Vsync_pol = param->Vsync_pol; + csi_param.Hsync_pol = param->Hsync_pol; + csi_init_interface(param->width, param->height, param->pixel_fmt, + csi_param); + + if (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY) { + cam->streamparm.parm.capture.capturemode = 0; + } else { + cam->streamparm.parm.capture.capturemode = + V4L2_MODE_HIGHQUALITY; + cam->streamparm.parm.capture.extendedmode = + parm->parm.capture.extendedmode; + cam->streamparm.parm.capture.readbuffers = 1; + } + return 0; +} + +/*! + * Dequeue one V4L capture buffer + * + * @param cam structure cam_data * + * @param buf structure v4l2_buffer * + * + * @return status 0 success, EINVAL invalid frame number, + * ETIME timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_dqueue(cam_data * cam, struct v4l2_buffer *buf) +{ + int retval = 0; + struct mxc_v4l_frame *frame; + + if (!wait_event_interruptible_timeout(cam->enc_queue, + cam->enc_counter != 0, 10 * HZ)) { + if ((dq_timeout_cnt & 0x1f) == 0) + printk(KERN_ERR + "mxc_v4l_dqueue timeout enc_counter %x\n", + cam->enc_counter); + dq_timeout_cnt++; + return -ETIME; + } else if (signal_pending(current)) { + if (dq_intr_cnt == 0) + printk(KERN_ERR + "mxc_v4l_dqueue() interrupt received %d\n", + dq_intr_cnt); + dq_intr_cnt++; + return -ERESTARTSYS; + } + + cam->enc_counter--; + + frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); + list_del(cam->done_q.next); + if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { + frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; + } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + printk(KERN_ERR "VIDIOC_DQBUF: Buffer not filled.\n"); + frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { + printk(KERN_ERR "VIDIOC_DQBUF: Buffer not queued.\n"); + retval = -EINVAL; + } + + buf->bytesused = cam->v2f.fmt.pix.sizeimage; + buf->index = frame->index; + buf->flags = frame->buffer.flags; + buf->m = cam->frame[frame->index].buffer.m; + return retval; +} + +/*! + * Get the current attached camera device + * + * @param inode struct i2c_client * + * + * @param int int * p_input_index + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static int mxc_get_video_input(cam_data * cam) +{ + int retval = 0; + csi_enable_mclk(CSI_MCLK_I2C, true, true); + retval = cam->cam_sensor->get_status(); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + return retval; +} + +/*! + * V4L interface - open function + * + * @param inode structure inode * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_open(struct inode *inode, struct file *file) +{ + sensor_interface *param; + csi_signal_cfg_t csi_param; + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + int err = 0; + + dq_intr_cnt = 0; + dq_timeout_cnt = 0; + empty_wq_cnt = 0; + if (!cam) { + pr_info("Internal error, cam_data not found!\n"); + return -ENODEV; + } + + err = mxc_get_video_input(cam); + if (0 != err) + return -ENODEV; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + if (signal_pending(current)) + goto oops; + + if (cam->open_count++ == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + + err = prp_enc_select(cam); + + cam->enc_counter = 0; + cam->skip_frame = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); + + csi_enable_mclk(CSI_MCLK_I2C, true, true); + param = cam->cam_sensor->reset(); + if (param == NULL) { + cam->open_count--; + csi_enable_mclk(CSI_MCLK_I2C, false, false); + err = -ENODEV; + goto oops; + } + csi_param.sens_clksrc = 0; + csi_param.clk_mode = param->clk_mode; + csi_param.pixclk_pol = param->pixclk_pol; + csi_param.data_width = param->data_width; + csi_param.data_pol = param->data_pol; + csi_param.ext_vsync = param->ext_vsync; + csi_param.Vsync_pol = param->Vsync_pol; + csi_param.Hsync_pol = param->Hsync_pol; + csi_init_interface(param->width, param->height, + param->pixel_fmt, csi_param); + cam->cam_sensor->get_color(&cam->bright, &cam->saturation, + &cam->red, &cam->green, &cam->blue); + if (cam->cam_sensor->get_ae_mode) + cam->cam_sensor->get_ae_mode(&cam->ae_mode); + cam->cam_sensor->get_control_params(&cam->ae_enable, + &cam->awb_enable, + &cam->flicker_ctrl); + csi_enable_mclk(CSI_MCLK_I2C, false, false); + prp_init(cam); + + } + + file->private_data = dev; + oops: + up(&cam->busy_lock); + return err; +} + +/*! + * V4L interface - close function + * + * @param inode struct inode * + * @param file struct file * + * + * @return 0 success + */ +static int mxc_v4l_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + int err = 0; + cam_data *cam = dev->priv; + + /* for the case somebody hit the ctrl C */ + if (cam->overlay_pid == current->pid) { + err = stop_preview(cam); + cam->overlay_on = false; + } + if (cam->capture_pid == current->pid) { + err |= mxc_streamoff(cam); + cam->capture_on = false; + wake_up_interruptible(&cam->enc_queue); + wake_up_interruptible(&cam->overflow_queue); + } + + if (--cam->open_count == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + pr_debug("mxc_v4l_close: release resource\n"); + + err |= prp_enc_deselect(cam); + + mxc_free_frame_buf(cam); + file->private_data = NULL; + + /* capture off */ + wake_up_interruptible(&cam->enc_queue); + wake_up_interruptible(&cam->overflow_queue); + mxc_free_frames(cam); + cam->enc_counter++; + prp_exit(cam); + } + + return err; +} + +#ifdef CONFIG_VIDEO_MXC_CSI_DMA +#include + +#define CSI_DMA_STATUS_IDLE 0 /* DMA is not started */ +#define CSI_DMA_STATUS_WORKING 1 /* DMA is transfering the data */ +#define CSI_DMA_STATUS_DONE 2 /* One frame completes successfully */ +#define CSI_DMA_STATUS_ERROR 3 /* Error occurs during the DMA */ + +/* + * Sometimes the start of the DMA is not synchronized with the CSI + * SOF (Start of Frame) interrupt which will lead to incorrect + * captured image. In this case the driver will re-try capturing + * another frame. The following macro defines the maximum re-try + * times. + */ +#define CSI_DMA_RETRY 8 + +/* + * Size of the physical contiguous memory area used to hold image data + * transfered by DMA. It can be less than the size of the image data. + */ +#define CSI_MEM_SIZE (1024 * 600) + +/* Number of bytes for one DMA transfer */ +#define CSI_DMA_LENGTH (1024 * 200) + +static int g_dma_channel = 0; +static int g_dma_status = CSI_DMA_STATUS_DONE; +static volatile int g_dma_completed; /* number of completed DMA transfers */ +static volatile int g_dma_copied; /* number of copied DMA transfers */ +static struct tasklet_struct g_dma_tasklet; +static char *g_user_buf; /* represents the buf passed by read() */ +static int g_user_count; /* represents the count passed by read() */ + +/*! + * @brief setup the DMA to transfer data + * There may be more than one DMA to transfer the whole image. Those + * DMAs work like chain. This function is used to setup the DMA in + * case there is enough space to hold the data. + * @param data pointer to the cam structure + */ +static void mxc_csi_dma_chaining(void *data) +{ + cam_data *cam = (cam_data *) data; + int count, chained = 0; + int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH; + mxc_dma_requestbuf_t dma_request; + + while (chained * CSI_DMA_LENGTH < g_user_count) { + /* + * Calculate how many bytes the DMA should transfer. It may + * be less than CSI_DMA_LENGTH if the DMA is the last one. + */ + if ((chained + 1) * CSI_DMA_LENGTH > g_user_count) + count = g_user_count - chained * CSI_DMA_LENGTH; + else + count = CSI_DMA_LENGTH; + pr_debug("%s() DMA chained count = %d\n", __FUNCTION__, count); + + /* Config DMA */ + memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t)); + dma_request.dst_addr = cam->still_buf + + (chained % max_dma) * CSI_DMA_LENGTH; + dma_request.src_addr = (dma_addr_t) CSI_CSIRXFIFO_PHYADDR; + dma_request.num_of_bytes = count; + mxc_dma_config(g_dma_channel, &dma_request, 1, + MXC_DMA_MODE_READ); + + chained++; + } +} + +/*! + * @brief Copy image data from physical contiguous memory to user space buffer + * Once the data are copied, there will be more spare space in the + * physical contiguous memory to receive data from DMA. + * @param data pointer to the cam structure + */ +static void mxc_csi_dma_task(unsigned long data) +{ + cam_data *cam = (cam_data *) data; + int count; + int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH; + + while (g_dma_copied < g_dma_completed) { + /* + * Calculate how many bytes the DMA has transfered. It may + * be less than CSI_DMA_LENGTH if the DMA is the last one. + */ + if ((g_dma_copied + 1) * CSI_DMA_LENGTH > g_user_count) + count = g_user_count - g_dma_copied * CSI_DMA_LENGTH; + else + count = CSI_DMA_LENGTH; + if (copy_to_user(g_user_buf + g_dma_copied * CSI_DMA_LENGTH, + cam->still_buf_vaddr + (g_dma_copied % max_dma) + * CSI_DMA_LENGTH, count)) + pr_debug("Warning: some bytes not copied\n"); + + g_dma_copied++; + } + + /* If the whole image has been captured */ + if (g_dma_copied * CSI_DMA_LENGTH >= g_user_count) { + cam->still_counter++; + wake_up_interruptible(&cam->still_queue); + } + + pr_debug("%s() DMA completed = %d copied = %d\n", + __FUNCTION__, g_dma_completed, g_dma_copied); +} + +/*! + * @brief DMA interrupt callback function + * @param data pointer to the cam structure + * @param error DMA error flag + * @param count number of bytes transfered by the DMA + */ +static void mxc_csi_dma_callback(void *data, int error, unsigned int count) +{ + cam_data *cam = (cam_data *) data; + int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH; + unsigned long lock_flags; + + spin_lock_irqsave(&cam->int_lock, lock_flags); + + g_dma_completed++; + + if (error != MXC_DMA_DONE) { + g_dma_status = CSI_DMA_STATUS_ERROR; + pr_debug("%s() DMA error\n", __FUNCTION__); + } + + /* If the whole image has been captured */ + if ((g_dma_status != CSI_DMA_STATUS_ERROR) + && (g_dma_completed * CSI_DMA_LENGTH >= g_user_count)) + g_dma_status = CSI_DMA_STATUS_DONE; + + if ((g_dma_status == CSI_DMA_STATUS_WORKING) && + (g_dma_completed >= g_dma_copied + max_dma)) { + g_dma_status = CSI_DMA_STATUS_ERROR; + pr_debug("%s() Previous buffer over written\n", __FUNCTION__); + } + + /* Schedule the tasklet */ + tasklet_schedule(&g_dma_tasklet); + + spin_unlock_irqrestore(&cam->int_lock, lock_flags); + + pr_debug("%s() count = %d bytes\n", __FUNCTION__, count); +} + +/*! + * @brief CSI interrupt callback function + * @param data pointer to the cam structure + * @param status CSI interrupt status + */ +static void mxc_csi_irq_callback(void *data, unsigned long status) +{ + cam_data *cam = (cam_data *) data; + unsigned long lock_flags; + + spin_lock_irqsave(&cam->int_lock, lock_flags); + + /* Wait for SOF (Start of Frame) interrupt to sync the image */ + if (status & BIT_SOF_INT) { + if (g_dma_status == CSI_DMA_STATUS_IDLE) { + /* Start DMA transfer to capture image */ + mxc_dma_enable(g_dma_channel); + g_dma_status = CSI_DMA_STATUS_WORKING; + pr_debug("%s() DMA started.\n", __FUNCTION__); + } else if (g_dma_status == CSI_DMA_STATUS_WORKING) { + /* + * Another SOF occurs during DMA transfer. In this + * case the image is not synchronized so need to + * report error and probably try again. + */ + g_dma_status = CSI_DMA_STATUS_ERROR; + pr_debug("%s() Image is not synchronized with DMA - " + "SOF before DMA completes\n", __FUNCTION__); + } + } + + spin_unlock_irqrestore(&cam->int_lock, lock_flags); + + pr_debug("%s() g_dma_status = %d\n", __FUNCTION__, g_dma_status); +} + +/*! + * V4L interface - read function + * + * @param file struct file * + * @param read buf char * + * @param count size_t + * @param ppos structure loff_t * + * + * @return bytes read + */ +static ssize_t +mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t * ppos) +{ + int err = 0; + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + int retry = CSI_DMA_RETRY; + + g_user_buf = buf; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + /* Video capture and still image capture are exclusive */ + if (cam->capture_on == true) { + err = -EBUSY; + goto exit0; + } + + /* The CSI-DMA can not do CSC */ + if (cam->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) { + pr_info("mxc_v4l_read support YUYV pixel format only\n"); + err = -EINVAL; + goto exit0; + } + + /* The CSI-DMA can not do resize or crop */ + if ((cam->v2f.fmt.pix.width != cam->crop_bounds.width) + || (cam->v2f.fmt.pix.height != cam->crop_bounds.height)) { + pr_info("mxc_v4l_read resize is not supported\n"); + pr_info("supported image size width = %d height = %d\n", + cam->crop_bounds.width, cam->crop_bounds.height); + err = -EINVAL; + goto exit0; + } + if ((cam->crop_current.left != cam->crop_bounds.left) + || (cam->crop_current.width != cam->crop_bounds.width) + || (cam->crop_current.top != cam->crop_bounds.top) + || (cam->crop_current.height != cam->crop_bounds.height)) { + pr_info("mxc_v4l_read cropping is not supported\n"); + err = -EINVAL; + goto exit0; + } + + cam->still_buf_vaddr = dma_alloc_coherent(0, + PAGE_ALIGN(CSI_MEM_SIZE), + &cam->still_buf, + GFP_DMA | GFP_KERNEL); + + if (!cam->still_buf_vaddr) { + pr_info("mxc_v4l_read failed at allocate still_buf\n"); + err = -ENOBUFS; + goto exit0; + } + + /* Initialize DMA */ + g_dma_channel = mxc_dma_request(MXC_DMA_CSI_RX, "CSI RX DMA"); + if (g_dma_channel < 0) { + pr_debug("mxc_v4l_read failed to request DMA channel\n"); + err = -EIO; + goto exit1; + } + + err = mxc_dma_callback_set(g_dma_channel, + mxc_csi_dma_callback, + cam); + if (err != 0) { + pr_debug("mxc_v4l_read failed to set DMA callback\n"); + err = -EIO; + goto exit2; + } + + g_user_buf = buf; + if (cam->v2f.fmt.pix.sizeimage < count) + g_user_count = cam->v2f.fmt.pix.sizeimage; + else + g_user_count = count & ~0x3; + + tasklet_init(&g_dma_tasklet, mxc_csi_dma_task, (unsigned long)cam); + g_dma_status = CSI_DMA_STATUS_DONE; + csi_set_callback(mxc_csi_irq_callback, cam); + csi_enable_prpif(0); + + /* clear current SOF first */ + csi_clear_status(BIT_SOF_INT); + csi_enable_mclk(CSI_MCLK_RAW, true, true); + + do { + g_dma_completed = g_dma_copied = 0; + mxc_csi_dma_chaining(cam); + cam->still_counter = 0; + g_dma_status = CSI_DMA_STATUS_IDLE; + + if (!wait_event_interruptible_timeout(cam->still_queue, + cam->still_counter != 0, + 10 * HZ)) { + pr_info("mxc_v4l_read timeout counter %x\n", + cam->still_counter); + err = -ETIME; + goto exit3; + } + + if (g_dma_status == CSI_DMA_STATUS_DONE) + break; + + if (retry-- == 0) + break; + + pr_debug("Now retry image capture\n"); + } while (1); + + if (g_dma_status != CSI_DMA_STATUS_DONE) + err = -EIO; + + exit3: + csi_enable_prpif(1); + g_dma_status = CSI_DMA_STATUS_DONE; + csi_set_callback(0, 0); + csi_enable_mclk(CSI_MCLK_RAW, false, false); + tasklet_kill(&g_dma_tasklet); + + exit2: + mxc_dma_free(g_dma_channel); + + exit1: + dma_free_coherent(0, PAGE_ALIGN(CSI_MEM_SIZE), + cam->still_buf_vaddr, cam->still_buf); + cam->still_buf = 0; + + exit0: + up(&cam->busy_lock); + if (err < 0) + return err; + else + return g_user_count; +} +#else +/*! + * V4L interface - read function + * + * @param file struct file * + * @param read buf char * + * @param count size_t + * @param ppos structure loff_t * + * + * @return bytes read + */ +static ssize_t +mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t * ppos) +{ + int err = 0; + u8 *v_address; + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + /* Video capture and still image capture are exclusive */ + if (cam->capture_on == true) { + err = -EBUSY; + goto exit0; + } + + v_address = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->still_buf, GFP_DMA | GFP_KERNEL); + + if (!v_address) { + pr_info("mxc_v4l_read failed at allocate still_buf\n"); + err = -ENOBUFS; + goto exit0; + } + + if (prp_still_select(cam)) { + err = -EIO; + goto exit1; + } + + cam->still_counter = 0; + if (cam->csi_start(cam)) { + err = -EIO; + goto exit2; + } + + if (!wait_event_interruptible_timeout(cam->still_queue, + cam->still_counter != 0, + 10 * HZ)) { + pr_info("mxc_v4l_read timeout counter %x\n", + cam->still_counter); + err = -ETIME; + goto exit2; + } + err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage); + + exit2: + prp_still_deselect(cam); + + exit1: + dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address, + cam->still_buf); + cam->still_buf = 0; + + exit0: + up(&cam->busy_lock); + if (err < 0) + return err; + else + return (cam->v2f.fmt.pix.sizeimage - err); +} +#endif /* CONFIG_VIDEO_MXC_CSI_DMA */ + +/*! + * V4L interface - ioctl function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @param ioctlnr unsigned int + * + * @param arg void * + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static int +mxc_v4l_do_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + int retval = 0; + unsigned long lock_flags; + + if (!cam) + return -EBADF; + + wait_event_interruptible(cam->power_queue, cam->low_power == false); + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + switch (ioctlnr) { + /*! + * V4l2 VIDIOC_QUERYCAP ioctl + */ + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *cap = arg; + strcpy(cap->driver, "mxc_v4l2"); + cap->version = KERNEL_VERSION(0, 1, 11); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE; + cap->card[0] = '\0'; + cap->bus_info[0] = '\0'; + retval = 0; + break; + } + + /*! + * V4l2 VIDIOC_G_FMT ioctl + */ + case VIDIOC_G_FMT:{ + struct v4l2_format *gf = arg; + retval = mxc_v4l2_g_fmt(cam, gf); + break; + } + + /*! + * V4l2 VIDIOC_S_FMT ioctl + */ + case VIDIOC_S_FMT:{ + struct v4l2_format *sf = arg; + retval = mxc_v4l2_s_fmt(cam, sf); + break; + } + + /*! + * V4l2 VIDIOC_REQBUFS ioctl + */ + case VIDIOC_REQBUFS:{ + struct v4l2_requestbuffers *req = arg; + int i; + if (req->count > FRAME_NUM) { + pr_info("VIDIOC_REQBUFS: not enough buffer\n"); + req->count = FRAME_NUM; + } + + if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + ((req->memory != V4L2_MEMORY_MMAP) + && (req->memory != V4L2_MEMORY_USERPTR))) { + pr_debug("VIDIOC_REQBUFS: wrong buffer type\n"); + retval = -EINVAL; + break; + } + + mxc_streamoff(cam); + mxc_free_frame_buf(cam); + + if (req->memory == V4L2_MEMORY_MMAP) + retval = + mxc_allocate_frame_buf(cam, req->count); + else if (req->memory == V4L2_MEMORY_USERPTR) { + for (i = 0; i < req->count; i++) { + cam->frame[i].vaddress = 0; + cam->frame[i].buffer.index = i; + cam->frame[i].buffer.type = + V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buffer.flags = 0; + cam->frame[i].buffer.length = 0; + cam->frame[i].buffer.memory = + V4L2_MEMORY_USERPTR; + cam->frame[i].buffer.m.offset = 0; + cam->frame[i].index = i; + } + } + break; + } + + /*! + * V4l2 VIDIOC_QUERYBUF ioctl + */ + case VIDIOC_QUERYBUF:{ + struct v4l2_buffer *buf = arg; + int index = buf->index; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_debug + ("VIDIOC_QUERYBUFS: wrong buffer type\n"); + retval = -EINVAL; + break; + } + + memset(buf, 0, sizeof(buf)); + buf->index = index; + + down(&cam->param_lock); + retval = mxc_v4l2_buffer_status(cam, buf); + up(&cam->param_lock); + break; + } + + /*! + * V4l2 VIDIOC_QBUF ioctl + */ + case VIDIOC_QBUF:{ + struct v4l2_buffer *buf = arg; + int index = buf->index; + + pr_debug("VIDIOC_QBUF: %d\n", buf->index); + wait_event_interruptible(cam->overflow_queue, + cam->overflow == 0); + spin_lock_irqsave(&cam->int_lock, lock_flags); + if (cam->frame[index].buffer.memory == V4L2_MEMORY_MMAP) { + if ((cam->frame[index].buffer.flags & 0x7) == + V4L2_BUF_FLAG_MAPPED) { + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + if (cam->skip_frame > 0) { + prphw_enable(PRP_CHANNEL_2); + list_add_tail(&cam-> + frame[index]. + queue, + &cam->working_q); + retval = + cam->enc_update_eba(cam-> + frame + [index]. + paddress, + &cam-> + ping_pong_csi); + cam->skip_frame = 0; + } else { + list_add_tail(&cam-> + frame[index]. + queue, + &cam->ready_q); + } + } else if (cam->frame[index].buffer.flags & + V4L2_BUF_FLAG_QUEUED) { + pr_debug + ("VIDIOC_QBUF: buffer already queued\n"); + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_DONE) { + pr_debug + ("VIDIOC_QBUF: overwrite done buffer.\n"); + cam->frame[index].buffer.flags &= + ~V4L2_BUF_FLAG_DONE; + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + } + } else { + cam->frame[index].buffer.flags = + (V4L2_BUF_FLAG_MAPPED | + V4L2_BUF_FLAG_QUEUED); + cam->frame[index].buffer.m.offset = + buf->m.offset; + cam->frame[index].paddress = buf->m.offset; + cam->frame[index].buffer.length = buf->length; + if (cam->skip_frame > 0) { + prphw_enable(PRP_CHANNEL_2); + list_add_tail(&cam->frame[index].queue, + &cam->working_q); + retval = + cam->enc_update_eba(cam-> + frame[index]. + paddress, + &cam-> + ping_pong_csi); + cam->skip_frame = 0; + } else { + list_add_tail(&cam->frame[index].queue, + &cam->ready_q); + } + } + buf->flags = cam->frame[index].buffer.flags; + spin_unlock_irqrestore(&cam->int_lock, lock_flags); + break; + } + + /*! + * V4l2 VIDIOC_DQBUF ioctl + */ + case VIDIOC_DQBUF:{ + struct v4l2_buffer *buf = arg; + + retval = mxc_v4l_dqueue(cam, buf); + + break; + } + + /*! + * V4l2 VIDIOC_STREAMON ioctl + */ + case VIDIOC_STREAMON:{ + cam->capture_on = true; + retval = mxc_streamon(cam); + break; + } + + /*! + * V4l2 VIDIOC_STREAMOFF ioctl + */ + case VIDIOC_STREAMOFF:{ + retval = mxc_streamoff(cam); + cam->capture_on = false; + break; + } + + /*! + * V4l2 VIDIOC_G_CTRL ioctl + */ + case VIDIOC_G_CTRL:{ + retval = mxc_get_v42l_control(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_S_CTRL ioctl + */ + case VIDIOC_S_CTRL:{ + retval = mxc_set_v42l_control(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_CROPCAP ioctl + */ + case VIDIOC_CROPCAP:{ + struct v4l2_cropcap *cap = arg; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + cap->bounds = cam->crop_bounds; + cap->defrect = cam->crop_defrect; + break; + } + + /*! + * V4l2 VIDIOC_G_CROP ioctl + */ + case VIDIOC_G_CROP:{ + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + crop->c = cam->crop_current; + break; + } + + /*! + * V4l2 VIDIOC_S_CROP ioctl + */ + case VIDIOC_S_CROP:{ + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = &cam->crop_bounds; + int i; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + + crop->c.top = (crop->c.top < b->top) ? b->top + : crop->c.top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top + b->height - crop->c.top) + crop->c.height = + b->top + b->height - crop->c.top; + + crop->c.left = (crop->c.left < b->left) ? b->left + : crop->c.left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + crop->c.width &= ~0x1; + + /* + * MX27 PrP limitation: + * The right spare space (CSI_FRAME_X_SIZE + * - SOURCE_LINE_STRIDE - PICTURE_X_SIZE)) must be + * multiple of 32. + * So we tune the crop->c.left value to the closest + * desired cropping value and meet the PrP requirement. + */ + i = ((b->left + b->width) + - (crop->c.left + crop->c.width)) % 32; + if (i <= 16) { + if (crop->c.left + crop->c.width + i + <= b->left + b->width) + crop->c.left += i; + else if (crop->c.left - (32 - i) >= b->left) + crop->c.left -= 32 - i; + else { + retval = -EINVAL; + break; + } + } else { + if (crop->c.left - (32 - i) >= b->left) + crop->c.left -= 32 - i; + else if (crop->c.left + crop->c.width + i + <= b->left + b->width) + crop->c.left += i; + else { + retval = -EINVAL; + break; + } + } + + cam->crop_current = crop->c; + + break; + } + + /*! + * V4l2 VIDIOC_OVERLAY ioctl + */ + case VIDIOC_OVERLAY:{ + int *on = arg; + if (*on) { + cam->overlay_on = true; + retval = start_preview(cam); + } + if (!*on) { + retval = stop_preview(cam); + cam->overlay_on = false; + } + break; + } + + /*! + * V4l2 VIDIOC_G_FBUF ioctl + */ + case VIDIOC_G_FBUF:{ + struct v4l2_framebuffer *fb = arg; + struct fb_var_screeninfo *var; + + if (cam->output >= num_registered_fb) { + retval = -EINVAL; + break; + } + + var = ®istered_fb[cam->output]->var; + cam->v4l2_fb.fmt.width = var->xres; + cam->v4l2_fb.fmt.height = var->yres; + cam->v4l2_fb.fmt.bytesperline = + var->xres_virtual * var->bits_per_pixel; + cam->v4l2_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; + *fb = cam->v4l2_fb; + break; + } + + /*! + * V4l2 VIDIOC_S_FBUF ioctl + */ + case VIDIOC_S_FBUF:{ + struct v4l2_framebuffer *fb = arg; + cam->v4l2_fb.flags = fb->flags; + cam->v4l2_fb.fmt.pixelformat = fb->fmt.pixelformat; + break; + } + + case VIDIOC_G_PARM:{ + struct v4l2_streamparm *parm = arg; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_debug("VIDIOC_G_PARM invalid type\n"); + retval = -EINVAL; + break; + } + parm->parm.capture = cam->streamparm.parm.capture; + break; + } + case VIDIOC_S_PARM:{ + struct v4l2_streamparm *parm = arg; + retval = mxc_v4l2_s_param(cam, parm); + break; + } + + /* linux v4l2 bug, kernel c0485619 user c0405619 */ + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *e = arg; + *e = cam->standard; + pr_debug("VIDIOC_ENUMSTD call\n"); + retval = 0; + break; + } + + case VIDIOC_G_STD:{ + v4l2_std_id *e = arg; + *e = cam->standard.id; + break; + } + + case VIDIOC_S_STD:{ + break; + } + + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *output = arg; + + if (output->index >= num_registered_fb) { + retval = -EINVAL; + break; + } + + strncpy(output->name, + registered_fb[output->index]->fix.id, 31); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->audioset = 0; + output->modulator = 0; + output->std = V4L2_STD_UNKNOWN; + + break; + } + case VIDIOC_G_OUTPUT: + { + int *p_output_num = arg; + + *p_output_num = cam->output; + break; + } + case VIDIOC_S_OUTPUT: + { + int *p_output_num = arg; + + if (*p_output_num >= num_registered_fb) { + retval = -EINVAL; + break; + } + + cam->output = *p_output_num; + break; + } + + case VIDIOC_G_INPUT: + { + int *p_input_index = arg; + + retval = mxc_get_video_input(cam); + if (0 == retval) + *p_input_index = 1; + else + *p_input_index = -ENODEV; + + break; + } + + case VIDIOC_ENUM_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_QUERYCTRL: + case VIDIOC_ENUMINPUT: + case VIDIOC_S_INPUT: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + default: + retval = -EINVAL; + break; + } + + up(&cam->busy_lock); + return retval; +} + +/* + * V4L interface - ioctl function + * + * @return None + */ +static int +mxc_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mxc_v4l_do_ioctl); +} + +/*! + * V4L interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error + */ +static int mxc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + unsigned long size; + int res = 0; + cam_data *cam = dev->priv; + + pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + size = vma->vm_end - vma->vm_start; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + vma->vm_pgoff, size, vma->vm_page_prot)) { + pr_debug("mxc_mmap: remap_pfn_range failed\n"); + res = -ENOBUFS; + goto mxc_mmap_exit; + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + + mxc_mmap_exit: + up(&cam->busy_lock); + return res; +} + +/*! + * V4L interface - poll function + * + * @param file structure file * + * + * @param wait structure poll_table * + * + * @return status POLLIN | POLLRDNORM + */ +static unsigned int mxc_poll(struct file *file, poll_table * wait) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + wait_queue_head_t *queue = NULL; + int res = POLLIN | POLLRDNORM; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + queue = &cam->enc_queue; + poll_wait(file, queue, wait); + + up(&cam->busy_lock); + return res; +} + +static struct +file_operations mxc_v4l_fops = { + .owner = THIS_MODULE, + .open = mxc_v4l_open, + .release = mxc_v4l_close, + .read = mxc_v4l_read, + .ioctl = mxc_v4l_ioctl, + .mmap = mxc_mmap, + .poll = mxc_poll, +}; + +static struct video_device mxc_v4l_template = { + .owner = THIS_MODULE, + .name = "Mxc Camera", + .type = 0, + .type2 = VID_TYPE_CAPTURE, + .hardware = 0, + .fops = &mxc_v4l_fops, + .release = video_device_release, +}; + +static void camera_platform_release(struct device *device) +{ +} + +/*! Device Definition for Mt9v111 devices */ +static struct platform_device mxc_v4l2_devices = { + .name = "mxc_v4l2", + .dev = { + .release = camera_platform_release, + }, + .id = 0, +}; + +extern struct camera_sensor camera_sensor_if; + +/*! +* Camera V4l2 callback function. +* +* @return status +*/ +static void camera_callback(u32 mask, void *dev) +{ + struct mxc_v4l_frame *done_frame; + struct mxc_v4l_frame *ready_frame; + + cam_data *cam = (cam_data *) dev; + if (cam == NULL) + return; + + if (mask == 1) { + if (cam->overflow == 0) { + cam->overflow = 1; + queue_work(v4l2_work, &prp_reset_work); + } + return; + } + + if (list_empty(&cam->working_q)) { + if (empty_wq_cnt == 0) { + printk(KERN_ERR + "camera_callback: working queue empty %d\n", + empty_wq_cnt); + } + empty_wq_cnt++; + if (list_empty(&cam->ready_q)) { + prphw_disable(PRP_CHANNEL_2); + cam->skip_frame++; + } else { + ready_frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, + queue); + list_del(cam->ready_q.next); + list_add_tail(&ready_frame->queue, &cam->working_q); + cam->enc_update_eba(ready_frame->paddress, + &cam->ping_pong_csi); + } + return; + } + + done_frame = + list_entry(cam->working_q.next, struct mxc_v4l_frame, queue); + if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; + done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + + list_del(cam->working_q.next); + if (list_empty(&cam->ready_q)) { + if (list_empty(&cam->working_q)) + prphw_disable(PRP_CHANNEL_2); + cam->skip_frame++; + } else { + ready_frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, + queue); + list_del(cam->ready_q.next); + list_add_tail(&ready_frame->queue, &cam->working_q); + cam->enc_update_eba(ready_frame->paddress, + &cam->ping_pong_csi); + } + + /* Added to the done queue */ + list_add_tail(&done_frame->queue, &cam->done_q); + + /* Wake up the queue */ + cam->enc_counter++; + wake_up_interruptible(&cam->enc_queue); + } else { + printk(KERN_ERR "camera_callback :buffer not queued\n"); + } +} + +/*! + * initialize cam_data structure + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static void init_camera_struct(cam_data * cam) +{ + int i; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(cam_data)); + + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); + + cam->video_dev = video_device_alloc(); + if (cam->video_dev == NULL) + return; + + *(cam->video_dev) = mxc_v4l_template; + + video_set_drvdata(cam->video_dev, cam); + dev_set_drvdata(&mxc_v4l2_devices.dev, cam); + cam->video_dev->minor = -1; + + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].paddress = 0; + } + + init_waitqueue_head(&cam->enc_queue); + init_waitqueue_head(&cam->still_queue); + init_waitqueue_head(&cam->overflow_queue); + cam->overflow = 0; + + /* setup cropping */ + cam->crop_bounds.left = 0; + cam->crop_bounds.width = 640; + cam->crop_bounds.top = 0; + cam->crop_bounds.height = 480; + cam->crop_current = cam->crop_defrect = cam->crop_bounds; + cam->streamparm.parm.capture.capturemode = 0; + + cam->standard.index = 0; + cam->standard.id = V4L2_STD_UNKNOWN; + cam->standard.frameperiod.denominator = 30; + cam->standard.frameperiod.numerator = 1; + cam->standard.framelines = 480; + cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + cam->overlay_on = false; + cam->capture_on = false; + cam->skip_frame = 0; + cam->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + cam->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY; + + cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; + cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; + cam->v2f.fmt.pix.width = 288; + cam->v2f.fmt.pix.height = 352; + cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + cam->win.w.width = 160; + cam->win.w.height = 160; + cam->win.w.left = 0; + cam->win.w.top = 0; + + cam->cam_sensor = &camera_sensor_if; + cam->enc_callback = camera_callback; + + init_waitqueue_head(&cam->power_queue); + cam->int_lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&cam->int_lock); +} + +extern void gpio_sensor_active(void); +extern void gpio_sensor_inactive(void); + +/*! + * camera_power function + * Turn Sensor power On/Off + * + * @param cameraOn true to turn camera on, otherwise shut down + * + * @return status + */ +static u8 camera_power(bool cameraOn) +{ + if (cameraOn == true) { + gpio_sensor_active(); + csi_enable_mclk(csi_mclk_flag_backup, true, true); + } else { + csi_mclk_flag_backup = csi_read_mclk_flag(); + csi_enable_mclk(csi_mclk_flag_backup, false, false); + gpio_sensor_inactive(); + } + return 0; +} + +/*! + * This function is called to put the sensor in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure used to give information on which I2C + * to suspend + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state) +{ + cam_data *cam = platform_get_drvdata(pdev); + + cam->low_power = true; + + if (cam->overlay_on == true) + stop_preview(cam); + if ((cam->capture_on == true) && cam->enc_disable) { + cam->enc_disable(cam); + } + camera_power(false); + + return 0; +} + +/*! + * This function is called to bring the sensor back from a low power state.Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxc_v4l2_resume(struct platform_device *pdev) +{ + cam_data *cam = platform_get_drvdata(pdev); + + cam->low_power = false; + wake_up_interruptible(&cam->power_queue); + + if (cam->overlay_on == true) + start_preview(cam); + if (cam->capture_on == true) + mxc_streamon(cam); + camera_power(true); + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_v4l2_driver = { + .driver = { + .name = "mxc_v4l2", + .owner = THIS_MODULE, + .bus = &platform_bus_type, + }, + .probe = NULL, + .remove = NULL, + .suspend = mxc_v4l2_suspend, + .resume = mxc_v4l2_resume, + .shutdown = NULL, +}; + +/*! + * Entry point for the V4L2 + * + * @return Error code indicating success or failure + */ +static __init int camera_init(void) +{ + u8 err = 0; + cam_data *cam; + + if ((g_cam = cam = kzalloc(sizeof(cam_data), GFP_KERNEL)) == NULL) { + pr_debug("failed to mxc_v4l_register_camera\n"); + return -ENOMEM; + } + + init_camera_struct(cam); + + v4l2_work = create_singlethread_workqueue("v4l2_emma"); + + /* Register the I2C device */ + err = platform_device_register(&mxc_v4l2_devices); + if (err != 0) { + pr_debug("camera_init: platform_device_register failed.\n"); + video_device_release(cam->video_dev); + kfree(cam); + g_cam = NULL; + } + + /* Register the device driver structure. */ + err = platform_driver_register(&mxc_v4l2_driver); + if (err != 0) { + platform_device_unregister(&mxc_v4l2_devices); + pr_debug("camera_init: driver_register failed.\n"); + video_device_release(cam->video_dev); + kfree(cam); + g_cam = NULL; + return err; + } + + /* register v4l device */ + err = video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr); + if (err != 0) { + platform_driver_unregister(&mxc_v4l2_driver); + platform_device_unregister(&mxc_v4l2_devices); + video_device_release(cam->video_dev); + kfree(cam); + g_cam = NULL; + pr_debug("video_register_device failed\n"); + return err; + } + + return err; +} + +/*! + * Exit and cleanup for the V4L2 + * + */ +static void __exit camera_exit(void) +{ + pr_debug("unregistering video\n"); + + flush_workqueue(v4l2_work); + destroy_workqueue(v4l2_work); + + video_unregister_device(g_cam->video_dev); + + platform_driver_unregister(&mxc_v4l2_driver); + platform_device_unregister(&mxc_v4l2_devices); + + if (g_cam->open_count) { + pr_debug("camera open -- setting ops to NULL\n"); + } else { + pr_debug("freeing camera\n"); + mxc_free_frame_buf(g_cam); + kfree(g_cam); + g_cam = NULL; + } +} + +module_init(camera_init); +module_exit(camera_exit); + +module_param(video_nr, int, 0444); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mxc_v4l2_capture.c linux-2.6.28-karo/drivers/media/video/mxc/capture/mxc_v4l2_capture.c --- linux-2.6.28/drivers/media/video/mxc/capture/mxc_v4l2_capture.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mxc_v4l2_capture.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1867 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file drivers/media/video/mxc/capture/mxc_v4l2_capture.c + * + * @brief Mxc Video For Linux 2 driver + * + * @ingroup MXC_V4L2_CAPTURE + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +static int csi_mclk_flag_backup; +static int video_nr = -1; +cam_data *g_cam; +EXPORT_SYMBOL(g_cam); + +#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 2 +static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = { + { + .index = 0, + .name = "DISP3", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 1, + .name = "DISP0", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + } +}; + +/*! + * Free frame buffers + * + * @param cam Structure cam_data * + * + * @return status 0 success. + */ +static int mxc_free_frame_buf(cam_data * cam) +{ + int i; + + for (i = 0; i < FRAME_NUM; i++) { + if (cam->frame[i].vaddress != 0) { + dma_free_coherent(0, cam->frame[i].buffer.length, + cam->frame[i].vaddress, + cam->frame[i].paddress); + cam->frame[i].vaddress = 0; + } + } + + return 0; +} + +/*! + * Allocate frame buffers + * + * @param cam Structure cam_data * + * + * @param count int number of buffer need to allocated + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int mxc_allocate_frame_buf(cam_data * cam, int count) +{ + int i; + + for (i = 0; i < count; i++) { + cam->frame[i].vaddress = + dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->frame[i].paddress, + GFP_DMA | GFP_KERNEL); + if (cam->frame[i].vaddress == 0) { + printk(KERN_ERR "mxc_allocate_frame_buf failed.\n"); + mxc_free_frame_buf(cam); + return -ENOBUFS; + } + cam->frame[i].buffer.index = i; + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buffer.m.offset = cam->frame[i].paddress; + cam->frame[i].index = i; + } + + return 0; +} + +/*! + * Free frame buffers status + * + * @param cam Structure cam_data * + * + * @return none + */ +static void mxc_free_frames(cam_data * cam) +{ + int i; + + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + } + + cam->enc_counter = 0; + cam->skip_frame = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); +} + +/*! + * Return the buffer status + * + * @param cam Structure cam_data * + * @param buf Structure v4l2_buffer * + * + * @return status 0 success, EINVAL failed. + */ +static int mxc_v4l2_buffer_status(cam_data * cam, struct v4l2_buffer *buf) +{ + if (buf->index < 0 || buf->index >= FRAME_NUM) { + printk(KERN_ERR + "mxc_v4l2_buffer_status buffers not allocated\n"); + return -EINVAL; + } + + memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); + return 0; +} + +/*! + * start the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamon(cam_data * cam) +{ + struct mxc_v4l_frame *frame; + int err = 0; + + if (list_empty(&cam->ready_q)) { + printk(KERN_ERR "mxc_streamon buffer not been queued yet\n"); + return -EINVAL; + } + + cam->capture_pid = current->pid; + + if (cam->enc_enable) { + err = cam->enc_enable(cam); + if (err != 0) { + return err; + } + } + + cam->ping_pong_csi = 0; + if (cam->enc_update_eba) { + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + err = + cam->enc_update_eba(frame->buffer.m.offset, + &cam->ping_pong_csi); + + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + err |= + cam->enc_update_eba(frame->buffer.m.offset, + &cam->ping_pong_csi); + } else { + return -EINVAL; + } + + cam->capture_on = true; + return err; +} + +/*! + * Shut down the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamoff(cam_data * cam) +{ + int err = 0; + + if (cam->capture_on == false) + return 0; + + if (cam->enc_disable) { + err = cam->enc_disable(cam); + } + mxc_free_frames(cam); + cam->capture_on = false; + return err; +} + +/*! + * Valid whether the palette is supported + * + * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + return ((palette == V4L2_PIX_FMT_RGB565) || + (palette == V4L2_PIX_FMT_BGR24) || + (palette == V4L2_PIX_FMT_RGB24) || + (palette == V4L2_PIX_FMT_BGR32) || + (palette == V4L2_PIX_FMT_RGB32) || + (palette == V4L2_PIX_FMT_YUV422P) || + (palette == V4L2_PIX_FMT_UYVY) || + (palette == V4L2_PIX_FMT_YUV420)); +} + +/*! + * Valid and adjust the overlay window size, position + * + * @param cam structure cam_data * + * @param win struct v4l2_window * + * + * @return 0 + */ +static int verify_preview(cam_data * cam, struct v4l2_window *win) +{ + int i = 0; + int *width, *height; + + do { + cam->overlay_fb = (struct fb_info *)registered_fb[i]; + if (cam->overlay_fb == NULL) { + printk(KERN_ERR "verify_preview No matched.\n"); + return -1; + } + if (strncmp(cam->overlay_fb->fix.id, + mxc_capture_outputs[cam->output].name, 5) == 0) { + break; + } + } while (++i < FB_MAX); + + /* 4 bytes alignment for both FG and BG */ + if (cam->overlay_fb->var.bits_per_pixel == 24) { + win->w.left -= win->w.left % 4; + } else if (cam->overlay_fb->var.bits_per_pixel == 16) { + win->w.left -= win->w.left % 2; + } + + if (win->w.width + win->w.left > cam->overlay_fb->var.xres) + win->w.width = cam->overlay_fb->var.xres - win->w.left; + if (win->w.height + win->w.top > cam->overlay_fb->var.yres) + win->w.height = cam->overlay_fb->var.yres - win->w.top; + + /* stride line limitation */ + win->w.height -= win->w.height % 8; + win->w.width -= win->w.width % 8; + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + height = &win->w.width; + width = &win->w.height; + } else { + width = &win->w.width; + height = &win->w.height; + } + + if ((cam->crop_bounds.width / *width > 8) || + ((cam->crop_bounds.width / *width == 8) && + (cam->crop_bounds.width % *width))) { + *width = cam->crop_bounds.width / 8; + if (*width % 8) + *width += 8 - *width % 8; + if (*width + win->w.left > cam->overlay_fb->var.xres) { + printk(KERN_ERR "width exceed resize limit.\n"); + return -1; + } + printk(KERN_ERR "width exceed limit resize to %d.\n", *width); + } + + if ((cam->crop_bounds.height / *height > 8) || + ((cam->crop_bounds.height / *height == 8) && + (cam->crop_bounds.height % *height))) { + *height = cam->crop_bounds.height / 8; + if (*height % 8) + *height += 8 - *height % 8; + if (*height + win->w.top > cam->overlay_fb->var.yres) { + printk(KERN_ERR "height exceed resize limit.\n"); + return -1; + } + printk(KERN_ERR "height exceed limit resize to %d.\n", *height); + } + + return 0; +} + +/*! + * start the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int start_preview(cam_data * cam) +{ + int err = 0; +#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE) + if (cam->output == 0) { + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) + err = prp_vf_sdc_select(cam); + else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) + err = prp_vf_sdc_select_bg(cam); + if (err != 0) + return err; + + err = cam->vf_start_sdc(cam); + } +#endif + +#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE) + if (cam->output == 1) { + err = prp_vf_adc_select(cam); + if (err != 0) + return err; + + err = cam->vf_start_adc(cam); + } +#endif + + return err; +} + +/*! + * shut down the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int stop_preview(cam_data * cam) +{ + int err = 0; + +#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE) + if (cam->output == 1) { + err = prp_vf_adc_deselect(cam); + } +#endif + +#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE) + if (cam->output == 0) { + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) + err = prp_vf_sdc_deselect(cam); + else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) + err = prp_vf_sdc_deselect_bg(cam); + } +#endif + + return err; +} + +/*! + * V4L2 - mxc_v4l2_g_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_g_fmt(cam_data * cam, struct v4l2_format *f) +{ + int retval = 0; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->fmt.pix = cam->v2f.fmt.pix; + retval = 0; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + f->fmt.win = cam->win; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/*! + * V4L2 - mxc_v4l2_s_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_fmt(cam_data * cam, struct v4l2_format *f) +{ + int retval = 0; + int size = 0; + int bytesperline = 0; + int *width, *height; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (!valid_mode(f->fmt.pix.pixelformat)) { + printk(KERN_ERR + "mxc_v4l2_s_fmt: format not supported\n"); + return -EINVAL; + } + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + height = &f->fmt.pix.width; + width = &f->fmt.pix.height; + } else { + width = &f->fmt.pix.width; + height = &f->fmt.pix.height; + } + + /* stride line limitation */ + *width -= *width % 8; + *height -= *height % 8; + + if ((cam->crop_bounds.width / *width > 8) || + ((cam->crop_bounds.width / *width == 8) && + (cam->crop_bounds.width % *width))) { + *width = cam->crop_bounds.width / 8; + if (*width % 8) + *width += 8 - *width % 8; + printk(KERN_ERR "width exceed limit resize to %d.\n", + *width); + } + + if ((cam->crop_bounds.height / *height > 8) || + ((cam->crop_bounds.height / *height == 8) && + (cam->crop_bounds.height % *height))) { + *height = cam->crop_bounds.height / 8; + if (*height % 8) + *height += 8 - *height % 8; + printk(KERN_ERR "height exceed limit resize to %d.\n", + *height); + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_BGR24: + size = f->fmt.pix.width * f->fmt.pix.height * 3; + bytesperline = f->fmt.pix.width * 3; + break; + case V4L2_PIX_FMT_RGB24: + size = f->fmt.pix.width * f->fmt.pix.height * 3; + bytesperline = f->fmt.pix.width * 3; + break; + case V4L2_PIX_FMT_BGR32: + size = f->fmt.pix.width * f->fmt.pix.height * 4; + bytesperline = f->fmt.pix.width * 4; + break; + case V4L2_PIX_FMT_RGB32: + size = f->fmt.pix.width * f->fmt.pix.height * 4; + bytesperline = f->fmt.pix.width * 4; + break; + case V4L2_PIX_FMT_YUV422P: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width; + break; + case V4L2_PIX_FMT_UYVY: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_YUV420: + size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; + bytesperline = f->fmt.pix.width; + break; + default: + break; + } + + if (f->fmt.pix.bytesperline < bytesperline) { + f->fmt.pix.bytesperline = bytesperline; + } else { + bytesperline = f->fmt.pix.bytesperline; + } + + if (f->fmt.pix.sizeimage < size) { + f->fmt.pix.sizeimage = size; + } else { + size = f->fmt.pix.sizeimage; + } + + cam->v2f.fmt.pix = f->fmt.pix; + + if (cam->v2f.fmt.pix.priv != 0) { + if (copy_from_user(&cam->offset, + (void *)cam->v2f.fmt.pix.priv, + sizeof(cam->offset))) { + retval = -EFAULT; + break; + } + } + retval = 0; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + retval = verify_preview(cam, &f->fmt.win); + cam->win = f->fmt.win; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/*! + * get control param + * + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_get_v42l_control(cam_data * cam, struct v4l2_control *c) +{ + int status = 0; + + switch (c->id) { + case V4L2_CID_HFLIP: + if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) + c->value = 1; + break; + case V4L2_CID_VFLIP: + if (cam->rotation == IPU_ROTATE_VERT_FLIP) + c->value = 1; + break; + case V4L2_CID_MXC_ROT: + c->value = cam->rotation; + break; + case V4L2_CID_BRIGHTNESS: + c->value = cam->bright; + break; + case V4L2_CID_HUE: + c->value = cam->hue; + break; + case V4L2_CID_CONTRAST: + c->value = cam->contrast; + break; + case V4L2_CID_SATURATION: + c->value = cam->saturation; + break; + case V4L2_CID_RED_BALANCE: + c->value = cam->red; + break; + case V4L2_CID_BLUE_BALANCE: + c->value = cam->blue; + break; + case V4L2_CID_BLACK_LEVEL: + c->value = cam->ae_mode; + break; + default: + status = -EINVAL; + } + return status; +} + +/*! + * V4L2 - set_control function + * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing. + * 0 for normal operation + * 1 for vertical flip + * 2 for horizontal flip + * 3 for horizontal and vertical flip + * 4 for 90 degree rotation + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_set_v42l_control(cam_data * cam, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + if (c->value == 1) { + if ((cam->rotation != IPU_ROTATE_VERT_FLIP) && + (cam->rotation != IPU_ROTATE_180)) + cam->rotation = IPU_ROTATE_HORIZ_FLIP; + else + cam->rotation = IPU_ROTATE_180; + } else { + if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) + cam->rotation = IPU_ROTATE_NONE; + if (cam->rotation == IPU_ROTATE_180) + cam->rotation = IPU_ROTATE_VERT_FLIP; + } + break; + case V4L2_CID_VFLIP: + if (c->value == 1) { + if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) && + (cam->rotation != IPU_ROTATE_180)) + cam->rotation = IPU_ROTATE_VERT_FLIP; + else + cam->rotation = IPU_ROTATE_180; + } else { + if (cam->rotation == IPU_ROTATE_VERT_FLIP) + cam->rotation = IPU_ROTATE_NONE; + if (cam->rotation == IPU_ROTATE_180) + cam->rotation = IPU_ROTATE_HORIZ_FLIP; + } + break; + case V4L2_CID_MXC_ROT: + switch (c->value) { + case V4L2_MXC_ROTATE_NONE: + cam->rotation = IPU_ROTATE_NONE; + break; + case V4L2_MXC_ROTATE_VERT_FLIP: + cam->rotation = IPU_ROTATE_VERT_FLIP; + break; + case V4L2_MXC_ROTATE_HORIZ_FLIP: + cam->rotation = IPU_ROTATE_HORIZ_FLIP; + break; + case V4L2_MXC_ROTATE_180: + cam->rotation = IPU_ROTATE_180; + break; + case V4L2_MXC_ROTATE_90_RIGHT: + cam->rotation = IPU_ROTATE_90_RIGHT; + break; + case V4L2_MXC_ROTATE_90_RIGHT_VFLIP: + cam->rotation = IPU_ROTATE_90_RIGHT_VFLIP; + break; + case V4L2_MXC_ROTATE_90_RIGHT_HFLIP: + cam->rotation = IPU_ROTATE_90_RIGHT_HFLIP; + break; + case V4L2_MXC_ROTATE_90_LEFT: + cam->rotation = IPU_ROTATE_90_LEFT; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_HUE: + cam->hue = c->value; + break; + case V4L2_CID_CONTRAST: + cam->contrast = c->value; + break; + case V4L2_CID_BRIGHTNESS: + cam->bright = c->value; + case V4L2_CID_SATURATION: + cam->saturation = c->value; + case V4L2_CID_RED_BALANCE: + cam->red = c->value; + case V4L2_CID_BLUE_BALANCE: + cam->blue = c->value; + ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true); + cam->cam_sensor->set_color(cam->bright, cam->saturation, + cam->red, cam->green, cam->blue); + ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_BLACK_LEVEL: + cam->ae_mode = c->value & 0x03; + ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true); + if (cam->cam_sensor->set_ae_mode) + cam->cam_sensor->set_ae_mode(cam->ae_mode); + ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false); + break; + case V4L2_CID_MXC_FLASH: + ipu_csi_flash_strobe(true); + break; + default: + return -EINVAL; + } + return 0; +} + +/*! + * V4L2 - mxc_v4l2_s_param function + * + * @param cam structure cam_data * + * + * @param parm structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_param(cam_data * cam, struct v4l2_streamparm *parm) +{ + sensor_interface *param; + ipu_csi_signal_cfg_t csi_param; + int err = 0; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR "mxc_v4l2_s_param invalid type\n"); + return -EINVAL; + } + + if (parm->parm.capture.timeperframe.denominator > + cam->standard.frameperiod.denominator) { + printk(KERN_ERR "mxc_v4l2_s_param frame rate %d larger " + "than standard supported %d\n", + parm->parm.capture.timeperframe.denominator, + cam->standard.frameperiod.denominator); + return -EINVAL; + } + + /* Stop the viewfinder */ + if (cam->overlay_on == true) { + stop_preview(cam); + } + + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + + ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true); + param = cam->cam_sensor->config + (&parm->parm.capture.timeperframe.denominator, + parm->parm.capture.capturemode); + ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false); + cam->streamparm.parm.capture.timeperframe = + parm->parm.capture.timeperframe; + + if ((parm->parm.capture.capturemode != 0) && + (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY)) { + printk(KERN_ERR + "mxc_v4l2_s_param frame un-supported capture mode\n"); + err = -EINVAL; + goto exit; + } + + if (parm->parm.capture.capturemode == + cam->streamparm.parm.capture.capturemode) { + goto exit; + } + + /* resolution changed, so need to re-program the CSI */ + csi_param.sens_clksrc = 0; + csi_param.clk_mode = param->clk_mode; + csi_param.pixclk_pol = param->pixclk_pol; + csi_param.data_width = param->data_width; + csi_param.data_pol = param->data_pol; + csi_param.ext_vsync = param->ext_vsync; + csi_param.Vsync_pol = param->Vsync_pol; + csi_param.Hsync_pol = param->Hsync_pol; + ipu_csi_init_interface(param->width, param->height, + param->pixel_fmt, csi_param); + ipu_csi_set_window_size(param->width + 1, param->height + 1); + + if (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY) { + cam->streamparm.parm.capture.capturemode = 0; + } else { + cam->streamparm.parm.capture.capturemode = + V4L2_MODE_HIGHQUALITY; + cam->streamparm.parm.capture.extendedmode = + parm->parm.capture.extendedmode; + cam->streamparm.parm.capture.readbuffers = 1; + } + + exit: + if (cam->overlay_on == true) { + start_preview(cam); + } + + return err; +} + +/*! + * Dequeue one V4L capture buffer + * + * @param cam structure cam_data * + * @param buf structure v4l2_buffer * + * + * @return status 0 success, EINVAL invalid frame number, + * ETIME timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_dqueue(cam_data * cam, struct v4l2_buffer *buf) +{ + int retval = 0; + struct mxc_v4l_frame *frame; + + if (!wait_event_interruptible_timeout(cam->enc_queue, + cam->enc_counter != 0, 10 * HZ)) { + printk(KERN_ERR "mxc_v4l_dqueue timeout enc_counter %x\n", + cam->enc_counter); + return -ETIME; + } else if (signal_pending(current)) { + printk(KERN_ERR "mxc_v4l_dqueue() interrupt received\n"); + mxc_free_frames(cam); + return -ERESTARTSYS; + } + + cam->enc_counter--; + + frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); + list_del(cam->done_q.next); + if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { + frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; + } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + printk(KERN_ERR "VIDIOC_DQBUF: Buffer not filled.\n"); + frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { + printk(KERN_ERR "VIDIOC_DQBUF: Buffer not queued.\n"); + retval = -EINVAL; + } + + buf->bytesused = cam->v2f.fmt.pix.sizeimage; + buf->index = frame->index; + buf->flags = frame->buffer.flags; + buf->m = cam->frame[frame->index].buffer.m; + + return retval; +} + +/*! + * V4L interface - open function + * + * @param inode structure inode * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_open(struct inode *inode, struct file *file) +{ + sensor_interface *param; + ipu_csi_signal_cfg_t csi_param; + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + int err = 0; + + if (!cam) { + printk(KERN_ERR "Internal error, cam_data not found!\n"); + return -EBADF; + } + + down(&cam->busy_lock); + + err = 0; + if (signal_pending(current)) + goto oops; + + if (cam->open_count++ == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) + err = prp_enc_select(cam); +#endif + + cam->enc_counter = 0; + cam->skip_frame = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); + + ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true); + param = cam->cam_sensor->reset(); + if (param == NULL) { + cam->open_count--; + ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false); + err = -ENODEV; + goto oops; + } + + csi_param.sens_clksrc = 0; + csi_param.clk_mode = param->clk_mode; + csi_param.pixclk_pol = param->pixclk_pol; + csi_param.data_width = param->data_width; + csi_param.data_pol = param->data_pol; + csi_param.ext_vsync = param->ext_vsync; + csi_param.Vsync_pol = param->Vsync_pol; + csi_param.Hsync_pol = param->Hsync_pol; + ipu_csi_init_interface(param->width, param->height, + param->pixel_fmt, csi_param); + + cam->cam_sensor->get_color(&cam->bright, &cam->saturation, + &cam->red, &cam->green, &cam->blue); + if (cam->cam_sensor->get_ae_mode) + cam->cam_sensor->get_ae_mode(&cam->ae_mode); + + /* pr_info("mxc_v4l_open saturation %x ae_mode %x\n", + cam->saturation, cam->ae_mode); */ + + ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false); + } + + file->private_data = dev; + oops: + up(&cam->busy_lock); + return err; +} + +/*! + * V4L interface - close function + * + * @param inode struct inode * + * @param file struct file * + * + * @return 0 success + */ +static int mxc_v4l_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + int err = 0; + cam_data *cam = dev->priv; + + if (!cam) { + printk(KERN_ERR "Internal error, cam_data not found!\n"); + return -EBADF; + } + + /* for the case somebody hit the ctrl C */ + if (cam->overlay_pid == current->pid) { + err = stop_preview(cam); + cam->overlay_on = false; + } + if (cam->capture_pid == current->pid) { + err |= mxc_streamoff(cam); + wake_up_interruptible(&cam->enc_queue); + } + + if (--cam->open_count == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + pr_info("mxc_v4l_close: release resource\n"); + +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) + err |= prp_enc_deselect(cam); +#endif + mxc_free_frame_buf(cam); + file->private_data = NULL; + + /* capture off */ + wake_up_interruptible(&cam->enc_queue); + mxc_free_frames(cam); + cam->enc_counter++; + } + return err; +} + +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) +/* + * V4L interface - read function + * + * @param file struct file * + * @param read buf char * + * @param count size_t + * @param ppos structure loff_t * + * + * @return bytes read + */ +static ssize_t +mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t * ppos) +{ + int err = 0; + u8 *v_address; + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + /* Stop the viewfinder */ + if (cam->overlay_on == true) + stop_preview(cam); + + v_address = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->still_buf, GFP_DMA | GFP_KERNEL); + + if (!v_address) { + err = -ENOBUFS; + goto exit0; + } + + err = prp_still_select(cam); + if (err != 0) { + err = -EIO; + goto exit1; + } + + cam->still_counter = 0; + err = cam->csi_start(cam); + if (err != 0) { + err = -EIO; + goto exit2; + } + + if (!wait_event_interruptible_timeout(cam->still_queue, + cam->still_counter != 0, + 10 * HZ)) { + printk(KERN_ERR "mxc_v4l_read timeout counter %x\n", + cam->still_counter); + err = -ETIME; + goto exit2; + } + err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage); + + exit2: + prp_still_deselect(cam); + + exit1: + dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address, + cam->still_buf); + cam->still_buf = 0; + + exit0: + if (cam->overlay_on == true) { + start_preview(cam); + } + + up(&cam->busy_lock); + if (err < 0) + return err; + + return (cam->v2f.fmt.pix.sizeimage - err); +} +#endif + +/*! + * V4L interface - ioctl function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @param ioctlnr unsigned int + * + * @param arg void * + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static int +mxc_v4l_do_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + int retval = 0; + unsigned long lock_flags; + + wait_event_interruptible(cam->power_queue, cam->low_power == false); + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + switch (ioctlnr) { + /*! + * V4l2 VIDIOC_QUERYCAP ioctl + */ + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *cap = arg; + strcpy(cap->driver, "mxc_v4l2"); + cap->version = KERNEL_VERSION(0, 1, 11); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE; + cap->card[0] = '\0'; + cap->bus_info[0] = '\0'; + retval = 0; + break; + } + + /*! + * V4l2 VIDIOC_G_FMT ioctl + */ + case VIDIOC_G_FMT:{ + struct v4l2_format *gf = arg; + retval = mxc_v4l2_g_fmt(cam, gf); + break; + } + + /*! + * V4l2 VIDIOC_S_FMT ioctl + */ + case VIDIOC_S_FMT:{ + struct v4l2_format *sf = arg; + retval = mxc_v4l2_s_fmt(cam, sf); + break; + } + + /*! + * V4l2 VIDIOC_REQBUFS ioctl + */ + case VIDIOC_REQBUFS:{ + struct v4l2_requestbuffers *req = arg; + if (req->count > FRAME_NUM) { + printk(KERN_ERR + "VIDIOC_REQBUFS: not enough buffer\n"); + req->count = FRAME_NUM; + } + + if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (req->memory != V4L2_MEMORY_MMAP)) { + printk(KERN_ERR + "VIDIOC_REQBUFS: wrong buffer type\n"); + retval = -EINVAL; + break; + } + + mxc_streamoff(cam); + mxc_free_frame_buf(cam); + + retval = mxc_allocate_frame_buf(cam, req->count); + break; + } + + /*! + * V4l2 VIDIOC_QUERYBUF ioctl + */ + case VIDIOC_QUERYBUF:{ + struct v4l2_buffer *buf = arg; + int index = buf->index; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR + "VIDIOC_QUERYBUFS: wrong buffer type\n"); + retval = -EINVAL; + break; + } + + memset(buf, 0, sizeof(buf)); + buf->index = index; + + down(&cam->param_lock); + retval = mxc_v4l2_buffer_status(cam, buf); + up(&cam->param_lock); + break; + } + + /*! + * V4l2 VIDIOC_QBUF ioctl + */ + case VIDIOC_QBUF:{ + struct v4l2_buffer *buf = arg; + int index = buf->index; + + spin_lock_irqsave(&cam->int_lock, lock_flags); + cam->frame[index].buffer.m.offset = buf->m.offset; + if ((cam->frame[index].buffer.flags & 0x7) == + V4L2_BUF_FLAG_MAPPED) { + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + if (cam->skip_frame > 0) { + list_add_tail(&cam->frame[index].queue, + &cam->working_q); + retval = + cam->enc_update_eba(cam-> + frame[index]. + buffer.m.offset, + &cam-> + ping_pong_csi); + cam->skip_frame = 0; + } else { + list_add_tail(&cam->frame[index].queue, + &cam->ready_q); + } + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_QUEUED) { + printk(KERN_ERR + "VIDIOC_QBUF: buffer already queued\n"); + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_DONE) { + printk(KERN_ERR + "VIDIOC_QBUF: overwrite done buffer.\n"); + cam->frame[index].buffer.flags &= + ~V4L2_BUF_FLAG_DONE; + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + } + + buf->flags = cam->frame[index].buffer.flags; + spin_unlock_irqrestore(&cam->int_lock, lock_flags); + break; + } + + /*! + * V4l2 VIDIOC_DQBUF ioctl + */ + case VIDIOC_DQBUF:{ + struct v4l2_buffer *buf = arg; + + retval = mxc_v4l_dqueue(cam, buf); + + break; + } + + /*! + * V4l2 VIDIOC_STREAMON ioctl + */ + case VIDIOC_STREAMON:{ + retval = mxc_streamon(cam); + break; + } + + /*! + * V4l2 VIDIOC_STREAMOFF ioctl + */ + case VIDIOC_STREAMOFF:{ + retval = mxc_streamoff(cam); + break; + } + + /*! + * V4l2 VIDIOC_G_CTRL ioctl + */ + case VIDIOC_G_CTRL:{ + retval = mxc_get_v42l_control(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_S_CTRL ioctl + */ + case VIDIOC_S_CTRL:{ + retval = mxc_set_v42l_control(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_CROPCAP ioctl + */ + case VIDIOC_CROPCAP:{ + struct v4l2_cropcap *cap = arg; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + cap->bounds = cam->crop_bounds; + cap->defrect = cam->crop_defrect; + break; + } + + /*! + * V4l2 VIDIOC_G_CROP ioctl + */ + case VIDIOC_G_CROP:{ + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + crop->c = cam->crop_current; + break; + } + + /*! + * V4l2 VIDIOC_S_CROP ioctl + */ + case VIDIOC_S_CROP:{ + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = &cam->crop_bounds; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + + crop->c.top = (crop->c.top < b->top) ? b->top + : crop->c.top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top + b->height - crop->c.top) + crop->c.height = + b->top + b->height - crop->c.top; + + crop->c.left = (crop->c.left < b->left) ? b->left + : crop->c.left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + crop->c.width -= crop->c.width % 8; + crop->c.left -= crop->c.left % 4; + cam->crop_current = crop->c; + + ipu_csi_set_window_size(cam->crop_current.width, + cam->crop_current.height); + ipu_csi_set_window_pos(cam->crop_current.left, + cam->crop_current.top); + break; + } + + /*! + * V4l2 VIDIOC_OVERLAY ioctl + */ + case VIDIOC_OVERLAY:{ + int *on = arg; + if (*on) { + cam->overlay_on = true; + cam->overlay_pid = current->pid; + retval = start_preview(cam); + } + if (!*on) { + retval = stop_preview(cam); + cam->overlay_on = false; + } + break; + } + + /*! + * V4l2 VIDIOC_G_FBUF ioctl + */ + case VIDIOC_G_FBUF:{ + struct v4l2_framebuffer *fb = arg; + *fb = cam->v4l2_fb; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + break; + } + + /*! + * V4l2 VIDIOC_S_FBUF ioctl + */ + case VIDIOC_S_FBUF:{ + struct v4l2_framebuffer *fb = arg; + cam->v4l2_fb = *fb; + break; + } + + case VIDIOC_G_PARM:{ + struct v4l2_streamparm *parm = arg; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR "VIDIOC_G_PARM invalid type\n"); + retval = -EINVAL; + break; + } + parm->parm.capture = cam->streamparm.parm.capture; + break; + } + case VIDIOC_S_PARM:{ + struct v4l2_streamparm *parm = arg; + retval = mxc_v4l2_s_param(cam, parm); + break; + } + + /* linux v4l2 bug, kernel c0485619 user c0405619 */ + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *e = arg; + *e = cam->standard; + printk(KERN_ERR "VIDIOC_ENUMSTD call\n"); + retval = 0; + break; + } + + case VIDIOC_G_STD:{ + v4l2_std_id *e = arg; + *e = cam->standard.id; + break; + } + + case VIDIOC_S_STD:{ + break; + } + + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *output = arg; + + if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { + retval = -EINVAL; + break; + } + + *output = mxc_capture_outputs[output->index]; + + break; + } + case VIDIOC_G_OUTPUT: + { + int *p_output_num = arg; + + *p_output_num = cam->output; + break; + } + case VIDIOC_S_OUTPUT: + { + int *p_output_num = arg; + + if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { + retval = -EINVAL; + break; + } + + cam->output = *p_output_num; + break; + } + + case VIDIOC_ENUM_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_QUERYCTRL: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + default: + retval = -EINVAL; + break; + } + + up(&cam->busy_lock); + return retval; +} + +/* + * V4L interface - ioctl function + * + * @return None + */ +static int +mxc_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mxc_v4l_do_ioctl); +} + +/*! + * V4L interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error + */ +static int mxc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + unsigned long size; + int res = 0; + cam_data *cam = dev->priv; + + pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + size = vma->vm_end - vma->vm_start; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + vma->vm_pgoff, size, vma->vm_page_prot)) { + printk(KERN_ERR "mxc_mmap: remap_pfn_range failed\n"); + res = -ENOBUFS; + goto mxc_mmap_exit; + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + + mxc_mmap_exit: + up(&cam->busy_lock); + return res; +} + +/*! + * V4L interface - poll function + * + * @param file structure file * + * + * @param wait structure poll_table * + * + * @return status POLLIN | POLLRDNORM + */ +static unsigned int mxc_poll(struct file *file, poll_table * wait) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = dev->priv; + wait_queue_head_t *queue = NULL; + int res = POLLIN | POLLRDNORM; + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + queue = &cam->enc_queue; + poll_wait(file, queue, wait); + + up(&cam->busy_lock); + return res; +} + +static struct +file_operations mxc_v4l_fops = { + .owner = THIS_MODULE, + .open = mxc_v4l_open, + .release = mxc_v4l_close, + .read = mxc_v4l_read, + .ioctl = mxc_v4l_ioctl, + .mmap = mxc_mmap, + .poll = mxc_poll, +}; + +static struct video_device mxc_v4l_template = { + .owner = THIS_MODULE, + .name = "Mxc Camera", + .type = 0, + .type2 = VID_TYPE_CAPTURE, + .hardware = 0, + .fops = &mxc_v4l_fops, + .release = video_device_release, +}; + +static void camera_platform_release(struct device *device) +{ +} + +/*! Device Definition for Mt9v111 devices */ +static struct platform_device mxc_v4l2_devices = { + .name = "mxc_v4l2", + .dev = { + .release = camera_platform_release, + }, + .id = 0, +}; + +extern struct camera_sensor camera_sensor_if; + +/*! +* Camera V4l2 callback function. +* +* @param mask u32 +* +* @param dev void device structure +* +* @return status +*/ +static void camera_callback(u32 mask, void *dev) +{ + struct mxc_v4l_frame *done_frame; + struct mxc_v4l_frame *ready_frame; + + cam_data *cam = (cam_data *) dev; + if (cam == NULL) + return; + + if (list_empty(&cam->working_q)) { + printk(KERN_ERR "camera_callback: working queue empty\n"); + return; + } + + done_frame = + list_entry(cam->working_q.next, struct mxc_v4l_frame, queue); + if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; + done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + + if (list_empty(&cam->ready_q)) { + cam->skip_frame++; + } else { + ready_frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, + queue); + list_del(cam->ready_q.next); + list_add_tail(&ready_frame->queue, &cam->working_q); + cam->enc_update_eba(ready_frame->buffer.m.offset, + &cam->ping_pong_csi); + } + + /* Added to the done queue */ + list_del(cam->working_q.next); + list_add_tail(&done_frame->queue, &cam->done_q); + + /* Wake up the queue */ + cam->enc_counter++; + wake_up_interruptible(&cam->enc_queue); + } else { + printk(KERN_ERR "camera_callback :buffer not queued\n"); + } +} + +/*! + * initialize cam_data structure + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static void init_camera_struct(cam_data * cam) +{ + /* Default everything to 0 */ + memset(cam, 0, sizeof(cam_data)); + + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); + + cam->video_dev = video_device_alloc(); + if (cam->video_dev == NULL) + return; + + *(cam->video_dev) = mxc_v4l_template; + + video_set_drvdata(cam->video_dev, cam); + dev_set_drvdata(&mxc_v4l2_devices.dev, (void *)cam); + cam->video_dev->minor = -1; + + init_waitqueue_head(&cam->enc_queue); + init_waitqueue_head(&cam->still_queue); + + /* setup cropping */ + cam->crop_bounds.left = 0; + cam->crop_bounds.width = 640; + cam->crop_bounds.top = 0; + cam->crop_bounds.height = 480; + cam->crop_current = cam->crop_defrect = cam->crop_bounds; + ipu_csi_set_window_size(cam->crop_current.width, + cam->crop_current.height); + ipu_csi_set_window_pos(cam->crop_current.left, cam->crop_current.top); + cam->streamparm.parm.capture.capturemode = 0; + + cam->standard.index = 0; + cam->standard.id = V4L2_STD_UNKNOWN; + cam->standard.frameperiod.denominator = 30; + cam->standard.frameperiod.numerator = 1; + cam->standard.framelines = 480; + cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + cam->overlay_on = false; + cam->capture_on = false; + cam->skip_frame = 0; + cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; + + cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; + cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; + cam->v2f.fmt.pix.width = 288; + cam->v2f.fmt.pix.height = 352; + cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + cam->win.w.width = 160; + cam->win.w.height = 160; + cam->win.w.left = 0; + cam->win.w.top = 0; + + cam->cam_sensor = &camera_sensor_if; + cam->enc_callback = camera_callback; + init_waitqueue_head(&cam->power_queue); + cam->int_lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&cam->int_lock); +} + +extern void gpio_sensor_active(void); +extern void gpio_sensor_inactive(void); + +/*! + * camera_power function + * Turn Sensor power On/Off + * + * @param cameraOn true to turn camera on, otherwise shut down + * + * @return status + */ +static u8 camera_power(bool cameraOn) +{ + if (cameraOn == true) { + gpio_sensor_active(); + ipu_csi_enable_mclk(csi_mclk_flag_backup, true, true); + } else { + csi_mclk_flag_backup = ipu_csi_read_mclk_flag(); + ipu_csi_enable_mclk(csi_mclk_flag_backup, false, false); + gpio_sensor_inactive(); + } + return 0; +} + +/*! + * This function is called to put the sensor in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure used to give information on which I2C + * to suspend + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state) +{ + cam_data *cam = platform_get_drvdata(pdev); + + if (cam == NULL) { + return -1; + } + + cam->low_power = true; + + if (cam->overlay_on == true) + stop_preview(cam); + if ((cam->capture_on == true) && cam->enc_disable) { + cam->enc_disable(cam); + } + camera_power(false); + + return 0; +} + +/*! + * This function is called to bring the sensor back from a low power state.Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxc_v4l2_resume(struct platform_device *pdev) +{ + cam_data *cam = platform_get_drvdata(pdev); + + if (cam == NULL) { + return -1; + } + + cam->low_power = false; + wake_up_interruptible(&cam->power_queue); + + if (cam->overlay_on == true) + start_preview(cam); + if (cam->capture_on == true) + mxc_streamon(cam); + camera_power(true); + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_v4l2_driver = { + .driver = { + .name = "mxc_v4l2", + }, + .probe = NULL, + .remove = NULL, + .suspend = mxc_v4l2_suspend, + .resume = mxc_v4l2_resume, + .shutdown = NULL, +}; + +/*! + * Entry point for the V4L2 + * + * @return Error code indicating success or failure + */ +static __init int camera_init(void) +{ + u8 err = 0; + + /* Register the device driver structure. */ + err = platform_driver_register(&mxc_v4l2_driver); + if (err != 0) { + printk("camera_init: platform_driver_register failed.\n"); + return err; + } + + if ((g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "failed to mxc_v4l_register_camera\n"); + return -1; + } + + init_camera_struct(g_cam); + + /* Register the I2C device */ + err = platform_device_register(&mxc_v4l2_devices); + if (err != 0) { + printk(KERN_ERR + "camera_init: platform_device_register failed.\n"); + video_device_release(g_cam->video_dev); + kfree(g_cam); + g_cam = NULL; + } + + /* register v4l device */ + if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr) + == -1) { + platform_device_unregister(&mxc_v4l2_devices); + platform_driver_unregister(&mxc_v4l2_driver); + video_device_release(g_cam->video_dev); + kfree(g_cam); + g_cam = NULL; + printk(KERN_ERR "video_register_device failed\n"); + return -1; + } + + return err; +} + +/*! + * Exit and cleanup for the V4L2 + * + */ +static void __exit camera_exit(void) +{ + pr_info("unregistering video\n"); + video_unregister_device(g_cam->video_dev); + + platform_driver_unregister(&mxc_v4l2_driver); + platform_device_unregister(&mxc_v4l2_devices); + + if (g_cam->open_count) { + printk(KERN_ERR "camera open -- setting ops to NULL\n"); + } else { + pr_info("freeing camera\n"); + mxc_free_frame_buf(g_cam); + kfree(g_cam); + g_cam = NULL; + } +} + +module_init(camera_init); +module_exit(camera_exit); + +module_param(video_nr, int, 0444); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/mxc_v4l2_capture.h linux-2.6.28-karo/drivers/media/video/mxc/capture/mxc_v4l2_capture.h --- linux-2.6.28/drivers/media/video/mxc/capture/mxc_v4l2_capture.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/mxc_v4l2_capture.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,190 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver + */ +/*! + * @file mxc_v4l2_capture.h + * + * @brief mxc V4L2 capture device API Header file + * + * It include all the defines for frame operations, also three structure defines + * use case ops structure, common v4l2 driver structure and frame structure. + * + * @ingroup MXC_V4L2_CAPTURE + */ +#ifndef __MXC_V4L2_CAPTURE_H__ +#define __MXC_V4L2_CAPTURE_H__ + +#include +#include +#include + +#include +#include +#include + +#define FRAME_NUM 3 + +/*! + * v4l2 frame structure. + */ +struct mxc_v4l_frame { + u32 paddress; + void *vaddress; + int count; + int width; + int height; + + struct v4l2_buffer buffer; + struct list_head queue; + int index; +}; + +typedef struct { + u8 clk_mode; + u8 ext_vsync; + u8 Vsync_pol; + u8 Hsync_pol; + u8 pixclk_pol; + u8 data_pol; + u8 data_width; + u16 width; + u16 height; + u32 pixel_fmt; + u32 mclk; +} sensor_interface; + +/* Sensor control function */ +struct camera_sensor { + struct module *owner; + void (*set_color) (int bright, int saturation, int red, int green, + int blue); + void (*get_color) (int *bright, int *saturation, int *red, int *green, + int *blue); + void (*set_ae_mode) (int ae_mode); + void (*get_ae_mode) (int *ae_mode); + void (*set_ae) (int active); + void (*set_ae_limit) (int limit); + void (*set_awb) (int active); + void (*flicker_control) (int control); + void (*get_control_params) (int *ae, int *awb, int *flicker); + sensor_interface *(*config) (int *frame_rate, int high_quality); + sensor_interface *(*reset) (void); + int (*get_status) (void); +}; + +/*! + * common v4l2 driver structure. + */ +typedef struct _cam_data { + struct video_device *video_dev; + + /* semaphore guard against SMP multithreading */ + struct semaphore busy_lock; + + int open_count; + + /* params lock for this camera */ + struct semaphore param_lock; + + /* Encorder */ + struct list_head ready_q; + struct list_head done_q; + struct list_head working_q; + int ping_pong_csi; + spinlock_t int_lock; + struct mxc_v4l_frame frame[FRAME_NUM]; + int skip_frame; + wait_queue_head_t overflow_queue; + int overflow; + wait_queue_head_t enc_queue; + int enc_counter; + dma_addr_t rot_enc_bufs[2]; + void *rot_enc_bufs_vaddr[2]; + int rot_enc_buf_size[2]; + enum v4l2_buf_type type; + + /* still image capture */ + wait_queue_head_t still_queue; + int still_counter; + dma_addr_t still_buf; + void *still_buf_vaddr; + + /* overlay */ + struct v4l2_window win; + struct v4l2_framebuffer v4l2_fb; + dma_addr_t vf_bufs[2]; + void *vf_bufs_vaddr[2]; + int vf_bufs_size[2]; + dma_addr_t rot_vf_bufs[2]; + void *rot_vf_bufs_vaddr[2]; + int rot_vf_buf_size[2]; + bool overlay_active; + int output; + struct fb_info *overlay_fb; + + /* v4l2 format */ + struct v4l2_format v2f; + int rotation; + struct v4l2_mxc_offset offset; + + /* V4l2 control bit */ + int bright; + int hue; + int contrast; + int saturation; + int red; + int green; + int blue; + int ae_mode; + int ae_enable; + int ae_limit; + int awb_enable; + int flicker_ctrl; + + /* standart */ + struct v4l2_streamparm streamparm; + struct v4l2_standard standard; + + /* crop */ + struct v4l2_rect crop_bounds; + struct v4l2_rect crop_defrect; + struct v4l2_rect crop_current; + + int (*enc_update_eba) (dma_addr_t eba, int *bufferNum); + int (*enc_enable) (void *private); + int (*enc_disable) (void *private); + void (*enc_callback) (u32 mask, void *dev); + int (*vf_start_adc) (void *private); + int (*vf_stop_adc) (void *private); + int (*vf_start_sdc) (void *private); + int (*vf_stop_sdc) (void *private); + int (*csi_start) (void *private); + int (*csi_stop) (void *private); + + /* misc status flag */ + bool overlay_on; + bool capture_on; + int overlay_pid; + int capture_pid; + bool low_power; + wait_queue_head_t power_queue; + + /* camera sensor interface */ + struct camera_sensor *cam_sensor; +} cam_data; + +void set_mclk_rate(uint32_t * p_mclk_freq); +#endif /* __MXC_V4L2_CAPTURE_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/ov2640.c linux-2.6.28-karo/drivers/media/video/mxc/capture/ov2640.c --- linux-2.6.28/drivers/media/video/mxc/capture/ov2640.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/ov2640.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,817 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NONSENSE +#include +#endif + +#include "mxc_v4l2_capture.h" + +#define OV2640_DEBUG +#ifdef OV2640_DEBUG +#define PRINTK(fmt...) printk(KERN_DEBUG fmt) +#else +#define PRINTK(fmt...) +#endif + +#define OV2640_I2C_ADDRESS 0x30 + +static sensor_interface *interface_param = NULL; +static int reset_frame_rate = 30; +static int ov2640_attach(struct i2c_adapter *adapter); +static int ov2640_detach(struct i2c_client *client); + +static struct i2c_driver ov2640_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "OV2640 Client", + }, + .attach_adapter = ov2640_attach, + .detach_client = ov2640_detach, +}; + +static struct i2c_client ov2640_i2c_client = { + .name = "ov2640 I2C dev", + .addr = OV2640_I2C_ADDRESS, + .driver = &ov2640_i2c_driver, +}; + +/*! + * ov2640 I2C attach function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int ov2640_attach(struct i2c_adapter *adapter) +{ + int ret; + + if (strcmp(adapter->name, MXC_ADAPTER_NAME) != 0) { + PRINTK("%s: %s\n", __func__, adapter->name); + return -ENODEV; + } + ov2640_i2c_client.adapter = adapter; + + ret = i2c_attach_client(&ov2640_i2c_client); + if (ret != 0) { + ov2640_i2c_client.adapter = NULL; + PRINTK("%s: i2c_attach_client failed\n", __func__); + return ret; + } + + interface_param = kzalloc(sizeof(sensor_interface), GFP_KERNEL); + if (!interface_param) { + PRINTK("%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +/*! + * ov2640 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int ov2640_detach(struct i2c_client *client) +{ + int err; +#ifdef NONSENSE + PMIC_STATUS ret; +#endif + err = i2c_detach_client(&ov2640_i2c_client); + ov2640_i2c_client.adapter = NULL; + + kfree(interface_param); + interface_param = NULL; + +#ifdef NONSENSE + /* DOVDD */ + if ((ret = pmic_power_regulator_off(REGU_GPO3)) < 0) { + PRINTK("%s:REGU_GPO3 power off error:%d\n", __func__, ret); + return 0; + } else { + PRINTK("%s:REGU_GPO3 power off ok\n", __func__); + } + PRINTK("%s:OV2640 power off ok\n", __func__); +#endif + return err; +} + +static int +ov2640_i2c_client_xfer(int addr, u8 reg, char *buf, int num, int tran_flag) +{ + struct i2c_msg msg[2]; + int ret; + + msg[0].addr = addr; + msg[0].len = 1; + msg[0].buf = ® + msg[0].flags = tran_flag; + msg[0].flags &= ~I2C_M_RD; + + msg[1].addr = addr; + msg[1].len = num; + msg[1].buf = buf; + msg[1].flags = tran_flag; + + if (tran_flag & MXC_I2C_FLAG_READ) { + msg[1].flags |= I2C_M_RD; + } else { + msg[1].flags &= ~I2C_M_RD; + } + +#if 1 + BUG_ON(ov2640_i2c_client.adapter == NULL); +#else + if (ov2640_i2c_client.adapter == NULL) { + PRINTK("%s: adapter error\n", __func__); + return -ENODEV; + } +#endif + ret = i2c_transfer(ov2640_i2c_client.adapter, msg, 2); + if (ret >= 0) { + /* PRINTK("%s:i2c transfer num:%d\n", __func__, ret); */ + return 0; + } + PRINTK("%s: i2c transfer error: %d\n", __func__, ret); + return ret; +} + +static int ov2640_write_reg(u8 reg, u8 val) +{ + int ret; + + ret = ov2640_i2c_client_xfer(OV2640_I2C_ADDRESS, reg, &val, 1, 0); + if (ret < 0) { + PRINTK("%s: write reg error: reg=%02x, val=%02x ret=%d\n", + __func__, reg, val, ret); + return ret; + } else if (ret != 1) { + PRINTK("%s: write reg error: reg=%02x, val=%02x ret=%d\n", + __func__, reg, val, ret); + return -EIO; + } + return 0; +} + +/* should be replaced by width and height version. */ +static int ov2640_init_1600_1120(void) +{ + int ret; + + ret = ov2640_write_reg(0xff, 1); + if (ret != 0) { + return ret; + } + ov2640_write_reg(0x12, 0x80); + udelay(1000); + ov2640_write_reg(0xff, 0x00); + ov2640_write_reg(0x2c, 0xff); + ov2640_write_reg(0x2e, 0xdf); + ov2640_write_reg(0xff, 0x01); + ov2640_write_reg(0x3c, 0x32); + ov2640_write_reg(0x11, 0x01); + ov2640_write_reg(0x09, 0x00); + ov2640_write_reg(0x04, 0x28); + ov2640_write_reg(0x13, 0xe5); + ov2640_write_reg(0x14, 0x48); + ov2640_write_reg(0x2c, 0x0c); + ov2640_write_reg(0x33, 0x78); + ov2640_write_reg(0x3a, 0x33); + ov2640_write_reg(0x3b, 0xfb); + ov2640_write_reg(0x3e, 0x00); + ov2640_write_reg(0x43, 0x11); + ov2640_write_reg(0x16, 0x10); + ov2640_write_reg(0x39, 0x82); + ov2640_write_reg(0x35, 0x88); + ov2640_write_reg(0x22, 0x0a); + ov2640_write_reg(0x37, 0x40); + ov2640_write_reg(0x23, 0x00); + ov2640_write_reg(0x34, 0xa0); + ov2640_write_reg(0x36, 0x1a); + ov2640_write_reg(0x06, 0x02); + ov2640_write_reg(0x07, 0xc0); + ov2640_write_reg(0x0d, 0xb7); + ov2640_write_reg(0x0e, 0x01); + ov2640_write_reg(0x4c, 0x00); + ov2640_write_reg(0x4a, 0x81); + ov2640_write_reg(0x21, 0x99); + ov2640_write_reg(0x24, 0x40); + ov2640_write_reg(0x25, 0x38); + ov2640_write_reg(0x26, 0x82); + ov2640_write_reg(0x5c, 0x00); + ov2640_write_reg(0x63, 0x00); + ov2640_write_reg(0x46, 0x3f); + ov2640_write_reg(0x0c, 0x3c); + ov2640_write_reg(0x5d, 0x55); + ov2640_write_reg(0x5e, 0x7d); + ov2640_write_reg(0x5f, 0x7d); + ov2640_write_reg(0x60, 0x55); + ov2640_write_reg(0x61, 0x70); + ov2640_write_reg(0x62, 0x80); + ov2640_write_reg(0x7c, 0x05); + ov2640_write_reg(0x20, 0x80); + ov2640_write_reg(0x28, 0x30); + ov2640_write_reg(0x6c, 0x00); + ov2640_write_reg(0x6d, 0x80); + ov2640_write_reg(0x6e, 0x00); + ov2640_write_reg(0x70, 0x02); + ov2640_write_reg(0x71, 0x94); + ov2640_write_reg(0x73, 0xc1); + ov2640_write_reg(0x3d, 0x34); + ov2640_write_reg(0x5a, 0x57); + ov2640_write_reg(0x4f, 0xbb); + ov2640_write_reg(0x50, 0x9c); + ov2640_write_reg(0xff, 0x00); + ov2640_write_reg(0xe5, 0x7f); + ov2640_write_reg(0xf9, 0xc0); + ov2640_write_reg(0x41, 0x24); + ov2640_write_reg(0x44, 0x06); + ov2640_write_reg(0xe0, 0x14); + ov2640_write_reg(0x76, 0xff); + ov2640_write_reg(0x33, 0xa0); + ov2640_write_reg(0x42, 0x20); + ov2640_write_reg(0x43, 0x18); + ov2640_write_reg(0x4c, 0x00); + ov2640_write_reg(0x87, 0xd0); + ov2640_write_reg(0xd7, 0x03); + ov2640_write_reg(0xd9, 0x10); + ov2640_write_reg(0xd3, 0x82); + ov2640_write_reg(0xc8, 0x08); + ov2640_write_reg(0xc9, 0x80); + ov2640_write_reg(0x7c, 0x00); + ov2640_write_reg(0x7d, 0x00); + ov2640_write_reg(0x7c, 0x03); + ov2640_write_reg(0x7d, 0x48); + ov2640_write_reg(0x7d, 0x48); + ov2640_write_reg(0x7c, 0x08); + ov2640_write_reg(0x7d, 0x20); + ov2640_write_reg(0x7d, 0x10); + ov2640_write_reg(0x7d, 0x0e); + ov2640_write_reg(0x90, 0x00); + ov2640_write_reg(0x91, 0x0e); + ov2640_write_reg(0x91, 0x1a); + ov2640_write_reg(0x91, 0x31); + ov2640_write_reg(0x91, 0x5a); + ov2640_write_reg(0x91, 0x69); + ov2640_write_reg(0x91, 0x75); + ov2640_write_reg(0x91, 0x7e); + ov2640_write_reg(0x91, 0x88); + ov2640_write_reg(0x91, 0x8f); + ov2640_write_reg(0x91, 0x96); + ov2640_write_reg(0x91, 0xa3); + ov2640_write_reg(0x91, 0xaf); + ov2640_write_reg(0x91, 0xc4); + ov2640_write_reg(0x91, 0xd7); + ov2640_write_reg(0x91, 0xe8); + ov2640_write_reg(0x91, 0x20); + + ov2640_write_reg(0x92, 0x00); + ov2640_write_reg(0x93, 0x06); + ov2640_write_reg(0x93, 0xe3); + ov2640_write_reg(0x93, 0x03); + ov2640_write_reg(0x93, 0x03); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x02); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + + ov2640_write_reg(0x96, 0x00); + ov2640_write_reg(0x97, 0x08); + ov2640_write_reg(0x97, 0x19); + ov2640_write_reg(0x97, 0x02); + ov2640_write_reg(0x97, 0x0c); + ov2640_write_reg(0x97, 0x24); + ov2640_write_reg(0x97, 0x30); + ov2640_write_reg(0x97, 0x28); + ov2640_write_reg(0x97, 0x26); + ov2640_write_reg(0x97, 0x02); + ov2640_write_reg(0x97, 0x98); + ov2640_write_reg(0x97, 0x80); + ov2640_write_reg(0x97, 0x00); + ov2640_write_reg(0x97, 0x00); + + ov2640_write_reg(0xa4, 0x00); + ov2640_write_reg(0xa8, 0x00); + ov2640_write_reg(0xc5, 0x11); + ov2640_write_reg(0xc6, 0x51); + ov2640_write_reg(0xbf, 0x80); + ov2640_write_reg(0xc7, 0x10); + ov2640_write_reg(0xb6, 0x66); + ov2640_write_reg(0xb8, 0xa5); + ov2640_write_reg(0xb7, 0x64); + ov2640_write_reg(0xb9, 0x7c); + ov2640_write_reg(0xb3, 0xaf); + ov2640_write_reg(0xb4, 0x97); + ov2640_write_reg(0xb5, 0xff); + ov2640_write_reg(0xb0, 0xc5); + ov2640_write_reg(0xb1, 0x94); + ov2640_write_reg(0xb2, 0x0f); + ov2640_write_reg(0xc4, 0x5c); + + ov2640_write_reg(0xa6, 0x00); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x1b); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x19); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x19); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + + ov2640_write_reg(0xc0, 0xc8); + ov2640_write_reg(0xc1, 0x96); + ov2640_write_reg(0x86, 0x3d); + ov2640_write_reg(0x50, 0x00); + ov2640_write_reg(0x51, 0x90); + ov2640_write_reg(0x52, 0x18); + ov2640_write_reg(0x53, 0x00); + ov2640_write_reg(0x54, 0x00); + ov2640_write_reg(0x55, 0x88); + ov2640_write_reg(0x57, 0x00); + ov2640_write_reg(0x5a, 0x90); + ov2640_write_reg(0x5b, 0x18); + ov2640_write_reg(0x5c, 0x05); + ov2640_write_reg(0xc3, 0xef); + ov2640_write_reg(0x7f, 0x00); + ov2640_write_reg(0xda, 0x01); + ov2640_write_reg(0xe5, 0x1f); + ov2640_write_reg(0xe1, 0x67); + ov2640_write_reg(0xe0, 0x00); + ov2640_write_reg(0xdd, 0x7f); + ov2640_write_reg(0x05, 0x00); + + return 0; +} + +static int ov2640_init_800_600(void) +{ + int ret; + ret = ov2640_write_reg(0xff, 0x00); + if (ret != 0) { + return ret; + } + ov2640_write_reg(0xff, 0x01); + ov2640_write_reg(0x12, 0x80); + udelay(1000); + ov2640_write_reg(0xff, 0x00); + ov2640_write_reg(0x2c, 0xff); + ov2640_write_reg(0x2e, 0xdf); + ov2640_write_reg(0xff, 0x01); + ov2640_write_reg(0x3c, 0x32); + ov2640_write_reg(0x11, 0x01); + ov2640_write_reg(0x09, 0x00); + ov2640_write_reg(0x04, 0x28); + ov2640_write_reg(0x13, 0xe5); + ov2640_write_reg(0x14, 0x48); + ov2640_write_reg(0x2c, 0x0c); + ov2640_write_reg(0x33, 0x78); + ov2640_write_reg(0x3a, 0x33); + ov2640_write_reg(0x3b, 0xfb); + ov2640_write_reg(0x3e, 0x00); + ov2640_write_reg(0x43, 0x11); + ov2640_write_reg(0x16, 0x10); + ov2640_write_reg(0x39, 0x92); + ov2640_write_reg(0x35, 0xda); + ov2640_write_reg(0x22, 0x1a); + ov2640_write_reg(0x37, 0xc3); + ov2640_write_reg(0x23, 0x00); + ov2640_write_reg(0x34, 0xc0); + ov2640_write_reg(0x36, 0x1a); + ov2640_write_reg(0x06, 0x88); + ov2640_write_reg(0x07, 0xc0); + ov2640_write_reg(0x0d, 0x87); + ov2640_write_reg(0x0e, 0x41); + ov2640_write_reg(0x4c, 0x00); + ov2640_write_reg(0x4a, 0x81); + ov2640_write_reg(0x21, 0x99); + ov2640_write_reg(0x24, 0x40); + ov2640_write_reg(0x25, 0x38); + ov2640_write_reg(0x26, 0x82); + ov2640_write_reg(0x5c, 0x00); + ov2640_write_reg(0x63, 0x00); + ov2640_write_reg(0x46, 0x22); + ov2640_write_reg(0x0c, 0x3c); + ov2640_write_reg(0x5d, 0x55); + ov2640_write_reg(0x5e, 0x7d); + ov2640_write_reg(0x5f, 0x7d); + ov2640_write_reg(0x60, 0x55); + ov2640_write_reg(0x61, 0x70); + ov2640_write_reg(0x62, 0x80); + ov2640_write_reg(0x7c, 0x05); + ov2640_write_reg(0x20, 0x80); + ov2640_write_reg(0x28, 0x30); + ov2640_write_reg(0x6c, 0x00); + ov2640_write_reg(0x6d, 0x80); + ov2640_write_reg(0x6e, 0x00); + ov2640_write_reg(0x70, 0x02); + ov2640_write_reg(0x71, 0x94); + ov2640_write_reg(0x73, 0xc1); + ov2640_write_reg(0x12, 0x40); + ov2640_write_reg(0x17, 0x11); + ov2640_write_reg(0x18, 0x43); + ov2640_write_reg(0x19, 0x00); + ov2640_write_reg(0x1a, 0x4b); + ov2640_write_reg(0x32, 0x09); + ov2640_write_reg(0x37, 0xc0); + ov2640_write_reg(0x4f, 0xca); + ov2640_write_reg(0x50, 0xa8); + ov2640_write_reg(0x6d, 0x00); + ov2640_write_reg(0x3d, 0x38); + ov2640_write_reg(0xff, 0x00); + ov2640_write_reg(0xe5, 0x7f); + ov2640_write_reg(0xf9, 0xc0); + ov2640_write_reg(0x41, 0x24); + ov2640_write_reg(0x44, 0x06); + ov2640_write_reg(0xe0, 0x14); + ov2640_write_reg(0x76, 0xff); + ov2640_write_reg(0x33, 0xa0); + ov2640_write_reg(0x42, 0x20); + ov2640_write_reg(0x43, 0x18); + ov2640_write_reg(0x4c, 0x00); + ov2640_write_reg(0x87, 0xd0); + ov2640_write_reg(0x88, 0x3f); + ov2640_write_reg(0xd7, 0x03); + ov2640_write_reg(0xd9, 0x10); + ov2640_write_reg(0xd3, 0x82); + ov2640_write_reg(0xc8, 0x08); + ov2640_write_reg(0xc9, 0x80); + ov2640_write_reg(0x7c, 0x00); + ov2640_write_reg(0x7d, 0x00); + ov2640_write_reg(0x7c, 0x03); + ov2640_write_reg(0x7d, 0x48); + ov2640_write_reg(0x7d, 0x48); + ov2640_write_reg(0x7c, 0x08); + ov2640_write_reg(0x7d, 0x20); + ov2640_write_reg(0x7d, 0x10); + ov2640_write_reg(0x7d, 0x0e); + ov2640_write_reg(0x90, 0x00); + ov2640_write_reg(0x91, 0x0e); + ov2640_write_reg(0x91, 0x1a); + ov2640_write_reg(0x91, 0x31); + ov2640_write_reg(0x91, 0x5a); + ov2640_write_reg(0x91, 0x69); + ov2640_write_reg(0x91, 0x75); + ov2640_write_reg(0x91, 0x7e); + ov2640_write_reg(0x91, 0x88); + ov2640_write_reg(0x91, 0x8f); + ov2640_write_reg(0x91, 0x96); + ov2640_write_reg(0x91, 0xa3); + ov2640_write_reg(0x91, 0xaf); + ov2640_write_reg(0x91, 0xc4); + ov2640_write_reg(0x91, 0xd7); + ov2640_write_reg(0x91, 0xe8); + ov2640_write_reg(0x91, 0x20); + + ov2640_write_reg(0x92, 0x00); + ov2640_write_reg(0x93, 0x06); + ov2640_write_reg(0x93, 0xe3); + ov2640_write_reg(0x93, 0x03); + ov2640_write_reg(0x93, 0x03); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x02); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + ov2640_write_reg(0x93, 0x00); + + ov2640_write_reg(0x96, 0x00); + ov2640_write_reg(0x97, 0x08); + ov2640_write_reg(0x97, 0x19); + ov2640_write_reg(0x97, 0x02); + ov2640_write_reg(0x97, 0x0c); + ov2640_write_reg(0x97, 0x24); + ov2640_write_reg(0x97, 0x30); + ov2640_write_reg(0x97, 0x28); + ov2640_write_reg(0x97, 0x26); + ov2640_write_reg(0x97, 0x02); + ov2640_write_reg(0x97, 0x98); + ov2640_write_reg(0x97, 0x80); + ov2640_write_reg(0x97, 0x00); + ov2640_write_reg(0x97, 0x00); + + ov2640_write_reg(0xa4, 0x00); + ov2640_write_reg(0xa8, 0x00); + ov2640_write_reg(0xc5, 0x11); + ov2640_write_reg(0xc6, 0x51); + ov2640_write_reg(0xbf, 0x80); + ov2640_write_reg(0xc7, 0x10); + ov2640_write_reg(0xb6, 0x66); + ov2640_write_reg(0xb8, 0xa5); + ov2640_write_reg(0xb7, 0x64); + ov2640_write_reg(0xb9, 0x7c); + ov2640_write_reg(0xb3, 0xaf); + ov2640_write_reg(0xb4, 0x97); + ov2640_write_reg(0xb5, 0xff); + ov2640_write_reg(0xb0, 0xc5); + ov2640_write_reg(0xb1, 0x94); + ov2640_write_reg(0xb2, 0x0f); + ov2640_write_reg(0xc4, 0x5c); + + ov2640_write_reg(0xa6, 0x00); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x1b); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x19); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + ov2640_write_reg(0xa7, 0x20); + ov2640_write_reg(0xa7, 0xd8); + ov2640_write_reg(0xa7, 0x19); + ov2640_write_reg(0xa7, 0x31); + ov2640_write_reg(0xa7, 0x00); + ov2640_write_reg(0xa7, 0x18); + + ov2640_write_reg(0xc0, 0x64); + ov2640_write_reg(0xc1, 0x4b); + ov2640_write_reg(0x86, 0x1d); + ov2640_write_reg(0x50, 0x00); + ov2640_write_reg(0x51, 0xc8); + ov2640_write_reg(0x52, 0x96); + ov2640_write_reg(0x53, 0x00); + ov2640_write_reg(0x54, 0x00); + ov2640_write_reg(0x55, 0x00); + ov2640_write_reg(0x57, 0x00); + ov2640_write_reg(0x5a, 0xc8); + ov2640_write_reg(0x5b, 0x96); + ov2640_write_reg(0x5c, 0x00); + ov2640_write_reg(0xc3, 0xef); + ov2640_write_reg(0x7f, 0x00); + ov2640_write_reg(0xda, 0x01); + ov2640_write_reg(0xe5, 0x1f); + ov2640_write_reg(0xe1, 0x67); + ov2640_write_reg(0xe0, 0x00); + ov2640_write_reg(0xdd, 0x7f); + ov2640_write_reg(0x05, 0x00); + + return 0; +} + +/*! + * ov2640 sensor interface Initialization + * @param param sensor_interface * + * @param width u32 + * @param height u32 + * @return None + */ +static void ov2640_interface(sensor_interface * param, u32 width, u32 height) +{ + param->Vsync_pol = 0x0; + param->clk_mode = 0x0; /* gated */ + param->pixclk_pol = 0x0; + param->data_width = 0x1; + param->data_pol = 0x0; + param->ext_vsync = 0x0; + param->Vsync_pol = 0x0; + param->Hsync_pol = 0x0; + param->width = width - 1; + param->height = height - 1; + param->pixel_fmt = IPU_PIX_FMT_UYVY; + param->mclk = 27000000; +} + +static void +ov2640_set_color(int bright, int saturation, int red, int green, int blue) +{ + return; +} + +static void +ov2640_get_color(int *bright, int *saturation, int *red, int *green, int *blue) +{ + return; +} + +static void ov2640_set_ae_mode(int ae_mode) +{ + return; +} + +static void ov2640_get_ae_mode(int *ae_mode) +{ + return; +} + +extern void gpio_sensor_active(void); +extern cam_data *g_cam; + +static sensor_interface *ov2640_config(int *frame_rate, int high_quality) +{ + u32 out_width, out_height; + + if (interface_param == NULL) { + return NULL; + } +#ifdef NONSENSE + PMIC_STATUS ret; + t_regulator_voltage voltage; + + /* AVDD--2.8v */ + voltage.vmmc1 = VMMC1_2_8V; + if ((ret = pmic_power_regulator_set_voltage(REGU_VMMC1, voltage)) < 0) { + PRINTK("%s:vmmc1 set voltage error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:vmmc1 set voltage ok\n", __func__); + } + + if ((ret = pmic_power_regulator_on(REGU_VMMC1)) < 0) { + PRINTK("%s:vmmc1 power on error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:vmmc1 power on ok\n", __func__); + } + + /* DVDD--1.3v */ + voltage.vvib = VVIB_1_3V; + if ((ret = pmic_power_regulator_set_voltage(REGU_VVIB, voltage)) < 0) { + PRINTK("%s:VVIB set voltage error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:VVIB set voltage ok\n", __func__); + } + if ((ret = pmic_power_regulator_on(REGU_VVIB)) < 0) { + PRINTK("%s:VVIB power regulator on error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:VVIB power on ok\n", __func__); + } + + /* DOVDD--2v(1.8-3.0) */ + voltage.sw2b = SW2B_2V; + if ((ret = pmic_power_regulator_set_voltage(SW_SW2B, voltage)) < 0) { + PRINTK("%s:SW2B set voltage error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:SW2B set voltage ok\n", __func__); + } + if (pmic_power_set_regen_assig(REGU_GPO3, 1) < 0) { + PRINTK("%s:set_regen_assig error\n", __func__); + return NULL; + } else { + PRINTK("%s:set_regen_assig ok\n", __func__); + } + if ((ret = pmic_power_regulator_on(REGU_GPO3)) < 0) { + PRINTK("%s:REGU_GPO3 power on error:%d\n", __func__, ret); + return NULL; + } else { + PRINTK("%s:REGU_GPO3 power on ok\n", __func__); + } + PRINTK("%s:OV2640 power on ok\n", __func__); +#endif + if (high_quality) { + out_width = 1600; + out_height = 1120; + g_cam->crop_bounds.left = 0; + g_cam->crop_bounds.width = 1600; + g_cam->crop_bounds.top = 0; + g_cam->crop_bounds.height = 1120; + g_cam->crop_current = g_cam->crop_defrect = g_cam->crop_bounds; +#ifdef CONFIG_ARCH_MX3 + ipu_csi_set_window_size(g_cam->crop_current.width, + g_cam->crop_current.height); + ipu_csi_set_window_pos(g_cam->crop_current.left, + g_cam->crop_current.top); +#endif + g_cam->streamparm.parm.capture.capturemode = 1; + } else { + out_width = 640; + out_height = 480; + g_cam->crop_bounds.left = 0; + g_cam->crop_bounds.width = 640; + g_cam->crop_bounds.top = 0; + g_cam->crop_bounds.height = 480; + g_cam->crop_current = g_cam->crop_defrect = g_cam->crop_bounds; +#ifdef CONFIG_ARCH_MX3 + ipu_csi_set_window_size(g_cam->crop_current.width, + g_cam->crop_current.height); + ipu_csi_set_window_pos(g_cam->crop_current.left, + g_cam->crop_current.top); +#endif + g_cam->streamparm.parm.capture.capturemode = 0; + } + ov2640_interface(interface_param, out_width, out_height); + set_mclk_rate(&interface_param->mclk); + + if (high_quality) { + if (ov2640_init_1600_1120() != 0) { + return NULL; + } + } else { + if (ov2640_init_800_600() != 0) { + return NULL; + } + } + return interface_param; +} + +static void ov2640_get_control_params(int *ae, int *awb, int *flicker) +{ + *ae = 0; + *awb = 0; + *flicker = 0; +} + +static sensor_interface *ov2640_reset(void) +{ + return ov2640_config(&reset_frame_rate, 0); +} + +static int ov2640_get_status(void) +{ + return 0; +} + +struct camera_sensor camera_sensor_if = { + .set_color = ov2640_set_color, + .get_color = ov2640_get_color, + .set_ae_mode = ov2640_set_ae_mode, + .get_ae_mode = ov2640_get_ae_mode, + .get_control_params = ov2640_get_control_params, + .config = ov2640_config, + .reset = ov2640_reset, + .get_status = ov2640_get_status, +}; + +EXPORT_SYMBOL(camera_sensor_if); + +/*! + * ov2640 init function + * + * @return Error code indicating success or failure + */ +static __init int ov2640_init(void) +{ + u8 err; + + gpio_sensor_active(); + + err = i2c_add_driver(&ov2640_i2c_driver); + + return err; +} + +extern void gpio_sensor_inactive(void); +/*! + * OV2640 cleanup function + * + * @return Error code indicating success or failure + */ +static void __exit ov2640_clean(void) +{ + i2c_del_driver(&ov2640_i2c_driver); + + gpio_sensor_inactive(); +} + +module_init(ov2640_init); +module_exit(ov2640_clean); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("OV2640 Camera Driver"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/capture/sensor_clock.c linux-2.6.28-karo/drivers/media/video/mxc/capture/sensor_clock.c --- linux-2.6.28/drivers/media/video/mxc/capture/sensor_clock.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/capture/sensor_clock.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,56 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file sensor_clock.c + * + * @brief camera clock function + * + * @ingroup Camera + */ +#include +#include +#include +#include +#include + +/* + * set_mclk_rate + * + * @param p_mclk_freq mclk frequence + * + */ +void set_mclk_rate(uint32_t * p_mclk_freq) +{ + struct clk *clk; + int i; + uint32_t freq = 0; + uint32_t step = *p_mclk_freq / 8; + + clk = clk_get(NULL, "csi_clk"); + + for (i = 0; i <= 8; i++) { + freq = clk_round_rate(clk, *p_mclk_freq - (i * step)); + if (freq <= *p_mclk_freq) + break; + } + clk_set_rate(clk, freq); + + *p_mclk_freq = freq; + + clk_put(clk); + pr_debug("mclk frequency = %d\n", *p_mclk_freq); +} + +/* Exported symbols for modules. */ +EXPORT_SYMBOL(set_mclk_rate); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/Makefile linux-2.6.28-karo/drivers/media/video/mxc/opl/Makefile --- linux-2.6.28/drivers/media/video/mxc/opl/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,5 @@ +opl-objs := opl_mod.o rotate90_u16.o rotate270_u16.o \ + rotate90_u16_qcif.o rotate270_u16_qcif.o \ + vmirror_u16.o hmirror_rotate180_u16.o + +obj-$(CONFIG_VIDEO_MXC_OPL) += opl.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c linux-2.6.28-karo/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c --- linux-2.6.28/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,259 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include "opl.h" + +static inline u32 rot_left_u16(u16 x, unsigned int n) +{ + return (x << n) | (x >> (16 - n)); +} + +static inline u32 rot_left_u32(u32 x, unsigned int n) +{ + return (x << n) | (x >> (32 - n)); +} + +static inline u32 byte_swap_u32(u32 x) +{ + u32 t1, t2, t3; + + t1 = x ^ ((x << 16) | x >> 16); + t2 = t1 & 0xff00ffff; + t3 = (x >> 8) | (x << 24); + return t3 ^ (t2 >> 8); +} + +static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); + +int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride) +{ + if (!src || !dst) + return OPLERR_NULL_PTR; + + if (width == 0 || height == 0 || src_line_stride == 0 + || dst_line_stride == 0) + return OPLERR_BAD_ARG; + + if (width % 8 == 0) + return opl_hmirror_u16_by8(src, src_line_stride, width, height, + dst, dst_line_stride, 0); + else if (width % 4 == 0) + return opl_hmirror_u16_by4(src, src_line_stride, width, height, + dst, dst_line_stride, 0); + else if (width % 2 == 0) + return opl_hmirror_u16_by2(src, src_line_stride, width, height, + dst, dst_line_stride, 0); + else /* (width % 1) */ + return opl_hmirror_u16_by1(src, src_line_stride, width, height, + dst, dst_line_stride, 0); +} + +int opl_rotate180_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride) +{ + if (!src || !dst) + return OPLERR_NULL_PTR; + + if (width == 0 || height == 0 || src_line_stride == 0 + || dst_line_stride == 0) + return OPLERR_BAD_ARG; + + if (width % 8 == 0) + return opl_hmirror_u16_by8(src, src_line_stride, width, height, + dst, dst_line_stride, 1); + else if (width % 4 == 0) + return opl_hmirror_u16_by4(src, src_line_stride, width, height, + dst, dst_line_stride, 1); + else if (width % 2 == 0) + return opl_hmirror_u16_by2(src, src_line_stride, width, height, + dst, dst_line_stride, 1); + else /* (width % 1) */ + return opl_hmirror_u16_by1(src, src_line_stride, width, height, + dst, dst_line_stride, 1); +} + +static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const u8 *src_row_addr; + const u8 *psrc; + u8 *dst_row_addr, *pdst; + int i, j; + u16 pixel; + + src_row_addr = src; + if (vmirror) { + dst_row_addr = dst + dst_line_stride * (height - 1); + dst_line_stride = -dst_line_stride; + } else + dst_row_addr = dst; + + /* Loop over all rows */ + for (i = 0; i < height; i++) { + /* Loop over each pixel */ + psrc = src_row_addr; + pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL + - (BYTES_PER_PIXEL - BYTES_PER_PIXEL); + for (j = 0; j < width; j++) { + pixel = *(u16 *) psrc; + *(u16 *) pdst = pixel; + psrc += BYTES_PER_PIXEL; + pdst -= BYTES_PER_PIXEL; + } + src_row_addr += src_line_stride; + dst_row_addr += dst_line_stride; + } + + return OPLERR_SUCCESS; +} + +static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const u8 *src_row_addr; + const u8 *psrc; + u8 *dst_row_addr, *pdst; + int i, j; + u32 pixelsin, pixelsout; + + src_row_addr = src; + if (vmirror) { + dst_row_addr = dst + dst_line_stride * (height - 1); + dst_line_stride = -dst_line_stride; + } else + dst_row_addr = dst; + + /* Loop over all rows */ + for (i = 0; i < height; i++) { + /* Loop over each pixel */ + psrc = src_row_addr; + pdst = dst_row_addr + (width - 2) * BYTES_PER_PIXEL; + for (j = 0; j < (width >> 1); j++) { + pixelsin = *(u32 *) psrc; + pixelsout = rot_left_u32(pixelsin, 16); + *(u32 *) pdst = pixelsout; + psrc += BYTES_PER_2PIXEL; + pdst -= BYTES_PER_2PIXEL; + } + src_row_addr += src_line_stride; + dst_row_addr += dst_line_stride; + } + + return OPLERR_SUCCESS; +} + +static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const u8 *src_row_addr; + const u8 *psrc; + u8 *dst_row_addr, *pdst; + int i, j; + + union doubleword { + u64 dw; + u32 w[2]; + }; + + union doubleword inbuf; + union doubleword outbuf; + + src_row_addr = src; + if (vmirror) { + dst_row_addr = dst + dst_line_stride * (height - 1); + dst_line_stride = -dst_line_stride; + } else + dst_row_addr = dst; + + /* Loop over all rows */ + for (i = 0; i < height; i++) { + /* Loop over each pixel */ + psrc = src_row_addr; + pdst = dst_row_addr + (width - 4) * BYTES_PER_PIXEL; + for (j = 0; j < (width >> 2); j++) { + inbuf.dw = *(u64 *) psrc; + outbuf.w[0] = rot_left_u32(inbuf.w[1], 16); + outbuf.w[1] = rot_left_u32(inbuf.w[0], 16); + *(u64 *) pdst = outbuf.dw; + psrc += BYTES_PER_4PIXEL; + pdst -= BYTES_PER_4PIXEL; + } + src_row_addr += src_line_stride; + dst_row_addr += dst_line_stride; + } + return OPLERR_SUCCESS; +} + +static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const u8 *src_row_addr; + const u8 *psrc; + u8 *dst_row_addr, *pdst; + int i, j; + + src_row_addr = src; + if (vmirror) { + dst_row_addr = dst + dst_line_stride * (height - 1); + dst_line_stride = -dst_line_stride; + } else + dst_row_addr = dst; + + /* Loop over all rows */ + for (i = 0; i < height; i++) { + /* Loop over each pixel */ + psrc = src_row_addr; + pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL - 2; + for (j = (width >> 3); j > 0; j--) { + __asm__ volatile ( + "ldmia %0!,{r2-r5}\n\t" + "mov r6, r2\n\t" + "mov r7, r3\n\t" + "mov r2, r5, ROR #16\n\t" + "mov r3, r4, ROR #16\n\t" + "mov r4, r7, ROR #16\n\t" + "mov r5, r6, ROR #16\n\t" + "stmda %1!,{r2-r5}\n\t" + + :"+r"(psrc), "+r"(pdst) + :"0"(psrc), "1"(pdst) + :"r2", "r3", "r4", "r5", "r6", "r7", + "memory" + ); + } + src_row_addr += src_line_stride; + dst_row_addr += dst_line_stride; + } + + return OPLERR_SUCCESS; +} + +EXPORT_SYMBOL(opl_hmirror_u16); +EXPORT_SYMBOL(opl_rotate180_u16); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/opl.h linux-2.6.28-karo/drivers/media/video/mxc/opl/opl.h --- linux-2.6.28/drivers/media/video/mxc/opl/opl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/opl.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,162 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup OPLIP OPL Image Processing + */ +/*! + * @file opl.h + * + * @brief The OPL (Open Primitives Library) Image Processing library defines + * efficient functions for rotation and mirroring. + * + * It includes ARM9-optimized rotation and mirroring functions. It is derived + * from the original OPL project which is found at sourceforge.freescale.net. + * + * @ingroup OPLIP + */ +#ifndef __OPL_H__ +#define __OPL_H__ + +#include + +#define BYTES_PER_PIXEL 2 +#define CACHE_LINE_WORDS 8 +#define BYTES_PER_WORD 4 + +#define BYTES_PER_2PIXEL (BYTES_PER_PIXEL * 2) +#define BYTES_PER_4PIXEL (BYTES_PER_PIXEL * 4) +#define BYTES_PER_8PIXEL (BYTES_PER_PIXEL * 8) + +#define QCIF_Y_WIDTH 176 +#define QCIF_Y_HEIGHT 144 + +/*! Enumerations of opl error code */ +enum opl_error { + OPLERR_SUCCESS = 0, + OPLERR_NULL_PTR, + OPLERR_BAD_ARG, + OPLERR_DIV_BY_ZERO, + OPLERR_OVER_FLOW, + OPLERR_UNDER_FLOW, + OPLERR_MISALIGNED, +}; + +/*! + * @brief Rotate a 16bbp buffer 90 degrees clockwise. + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride); + +/*! + * @brief Rotate a 16bbp buffer 180 degrees clockwise. + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_rotate180_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride); + +/*! + * @brief Rotate a 16bbp buffer 270 degrees clockwise + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_rotate270_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride); + +/*! + * @brief Mirror a 16bpp buffer horizontally + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride); + +/*! + * @brief Mirror a 16bpp buffer vertically + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride); + +/*! + * @brief Rotate a 16bbp buffer 90 degrees clockwise and mirror vertically + * It is equivalent to rotate 270 degree and mirror horizontally + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride); + +/*! + * @brief Rotate a 16bbp buffer 270 degrees clockwise and mirror vertically + * It is equivalent to rotate 90 degree and mirror horizontally + * + * @param src Pointer to the input buffer + * @param src_line_stride Length in bytes of a raster line of the input buffer + * @param width Width in pixels of the region in the input buffer + * @param height Height in pixels of the region in the input buffer + * @param dst Pointer to the output buffer + * @param dst_line_stride Length in bytes of a raster line of the output buffer + * + * @return Standard OPL error code. See enumeration for possible result codes. + */ +int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride); + +#endif /* __OPL_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/opl_mod.c linux-2.6.28-karo/drivers/media/video/mxc/opl/opl_mod.c --- linux-2.6.28/drivers/media/video/mxc/opl/opl_mod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/opl_mod.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,30 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include + +static __init int opl_init(void) +{ + return 0; +} + +static void __exit opl_exit(void) +{ +} + +module_init(opl_init); +module_exit(opl_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("OPL Software Rotation/Mirroring"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/rotate270_u16.c linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate270_u16.c --- linux-2.6.28/drivers/media/video/mxc/opl/rotate270_u16.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate270_u16.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,285 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include "opl.h" + +static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror); +static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror); +int opl_rotate270_u16_qcif(const u8 * src, u8 * dst); + +int opl_rotate270_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride) +{ + return opl_rotate270_vmirror_u16_both(src, src_line_stride, width, + height, dst, dst_line_stride, 0); +} + +int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride) +{ + return opl_rotate270_vmirror_u16_both(src, src_line_stride, width, + height, dst, dst_line_stride, 1); +} + +static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL; + const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL / 4; + + if (!src || !dst) + return OPLERR_NULL_PTR; + + if (width == 0 || height == 0 || src_line_stride == 0 + || dst_line_stride == 0) + return OPLERR_BAD_ARG; + + /* The QCIF algorithm doesn't support vertical mirroring */ + if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT + && src_line_stride == QCIF_Y_WIDTH * 2 + && src_line_stride == QCIF_Y_HEIGHT * 2) + return opl_rotate270_u16_qcif(src, dst); + else if (width % BLOCK_SIZE_PIXELS == 0 + && height % BLOCK_SIZE_PIXELS == 0) + return opl_rotate270_u16_by16(src, src_line_stride, width, + height, dst, dst_line_stride, + vmirror); + else if (width % BLOCK_SIZE_PIXELS_BY4 == 0 + && height % BLOCK_SIZE_PIXELS_BY4 == 0) + return opl_rotate270_u16_by4(src, src_line_stride, width, + height, dst, dst_line_stride, + vmirror); + else + return OPLERR_BAD_ARG; +} + +/* + * Rotate Counter Clockwise, divide RGB component into 16 row strips, read + * non sequentially and write sequentially. This is done in 16 line strips + * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels + * are 2 bytes. The 16 reads will be cache misses, but the next 240 should + * be from cache. The writes to the output buffer will be sequential for 16 + * writes. + * + * Example: + * Input data matrix: output matrix + * + * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 | + * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 | + * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 | + * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 | + * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially + * Read first column + * Start at the bottom + * Move to next column and repeat + * + * Loop over k decreasing (blocks) + * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k) + * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES) + * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k) + * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1) + * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + * + * Loop over i decreasing (width) + * Each pix: + * in_block_ptr += RGB_WIDTH_BYTES + * out_block_ptr += 4 + * + * Each row of block: + * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2 + * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS; + * + * It may perform vertical mirroring too depending on the vmirror flag. + */ +static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL; + const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS + - BYTES_PER_PIXEL; + const int OUT_INDEX = vmirror ? + -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS + : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS; + const u8 *in_block_ptr; + u8 *out_block_ptr; + int i, k; + + for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) { + in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k) + * BLOCK_SIZE_PIXELS) * src_line_stride; + out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k) + * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL) + + (width - 1) * dst_line_stride; + + /* + * For vertical mirroring the writing starts from the + * first line + */ + if (vmirror) + out_block_ptr -= dst_line_stride * (width - 1); + + for (i = width; i > 0; i--) { + __asm__ volatile ( + "ldrh r2, [%0], %4\n\t" + "ldrh r3, [%0], %4\n\t" + "ldrh r4, [%0], %4\n\t" + "ldrh r5, [%0], %4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], %4\n\t" + "ldrh r3, [%0], %4\n\t" + "ldrh r4, [%0], %4\n\t" + "ldrh r5, [%0], %4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], %4\n\t" + "ldrh r3, [%0], %4\n\t" + "ldrh r4, [%0], %4\n\t" + "ldrh r5, [%0], %4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], %4\n\t" + "ldrh r3, [%0], %4\n\t" + "ldrh r4, [%0], %4\n\t" + "ldrh r5, [%0], %4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */ + :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */ + :"r2", "r3", "r4", "r5", "memory" /* modify */ + ); + in_block_ptr -= IN_INDEX; + out_block_ptr -= OUT_INDEX; + } + } + + return OPLERR_SUCCESS; +} + +/* + * Rotate Counter Clockwise, divide RGB component into 4 row strips, read + * non sequentially and write sequentially. This is done in 4 line strips + * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels + * are 2 bytes. The 4 reads will be cache misses, but the next 60 should + * be from cache. The writes to the output buffer will be sequential for 4 + * writes. + * + * Example: + * Input data matrix: output matrix + * + * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 | + * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 | + * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 | + * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 | + * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially + * Read first column + * Start at the bottom + * Move to next column and repeat + * + * Loop over k decreasing (blocks) + * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k) + * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES) + * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k) + * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1) + * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + * + * Loop over i decreasing (width) + * Each pix: + * in_block_ptr += RGB_WIDTH_BYTES + * out_block_ptr += 4 + * + * Each row of block: + * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2 + * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS; + * + * It may perform vertical mirroring too depending on the vmirror flag. + */ +static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL / 4; + const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS + - BYTES_PER_PIXEL; + const int OUT_INDEX = vmirror ? + -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS + : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS; + const u8 *in_block_ptr; + u8 *out_block_ptr; + int i, k; + + for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) { + in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k) + * BLOCK_SIZE_PIXELS) * src_line_stride; + out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k) + * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL) + + (width - 1) * dst_line_stride; + + /* + * For vertical mirroring the writing starts from the + * first line + */ + if (vmirror) + out_block_ptr -= dst_line_stride * (width - 1); + + for (i = width; i > 0; i--) { + __asm__ volatile ( + "ldrh r2, [%0], %4\n\t" + "ldrh r3, [%0], %4\n\t" + "ldrh r4, [%0], %4\n\t" + "ldrh r5, [%0], %4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */ + :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */ + :"r2", "r3", "r4", "r5", "memory" /* modify */ + ); + in_block_ptr -= IN_INDEX; + out_block_ptr -= OUT_INDEX; + } + } + + return OPLERR_SUCCESS; +} + +EXPORT_SYMBOL(opl_rotate270_u16); +EXPORT_SYMBOL(opl_rotate270_vmirror_u16); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/rotate270_u16_qcif.S linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate270_u16_qcif.S --- linux-2.6.28/drivers/media/video/mxc/opl/rotate270_u16_qcif.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate270_u16_qcif.S 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,70 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include + + .text + .align 2 +ENTRY(opl_rotate270_u16_qcif) + STMFD sp!,{r4-r10} + MOV r12,#0x160 + MOV r10,#0x90 + MOV r3,r10,LSR #4 +.L1.16: + RSB r2,r3,r10,LSR #4 + MOV r5,r2,LSL #5 + MOV r4,r12,LSR #1 + SMULBB r4,r5,r4 + ADD r2,r1,r2,LSL #5 + ADD r5,r2,#0xc000 + ADD r5,r5,#0x4e0 + MOV r2,r12,LSR #1 + ADD r4,r0,r4 +.L1.52: + LDRH r6,[r4],r12 + LDRH r7,[r4],r12 + LDRH r8,[r4],r12 + LDRH r9,[r4],r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + SUBS r2,r2,#1 + LDRH r6,[r4],r12 + LDRH r7,[r4],r12 + LDRH r8,[r4],r12 + LDRH r9,[r4],r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + LDRH r6,[r4],r12 + LDRH r7,[r4],r12 + LDRH r8,[r4],r12 + LDRH r9,[r4],r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + LDRH r6,[r4],r12 + LDRH r7,[r4],r12 + LDRH r8,[r4],r12 + LDRH r9,[r4],r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + SUB r4,r4,#0x1500 + STMIA r5,{r6,r7} + SUB r5,r5,#0x138 + SUB r4,r4,#0xfe + BGT .L1.52 + SUBS r3,r3,#1 + BGT .L1.16 + LDMFD sp!,{r4-r10} + BX lr + .size opl_rotate270_u16_qcif, . - opl_rotate270_u16_qcif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/rotate90_u16.c linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate90_u16.c --- linux-2.6.28/drivers/media/video/mxc/opl/rotate90_u16.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate90_u16.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include "opl.h" + +static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror); +static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror); +int opl_rotate90_u16_qcif(const u8 * src, u8 * dst); + +int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride) +{ + return opl_rotate90_vmirror_u16_both(src, src_line_stride, width, + height, dst, dst_line_stride, 0); +} + +int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride) +{ + return opl_rotate90_vmirror_u16_both(src, src_line_stride, width, + height, dst, dst_line_stride, 1); +} + +static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride, + int width, int height, u8 * dst, + int dst_line_stride, int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL; + const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL / 4; + + if (!src || !dst) + return OPLERR_NULL_PTR; + + if (width == 0 || height == 0 || src_line_stride == 0 + || dst_line_stride == 0) + return OPLERR_BAD_ARG; + + /* The QCIF algorithm doesn't support vertical mirroring */ + if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT + && src_line_stride == QCIF_Y_WIDTH * 2 + && src_line_stride == QCIF_Y_HEIGHT * 2) + return opl_rotate90_u16_qcif(src, dst); + else if (width % BLOCK_SIZE_PIXELS == 0 + && height % BLOCK_SIZE_PIXELS == 0) + return opl_rotate90_u16_by16(src, src_line_stride, width, + height, dst, dst_line_stride, + vmirror); + else if (width % BLOCK_SIZE_PIXELS_BY4 == 0 + && height % BLOCK_SIZE_PIXELS_BY4 == 0) + return opl_rotate90_u16_by4(src, src_line_stride, width, height, + dst, dst_line_stride, vmirror); + else + return OPLERR_BAD_ARG; +} + +/* + * Performs clockwise rotation (and possibly vertical mirroring depending + * on the vmirror flag) using block sizes of 16x16 + * The algorithm is similar to 270 degree clockwise rotation algorithm + */ +static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL; + const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS; + const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS + + BYTES_PER_PIXEL; + const int OUT_INDEX = vmirror ? + -dst_line_stride - BLOCK_SIZE_BYTES + : dst_line_stride - BLOCK_SIZE_BYTES; + const u8 *in_block_ptr; + u8 *out_block_ptr; + int i, k; + + for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) { + in_block_ptr = src + src_line_stride * (height - 1) + - (src_line_stride * BLOCK_SIZE_PIXELS * + (height / BLOCK_SIZE_PIXELS - k)); + out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS * + ((height / BLOCK_SIZE_PIXELS) - k); + + /* + * For vertical mirroring the writing starts from the + * bottom line + */ + if (vmirror) + out_block_ptr += dst_line_stride * (width - 1); + + for (i = width; i > 0; i--) { + __asm__ volatile ( + "ldrh r2, [%0], -%4\n\t" + "ldrh r3, [%0], -%4\n\t" + "ldrh r4, [%0], -%4\n\t" + "ldrh r5, [%0], -%4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], -%4\n\t" + "ldrh r3, [%0], -%4\n\t" + "ldrh r4, [%0], -%4\n\t" + "ldrh r5, [%0], -%4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], -%4\n\t" + "ldrh r3, [%0], -%4\n\t" + "ldrh r4, [%0], -%4\n\t" + "ldrh r5, [%0], -%4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + "ldrh r2, [%0], -%4\n\t" + "ldrh r3, [%0], -%4\n\t" + "ldrh r4, [%0], -%4\n\t" + "ldrh r5, [%0], -%4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */ + :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */ + :"r2", "r3", "r4", "r5", "memory" /* modify */ + ); + in_block_ptr += IN_INDEX; + out_block_ptr += OUT_INDEX; + } + } + + return OPLERR_SUCCESS; +} + +/* + * Performs clockwise rotation (and possibly vertical mirroring depending + * on the vmirror flag) using block sizes of 4x4 + * The algorithm is similar to 270 degree clockwise rotation algorithm + */ +static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width, + int height, u8 * dst, int dst_line_stride, + int vmirror) +{ + const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD + / BYTES_PER_PIXEL / 4; + const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS; + const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS + + BYTES_PER_PIXEL; + const int OUT_INDEX = vmirror ? + -dst_line_stride - BLOCK_SIZE_BYTES + : dst_line_stride - BLOCK_SIZE_BYTES; + const u8 *in_block_ptr; + u8 *out_block_ptr; + int i, k; + + for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) { + in_block_ptr = src + src_line_stride * (height - 1) + - (src_line_stride * BLOCK_SIZE_PIXELS * + (height / BLOCK_SIZE_PIXELS - k)); + out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS + * ((height / BLOCK_SIZE_PIXELS) - k); + + /* + * For horizontal mirroring the writing starts from the + * bottom line + */ + if (vmirror) + out_block_ptr += dst_line_stride * (width - 1); + + for (i = width; i > 0; i--) { + __asm__ volatile ( + "ldrh r2, [%0], -%4\n\t" + "ldrh r3, [%0], -%4\n\t" + "ldrh r4, [%0], -%4\n\t" + "ldrh r5, [%0], -%4\n\t" + "orr r2, r2, r3, lsl #16\n\t" + "orr r4, r4, r5, lsl #16\n\t" + "str r2, [%1], #4\n\t" + "str r4, [%1], #4\n\t" + + :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */ + :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */ + :"r2", "r3", "r4", "r5", "memory" /* modify */ + ); + in_block_ptr += IN_INDEX; + out_block_ptr += OUT_INDEX; + } + } + + return OPLERR_SUCCESS; +} + +EXPORT_SYMBOL(opl_rotate90_u16); +EXPORT_SYMBOL(opl_rotate90_vmirror_u16); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/rotate90_u16_qcif.S linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate90_u16_qcif.S --- linux-2.6.28/drivers/media/video/mxc/opl/rotate90_u16_qcif.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/rotate90_u16_qcif.S 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include + + .text + .align 2 +ENTRY(opl_rotate90_u16_qcif) + STMFD sp!,{r4-r10} + MOV r12,#0x160 + MOV r10,#0x90 + MOV r3,r10,LSR #4 +.L1.216: + RSB r2,r3,r10,LSR #4 + MOV r4,#0x20 + SMULBB r5,r4,r2 + MOV r4,#0x1600 + SMULBB r2,r4,r2 + ADD r4,r0,#0xc000 + ADD r4,r4,#0x4a0 + SUB r4,r4,r2 + MOV r2,r12,LSR #1 + ADD r5,r1,r5 +.L1.256: + LDRH r6,[r4],-r12 + LDRH r7,[r4],-r12 + LDRH r8,[r4],-r12 + LDRH r9,[r4],-r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + SUBS r2,r2,#1 + LDRH r6,[r4],-r12 + LDRH r7,[r4],-r12 + LDRH r8,[r4],-r12 + LDRH r9,[r4],-r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + LDRH r6,[r4],-r12 + LDRH r7,[r4],-r12 + LDRH r8,[r4],-r12 + LDRH r9,[r4],-r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + STMIA r5!,{r6,r7} + LDRH r6,[r4],-r12 + LDRH r7,[r4],-r12 + LDRH r8,[r4],-r12 + LDRH r9,[r4],-r12 + ORR r6,r6,r7,LSL #16 + ORR r7,r8,r9,LSL #16 + ADD r4,r4,#0x1600 + STMIA r5!,{r6,r7} + ADD r5,r5,#0x100 + ADD r4,r4,#2 + BGT .L1.256 + SUBS r3,r3,#1 + BGT .L1.216 + LDMFD sp!,{r4-r10} + BX lr + .size opl_rotate90_u16_qcif, . - opl_rotate90_u16_qcif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/opl/vmirror_u16.c linux-2.6.28-karo/drivers/media/video/mxc/opl/vmirror_u16.c --- linux-2.6.28/drivers/media/video/mxc/opl/vmirror_u16.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/opl/vmirror_u16.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include "opl.h" + +int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height, + u8 * dst, int dst_line_stride) +{ + const u8 *src_row_addr; + u8 *dst_row_addr; + int i; + + if (!src || !dst) + return OPLERR_NULL_PTR; + + if (width == 0 || height == 0 || src_line_stride == 0 + || dst_line_stride == 0) + return OPLERR_BAD_ARG; + + src_row_addr = src; + dst_row_addr = dst + (height - 1) * dst_line_stride; + + /* Loop over all rows */ + for (i = 0; i < height; i++) { + /* memcpy each row */ + memcpy(dst_row_addr, src_row_addr, BYTES_PER_PIXEL * width); + src_row_addr += src_line_stride; + dst_row_addr -= dst_line_stride; + } + + return OPLERR_SUCCESS; +} + +EXPORT_SYMBOL(opl_vmirror_u16); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/Kconfig linux-2.6.28-karo/drivers/media/video/mxc/output/Kconfig --- linux-2.6.28/drivers/media/video/mxc/output/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,19 @@ +config VIDEO_MXC_OUTPUT_DEBUG + bool "Verbose MXC video output debugging" + depends on VIDEO_MXC_OUTPUT + default n + +config VIDEO_MXC_IPU_OUTPUT + bool + depends on VIDEO_MXC_OUTPUT && MXC_IPU + default y + ---help--- + This is the video4linux2 driver for IPU post processing video output. + +config VIDEO_MXC_EMMA_OUTPUT + tristate "EMMA video output postprocessor" + depends on VIDEO_MXC_OUTPUT && MXC_EMMA && FB_IMX + default y + ---help--- + This is the video4linux2 driver for EMMA post processing video output. + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/Makefile linux-2.6.28-karo/drivers/media/video/mxc/output/Makefile --- linux-2.6.28/drivers/media/video/mxc/output/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,8 @@ +ifneq ($(CONFIG_VIDEO_MXC_OUTPUT_DEBUG),) + EXTRA_CFLAGS += -DDEBUG +endif + +mx27_output-objs := mx27_v4l2_output.o mx27_pp.o +obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mx27_output.o + +obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc_v4l2_output.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/mx27_pp.c linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_pp.c --- linux-2.6.28/drivers/media/video/mxc/output/mx27_pp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_pp.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1114 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_pp.c + * + * @brief MX27 V4L2 Video Output Driver + * + * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality. + * + * @ingroup MXC_V4L2_OUTPUT + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mx27_pp.h" +#include "mxc_v4l2_output.h" + +#define SCALE_RETRY 32 /* to be more relax, less precise */ +#define PP_SKIP 1 +#define PP_TBL_MAX 40 + +static unsigned short scale_tbl[PP_TBL_MAX]; +static int g_hlen, g_vlen; + +static emma_pp_cfg g_pp_cfg; +static int g_disp_num = 0; +static char pp_dev[] = "emma_pp"; + +/*! + * @brief PP resizing routines + */ +static int scale_2d(emma_pp_scale *sz); + +static irqreturn_t pp_isr(int irq, void *dev_id); +static int set_output_addr(emma_pp_cfg *cfg, vout_data *vout); +static int pphw_reset(void); +static int pphw_enable(int flag); +static int pphw_ptr(emma_pp_cfg *cfg); +static int pphw_outptr(emma_pp_cfg *cfg); +static int pphw_cfg(emma_pp_cfg *cfg); +static int pphw_isr(void); +static int pphw_init(void); +static void pphw_exit(void); + +#define PP_DUMP(reg) pr_debug("%s[%08lx]\t = 0x%08x\n", #reg, \ + reg - IO_ADDRESS(EMMA_PP_BASE_ADDR) + EMMA_PP_BASE_ADDR, __raw_readl(reg)) +void pp_dump(void) +{ + PP_DUMP(PP_CNTL); + PP_DUMP(PP_INTRCNTL); + PP_DUMP(PP_INTRSTATUS); + PP_DUMP(PP_SOURCE_Y_PTR); + PP_DUMP(PP_SOURCE_CB_PTR); + PP_DUMP(PP_SOURCE_CR_PTR); + PP_DUMP(PP_DEST_RGB_PTR); + PP_DUMP(PP_QUANTIZER_PTR); + PP_DUMP(PP_PROCESS_FRAME_PARA); + PP_DUMP(PP_SOURCE_FRAME_WIDTH); + PP_DUMP(PP_DEST_DISPLAY_WIDTH); + PP_DUMP(PP_DEST_IMAGE_SIZE); + PP_DUMP(PP_DEST_FRAME_FMT_CNTL); + PP_DUMP(PP_RESIZE_INDEX); + PP_DUMP(PP_CSC_COEF_0123); + PP_DUMP(PP_CSC_COEF_4); +} + +static const unsigned char pp_coeftab[] = { + 2, 1, + 19, 10, + 17, 9, + 15, 8, + 13, 7, + 11, 6, + 20, 11, + 9, 5, + 16, 9, + 7, 4, + 19, 11, + 12, 7, + 17, 10, + 5, 3, + 18, 11, + 13, 8, + 8, 5, + 19, 12, + 11, 7, + 14, 9, + 17, 11, + 20, 13, + 3, 2, + 19, 13, + 16, 11, + 13, 9, + 10, 7, + 17, 12, + 7, 5, + 18, 13, + 11, 8, + 15, 11, + 19, 14, + 4, 3, + 17, 13, + 13, 10, + 9, 7, + 14, 11, + 19, 15, + 5, 4, + 16, 13, + 11, 9, + 17, 14, + 6, 5, + 19, 16, + 13, 11, + 20, 17, + 7, 6, + 15, 13, + 8, 7, + 17, 15, + 9, 8, + 19, 17, + 10, 9, + 11, 10, + 12, 11, + 13, 12, + 14, 13, + 15, 14, + 16, 15, + 17, 16, + 18, 17, + 19, 18, + 20, 19, + 1, 1, + 19, 20, + 18, 19, + 17, 18, + 16, 17, + 15, 16, + 14, 15, + 13, 14, + 12, 13, + 11, 12, + 10, 11, + 9, 10, + 17, 19, + 8, 9, + 15, 17, + 7, 8, + 13, 15, + 6, 7, + 17, 20, + 11, 13, + 16, 19, + 5, 6, + 14, 17, + 9, 11, + 13, 16, + 4, 5, + 15, 19, + 11, 14, + 7, 9, + 10, 13, + 13, 17, + 3, 4, + 14, 19, + 11, 15, + 8, 11, + 13, 18, + 5, 7, + 12, 17, + 7, 10, + 9, 13, + 11, 16, + 13, 19, + 2, 3, + 13, 20, + 11, 17, + 9, 14, + 7, 11, + 12, 19, + 5, 8, + 8, 13, + 11, 18, + 3, 5, + 10, 17, + 7, 12, + 11, 19, + 4, 7, + 9, 16, + 5, 9, + 11, 20, + 6, 11, + 7, 13, + 8, 15, + 9, 17, + 10, 19, + 1, 2, + 9, 19, + 8, 17, + 7, 15, + 6, 13, + 5, 11, + 9, 20, + 4, 9, + 7, 16, + 3, 7, + 8, 19, + 5, 12, + 7, 17, + 2, 5, + 7, 18, + 5, 13, + 3, 8, + 7, 19, + 4, 11, + 5, 14, + 6, 17, + 7, 20, + 1, 3, + 6, 19, + 5, 16, + 4, 13, + 3, 10, + 5, 17, + 2, 7, + 5, 18, + 3, 11, + 4, 15, + 5, 19, + 1, 4 +}; + +/*! + * @brief Set PP input address. + * @param ptr The pointer to the Y value of input + * @return Zero on success, others on failure + */ +int pp_ptr(unsigned long ptr, struct v4l2_mxc_offset offset) +{ + g_pp_cfg.ptr.y = ptr + offset.y_offset; + g_pp_cfg.ptr.u = ptr + offset.u_offset; + g_pp_cfg.ptr.v = ptr + offset.v_offset; + g_pp_cfg.ptr.qp = ptr + offset.qp_offset; + + return pphw_ptr(&g_pp_cfg); +} + +/*! + * @brief Enable or disable PP. + * @param flag Zero to disable PP, others to enable PP + * @return Zero on success, others on failure + */ +int pp_enable(int flag) +{ + return pphw_enable(flag); +} + +/*! + * @brief Get the display No. of last completed PP frame. + * @return The display No. of last completed PP frame. + */ +int pp_num_last(void) +{ + return g_disp_num ? 0 : 1; +} + +/*! + * @brief Initialize PP. + * @param vout Pointer to _vout_data structure + * @return Zero on success, others on failure + */ +static int interrupts; + +int pp_init(vout_data *vout) +{ + int ret = pphw_init(); + if (ret) { + return ret; + } + ret = pphw_enable(0); + if (ret) { + pphw_exit(); + return ret; + } + interrupts = 0; + ret = request_irq(MXC_INT_EMMAPP, pp_isr, 0, pp_dev, vout); + if (ret) { + pphw_exit(); + return ret; + } + return 0; +} + +/*! + * @brief Deinitialize PP. + * @param vout Pointer to _vout_data structure + */ +void pp_exit(vout_data *vout) +{ + free_irq(MXC_INT_EMMAPP, vout); + pr_debug("%s: Interrupts: %d\n", __FUNCTION__, interrupts); + pphw_enable(0); + pphw_exit(); +} + +/*! + * @brief Configure PP. + * @param vout Pointer to _vout_data structure + * @return Zero on success, others on failure + */ +int pp_cfg(vout_data *vout) +{ + unsigned long flags; + + /* PP accepts YUV420 input only */ + if ((vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) && + (vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV422P)) { +// if (vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) { + pr_debug("unsupported pixel format: %08x\n", vout->v2f.fmt.pix.pixelformat); + return -EINVAL; + } + + g_pp_cfg.operation = 0; + + memset(g_pp_cfg.csc_table, 0, sizeof(g_pp_cfg.csc_table)); + + /* Convert output pixel format to PP required format */ + switch (vout->v4l2_fb.fmt.pixelformat) { + case V4L2_PIX_FMT_BGR32: + g_pp_cfg.red_width = 8; + g_pp_cfg.green_width = 8; + g_pp_cfg.blue_width = 8; + g_pp_cfg.red_offset = 8; + g_pp_cfg.green_offset = 16; + g_pp_cfg.blue_offset = 24; + g_pp_cfg.rgb_resolution = 32; + break; + case V4L2_PIX_FMT_RGB32: + g_pp_cfg.red_width = 8; + g_pp_cfg.green_width = 8; + g_pp_cfg.blue_width = 8; + g_pp_cfg.red_offset = 24; + g_pp_cfg.green_offset = 16; + g_pp_cfg.blue_offset = 8; + g_pp_cfg.rgb_resolution = 32; + break; + case V4L2_PIX_FMT_YUYV: + g_pp_cfg.red_width = 0; + g_pp_cfg.green_width = 0; + g_pp_cfg.blue_width = 0; + g_pp_cfg.red_offset = 0; + g_pp_cfg.green_offset = 0; + g_pp_cfg.blue_offset = PP_PIX_YUYV; + g_pp_cfg.rgb_resolution = 16; + break; + case V4L2_PIX_FMT_UYVY: + g_pp_cfg.red_width = 0; + g_pp_cfg.green_width = 0; + g_pp_cfg.blue_width = 0; + g_pp_cfg.red_offset = 0; + g_pp_cfg.green_offset = 0; + g_pp_cfg.blue_offset = PP_PIX_UYVY; + g_pp_cfg.rgb_resolution = 16; + break; + case V4L2_PIX_FMT_RGB565: + default: + g_pp_cfg.red_width = 5; + g_pp_cfg.green_width = 6; + g_pp_cfg.blue_width = 5; + g_pp_cfg.red_offset = 11; + g_pp_cfg.green_offset = 5; + g_pp_cfg.blue_offset = 0; + g_pp_cfg.rgb_resolution = 16; + break; + } + spin_lock_irqsave(&vout->irq_lock, flags); + if (vout->active != NULL) { + g_pp_cfg.ptr.y = vout->active->dma_desc.dma_addr; + } else { + g_pp_cfg.ptr.y = 0; + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0; + if ((vout->crop_rect.width != 0) && (vout->crop_rect.height != 0)) { + g_pp_cfg.dim.in.width = vout->crop_rect.width; + g_pp_cfg.dim.in.height = vout->crop_rect.height; + } else { + g_pp_cfg.dim.in.width = vout->v2f.fmt.pix.width; + g_pp_cfg.dim.in.height = vout->v2f.fmt.pix.height; + } + + g_pp_cfg.dim.out.width = vout->crop_current.width; + g_pp_cfg.dim.out.height = vout->crop_current.height; + g_pp_cfg.dim.num.width = 0; + g_pp_cfg.dim.num.height = 0; + g_pp_cfg.dim.den.width = 0; + g_pp_cfg.dim.den.height = 0; + + pr_debug("input dimensions: %dx%d output dimensions: %dx%d\n", + g_pp_cfg.dim.in.width, g_pp_cfg.dim.in.height, + g_pp_cfg.dim.out.width, g_pp_cfg.dim.out.height); + + if (scale_2d(&g_pp_cfg.dim)) { + printk(KERN_ERR "unsupported resize ratio\n"); + return -EINVAL; + } + + g_pp_cfg.dim.out.width = vout->crop_current.width; + g_pp_cfg.dim.out.height = vout->crop_current.height; + + g_pp_cfg.in_y_stride = vout->v2f.fmt.pix.width; + g_pp_cfg.in_height = vout->v2f.fmt.pix.height; + if (set_output_addr(&g_pp_cfg, vout)) { + printk(KERN_ERR "failed to set pp output address\n"); + return -EINVAL; + } + + return pphw_cfg(&g_pp_cfg); +} + +irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id); + +/*! + * @brief PP IRQ handler. + */ +static irqreturn_t pp_isr(int irq, void *dev_id) +{ + int status; + vout_data *vout = dev_id; + +interrupts++; + status = pphw_isr(); + if ((status & 0x1) == 0) { /* Not frame complete interrupt */ + pr_debug("not pp frame complete interrupt\n"); + return IRQ_HANDLED; + } + + if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + g_disp_num = g_disp_num ? 0 : 1; + g_pp_cfg.outptr = (unsigned int)vout->display_bufs[g_disp_num]; + pphw_outptr(&g_pp_cfg); + } + + return mxc_v4l2out_pp_in_irq_handler(irq, dev_id); +} + +/*! + * @brief Set PP output address. + * @param cfg Pointer to emma_pp_cfg structure + * @param vout Pointer to _vout_data structure + * @return Zero on success, others on failure + */ +static int set_output_addr(emma_pp_cfg *cfg, vout_data *vout) +{ + if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + g_disp_num = 0; + cfg->outptr = (unsigned int)vout->display_bufs[g_disp_num]; + cfg->out_stride = vout->crop_current.width; + return 0; + } else { + struct fb_info *fb; + + fb = registered_fb[vout->output_fb_num[vout->cur_disp_output]]; + if (!fb) + return -ENODEV; + + cfg->outptr = fb->fix.smem_start; + cfg->outptr += vout->crop_current.top * fb->var.xres_virtual * + (fb->var.bits_per_pixel >> 3) + + vout->crop_current.left * (fb->var.bits_per_pixel >> 3); + cfg->out_stride = fb->var.xres_virtual; + + return 0; + } +} + +/*! + * @brief Get maximum common divisor. + * @param x First input value + * @param y Second input value + * @return Maximum common divisor of x and y + */ +static int gcd(int x, int y) +{ + int k; + + if (x < y) { + k = x; + x = y; + y = k; + } + + while ((k = x % y)) { + x = y; + y = k; + } + + return y; +} + +/*! + * @brief Get ratio. + * @param x First input value + * @param y Second input value + * @param den Denominator of the ratio (corresponding to y) + * @return Numerator of the ratio (corresponding to x) + */ +static int ratio(int x, int y, int *den) +{ + int g; + + if (!x || !y) + return 0; + + g = gcd(x, y); + *den = y / g; + + return x / g; +} + +/*! + * @brief Build PP coefficient entry + * Build one or more coefficient entries for PP coefficient table based + * on given coefficient. + * + * @param k The index of the coefficient in coefficient table + * @param coeff The weighting coefficient + * @param base The base of the coefficient + * @param nxt Number of pixels to be read + * + * @return The index of the next coefficient entry on success + * -1 on failure + */ +static int scale_0d(int k, int coeff, int base, int nxt) +{ + if (k >= PP_TBL_MAX) { + /* no more space in table */ + pr_debug("no space in scale table, k = %d\n", k); + return -ENOSPC; + } + + coeff = ((coeff << BC_COEF) + (base >> 1)) / base; + + /* + * Valid values for weighting coefficient are 0, 2 to 30, and 31. + * A value of 31 is treated as 32 and therefore 31 is an + * invalid co-efficient. + */ + if (coeff >= SZ_COEF - 1) + coeff--; + else if (coeff == 1) + coeff++; + coeff = coeff << BC_NXT; + + if (nxt < SZ_NXT) { + coeff |= nxt; + coeff <<= 1; + coeff |= 1; + } else { + /* + * src inc field is 2 bit wide, for 4+, use special + * code 0:0:1 to prevent dest inc + */ + coeff |= PP_SKIP; + coeff <<= 1; + coeff |= 1; + nxt -= PP_SKIP; + do { + pr_debug("tbl = %03X\n", coeff); + scale_tbl[k++] = coeff; + coeff = (nxt > PP_SKIP) ? PP_SKIP : nxt; + coeff <<= 1; + } while ((nxt -= PP_SKIP) > 0); + } + pr_debug("tbl = %03X\n", coeff); + scale_tbl[k++] = coeff; + + return k; +} + +/*! + * @brief Get approximate ratio + * + * @param pscale The pointer to scale_t structure which holdes + * coefficient tables + * @param mt Scale ratio numerator + * @param nt Scale ratio denominator + * @param *n denominator of approximate ratio + * @return numerator of approximate ratio + */ +static int approx_ratio(int mt, int nt, int *n) +{ + int index = sizeof(pp_coeftab) / sizeof(pp_coeftab[0]) / 2; + int left = 0; + int right = index - 1; + int nom = 0, den = 0; + while (index > 0) { + nom = pp_coeftab[(((right + left) >> 1) << 1)]; + den = pp_coeftab[(((right + left) >> 1) << 1) + 1]; + if ((nom * nt - mt * den) > 0) { + left = (right + left) >> 1; + } else { + right = (right + left) >> 1; + } + index = index >> 1; + } + *n = pp_coeftab[right * 2 + 1]; + nom = pp_coeftab[right * 2]; + return nom; +} + +/* + * @brief Build PP coefficient table + * Build PP coefficient table for one dimension (width or height) + * based on given input and output resolution + * + * @param inv input resolution + * @param outv output resolution + * @param k index of free table entry + * + * @return The index of the next free coefficient entry on success + * -1 on failure + */ +static int scale_1d(int inv, int outv, int k) +{ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + + if (inv == outv) + return scale_0d(k, 1, 1, 1); /* force scaling */ + + v = 0; + if (inv < outv) { + /* upscale: mix <= 2 input pixels per output pixel */ + do { + coeff = outv - v; + v += inv; + if (v >= outv) { + v -= outv; + nxt = 1; + } else + nxt = 0; + pr_debug("upscale: coeff = %d/%d nxt = %d\n", coeff, + outv, nxt); + k = scale_0d(k, coeff, outv, nxt); + if (k < 0) + return -1; + } while (v); + } else if (inv >= 2 * outv) { + /* PP doesn't support resize ratio > 2:1 except 4:1. */ + if ((inv != 2 * outv) && (inv != 4 * outv)) + return -1; + /* downscale: >=2:1 bilinear approximation */ + coeff = inv - 2 * outv; + v = 0; + nxt = 0; + do { + v += coeff; + nxt = 2; + while (v >= outv) { + v -= outv; + nxt++; + } + pr_debug("downscale: coeff = 1/2 nxt = %d\n", nxt); + k = scale_0d(k, 1, 2, nxt); + if (k < 0) + return -1; + } while (v); + } else { + /* downscale: bilinear */ + int in_pos_inc = 2 * outv; + int out_pos = inv; + int out_pos_inc = 2 * inv; + int init_carry = inv - outv; + int carry = init_carry; + + v = outv + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + pr_debug("downscale: coeff = %d/%d nxt = %d\n", coeff, + in_pos_inc, nxt); + k = scale_0d(k, coeff, in_pos_inc, nxt); + if (k < 0) + return -1; + } while (carry != init_carry); + } + return k; +} + +/* + * @brief Build PP coefficient table + * Build PP coefficient table for one dimension (width or height) + * based on given input and output resolution. The given input + * and output resolution might be not supported due to hardware + * limits. In this case this functin rounds the input and output + * to closest possible values and return them to caller. + * + * @param inv input resolution, might be modified after the call + * @param outv output resolution, might be modified after the call + * @param k index of free table entry + * + * @return The index of the next free coefficient entry on success + * -1 on failure + */ +static int scale_1d_smart(int *inv, int *outv, int index) +{ + int len, num, den, approx_num, approx_den; + static int num1, den1; + + if (!inv || !outv) + return -1; + + /* Both should be non-zero */ + if (!(*inv) || !(*outv)) + return -1; + + if ((*outv > 4 * (*inv)) || + ((*inv > 2 * (*outv)) && (*inv != 4 * (*outv)))) { + pr_debug("unsupported pp resize ration: inv/outv = %d/%d\n", + *inv, *outv); + return -1; + } + + num = ratio(*inv, *outv, &den); + pr_debug("%s: num=%d den=%d\n", __FUNCTION__, num, den); + + if (index == 0) { + if ((num > 20) || (den > 20)) { + approx_num = approx_ratio(num, den, &approx_den); + num = approx_num; + den = approx_den; + } + } else { + if ((num > (40 - index)) || (den > (40 - index))) { + approx_num = approx_ratio(num, den, &approx_den); + num = approx_num; + den = approx_den; + } + if ((num == num1) && (den == den1)) { + return index; + } + } + + if ((len = scale_1d(num, den, index)) < 0) { + return -1; + } else { + if (index == 0) { + num1 = num; + den1 = den; + } + return len; + } + +} + +/* + * @brief Build PP coefficient table for both width and height + * Build PP coefficient table for both width and height based on + * given resizing ratios. + * + * @param sz Structure contains resizing ratio informations + * + * @return 0 on success, others on failure + */ +static int scale_2d(emma_pp_scale *sz) +{ + int inv, outv; + + /* horizontal resizing. parameter check - must provide in size */ + if (!sz->in.width) + return -1; + + /* Resizing based on num:den */ + inv = sz->num.width; + outv = sz->den.width; + + if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) { + /* Resizing succeeded */ + sz->den.width = outv; + sz->out.width = (sz->in.width * outv) / inv; + } else { + /* Resizing based on in:out */ + inv = sz->in.width; + outv = sz->out.width; + + if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) { + /* Resizing succeeded */ + sz->out.width = outv; + sz->num.width = ratio(sz->in.width, sz->out.width, + &sz->den.width); + } else + return -1; + } + + sz->out.width &= ~1; + + /* vertical resizing. parameter check - must provide in size */ + if (!sz->in.height) + return -1; + + /* Resizing based on num:den */ + inv = sz->num.height; + outv = sz->den.height; + + if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) { + /* Resizing succeeded */ + sz->den.height = outv; + sz->out.height = (sz->in.height * outv) / inv; + } else { + /* Resizing based on in:out */ + inv = sz->in.height; + outv = sz->out.height; + + if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) { + /* Resizing succeeded */ + sz->out.height = outv; + sz->num.height = ratio(sz->in.height, sz->out.height, + &sz->den.height); + } else + return -1; + } + + return 0; +} + +/*! + * @brief Set PP resizing registers. + * @param sz Pointer to pp scaling structure + * @return Zero on success, others on failure + */ +static int pphw_scale(emma_pp_scale * sz) +{ + __raw_writel((sz->out.width << 16) | sz->out.height, + PP_DEST_IMAGE_SIZE); + __raw_writel(((g_hlen - 1) << 16) | (g_vlen == + g_hlen ? 0 : (g_hlen << 8)) | + (g_vlen - 1), PP_RESIZE_INDEX); + for (g_hlen = 0; g_hlen < g_vlen; g_hlen++) + __raw_writel(scale_tbl[g_hlen], + PP_RESIZE_COEF_TBL + g_hlen * 4); + + return 0; +} + +/*! + * @brief Reset PP. + * @return Zero on success, others on failure + */ +static int pphw_reset(void) +{ + int i; + u32 pp_cntl; + + __raw_writel(0x100, PP_CNTL); + + /* timeout */ + for (i = 0; i < 1000; i++) { + if (!((pp_cntl = __raw_readl(PP_CNTL)) & 0x100)) { + pr_debug("pp reset over\n"); + break; + } + } + + /* check reset value */ + if (pp_cntl != 0x876) { + pr_debug("pp reset value err = 0x%08X\n", pp_cntl); + return -EIO; + } + + return 0; +} + +/*! + * @brief Enable or disable PP. + * @param flag Zero to disable PP, others to enable PP + * @return Zero on success, others on failure + */ +static int pphw_enable(int flag) +{ + int ret = 0; + + if (flag) + __raw_writel(__raw_readl(PP_CNTL) | 1, PP_CNTL); + else + ret = pphw_reset(); + + return ret; +} + +/*! + * @brief Set PP input address. + * @param cfg The pointer to PP configuration parameter + * @return Zero on success, others on failure + */ +static int pphw_ptr(emma_pp_cfg *cfg) +{ + __raw_writel(cfg->ptr.y, PP_SOURCE_Y_PTR); + __raw_writel(cfg->ptr.u, PP_SOURCE_CB_PTR); + __raw_writel(cfg->ptr.v, PP_SOURCE_CR_PTR); + __raw_writel(cfg->ptr.qp, PP_QUANTIZER_PTR); + + return 0; +} + +/*! + * @brief Set PP output address. + * @param cfg The pointer to PP configuration parameter + * @return Zero on success, others on failure + */ +static int pphw_outptr(emma_pp_cfg *cfg) +{ + __raw_writel(cfg->outptr, PP_DEST_RGB_PTR); + return 0; +} + +/*! + * @brief Configuration PP. + * @param cfg The pointer to PP configuration parameter + * @return Zero on success, others on failure + */ +static int pphw_cfg(emma_pp_cfg *cfg) +{ + int rt; + register int r; + + pphw_scale(&cfg->dim); + + if (!cfg->in_y_stride) + cfg->in_y_stride = cfg->dim.in.width; + + if (!cfg->out_stride) + cfg->out_stride = cfg->dim.out.width; + + r = __raw_readl(PP_CNTL) & ~EN_MASK; + + /* config parms */ + r |= cfg->operation & EN_MASK; + if (cfg->operation & EN_MACROBLOCK) { + /* Macroblock Mode */ + r |= 0x0200; + __raw_writel(0x06, PP_INTRCNTL); + } else { + /* Frame mode */ + __raw_writel(0x05, PP_INTRCNTL); + } + + if (cfg->red_width | cfg->green_width | cfg->blue_width) { + /* color conversion to be performed */ + r |= EN_CSC; + if (!(cfg->red_offset | cfg->green_offset)) { + /* auto offset B:G:R LSb to Msb */ + cfg->green_offset = cfg->blue_offset + cfg->blue_width; + cfg->red_offset = cfg->green_offset + cfg->green_width; + } + if (!cfg->rgb_resolution) { + /* derive minimum resolution required */ + int w, w2; + + w = cfg->red_offset + cfg->red_width; + w2 = cfg->blue_offset + cfg->blue_width; + if (w < w2) + w = w2; + w2 = cfg->green_offset + cfg->green_width; + if (w < w2) + w = w2; + if (w > 16) + w = 24; + else if (w > 8) + w = 16; + else + w = 8; + cfg->rgb_resolution = w; + } + /* 00,11 - 32 bpp, 10 - 16 bpp, 01 - 8 bpp */ + r &= ~0xC00; + if (cfg->rgb_resolution < 32) + r |= (cfg->rgb_resolution << 7); + __raw_writel((cfg->red_offset << 26) | + (cfg->green_offset << 21) | + (cfg->blue_offset << 16) | + (cfg->red_width << 8) | + (cfg->green_width << 4) | + cfg->blue_width, PP_DEST_FRAME_FMT_CNTL); + } else { + /* add YUV422 formatting */ + static const unsigned int _422[] = { + 0x62000888, + 0x60100888, + 0x43080888, + 0x41180888 + }; + + __raw_writel(_422[(cfg->blue_offset >> 3) & 3], + PP_DEST_FRAME_FMT_CNTL); + cfg->rgb_resolution = 16; + r &= ~0xC00; + r |= (cfg->rgb_resolution << 7); + } + + /* add csc formatting */ + if (!cfg->csc_table[1]) { + static const unsigned short _csc[][6] = { + {0x80, 0xb4, 0x2c, 0x5b, 0x0e4, 0}, + {0x95, 0xcc, 0x32, 0x68, 0x104, 1}, + {0x80, 0xca, 0x18, 0x3c, 0x0ec, 0}, + {0x95, 0xe5, 0x1b, 0x44, 0x10e, 1}, + }; + memcpy(cfg->csc_table, _csc[cfg->csc_table[0]], + sizeof(_csc[0])); + } + __raw_writel((cfg->csc_table[0] << 24) | + (cfg->csc_table[1] << 16) | + (cfg->csc_table[2] << 8) | + cfg->csc_table[3], PP_CSC_COEF_0123); + __raw_writel((cfg->csc_table[5] ? (1 << 9) : 0) | cfg->csc_table[4], + PP_CSC_COEF_4); + + __raw_writel(r, PP_CNTL); + + pphw_ptr(cfg); + pphw_outptr(cfg); + + /* + * #MB in a row = input_width / 16pix + * 1 byte per QP per MB + * QP must be formatted to be 4-byte aligned + * YUV lines are to be 4-byte aligned as well + * So Y is 8 byte aligned, as U = V = Y/2 for 420 + * MPEG MBs are 16x16 anyway + */ + __raw_writel((cfg->dim.in.width << 16) | cfg->dim.in.height, + PP_PROCESS_FRAME_PARA); + __raw_writel(cfg->in_y_stride | (PP_CALC_QP_WIDTH(cfg) << 16), + PP_SOURCE_FRAME_WIDTH); + + /* in bytes */ + rt = cfg->rgb_resolution >> 3; + if (rt == 3) + rt = 4; + __raw_writel(cfg->out_stride * rt, PP_DEST_DISPLAY_WIDTH); + + pp_dump(); + return 0; +} + +/*! + * @brief Check PP interrupt status. + * @return PP interrupt status + */ +static int pphw_isr(void) +{ + unsigned long status; + + status = __raw_readl(PP_INTRSTATUS) & 7; + if (!status) { + pr_debug("pp: not my isr err\n"); + return status; + } + + if (status & 4) + pr_debug("pp: isr state error\n"); + + /* clear interrupt status */ + __raw_writel(status, PP_INTRSTATUS); + + return status; +} + +static struct clk *emma_clk; + +/*! + * @brief PP module clock enable + */ +static int pphw_init(void) +{ + emma_clk = clk_get(NULL, "emma_clk"); + if (IS_ERR(emma_clk)) { + printk(KERN_ERR "Could not get emma_clk: %ld\n", PTR_ERR(emma_clk)); + return PTR_ERR(emma_clk); + } + clk_enable(emma_clk); + return 0; +} + +/*! + * @brief PP module clock disable + */ +static void pphw_exit(void) +{ + clk_disable(emma_clk); + clk_put(emma_clk); +} diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/mx27_pp.h linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_pp.h --- linux-2.6.28/drivers/media/video/mxc/output/mx27_pp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_pp.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,181 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_pp.h + * + * @brief Header file for MX27 V4L2 Video Output Driver + * + * @ingroup MXC_V4L2_OUTPUT + */ +#ifndef __MX27_PP_H__ +#define __MX27_PP_H__ + +#include "mxc_v4l2_output.h" + +/* PP register definitions */ +#define PP_REG(ofs) (IO_ADDRESS(EMMA_PP_BASE_ADDR) + ofs) + +/* Register offsets */ +#define PP_CNTL PP_REG(0x00) +#define PP_INTRCNTL PP_REG(0x04) +#define PP_INTRSTATUS PP_REG(0x08) +#define PP_SOURCE_Y_PTR PP_REG(0x0C) +#define PP_SOURCE_CB_PTR PP_REG(0x10) +#define PP_SOURCE_CR_PTR PP_REG(0x14) +#define PP_DEST_RGB_PTR PP_REG(0x18) +#define PP_QUANTIZER_PTR PP_REG(0x1C) +#define PP_PROCESS_FRAME_PARA PP_REG(0x20) +#define PP_SOURCE_FRAME_WIDTH PP_REG(0x24) +#define PP_DEST_DISPLAY_WIDTH PP_REG(0x28) +#define PP_DEST_IMAGE_SIZE PP_REG(0x2C) +#define PP_DEST_FRAME_FMT_CNTL PP_REG(0x30) +#define PP_RESIZE_INDEX PP_REG(0x34) +#define PP_CSC_COEF_0123 PP_REG(0x38) +#define PP_CSC_COEF_4 PP_REG(0x3C) +#define PP_RESIZE_COEF_TBL PP_REG(0x100) + +/* resize table dimensions + dest pixel index left/32 right/32 #src pixels to read + 0 [BC_COEF] [BC_COEF] [BC_NXT] + : + pp_tbl_max-1 +*/ +#define BC_NXT 2 +#define BC_COEF 5 +#define SZ_COEF (1 << BC_COEF) +#define SZ_NXT (1 << BC_NXT) + +/* PP operations */ +#define EN_DEBLOCK 0x02 +#define EN_DERING 0x04 +#define EN_CSC 0x10 +#define EN_MACROBLOCK 0x20 +#define EN_DEF 0x16 +#define EN_MASK 0x36 +#define EN_BIGDATA 0x1000 +#define EN_BIGQP 0x2000 + +/* PP CSC tables */ +#define CSC_TBL_NONE 0x80 +#define CSC_TBL_REUSE 0x81 +#define CSC_TBL_A1 0x00 +#define CSC_TBL_A0 0x20 +#define CSC_TBL_B1 0x40 +#define CSC_TBL_B0 0x60 +/* converts from 4 decimal fixed point to hw setting & vice versa */ +#define PP_CSC_FP4_2_HW(coeff) ((((coeff) << 7) + 5000) / 10000) +#define PP_CSC_HW_2_FP4(coeff) ((((coeff) * 10000) + 64) >> 7) + +#define PP_PIX_YUYV 0 +#define PP_PIX_YVYU 8 +#define PP_PIX_UYVY 16 +#define PP_PIX_VYUY 24 + +/* PP size & width calculation macros */ +#define PP_CALC_QP_WIDTH(cfg) \ + (!((cfg)->operation & (EN_DEBLOCK | EN_DERING)) ? 0 : \ + (((((cfg)->in_y_stride + 15) >> 4) + 3) & ~3)) +#define PP_CALC_Y_SIZE(cfg) \ + ((cfg)->in_y_stride * (cfg)->in_height) +#define PP_CALC_CH_SIZE(cfg) (PP_CALC_Y_SIZE(cfg) >> 2) +#define PP_CALC_BPP(cfg) \ + ((cfg)->rgb_resolution > 16 ? 4 : ((cfg)->rgb_resolution >> 3)) +#define PP_CALC_YUV_SIZE(cfg) \ + ((PP_CALC_Y_SIZE(cfg) * 3) >> 1) +#define PP_CALC_QP_SIZE(cfg) \ + (PP_CALC_QP_WIDTH(cfg) * (((cfg)->in_height + 15) >> 4)) +#define PP_CALC_DEST_WIDTH(cfg) \ + (((cfg)->out_stride & ~1) * PP_CALC_BPP(cfg)) +#define PP_CALC_DEST_SIZE(cfg) \ + ((cfg)->dim.out.height * PP_CALC_DEST_WIDTH(cfg)) + +/* + * physical addresses for bus mastering + * v=0 -> yuv packed + * v=0 & qp=0 -> yuv packed with qp appended + */ +typedef struct _emma_pp_ptr { + unsigned int y; /* Y data (line align8) */ + unsigned int u; /* U data (line align4) */ + unsigned int v; /* V data (line align4) */ + unsigned int qp; /* Quantization (line align4) */ +} emma_pp_ptr; + +typedef struct _emma_pp_size { + int width; + int height; +} emma_pp_size; + +/* + * if num.width != 0 + * resize ratio = num.width : den.width + * else + * resize ratio = in.width : out.width + * same for height + */ +typedef struct _emma_pp_scale { + emma_pp_size num; + emma_pp_size den; + emma_pp_size in; /* clip */ + emma_pp_size out; /* 0 -> same as in */ +} emma_pp_scale; + +typedef struct _emma_pp_cfg { + unsigned char operation; /* OR of EN_xx defines */ + + /* + * input color coeff + * fixed pt 8 bits, steps of 1/128 + * csc[5] is 1 or 0 to indicate Y + 16 + * csc[0] is matrix id 0-3 while csc[1-5]=0 + */ + unsigned short csc_table[6]; + + /* + * Output color (shade width, shade offset, pixel resolution) + * Eg. 16bpp RGB565 resolution, the values could be: + * red_width = 5, green_width = 6, blue_width = 6 + * red_offset = 11, green_offset = 5, blue_offset = 0 (defaults) + * rgb_resolution = 16 (default) + * For YUV422: xxx_width=0, blue_offset=PP_PIX_xxx + */ + unsigned short red_width; + unsigned short green_width; + unsigned short blue_width; + /* if offsets are 0, the offsets are by width LSb to MSb B:G:R */ + unsigned short red_offset; + unsigned short blue_offset; + unsigned short green_offset; + /* if resolution is 0, the minimum for the sum of widths is chosen */ + short rgb_resolution; /* 8,16,24 bpp only */ + + emma_pp_ptr ptr; /* dma buffer pointers */ + unsigned int outptr; /* RGB/YUV output */ + emma_pp_scale dim; /* in/out dimensions */ + + /* pixels between two adjacent input Y rows */ + unsigned short in_y_stride; /* 0 = in_width */ + unsigned short in_height; /* Input image height */ + /* PIXELS between two adjacent output rows */ + unsigned short out_stride; /* 0 = out_width */ +} emma_pp_cfg; + +int pp_ptr(unsigned long ptr, struct v4l2_mxc_offset offset); +int pp_enable(int flag); +int pp_cfg(vout_data * vout); +int pp_init(vout_data * vout); +int pp_num_last(void); +void pp_exit(vout_data * vout); + +#endif /* __MX27_PP_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/mx27_v4l2_output.c linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_v4l2_output.c --- linux-2.6.28/drivers/media/video/mxc/output/mx27_v4l2_output.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/mx27_v4l2_output.c 2009-03-11 13:48:58.000000000 +0100 @@ -0,0 +1,1897 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mx27_v4l2_output.c + * + * @brief MX27 V4L2 Video Output Driver + * + * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality. + * + * @ingroup MXC_V4L2_OUTPUT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#include "mxc_v4l2_output.h" +#include "mx27_pp.h" + +//#define SDC_FG_FB_FORMAT V4L2_PIX_FMT_RGB565 +#define SDC_FG_FB_FORMAT V4L2_PIX_FMT_RGB32 + +struct v4l2_output mxc_outputs[1] = { + { + .index = 0, + .name = "DISP%d Video Out", + .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct, + but no other choice */ + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, +}; + +#ifdef DEBUG +int debug = 1; +#define dbg_lvl(n) ((n) < debug) +module_param(debug, int, S_IRUGO | S_IWUSR); + +#define DBG(lvl, fmt...) do { if (dbg_lvl(lvl)) printk(KERN_DEBUG fmt); } while (0) +#else +int debug; +#define dbg_lvl(n) 0 +#define DBG(lvl, fmt...) do { } while (0) +module_param(debug, int, 0); +#endif +#define pr_warn(fmt,arg...) printk(KERN_WARNING fmt,##arg) + + +static int video_nr = 16; + +/* debug counters */ +static uint32_t g_irq_cnt; +static uint32_t g_buf_output_cnt; +static uint32_t g_buf_q_cnt; +static uint32_t g_buf_dq_cnt; +static uint32_t g_paused_cnt; +static uint32_t g_busy_cnt; +static uint32_t g_late_cnt; + +static int dq_intr_cnt; + +static int using_default_fps = -1; + +static inline unsigned long diff_usec(struct timeval *t1, struct timeval *t0) +{ + unsigned long diff = (t1->tv_sec - t0->tv_sec) * 1000000; + + if (t1->tv_usec >= t0->tv_usec) { + diff += t1->tv_usec - t0->tv_usec; + } else { + diff += 1000000 - (t0->tv_usec - t1->tv_usec); + } + return diff; +} + +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + +static int fb_event_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + vout_data *vout = container_of(self, vout_data, fb_event_notifier); + struct fb_event *event = data; + struct fb_info *info = event->info; + unsigned long flags; + int blank, i; + + for (i = 0; i < num_registered_fb; i++) + if (registered_fb[i] == info) + break; + + /* + * Check if the event is sent by the framebuffer in which + * the video is displayed. + */ + if (i != vout->output_fb) + return 0; + + switch (action) { + case FB_EVENT_BLANK: + DBG(0, "%s: BLANK\n", __FUNCTION__); + blank = *(int *)event->data; + down(&vout->user_lock); + vout->fb_enabled = !blank; + if (blank && vout->pp_ready) { + if (pp_enable(1)) + pr_warn("unable to enable PP\n"); + vout->pp_ready = 0; + } + up(&vout->user_lock); + break; + case FB_EVENT_MXC_EOF: + down(&vout->user_lock); + vout->fb_enabled = 1; + if (vout->pp_ready) { + DBG(2, "%s: enable PP\n", __FUNCTION__); + if (pp_enable(1)) + pr_warn("unable to enable PP\n"); + vout->pp_ready = 0; + } + up(&vout->user_lock); + break; + } + + return 0; +} +#endif + +static inline struct v4l_queue *find_buffer(struct list_head *list, int index) +{ + struct v4l_queue *q; + + list_for_each_entry(q, list, head) { + if (q->buf.index == index) { + return q; + } + } + return NULL; +} + +static __inline unsigned long get_jiffies(struct timeval *t) +{ + struct timeval cur; + + if (WARN_ON(t->tv_usec >= 1000000)) { + t->tv_sec += t->tv_usec / 1000000; + t->tv_usec = t->tv_usec % 1000000; + } + + do_gettimeofday(&cur); + if ((t->tv_sec < cur.tv_sec) || + ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) { + DBG(0, "%s: timestamp is %luµs in the past\n", __FUNCTION__, + (cur.tv_sec - t->tv_sec) * 1000000 + cur.tv_usec - t->tv_usec); + g_late_cnt++; + return jiffies; + } + + if (t->tv_usec < cur.tv_usec) { + cur.tv_sec = t->tv_sec - cur.tv_sec - 1; + cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; + } else { + cur.tv_sec = t->tv_sec - cur.tv_sec; + cur.tv_usec = t->tv_usec - cur.tv_usec; + } + + return jiffies + timeval_to_jiffies(&cur); +} + +/* must be called with irq_lock held */ +static void mxc_v4l2out_start_pp(vout_data *vout) +{ + BUG_ON(vout->active == NULL); + DBG(0, "%s: Updating HW PTR to %08x\n", __FUNCTION__, + vout->active->dma_desc.dma_addr); + do_gettimeofday(&vout->frame_start); + if (pp_ptr(vout->active->dma_desc.dma_addr, vout->offset)) { + pr_warn("%s: unable to update buffer\n", __FUNCTION__); + return; + } +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + if (vout->tear_protection == TEARING_PROTECTION_ACTIVE) { + if (vout->fb_enabled && (vout->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY)) { + vout->pp_ready = 1; + } else { + pp_enable(1); + } + } else { + pp_enable(1); + } +#else + pp_enable(1); +#endif + vout->busy = 1; +} + +static __inline void mxc_v4l2out_schedule_frame(vout_data *vout, struct timeval *tv, int init) +{ + unsigned long timeout; + + DBG(1, "%s: ts %6lu.%06lu\n", __FUNCTION__, tv->tv_sec, tv->tv_usec); + if (0 || ((tv->tv_sec == 0) && (tv->tv_usec == 0))) { + if (using_default_fps != 1) { + DBG(-1, "%s: using default fps\n", __FUNCTION__); + using_default_fps = 1; + } + mxc_v4l2out_start_pp(vout); + return; + } else { + timeout = get_jiffies(tv); + if (using_default_fps != 0) { + struct timeval now; + do_gettimeofday(&now); + + DBG(-1, "%s: using timestamp %lu.%06lu (now: %lu.%06lu)\n", __FUNCTION__, + tv->tv_sec, tv->tv_usec, now.tv_sec, now.tv_usec); + using_default_fps = 0; + } + } +#ifdef DEBUG + { + unsigned long cur = jiffies; + static unsigned long last_tv; + static unsigned int last_fps; + static unsigned int last_avg; + static unsigned int fps_avg; + static unsigned int avg_count; + + if (vout->frame_count == 0) { + avg_count = 0; + } + if (avg_count++ == 0) { + last_tv = timeout; + fps_avg = 0; + last_fps = 0; + } else { + unsigned long t = timeout; + unsigned int fps; + + DBG(3, "%s: t=%lu last=%lu diff=%ld\n", __FUNCTION__, + t, last_tv, t - last_tv); + if (t - last_tv) { + fps = HZ / (t - last_tv); + fps_avg = ((fps_avg * (avg_count - 1)) + fps) / avg_count; + last_tv = t; + if (last_fps != fps || last_avg != fps_avg) { + DBG(1, "%s: FPS: %u AVG %u\n", __FUNCTION__, fps, fps_avg); + last_fps = fps; + last_avg = fps_avg; + } + } else { + avg_count--; + } + } + + DBG(1, "%s: frame %u start %lu cur %lu timeout %lu\n", __FUNCTION__, + vout->frame_count, vout->start_jiffies, cur, timeout); + } +#endif + if (!time_before(jiffies, timeout)) { + DBG(0, "%s: timeout already expired: %lu >= %lu\n", __FUNCTION__, + jiffies, timeout); + mxc_v4l2out_start_pp(vout); + return; + } + if (init) { + vout->output_timer.expires = timeout; + add_timer(&vout->output_timer); + } else { + WARN_ON(mod_timer(&vout->output_timer, timeout)); + } +} + +/*! + * Private function to free buffers + * + * @param bufs_paddr Array of physical address of buffers to be freed + * + * @param bufs_vaddr Array of virtual address of buffers to be freed + * + * @param num_buf Number of buffers to be freed + * + * @param size Size for each buffer to be free + * + * @return status 0 success. + */ +static void mxc_free_buffers(vout_data *vout, struct list_head *list) +{ + struct v4l_queue *q; + struct v4l_queue *tmp; + struct device *dev = vout->video_dev->parent; + unsigned long flags; + + list_for_each_entry(q, list, head) { + DBG(-1, "%s: dma_addr: %08x cpu_addr: %p size %08x\n", __FUNCTION__, + q->dma_desc.dma_addr, q->dma_desc.cpu_addr, q->dma_desc.size); + } + list_for_each_entry_safe(q, tmp, list, head) { + struct dma_buf_desc *dma = &q->dma_desc; + DBG(-1, "%s: dma_addr: %08x cpu_addr: %p size %08x\n", __FUNCTION__, + dma->dma_addr, dma->cpu_addr, dma->size); + if (dma->cpu_addr != NULL) { + dma_free_coherent(dev, + dma->size, dma->cpu_addr, + dma->dma_addr); + DBG(2, "freed @ paddr=0x%08x\n", dma->dma_addr); + } + spin_lock_irqsave(&vout->irq_lock, flags); + list_del(&q->head); + spin_unlock_irqrestore(&vout->irq_lock, flags); + kfree(q); + } +} + +/*! + * Private function to allocate buffers + * + * @param bufs_paddr Output array of physical address of buffers allocated + * + * @param bufs_vaddr Output array of virtual address of buffers allocated + * + * @param num_buf Input number of buffers to allocate + * + * @param size Input size for each buffer to allocate + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int mxc_allocate_buffers(vout_data *vout, struct list_head *list, int num_buf, int size) +{ + int i; + unsigned long flags; + struct device *dev = vout->video_dev->parent; + + DBG(2, "Trying to allocate %u buffers of %08x byte each\n", + num_buf, size); + + for (i = 0; i < num_buf; i++) { + struct v4l_queue *q = kzalloc(sizeof(*q), GFP_KERNEL); + struct dma_buf_desc *dma; + + if (q == NULL) { + pr_err("failed to allocate memory for queue descriptor\n"); + return i; + } + DBG(2, "%s: queue header allocated %p\n", __FUNCTION__, q); + q->buf.index = i; + dma = &q->dma_desc; + if (size != 0) { + DBG(2, "Trying to allocate %08x byte DMA memory for queue %p\n", + size, list); + dma->size = size; + dma->cpu_addr = dma_alloc_coherent(dev, + size, + &dma->dma_addr, + GFP_KERNEL); + if (dma->cpu_addr == NULL) { + pr_err("dma_alloc_coherent failed for buffer %u out of %u\n", + i, num_buf); + kfree(q); + return i; + } + DBG(2, "allocated @ paddr=0x%08x, size=%d\n", + dma->dma_addr, size); + } + spin_lock_irqsave(&vout->irq_lock, flags); + list_add_tail(&q->head, list); + spin_unlock_irqrestore(&vout->irq_lock, flags); + } + return i; +} + +static void mxc_v4l2out_timer_handler(unsigned long arg) +{ + unsigned long flags; + vout_data *vout = (vout_data *)arg; + struct timeval now; + + do_gettimeofday(&now); + + DBG(1, "timer handler: %lu\n", jiffies); + + spin_lock_irqsave(&vout->irq_lock, flags); + + if (vout->state == STATE_STREAM_OFF && vout->active == NULL) { + pr_warn("stream has stopped\n"); + spin_unlock_irqrestore(&vout->irq_lock, flags); + return; + } + + BUG_ON(vout->active == NULL); + + if (vout->busy) { + pr_warn("Buffer %d is still busy in frame %u\n", + vout->active->buf.index, vout->frame_count); + g_busy_cnt++; + } + vout->frame_count++; + mxc_v4l2out_start_pp(vout); + + spin_unlock_irqrestore(&vout->irq_lock, flags); +} + +irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id) +{ + unsigned long flags; + vout_data *vout = dev_id; + struct timeval now; + + spin_lock_irqsave(&vout->irq_lock, flags); + + do_gettimeofday(&now); + DBG(2, "%s: PP IRQ%d #%d\n", __FUNCTION__, irq, g_irq_cnt); + + g_irq_cnt++; + + if (WARN_ON(vout->state == STATE_STREAM_OFF && vout->active == NULL)) { + spin_unlock_irqrestore(&vout->irq_lock, flags); + return IRQ_HANDLED; + } +#if 0 + if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + struct fb_gwinfo gwinfo; + + gwinfo.enabled = 1; + gwinfo.alpha_value = 255; + gwinfo.ck_enabled = 0; + gwinfo.xpos = vout->crop_current.left; + gwinfo.ypos = vout->crop_current.top; + gwinfo.base = vout->display_bufs[pp_num_last()]; + gwinfo.xres = vout->crop_current.width; + gwinfo.yres = vout->crop_current.height; + gwinfo.xres_virtual = vout->crop_current.width; + gwinfo.vs_reversed = 0; + + mx2_gw_set(&gwinfo); + } +#endif + /* Process previous buffer */ + BUG_ON(vout->active == NULL); + BUG_ON(vout->active->buf.index >= vout->buffer_cnt); + DBG(3, "%s: queue entry %p g_irq_cnt %d\n", __FUNCTION__, + vout->active, g_irq_cnt); + DBG(0, "%s: frame %u finished after %lums\n", __FUNCTION__, + g_buf_output_cnt, diff_usec(&now, &vout->frame_start) / 1000); + g_buf_output_cnt++; + vout->active->buf.flags |= V4L2_BUF_FLAG_DONE; + list_add_tail(&vout->active->head, &vout->done_q); + DBG(2, "%s: buffer[%d] %p moved to done queue %p\n", __FUNCTION__, + vout->active->buf.index, vout->active, &vout->done_q); + vout->active = NULL; + vout->busy = 0; + wake_up_interruptible(&vout->v4l_bufq); + + BUG_ON(vout->active != NULL); + + if ((vout->state == STATE_STREAM_ON || + vout->state == STATE_STREAM_STOPPING || + vout->state == STATE_STREAM_PAUSED) && + !list_empty(&vout->ready_q)) { + vout->active = list_first_entry(&vout->ready_q, struct v4l_queue, head); + list_del_init(&vout->active->head); + vout->queued--; + g_buf_dq_cnt++; + DBG(3, "next buffer[%d] %p\n", vout->active->buf.index, vout->active); + if (vout->state == STATE_STREAM_PAUSED) { + vout->state = STATE_STREAM_ON; + } + mxc_v4l2out_schedule_frame(vout, &vout->active->buf.timestamp, 0); + } + if (list_empty(&vout->ready_q)) { + if (vout->state == STATE_STREAM_STOPPING) { + DBG(-1, "Stream idle\n"); + //vout->state = STATE_STREAM_OFF; + } else if (vout->state == STATE_STREAM_ON) { + DBG(0, "No more buffers ready; pausing stream; queued: %d\n", vout->queued); + g_paused_cnt++; + vout->state = STATE_STREAM_PAUSED; + } + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + + return IRQ_HANDLED; +} + +/*! + * Start the output stream + * + * @param vout structure vout_data * + * + * @return status 0 Success + * + * called with vout->user_lock held + */ +static int mxc_v4l2out_streamon(vout_data *vout) +{ + unsigned long flags; + int ret; + + if (vout->state != STATE_STREAM_OFF) { + return -EBUSY; + } + if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + int index = 0; + struct v4l_queue *q; + + /* Free previously allocated buffer */ + mxc_free_buffers(vout, &vout->display_q); + /* Allocate buffers for foreground */ + ret = mxc_allocate_buffers(vout, &vout->display_q, + 2, vout->display_buf_size); + if (ret != 2) { + pr_err("unable to allocate SDC FG buffers\n"); + return ret; + } + list_for_each_entry(q, &vout->display_q, head) { + DBG(2, "%s: Display buffer %d @ %08x\n", __FUNCTION__, + index, q->dma_desc.dma_addr); + vout->display_bufs[index++] = q->dma_desc.dma_addr; + } + } + + /* Configure PP */ + ret = pp_cfg(vout); + if (ret != 0) { + /* Free previously allocated buffer */ + mxc_free_buffers(vout, &vout->display_q); + pr_err("failed to config PP: %d\n", ret); + return ret; + } + +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + if (vout->tear_protection == TEARING_PROTECTION_ACTIVE) { + vout->output_fb = vout->output_fb_num[vout->cur_disp_output]; + vout->fb_enabled = 0; + vout->pp_ready = 0; + vout->fb_event_notifier.notifier_call = fb_event_notify; + fb_register_client(&vout->fb_event_notifier); + mx2fb_register_client(&vout->fb_event_notifier); + } +#endif + spin_lock_irqsave(&vout->irq_lock, flags); + if (list_empty(&vout->ready_q)) { + pr_warn("no buffers queued yet!\n"); + spin_unlock_irqrestore(&vout->irq_lock, flags); + return EINVAL; + } + BUG_ON(vout->active); + vout->active = list_first_entry(&vout->ready_q, struct v4l_queue, head); + list_del_init(&vout->active->head); + vout->queued--; + g_buf_dq_cnt++; + DBG(2, "%s: processing buffer[%d] %p from ready queue %p\n", __FUNCTION__, + vout->active->buf.index, vout->active, &vout->ready_q); + + vout->frame_count = 0; + vout->state = STATE_STREAM_ON; + vout->start_jiffies = jiffies; + mxc_v4l2out_schedule_frame(vout, &vout->active->buf.timestamp, 1); + spin_unlock_irqrestore(&vout->irq_lock, flags); + + return 0; +} + +/*! + * Shut down the voutera + * + * @param vout structure vout_data * + * + * @return status 0 Success + * + * called with vout->user_lock held + */ +static int mxc_v4l2out_streamoff(vout_data *vout) +{ + struct v4l_queue *q; + struct v4l_queue *tmp; + unsigned long flags; + + if (vout->state == STATE_STREAM_OFF) { + return -EINVAL; + } + + del_timer_sync(&vout->output_timer); + + pp_enable(0); /* Disable PP */ + + spin_lock_irqsave(&vout->irq_lock, flags); + + if (vout->active != NULL) { +#ifdef DEBUG + list_for_each_entry(q, &vout->free_q, head) { + DBG(-1, "%s: dma_addr: %08x cpu_addr: %p size %08x\n", __FUNCTION__, + q->dma_desc.dma_addr, q->dma_desc.cpu_addr, q->dma_desc.size); + } +#endif + DBG(-1, "%s: Moving active buffer %p (%08x:%p) to free list\n", __FUNCTION__, + vout->active, vout->active->dma_desc.dma_addr, + vout->active->dma_desc.cpu_addr); + list_add_tail(&vout->active->head, &vout->free_q); +#ifdef DEBUG + list_for_each_entry(q, &vout->free_q, head) { + DBG(-1, "%s: dma_addr: %08x cpu_addr: %p size %08x\n", __FUNCTION__, + q->dma_desc.dma_addr, q->dma_desc.cpu_addr, q->dma_desc.size); + } +#endif + vout->active = NULL; + } + list_for_each_entry_safe(q, tmp, &vout->ready_q, head) { + DBG(-1, "%s: Moving buffer %p (%08x:%p) from ready to free list\n", __FUNCTION__, + q, q->dma_desc.dma_addr, q->dma_desc.cpu_addr); + list_move_tail(&q->head, &vout->free_q); + vout->queued--; + } + list_for_each_entry_safe(q, tmp, &vout->done_q, head) { + DBG(-1, "%s: Moving buffer %p (%08x:%p) from done to free list\n", __FUNCTION__, + q, q->dma_desc.dma_addr, q->dma_desc.cpu_addr); + list_move_tail(&q->head, &vout->free_q); + } + + BUG_ON(vout->queued < 0); + if (WARN_ON(vout->queued)) { + DBG(-1, "queued=%d\n", vout->queued); + } + list_for_each_entry(q, &vout->free_q, head) { + DBG(-1, "%s: Reinitializing buffer %p (%08x:%p)\n", __FUNCTION__, + q, q->dma_desc.dma_addr, q->dma_desc.cpu_addr); + q->buf.flags = 0; + q->buf.timestamp.tv_sec = 0; + q->buf.timestamp.tv_usec = 0; + } + + vout->state = STATE_STREAM_OFF; +#if 0 + if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { + struct fb_gwinfo gwinfo; + + /* Disable graphic window */ + gwinfo.enabled = 0; + mx2_gw_set(&gwinfo); + } +#endif +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + if (vout->tear_protection == TEARING_PROTECTION_ACTIVE) { + vout->output_fb = -1; + vout->fb_enabled = 0; + vout->pp_ready = 0; + fb_unregister_client(&vout->fb_event_notifier); + mx2fb_unregister_client(&vout->fb_event_notifier); + } +#endif + spin_unlock_irqrestore(&vout->irq_lock, flags); + mxc_free_buffers(vout, &vout->display_q); + up(&vout->user_lock); + + return 0; +} + +/* + * Valid whether the palette is supported + * + * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return 1 if supported, 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + return palette == V4L2_PIX_FMT_YUV420 || + palette == V4L2_PIX_FMT_YUV422P; +} + +/* + * Returns bits per pixel for given pixel format + * + * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return bits per pixel of pixelformat + */ +static u32 fmt_to_bpp(u32 pixelformat) +{ + u32 bpp; + + switch (pixelformat) { + case V4L2_PIX_FMT_RGB565: + bpp = 16; + break; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + bpp = 24; + break; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + bpp = 32; + break; + default: + bpp = 8; + break; + } + return bpp; +} + +/* + * V4L2 - Handles VIDIOC_G_FMT Ioctl + * + * @param vout structure vout_data * + * + * @param v4l2_format structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + return -EINVAL; + } + *f = vout->v2f; + DBG(0, "%s: fmt=%dx%d %08x bpl=%d size=%d colorspace=%d\n", __FUNCTION__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage, f->fmt.pix.colorspace); + return 0; +} + +/* + * V4L2 - Handles VIDIOC_S_FMT Ioctl + * + * @param vout structure vout_data * + * + * @param v4l2_format structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f) +{ + int retval = 0; + u32 size = 0; + u32 bytesperline; + + DBG(0, "%s: fmt=%dx%d %08x bpl=%d size=%d colorspace=%d\n", __FUNCTION__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage, f->fmt.pix.colorspace); + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + pr_err("buffer type %d not supported\n", f->type); + retval = -EINVAL; + goto err0; + } + if (!valid_mode(f->fmt.pix.pixelformat)) { + pr_err("pixel format %08x not supported\n", f->fmt.pix.pixelformat); + retval = -EINVAL; + goto err0; + } + + bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) / 8; + if (f->fmt.pix.bytesperline < bytesperline) { + f->fmt.pix.bytesperline = bytesperline; + } else { + bytesperline = f->fmt.pix.bytesperline; + } + if (bytesperline == 0) { + return -EINVAL; + } + + vout->v2f.fmt.pix.width = f->fmt.pix.width; + vout->v2f.fmt.pix.height = f->fmt.pix.height; + vout->v2f.fmt.pix.priv = f->fmt.pix.priv; + if (vout->v2f.fmt.pix.priv) { + if (copy_from_user(&vout->crop_rect, + (void __user *)vout->v2f.fmt.pix.priv, + sizeof(struct v4l2_rect))) { + retval = -EFAULT; + goto err0; + } + } + DBG(0, "%s: format: %dx%d-%dbpp %u bytes per line\n", __FUNCTION__, + f->fmt.pix.width, f->fmt.pix.height, fmt_to_bpp(f->fmt.pix.pixelformat), + bytesperline); + + if ((vout->crop_rect.top > f->fmt.pix.height) || + (vout->crop_rect.left > f->fmt.pix.width)) { + retval = -EINVAL; + goto err0; + } + + if ((vout->crop_rect.top + vout->crop_rect.height) > f->fmt.pix.height) + vout->crop_rect.height = + f->fmt.pix.height - vout->crop_rect.top; + if ((vout->crop_rect.left + vout->crop_rect.width) > f->fmt.pix.width) + vout->crop_rect.width = f->fmt.pix.width - vout->crop_rect.left; + + vout->crop_rect.width -= vout->crop_rect.width % 8; + vout->crop_rect.height -= vout->crop_rect.height % 8; + vout->crop_rect.top -= vout->crop_rect.top % 2; + vout->crop_rect.left += vout->crop_rect.left % 2; + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV422P: + /* bytesperline for YUV planar formats is for + Y plane only */ + size = bytesperline * f->fmt.pix.height * 2; + if ((vout->crop_rect.width != 0) + && (vout->crop_rect.height != 0)) { + vout->offset.y_offset = + (vout->v2f.fmt.pix.width * vout->crop_rect.top) + + (vout->crop_rect.left); + vout->offset.u_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) + + ((vout->v2f.fmt.pix.width * + vout->crop_rect.top) >> 1) + + (vout->crop_rect.left >> 1); + vout->offset.v_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 1) + + ((vout->v2f.fmt.pix.width * + vout->crop_rect.top) >> 1) + + (vout->crop_rect.left >> 1); + vout->offset.qp_offset = + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) << 1) + + (((vout->v2f.fmt.pix.width * vout->crop_rect.top) + + vout->crop_rect.left) >> 10); + } else { + vout->offset.y_offset = 0; + vout->offset.u_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height); + vout->offset.v_offset = + vout->offset.u_offset + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 1); + vout->offset.qp_offset = + vout->offset.v_offset + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 1); + } + break; + case V4L2_PIX_FMT_YUV420: + size = (bytesperline * f->fmt.pix.height * 3) / 2; + if ((vout->crop_rect.width != 0) + && (vout->crop_rect.height != 0)) { + vout->offset.y_offset = + (vout->v2f.fmt.pix.width * vout->crop_rect.top) + + (vout->crop_rect.left); + vout->offset.u_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) + + ((vout->v2f.fmt.pix.width * + vout->crop_rect.top) >> 2) + + (vout->crop_rect.left >> 1); + vout->offset.v_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 2) + + ((vout->v2f.fmt.pix.width * + vout->crop_rect.top) >> 2) + + (vout->crop_rect.left >> 1); + vout->offset.qp_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 1) + + (((vout->v2f.fmt.pix.width * vout->crop_rect.top) + + vout->crop_rect.left) >> 10); + } else { + vout->offset.y_offset = 0; + vout->offset.u_offset = + (vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height); + vout->offset.v_offset = + vout->offset.u_offset + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 2); + vout->offset.qp_offset = + vout->offset.v_offset + + ((vout->v2f.fmt.pix.width * + vout->v2f.fmt.pix.height) >> 2); + } + break; + default: + size = bytesperline * f->fmt.pix.height; + vout->offset.y_offset = 0; + vout->offset.u_offset = + (vout->v2f.fmt.pix.width * vout->v2f.fmt.pix.height); + vout->offset.v_offset = + vout->offset.u_offset + + ((vout->v2f.fmt.pix.width * vout->v2f.fmt.pix.height) >> 2); + vout->offset.qp_offset = + vout->offset.v_offset + + ((vout->v2f.fmt.pix.width * vout->v2f.fmt.pix.height) >> 2); + break; + } + + /* Return the actual size of the image to the app */ + f->fmt.pix.sizeimage = size; + vout->v2f.fmt.pix.sizeimage = size; + vout->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat; + vout->v2f.fmt.pix.bytesperline = f->fmt.pix.bytesperline; + + DBG(0, "%s: Y: %08x..%08x\n", __FUNCTION__, + vout->offset.y_offset, vout->offset.y_offset + f->fmt.pix.height * bytesperline); + DBG(0, "%s: U: %08x..%08x\n", __FUNCTION__, + vout->offset.u_offset, vout->offset.u_offset + f->fmt.pix.height * bytesperline / 2); + DBG(0, "%s: V: %08x..%08x\n", __FUNCTION__, + vout->offset.v_offset, vout->offset.v_offset + f->fmt.pix.height * bytesperline / 2); + + retval = 0; + err0: + return retval; +} + +/* + * V4L2 - Handles VIDIOC_G_CTRL Ioctl + * + * @param vout structure vout_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_get_v4l2out_control(vout_data *vout, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + c->value = (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0; + break; + case V4L2_CID_VFLIP: + c->value = (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0; + break; + case (V4L2_CID_PRIVATE_BASE + 1): + c->value = vout->rotate; + break; + case V4L2_CID_MXC_TEAR_PROTECT: + c->value = vout->tear_protection; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * V4L2 - Handles VIDIOC_S_CTRL Ioctl + * + * @param vout structure vout_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_set_v4l2out_control(vout_data *vout, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + case V4L2_CID_MXC_ROT: + if (c->value < 0 || c->value > 7) { + return -EINVAL; + } + vout->rotate = c->value; + break; + case V4L2_CID_MXC_TEAR_PROTECT: +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + if (c->value == TEARING_PROTECTION_ACTIVE) + vout->tear_protection = TEARING_PROTECTION_ACTIVE; + else + vout->tear_protection = TEARING_PROTECTION_INACTIVE;; +#else + vout->tear_protection = TEARING_PROTECTION_UNSUPPORTED; +#endif + break; + default: + return -EINVAL; + } + return 0; +} + +/*! + * V4L2 interface - open function + * + * @param inode structure inode * + * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l2out_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + vout_data *vout = video_get_drvdata(dev); + int err; + + dq_intr_cnt = 0; + if (!vout) { + pr_info("Internal error, vout_data not found!\n"); + return -ENODEV; + } + + down(&vout->user_lock); + if (vout->open_count == 0) { + err = pp_init(vout); + if (err) { + goto out; + } + init_timer(&vout->output_timer); + vout->output_timer.function = mxc_v4l2out_timer_handler; + vout->output_timer.data = (unsigned long)vout; + + vout->state = STATE_STREAM_OFF; + vout->queued = 0; + g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0; + g_busy_cnt = 0; + g_paused_cnt = 0; + g_late_cnt = 0; + using_default_fps = -1; + vout->open_count++; + } +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + vout->tear_protection = TEARING_PROTECTION_ACTIVE; +#else + vout->tear_protection = TEARING_PROTECTION_UNSUPPORTED; +#endif + + file->private_data = dev; + out: + up(&vout->user_lock); + return err; +} + +/*! + * V4L2 interface - close function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @return 0 success + */ +static int mxc_v4l2out_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + vout_data *vout = video_get_drvdata(dev); + + down(&vout->user_lock); + if (vout->open_count) { + DBG(-1, "%s: %u interrupts handled frames output: %u queued: %u dequeued: %u paused: %u busy: %u late: %u\n", + __FUNCTION__, g_irq_cnt, g_buf_output_cnt, g_buf_q_cnt, g_buf_dq_cnt, + g_paused_cnt, g_busy_cnt, g_late_cnt); + + pp_exit(vout); + mxc_v4l2out_streamoff(vout); + file->private_data = NULL; + WARN_ON(!list_empty(&vout->done_q)); + if (WARN_ON(vout->active)) { + DBG(-1, "%s: Moving active buffer %p (%08x:%p) to done list\n", + __FUNCTION__, vout->active, vout->active->dma_desc.dma_addr, + vout->active->dma_desc.cpu_addr); + list_add_tail(&vout->active->head, &vout->done_q); + vout->active = NULL; + } + WARN_ON(!list_empty(&vout->ready_q)); + DBG(-1, "%s: Releasing buffers from ready queue\n", __FUNCTION__); + mxc_free_buffers(vout, &vout->ready_q); + DBG(-1, "%s: Releasing buffers from done queue\n", __FUNCTION__); + mxc_free_buffers(vout, &vout->done_q); + DBG(-1, "%s: Releasing buffers from free queue\n", __FUNCTION__); + mxc_free_buffers(vout, &vout->free_q); + vout->buffer_cnt = 0; + DBG(-1, "%s: Releasing display buffers\n", __FUNCTION__); + mxc_free_buffers(vout, &vout->display_q); + + /* capture off */ + wake_up_interruptible(&vout->v4l_bufq); + WARN_ON(--vout->open_count); + } + up(&vout->user_lock); + + return 0; +} + +static int do_dequeue(vout_data *vout, struct v4l2_buffer *user_buf) +{ + int retval; + unsigned long flags; + + if (vout->active == NULL && + list_empty(&vout->done_q) && + list_empty(&vout->ready_q)) { + return -EINVAL; + } + up(&vout->user_lock); + retval = wait_event_interruptible_timeout(vout->v4l_bufq, + !list_empty(&vout->done_q), + 10 * HZ); + down(&vout->user_lock); + spin_lock_irqsave(&vout->irq_lock, flags); + if (retval >= 0 && !list_empty(&vout->done_q)) { + struct v4l_queue *q; + struct v4l2_buffer *buf; + + q = list_first_entry(&vout->done_q, struct v4l_queue, head); + DBG(2, "%s: processing buffer[%d] %p from done queue %p\n", + __FUNCTION__, q->buf.index, q, &vout->done_q); + WARN_ON(!(q->buf.flags & V4L2_BUF_FLAG_DONE)); + q->buf.flags &= ~(V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_QUEUED); + list_move_tail(&q->head, &vout->free_q); + buf = &q->buf; + DBG(2, "%s: buffer[%d] %p moved to free queue %p\n", __FUNCTION__, + buf->index, q, &vout->free_q); + BUG_ON(buf->index >= vout->buffer_cnt); + if (user_buf != NULL) { + memcpy(user_buf, buf, sizeof(*user_buf)); + } + DBG(1, "VIDIOC_DQBUF: buffer %d: %d\n", buf->index, retval); + retval = 0; + } else if (retval == 0) { + pr_warn("VIDIOC_DQBUF: timeout\n"); + retval = -ETIME; + DBG(3, "VIDIOC_DQBUF: ret: %d\n", retval); + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + return retval; +} +/*! + * V4L2 interface - ioctl function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @param ioctlnr unsigned int + * + * @param arg void * + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static int +mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *vdev = file->private_data; + vout_data *vout = video_get_drvdata(vdev); + int retval = 0; + int i = 0; + + if (dbg_lvl(3)) { + v4l_printk_ioctl(ioctlnr); + printk(" arg=%p\n", arg); + } + + if (!vout) + return -EBADF; + + down(&vout->user_lock); + switch (ioctlnr) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + strcpy(cap->driver, "mxc_v4l2_output"); + cap->version = 0; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_STREAMING; + strlcpy(cap->card, "MX27 eMMA", sizeof(cap->card)); + cap->bus_info[0] = '\0'; + retval = 0; + } + break; + case VIDIOC_G_FMT: + { + struct v4l2_format *gf = arg; + retval = mxc_v4l2out_g_fmt(vout, gf); + } + break; + case VIDIOC_S_FMT: + { + struct v4l2_format *sf = arg; + if (vout->state != STATE_STREAM_OFF) { + retval = -EBUSY; + break; + } + retval = mxc_v4l2out_s_fmt(vout, sf); + } + break; + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *req = arg; + struct v4l_queue *q; + size_t bufsize = PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); + unsigned long flags; + + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || + (req->memory != V4L2_MEMORY_MMAP)) { + pr_warn("VIDIOC_REQBUFS: incorrect buffer type: %d\n", req->type); + retval = -EINVAL; + break; + } + + DBG(0, "%s: VIDIOC_REQBUFS: %d buffers of %u byte requested\n", + __FUNCTION__, req->count, bufsize); + + if (req->count == 0) { + spin_lock_irqsave(&vout->irq_lock, flags); + if (vout->state == STATE_STREAM_ON) { + vout->state = STATE_STREAM_STOPPING; + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + while (vout->active || !list_empty(&vout->ready_q)) { + DBG(-1, "%s: Waiting for stream to drain queued: %d\n", + __FUNCTION__, vout->queued); + retval = do_dequeue(vout, NULL); + if (retval != 0) { + DBG(-1, "do_dequeue() returned %d\n", retval); + } + if (retval == -ERESTARTSYS) { + break; + } + } +#if 1 + retval = mxc_v4l2out_streamoff(vout); +#else + pp_enable(0); /* Disable PP */ + WARN_ON(!list_empty(&vout->ready_q)); + WARN_ON(vout->active); + mxc_free_buffers(vout, &vout->done_q); + mxc_free_buffers(vout, &vout->free_q); +#endif + break; + } + if (vout->state != STATE_STREAM_OFF) { + pr_warn("VIDIOC_REQBUFS: streaming active\n"); + retval = -EBUSY; + break; + } +#if 0 + if (!list_empty(&vout->done_q) || !list_empty(&vout->ready_q)) { + pr_warn("VIDIOC_REQBUFS: busy buffers exist\n"); + retval = -EBUSY; + break; + } +#endif + /* This needs no interrupt locking, since we have the busy_lock + * and are in STATE_STREAM_OFF where there cannot be any interrupts + */ + if (!list_empty(&vout->free_q)) { + q = list_first_entry(&vout->free_q, struct v4l_queue, head); + if (q->buf.length != bufsize || + q->buf.type != req->type || + q->buf.memory != req->memory) { + pr_err("VIDIOC_REQBUFS: Mismatch between old and new buffer parameters\n"); + retval = -EINVAL; + break; + } + } + + if (req->count > MAX_FRAME_NUM) { + req->count = MAX_FRAME_NUM; + } + if (req->count == vout->buffer_cnt) { + break; + } else if (req->count < vout->buffer_cnt) { + struct list_head release_q; + struct list_head *tmp; + struct list_head *x; + + list_for_each_prev_safe(x, tmp, &vout->free_q) { + if (vout->buffer_cnt-- > req->count) { + q = container_of(x, struct v4l_queue, head); + DBG(-1, "%s: Moving buffer[%d:%d] %p (%08x:%p) from free to release list\n", + __FUNCTION__, vout->buffer_cnt, + q->buf.index, q, q->dma_desc.dma_addr, + q->dma_desc.cpu_addr); + list_move(x, &release_q); + } + } + BUG_ON(vout->buffer_cnt != req->count); + mxc_free_buffers(vout, &release_q); + break; + } + retval = mxc_allocate_buffers(vout, &vout->free_q, + req->count - vout->buffer_cnt, + bufsize); + if (retval + vout->buffer_cnt < req->count) { + pr_warn("Could only allocate %u of %u buffers\n", + retval + vout->buffer_cnt, req->count); + req->count = retval + vout->buffer_cnt; + } + vout->buffer_cnt = req->count; + i = 0; + list_for_each_entry(q, &vout->free_q, head) { + q->buf.memory = V4L2_MEMORY_MMAP; + q->buf.index = i; + q->buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->buf.length = bufsize; + q->buf.m.offset = q->dma_desc.dma_addr; + i++; + } + BUG_ON(i != vout->buffer_cnt); + } + break; + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + struct v4l_queue *q; + unsigned long flags; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + pr_warn("VIDIOC_QUERYBUF: incorrect buffer type: %d expected %d\n", + buf->type, V4L2_BUF_TYPE_VIDEO_OUTPUT); + retval = -EINVAL; + break; + } + if (buf->index >= vout->buffer_cnt) { + pr_warn("VIDIOC_QUERYBUF: buffer index out of range: %d [0..%d]\n", + buf->index, vout->buffer_cnt); + retval = -EINVAL; + break; + } + spin_lock_irqsave(&vout->irq_lock, flags); + q = find_buffer(&vout->free_q, buf->index); + if (q == NULL) { + q = find_buffer(&vout->ready_q, buf->index); + if (q == NULL) { + q = find_buffer(&vout->done_q, buf->index); + if (q != NULL) { + DBG(2, "VIDIOC_QUERYBUF: buffer[%d] %p found in done list\n", + q->buf.index, q); + } + } else { + DBG(2, "VIDIOC_QUERYBUF: buffer[%d] %p found in ready list\n", + q->buf.index, q); + } + } else { + DBG(2, "VIDIOC_QUERYBUF: buffer[%d] %p found in free list\n", + q->buf.index, q); + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + if (q == NULL) { + pr_warn("VIDIOC_QUERYBUF: buffer %d not found in any list\n", + buf->index); + retval = -ENOENT; + break; + } + memcpy(buf, &q->buf, sizeof(q->buf)); + } + break; + case VIDIOC_QBUF: + { + struct v4l2_buffer *buf = arg; + struct v4l_queue *q; + unsigned long flags; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + pr_err("VIDIOC_QBUF: invalid buffer type: %u\n", + buf->type); + retval = -EINVAL; + break; + } + + if (buf->index >= vout->buffer_cnt) { + pr_err("VIDIOC_QBUF: invalid buffer index: %u/%u\n", + buf->index, vout->buffer_cnt); + retval = -EINVAL; + break; + } + + if (vout->state == STATE_STREAM_STOPPING) { + pr_err("VIDIOC_QBUF: stream is draining\n"); + retval = -EINVAL; + break; + } + DBG(1, "VIDIOC_QBUF: %d/%d ts %6lu.%06lu\n", buf->index, + vout->queued, buf->timestamp.tv_sec, buf->timestamp.tv_usec); + + spin_lock_irqsave(&vout->irq_lock, flags); + q = find_buffer(&vout->free_q, buf->index); + if (q == NULL || (q->buf.flags & V4L2_BUF_FLAG_QUEUED)) { + spin_unlock_irqrestore(&vout->irq_lock, flags); + if (q == NULL) { + printk(KERN_ERR "buffer %d not on free queue %p\n", + buf->index, &vout->free_q); + retval = -ENOENT; + } else { + retval = -EBUSY; + } + break; + } + q->buf.timestamp = buf->timestamp; + buf = &q->buf; + WARN_ON(buf->flags & V4L2_BUF_FLAG_DONE); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + + BUG_ON(buf->index >= vout->buffer_cnt); + + if (vout->state == STATE_STREAM_PAUSED && + vout->active == NULL) { + DBG(0, "%s: Restarting stream\n", __FUNCTION__); + vout->active = q; + list_del_init(&vout->active->head); + vout->state = STATE_STREAM_ON; + + mxc_v4l2out_schedule_frame(vout, &buf->timestamp, 0); + } else { + list_move_tail(&q->head, &vout->ready_q); + vout->queued++; + g_buf_q_cnt++; + DBG(2, "%s: buffer[%d] %p moved to ready queue %p\n", __FUNCTION__, + buf->index, q, &vout->ready_q); + } + if (vout->state == STATE_STREAM_PAUSED) { + vout->state = STATE_STREAM_ON; + } + spin_unlock_irqrestore(&vout->irq_lock, flags); + } + break; + case VIDIOC_DQBUF: + { + struct v4l2_buffer *buf = arg; + + DBG(1, "VIDIOC_DQBUF: queued: %d\n", vout->queued); + + if (list_empty(&vout->done_q) && + (file->f_flags & O_NONBLOCK)) { + retval = -EAGAIN; + DBG(3, "VIDIOC_DQBUF: ret: %d\n", retval); + break; + } + + retval = do_dequeue(vout, buf); + if (retval == -ETIME) { + pr_warn("VIDIOC_DQBUF: timeout\n"); + DBG(3, "VIDIOC_DQBUF: ret: %d\n", retval); + } else if (retval == -ERESTARTSYS) { + if (dq_intr_cnt == 0) + DBG(0, "VIDIOC_DQBUF: interrupt received\n"); + dq_intr_cnt++; + DBG(0, "VIDIOC_DQBUF: ret: %d\n", retval); + } else { + DBG(0, "VIDIOC_DQBUF: ret: %d\n", retval); + } + } + break; + case VIDIOC_STREAMON: + retval = mxc_v4l2out_streamon(vout); + break; + case VIDIOC_STREAMOFF: + retval = mxc_v4l2out_streamoff(vout); + break; + case VIDIOC_G_CTRL: + retval = mxc_get_v4l2out_control(vout, arg); + break; + case VIDIOC_S_CTRL: + retval = mxc_set_v4l2out_control(vout, arg); + break; + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = arg; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + cap->bounds = vout->crop_bounds[vout->cur_disp_output]; + cap->defrect = vout->crop_bounds[vout->cur_disp_output]; + retval = 0; + } + break; + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + crop->c = vout->crop_current; + } + break; + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = &vout->crop_bounds[vout->cur_disp_output]; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + if (crop->c.height < 0) { + retval = -EINVAL; + break; + } + if (crop->c.width < 0) { + retval = -EINVAL; + break; + } + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = + b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.top = b->left; + if (crop->c.left > b->left + b->width) + crop->c.top = b->left + b->width; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + /* stride line limitation */ + crop->c.height -= crop->c.height % 8; + crop->c.width -= crop->c.width % 8; + + vout->crop_current = crop->c; + DBG(0, "%s: crop rect: %dx%d%+d%+d\n", __FUNCTION__, + crop->c.width, crop->c.height, + crop->c.left, crop->c.top); + + vout->display_buf_size = vout->crop_current.width * + vout->crop_current.height; + vout->display_buf_size *= + fmt_to_bpp(SDC_FG_FB_FORMAT) / 8; + } + break; + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *output = arg; + + if ((output->index >= 2) || !vout->output_enabled[output->index]) { + retval = -EINVAL; + break; + } + memcpy(output, &mxc_outputs[0], sizeof(*output)); + snprintf(output->name, sizeof(output->name), mxc_outputs[0].name, + output->index); + } + break; + case VIDIOC_G_OUTPUT: + { + u32 *index = arg; + + *index = vout->cur_disp_output; + } + break; + case VIDIOC_S_OUTPUT: + { + u32 *index = arg; + + if ((*index >= 2) || !vout->output_enabled[*index]) { + retval = -EINVAL; + break; + } + + if (vout->state != STATE_STREAM_OFF) { + retval = -EBUSY; + break; + } + + vout->cur_disp_output = *index; + } + break; + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *fb = arg; + + memcpy(fb, &vout->v4l2_fb, sizeof(*fb)); + } + break; + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *fb = arg; + + memcpy(&vout->v4l2_fb, fb, sizeof(vout->v4l2_fb)); + vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + } + break; + case VIDIOC_ENUM_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_PARM: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + default: + retval = -EINVAL; + } + + up(&vout->user_lock); + return retval; +} + +/* + * V4L2 interface - ioctl function + * + * @return None + */ +static int +mxc_v4l2out_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl); +} + +/*! + * V4L2 interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, + * ENOBUFS remap_page error + */ +static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + int res = 0; + vout_data *vout = video_get_drvdata(vdev); + + down(&vout->user_lock); + + /* make buffers write-thru cacheable */ + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) & + ~L_PTE_BUFFERABLE); + + if (remap_pfn_range(vma, start, vma->vm_pgoff, size, vma->vm_page_prot)) { + pr_err("%s: - remap_pfn_range failed\n", __FUNCTION__); + res = -ENOBUFS; + } + + up(&vout->user_lock); + return res; +} + +/*! + * V4L2 interface - poll function + * + * @param file structure file * + * + * @param wait structure poll_table * + * + * @return status POLLIN | POLLRDNORM + */ +static unsigned int mxc_v4l2out_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + vout_data *vout = video_get_drvdata(vdev); + + wait_queue_head_t *queue = NULL; + int res = POLLIN | POLLRDNORM; + + down(&vout->user_lock); + + queue = &vout->v4l_bufq; + poll_wait(file, queue, wait); + + up(&vout->user_lock); + return res; +} + +static struct file_operations mxc_v4l2out_fops = { + .owner = THIS_MODULE, + .open = mxc_v4l2out_open, + .release = mxc_v4l2out_close, + .ioctl = mxc_v4l2out_ioctl, + .mmap = mxc_v4l2out_mmap, + .poll = mxc_v4l2out_poll, +}; + +static struct video_device mxc_v4l2out_template = { + //.owner = THIS_MODULE, + .name = "MXC Video Output", + //.type = 0, + //.type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, + //.hardware = 39, + .fops = &mxc_v4l2out_fops, + .release = video_device_release, +}; + +/*! + * Probe routine for the framebuffer driver. It is called during the + * driver binding process. The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, IPU initialization. + * + * @return Appropriate error code to the kernel common code + */ +static int mxc_v4l2out_probe(struct platform_device *pdev) +{ + int ret; + int i; + vout_data *vout; + + /* + * Allocate sufficient memory for the fb structure + */ + vout = kzalloc(sizeof(vout_data), GFP_KERNEL); + if (!vout) + return -ENOMEM; + + vout->video_dev = video_device_alloc(); + if (vout->video_dev == NULL) { + kfree(vout); + return -ENOMEM; + } + *vout->video_dev = mxc_v4l2out_template; + + vout->video_dev->parent = &pdev->dev; + vout->video_dev->minor = -1; + + init_waitqueue_head(&vout->v4l_bufq); + + INIT_LIST_HEAD(&vout->free_q); + INIT_LIST_HEAD(&vout->ready_q); + INIT_LIST_HEAD(&vout->done_q); + + INIT_LIST_HEAD(&vout->display_q); + + spin_lock_init(&vout->irq_lock); + + init_MUTEX(&vout->user_lock); + //mutex_init(&vout->user_lock); + + /* register v4l device */ + ret = video_register_device(vout->video_dev, + VFL_TYPE_GRABBER, video_nr); + if (ret != 0) { + pr_err("video_register_device failed: %d\n", ret); + kfree(vout->video_dev); + kfree(vout); + return ret; + } + DBG(0, "mxc_v4l2out: registered device video%d\n", + vout->video_dev->minor & 0x1f); + + video_set_drvdata(vout->video_dev, vout); + platform_set_drvdata(pdev, vout); + + /* setup outputs and cropping */ + vout->cur_disp_output = -1; +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + vout->output_fb = -1; +#endif + for (i = 0; i < num_registered_fb && i < MXC_V4L2_OUT_NUM_OUTPUTS; i++) { + char *idstr = registered_fb[i]->fix.id; + DBG(0, "Checking FB '%s'\n", idstr); + if (strncmp(idstr, "IMX", 3) == 0) { + int disp_num = i; + vout->crop_bounds[disp_num].left = 0; + vout->crop_bounds[disp_num].top = 0; + vout->crop_bounds[disp_num].width = + registered_fb[i]->var.xres; + vout->crop_bounds[disp_num].height = + registered_fb[i]->var.yres; + vout->output_enabled[disp_num] = true; + vout->output_fb_num[disp_num] = i; + DBG(0, "crop bounds: %dx%d%+d%+d\n", + vout->crop_bounds[disp_num].left, + vout->crop_bounds[disp_num].top, + vout->crop_bounds[disp_num].width, + vout->crop_bounds[disp_num].height); + if (vout->cur_disp_output == -1) + vout->cur_disp_output = disp_num; + } + + } + if (vout->cur_disp_output >= 0) { + vout->crop_current = vout->crop_bounds[vout->cur_disp_output]; + } + + /* Setup framebuffer parameters */ + vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + vout->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY; + // FIXME: Use the pixelformat of the FB driver! + vout->v4l2_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + + return 0; +} + +static int mxc_v4l2out_remove(struct platform_device *pdev) +{ + vout_data *vout = platform_get_drvdata(pdev); + + video_unregister_device(vout->video_dev); + kfree(vout); + return 0; +} + +#define DRV_NAME "MXC Video Output" +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_v4l2out_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = mxc_v4l2out_probe, + .remove = mxc_v4l2out_remove, +}; + +#ifdef REGISTER_PDEV +#if 1 +static struct platform_device *mxc_v4l2out_device; +#else +static struct platform_device mxc_v4l2out_device = { + .name = "MXC Video Output", + .id = 0, +}; +#endif +#endif + +/*! + * mxc v4l2 init function + * + */ +static int mxc_v4l2out_init(void) +{ + int err; + + err = platform_driver_register(&mxc_v4l2out_driver); +#ifdef REGISTER_PDEV + if (err == 0) { +#if 1 + mxc_v4l2out_device = platform_device_register_simple(DRV_NAME, 0, NULL, 0); + if (IS_ERR(mxc_v4l2out_device)) { + platform_driver_unregister(&mxc_v4l2out_driver); + return PTR_ERR(mxc_v4l2out_device); + } +#else + platform_device_register(&mxc_v4l2out_device); +#endif + } +#endif + return err; +} + +/*! + * mxc v4l2 cleanup function + * + */ +static void mxc_v4l2out_clean(void) +{ + DBG(0, "unregistering video\n"); + + platform_driver_unregister(&mxc_v4l2out_driver); +#ifdef REGISTER_PDEV +#if 1 + platform_device_unregister(mxc_v4l2out_device); +#else + platform_device_unregister(&mxc_v4l2out_device); +#endif +#endif +} + +module_init(mxc_v4l2out_init); +module_exit(mxc_v4l2out_clean); + +module_param(video_nr, int, S_IRUGO); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2-driver for MXC video output"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/mxc_v4l2_output.c linux-2.6.28-karo/drivers/media/video/mxc/output/mxc_v4l2_output.c --- linux-2.6.28/drivers/media/video/mxc/output/mxc_v4l2_output.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/mxc_v4l2_output.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,1720 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file drivers/media/video/mxc/output/mxc_v4l2_output.c + * + * @brief MXC V4L2 Video Output Driver + * + * Video4Linux2 Output Device using MXC IPU Post-processing functionality. + * + * @ingroup MXC_V4L2_OUTPUT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mxc_v4l2_output.h" + +vout_data *g_vout; +#define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565 + +struct v4l2_output mxc_outputs[2] = { + { + .index = MXC_V4L2_OUT_2_SDC, + .name = "DISP3 Video Out", + .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct, + but no other choice */ + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN}, + { + .index = MXC_V4L2_OUT_2_ADC, + .name = "DISPx Video Out", + .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct, + but no other choice */ + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN} +}; + +static int video_nr = 16; +static spinlock_t g_lock = SPIN_LOCK_UNLOCKED; + +/* debug counters */ +uint32_t g_irq_cnt; +uint32_t g_buf_output_cnt; +uint32_t g_buf_q_cnt; +uint32_t g_buf_dq_cnt; + +static int dq_intr_cnt=0; +static int dq_timeout_cnt=0; + +#define QUEUE_SIZE (MAX_FRAME_NUM + 1) +static __inline int queue_size(v4l_queue * q) +{ + if (q->tail >= q->head) + return (q->tail - q->head); + else + return ((q->tail + QUEUE_SIZE) - q->head); +} + +static __inline int queue_buf(v4l_queue * q, int idx) +{ + if (((q->tail + 1) % QUEUE_SIZE) == q->head) + return -1; /* queue full */ + q->list[q->tail] = idx; + q->tail = (q->tail + 1) % QUEUE_SIZE; + return 0; +} + +static __inline int dequeue_buf(v4l_queue * q) +{ + int ret; + if (q->tail == q->head) + return -1; /* queue empty */ + ret = q->list[q->head]; + q->head = (q->head + 1) % QUEUE_SIZE; + return ret; +} + +static __inline int peek_next_buf(v4l_queue * q) +{ + if (q->tail == q->head) + return -1; /* queue empty */ + return q->list[q->head]; +} + +static __inline unsigned long get_jiffies(struct timeval *t) +{ + struct timeval cur; + + if (t->tv_usec >= 1000000) { + t->tv_sec += t->tv_usec / 1000000; + t->tv_usec = t->tv_usec % 1000000; + } + + do_gettimeofday(&cur); + if ((t->tv_sec < cur.tv_sec) + || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) + return jiffies; + + if (t->tv_usec < cur.tv_usec) { + cur.tv_sec = t->tv_sec - cur.tv_sec - 1; + cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; + } else { + cur.tv_sec = t->tv_sec - cur.tv_sec; + cur.tv_usec = t->tv_usec - cur.tv_usec; + } + + return jiffies + timeval_to_jiffies(&cur); +} + +/*! + * Private function to free buffers + * + * @param bufs_paddr Array of physical address of buffers to be freed + * + * @param bufs_vaddr Array of virtual address of buffers to be freed + * + * @param num_buf Number of buffers to be freed + * + * @param size Size for each buffer to be free + * + * @return status 0 success. + */ +static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], + int num_buf, int size) +{ + int i; + + for (i = 0; i < num_buf; i++) { + if (bufs_vaddr[i] != 0) { + dma_free_coherent(0, size, bufs_vaddr[i], + bufs_paddr[i]); + pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]); + bufs_paddr[i] = 0; + bufs_vaddr[i] = NULL; + } + } + return 0; +} + +/*! + * Private function to allocate buffers + * + * @param bufs_paddr Output array of physical address of buffers allocated + * + * @param bufs_vaddr Output array of virtual address of buffers allocated + * + * @param num_buf Input number of buffers to allocate + * + * @param size Input size for each buffer to allocate + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], + int num_buf, int size) +{ + int i; + + for (i = 0; i < num_buf; i++) { + bufs_vaddr[i] = dma_alloc_coherent(0, size, + &bufs_paddr[i], + GFP_DMA | GFP_KERNEL); + + if (bufs_vaddr[i] == 0) { + mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size); + printk(KERN_ERR "dma_alloc_coherent failed.\n"); + return -ENOBUFS; + } + pr_debug("allocated @ paddr=0x%08X, size=%d.\n", + (u32) bufs_paddr[i], size); + } + + return 0; +} + +/* + * Returns bits per pixel for given pixel format + * + * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return bits per pixel of pixelformat + */ +static u32 fmt_to_bpp(u32 pixelformat) +{ + u32 bpp; + + switch (pixelformat) { + case V4L2_PIX_FMT_RGB565: + bpp = 16; + break; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + bpp = 24; + break; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + bpp = 32; + break; + default: + bpp = 8; + break; + } + return bpp; +} + +static void mxc_v4l2out_timer_handler(unsigned long arg) +{ + int index; + unsigned long timeout; + unsigned long lock_flags = 0; + vout_data *vout = (vout_data *) arg; + + dev_dbg(vout->video_dev->dev, "timer handler: %lu\n", jiffies); + + spin_lock_irqsave(&g_lock, lock_flags); + + /* + * If timer occurs before IPU h/w is ready, then set the state to + * paused and the timer will be set again when next buffer is queued + * or PP comletes + */ + if (vout->ipu_buf[vout->next_rdy_ipu_buf] != -1) { + dev_dbg(vout->video_dev->dev, "IPU buffer busy\n"); + vout->state = STATE_STREAM_PAUSED; + goto exit0; + } + + /* Dequeue buffer and pass to IPU */ + index = dequeue_buf(&vout->ready_q); + if (index == -1) { /* no buffers ready, should never occur */ + dev_err(vout->video_dev->dev, + "mxc_v4l2out: timer - no queued buffers ready\n"); + goto exit0; + } + + g_buf_dq_cnt++; + vout->frame_count++; + vout->ipu_buf[vout->next_rdy_ipu_buf] = index; + if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index].m.offset) < 0) { + dev_err(vout->video_dev->dev, + "unable to update buffer %d address\n", + vout->next_rdy_ipu_buf); + goto exit0; + } + if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf) < 0) { + dev_err(vout->video_dev->dev, + "unable to set IPU buffer ready\n"); + } + vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf; + + /* Setup timer for next buffer */ + index = peek_next_buf(&vout->ready_q); + if (index != -1) { + /* if timestamp is 0, then default to 30fps */ + if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) + && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)) + timeout = + vout->start_jiffies + vout->frame_count * HZ / 30; + else + timeout = + get_jiffies(&vout->v4l2_bufs[index].timestamp); + + if (jiffies >= timeout) { + dev_dbg(vout->video_dev->dev, + "warning: timer timeout already expired.\n"); + } + if (mod_timer(&vout->output_timer, timeout)) + dev_dbg(vout->video_dev->dev, + "warning: timer was already set\n"); + + dev_dbg(vout->video_dev->dev, + "timer handler next schedule: %lu\n", timeout); + } else { + vout->state = STATE_STREAM_PAUSED; + } + + exit0: + spin_unlock_irqrestore(&g_lock, lock_flags); +} + +static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id) +{ + int last_buf; + int index; + unsigned long timeout; + unsigned long lock_flags = 0; + vout_data *vout = dev_id; + + spin_lock_irqsave(&g_lock, lock_flags); + + g_irq_cnt++; + + /* Process previous buffer */ + last_buf = vout->ipu_buf[vout->next_done_ipu_buf]; + if (last_buf != -1) { + g_buf_output_cnt++; + vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; + queue_buf(&vout->done_q, last_buf); + vout->ipu_buf[vout->next_done_ipu_buf] = -1; + wake_up_interruptible(&vout->v4l_bufq); + /* printk("pp_irq: buf %d done\n", vout->next_done_ipu_buf); */ + vout->next_done_ipu_buf = !vout->next_done_ipu_buf; + } + + if (vout->state == STATE_STREAM_STOPPING) { + if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { + vout->state = STATE_STREAM_OFF; + } + } else if ((vout->state == STATE_STREAM_PAUSED) + && ((index = peek_next_buf(&vout->ready_q)) != -1)) { + /* Setup timer for next buffer, when stream has been paused */ + pr_debug("next index %d\n", index); + + /* if timestamp is 0, then default to 30fps */ + if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) + && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)) + timeout = + vout->start_jiffies + vout->frame_count * HZ / 30; + else + timeout = + get_jiffies(&vout->v4l2_bufs[index].timestamp); + + if (jiffies >= timeout) { + pr_debug("warning: timer timeout already expired.\n"); + } + + vout->state = STATE_STREAM_ON; + + if (mod_timer(&vout->output_timer, timeout)) + pr_debug("warning: timer was already set\n"); + + pr_debug("timer handler next schedule: %lu\n", timeout); + } + + spin_unlock_irqrestore(&g_lock, lock_flags); + + return IRQ_HANDLED; +} + +/*! + * Start the output stream + * + * @param vout structure vout_data * + * + * @return status 0 Success + */ +static int mxc_v4l2out_streamon(vout_data * vout) +{ + struct device *dev = vout->video_dev->dev; + ipu_channel_params_t params; + struct mxcfb_pos fb_pos; + struct fb_var_screeninfo fbvar; + struct fb_info *fbi = + registered_fb[vout->output_fb_num[vout->cur_disp_output]]; + int pp_in_buf[2]; + u16 out_width; + u16 out_height; + ipu_channel_t display_input_ch = MEM_PP_MEM; + bool use_direct_adc = false; + + if (!vout) + return -EINVAL; + + if (vout->state != STATE_STREAM_OFF) + return -EBUSY; + + if (queue_size(&vout->ready_q) < 2) { + dev_err(dev, "2 buffers not been queued yet!\n"); + return -EINVAL; + } + + out_width = vout->crop_current.width; + out_height = vout->crop_current.height; + + vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0; + vout->ipu_buf[0] = pp_in_buf[0] = dequeue_buf(&vout->ready_q); + vout->ipu_buf[1] = pp_in_buf[1] = dequeue_buf(&vout->ready_q); + vout->frame_count = 2; + + ipu_enable_irq(IPU_IRQ_PP_IN_EOF); + + /* Init Display Channel */ +#ifdef CONFIG_FB_MXC_ASYNC_PANEL + if (vout->cur_disp_output < DISP3) { + mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0); + if (vout->rotate < IPU_ROTATE_90_RIGHT) { + dev_dbg(dev, "Using PP direct to ADC channel\n"); + use_direct_adc = true; + vout->display_ch = MEM_PP_ADC; + vout->post_proc_ch = MEM_PP_ADC; + + memset(¶ms, 0, sizeof(params)); + params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width; + params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height; + params.mem_pp_adc.in_pixel_fmt = + vout->v2f.fmt.pix.pixelformat; + params.mem_pp_adc.out_width = out_width; + params.mem_pp_adc.out_height = out_height; + params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT; +#ifdef CONFIG_FB_MXC_EPSON_PANEL + params.mem_pp_adc.out_left = + 2 + vout->crop_current.left; +#else + params.mem_pp_adc.out_left = + 12 + vout->crop_current.left; +#endif + params.mem_pp_adc.out_top = vout->crop_current.top; + if (ipu_init_channel(vout->post_proc_ch, ¶ms) != 0) { + dev_err(dev, "Error initializing PP chan\n"); + return -EINVAL; + } + + if (ipu_init_channel_buffer(vout->post_proc_ch, + IPU_INPUT_BUFFER, + params.mem_pp_adc. + in_pixel_fmt, + params.mem_pp_adc.in_width, + params.mem_pp_adc.in_height, + vout->v2f.fmt.pix. + bytesperline / + bytes_per_pixel(params. + mem_pp_adc. + in_pixel_fmt), + vout->rotate, + vout-> + v4l2_bufs[pp_in_buf[0]].m. + offset, + vout-> + v4l2_bufs[pp_in_buf[1]].m. + offset, + vout->offset.u_offset, + vout->offset.v_offset) != + 0) { + dev_err(dev, "Error initializing PP in buf\n"); + return -EINVAL; + } + + if (ipu_init_channel_buffer(vout->post_proc_ch, + IPU_OUTPUT_BUFFER, + params.mem_pp_adc. + out_pixel_fmt, out_width, + out_height, out_width, + vout->rotate, 0, 0, 0, + 0) != 0) { + dev_err(dev, + "Error initializing PP output buffer\n"); + return -EINVAL; + } + + } else { + dev_dbg(dev, "Using ADC SYS2 channel\n"); + vout->display_ch = ADC_SYS2; + vout->post_proc_ch = MEM_PP_MEM; + + if (vout->display_bufs[0]) { + mxc_free_buffers(vout->display_bufs, + vout->display_bufs_vaddr, + 2, vout->display_buf_size); + } + + vout->display_buf_size = vout->crop_current.width * + vout->crop_current.height * + fmt_to_bpp(SDC_FG_FB_FORMAT) / 8; + mxc_allocate_buffers(vout->display_bufs, + vout->display_bufs_vaddr, + 2, vout->display_buf_size); + + memset(¶ms, 0, sizeof(params)); + params.adc_sys2.disp = vout->cur_disp_output; + params.adc_sys2.ch_mode = WriteTemplateNonSeq; +#ifdef CONFIG_FB_MXC_EPSON_PANEL + params.adc_sys2.out_left = 2 + vout->crop_current.left; +#else + params.adc_sys2.out_left = 12 + vout->crop_current.left; +#endif + params.adc_sys2.out_top = vout->crop_current.top; + if (ipu_init_channel(ADC_SYS2, ¶ms) < 0) + return -EINVAL; + + if (ipu_init_channel_buffer(vout->display_ch, + IPU_INPUT_BUFFER, + SDC_FG_FB_FORMAT, + out_width, out_height, + out_width, IPU_ROTATE_NONE, + vout->display_bufs[0], + vout->display_bufs[1], 0, + 0) != 0) { + dev_err(dev, + "Error initializing SDC FG buffer\n"); + return -EINVAL; + } + } + } else +#endif + { /* Use SDC */ + dev_dbg(dev, "Using SDC channel\n"); + + fbvar = fbi->var; + fbvar.xres = fbvar.xres_virtual = out_width; + fbvar.yres = out_height; + fbvar.yres_virtual = out_height * 2; + fbvar.bits_per_pixel = 16; + fb_set_var(fbi, &fbvar); + + fb_pos.x = vout->crop_current.left; + fb_pos.y = vout->crop_current.top; + if (fbi->fbops->fb_ioctl) + fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, + (unsigned long)&fb_pos); + + vout->display_bufs[0] = fbi->fix.smem_start; + vout->display_bufs[1] = fbi->fix.smem_start + + (fbi->fix.line_length * fbi->var.yres); + vout->display_buf_size = vout->crop_current.width * + vout->crop_current.height * + fmt_to_bpp(SDC_FG_FB_FORMAT) / 8; + + if (vout->cur_disp_output == 3) + vout->display_ch = MEM_SDC_FG; + else + vout->display_ch = MEM_SDC_BG; + + vout->post_proc_ch = MEM_PP_MEM; + } + + /* Init PP */ + if (use_direct_adc == false) { + if (vout->rotate >= IPU_ROTATE_90_RIGHT) { + out_width = vout->crop_current.height; + out_height = vout->crop_current.width; + } + memset(¶ms, 0, sizeof(params)); + params.mem_pp_mem.in_width = vout->v2f.fmt.pix.width; + params.mem_pp_mem.in_height = vout->v2f.fmt.pix.height; + params.mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat; + params.mem_pp_mem.out_width = out_width; + params.mem_pp_mem.out_height = out_height; + params.mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT; + if (ipu_init_channel(vout->post_proc_ch, ¶ms) != 0) { + dev_err(dev, "Error initializing PP channel\n"); + return -EINVAL; + } + + if (ipu_init_channel_buffer(vout->post_proc_ch, + IPU_INPUT_BUFFER, + params.mem_pp_mem.in_pixel_fmt, + params.mem_pp_mem.in_width, + params.mem_pp_mem.in_height, + vout->v2f.fmt.pix.bytesperline / + bytes_per_pixel(params.mem_pp_mem. + in_pixel_fmt), + IPU_ROTATE_NONE, + vout->v4l2_bufs[pp_in_buf[0]].m. + offset, + vout->v4l2_bufs[pp_in_buf[1]].m. + offset, vout->offset.u_offset, + vout->offset.v_offset) != 0) { + dev_err(dev, "Error initializing PP input buffer\n"); + return -EINVAL; + } + + if (vout->rotate >= IPU_ROTATE_90_RIGHT) { + if (vout->rot_pp_bufs[0]) { + mxc_free_buffers(vout->rot_pp_bufs, + vout->rot_pp_bufs_vaddr, 2, + vout->display_buf_size); + } + if (mxc_allocate_buffers + (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2, + vout->display_buf_size) < 0) { + return -ENOBUFS; + } + + if (ipu_init_channel_buffer(vout->post_proc_ch, + IPU_OUTPUT_BUFFER, + params.mem_pp_mem. + out_pixel_fmt, out_width, + out_height, out_width, + IPU_ROTATE_NONE, + vout->rot_pp_bufs[0], + vout->rot_pp_bufs[1], 0, + 0) != 0) { + dev_err(dev, + "Error initializing PP output buffer\n"); + return -EINVAL; + } + + if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) { + dev_err(dev, + "Error initializing PP ROT channel\n"); + return -EINVAL; + } + + if (ipu_init_channel_buffer(MEM_ROT_PP_MEM, + IPU_INPUT_BUFFER, + params.mem_pp_mem. + out_pixel_fmt, out_width, + out_height, out_width, + vout->rotate, + vout->rot_pp_bufs[0], + vout->rot_pp_bufs[1], 0, + 0) != 0) { + dev_err(dev, + "Error initializing PP ROT input buffer\n"); + return -EINVAL; + } + + /* swap width and height */ + out_width = vout->crop_current.width; + out_height = vout->crop_current.height; + + if (ipu_init_channel_buffer(MEM_ROT_PP_MEM, + IPU_OUTPUT_BUFFER, + params.mem_pp_mem. + out_pixel_fmt, out_width, + out_height, out_width, + IPU_ROTATE_NONE, + vout->display_bufs[0], + vout->display_bufs[1], 0, + 0) != 0) { + dev_err(dev, + "Error initializing PP output buffer\n"); + return -EINVAL; + } + + if (ipu_link_channels(vout->post_proc_ch, + MEM_ROT_PP_MEM) < 0) { + return -EINVAL; + } + ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1); + + ipu_enable_channel(MEM_ROT_PP_MEM); + + display_input_ch = MEM_ROT_PP_MEM; + } else { + if (ipu_init_channel_buffer(vout->post_proc_ch, + IPU_OUTPUT_BUFFER, + params.mem_pp_mem. + out_pixel_fmt, out_width, + out_height, out_width, + vout->rotate, + vout->display_bufs[0], + vout->display_bufs[1], 0, + 0) != 0) { + dev_err(dev, + "Error initializing PP output buffer\n"); + return -EINVAL; + } + } + if (ipu_link_channels(display_input_ch, vout->display_ch) < 0) { + dev_err(dev, "Error linking ipu channels\n"); + return -EINVAL; + } + } + + vout->state = STATE_STREAM_PAUSED; + + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1); + + if (use_direct_adc == false) { + ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1); + + ipu_enable_channel(vout->post_proc_ch); + if ((vout->display_ch == MEM_SDC_FG) || + (vout->display_ch == MEM_SDC_BG)) { + acquire_console_sem(); + fb_blank(fbi, FB_BLANK_UNBLANK); + release_console_sem(); + } else { + ipu_enable_channel(vout->display_ch); + } + } else { + ipu_enable_channel(vout->post_proc_ch); + } + + vout->start_jiffies = jiffies; + dev_dbg(dev, + "streamon: start time = %lu jiffies\n", vout->start_jiffies); + + return 0; +} + +/*! + * Shut down the voutera + * + * @param vout structure vout_data * + * + * @return status 0 Success + */ +static int mxc_v4l2out_streamoff(vout_data * vout) +{ + struct fb_info *fbi = + registered_fb[vout->output_fb_num[vout->cur_disp_output]]; + int i, retval = 0; + unsigned long lockflag = 0; + + if (!vout) + return -EINVAL; + + if (vout->state == STATE_STREAM_OFF) { + return 0; + } + + spin_lock_irqsave(&g_lock, lockflag); + + del_timer(&vout->output_timer); + + if (vout->state == STATE_STREAM_ON) { + vout->state = STATE_STREAM_STOPPING; + } + + ipu_disable_irq(IPU_IRQ_PP_IN_EOF); + + spin_unlock_irqrestore(&g_lock, lockflag); + + if (vout->post_proc_ch == MEM_PP_MEM) { /* SDC or ADC with Rotation */ + if (vout->rotate >= IPU_ROTATE_90_RIGHT) { + ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM); + ipu_unlink_channels(MEM_ROT_PP_MEM, vout->display_ch); + ipu_disable_channel(MEM_ROT_PP_MEM, true); + } else { + ipu_unlink_channels(MEM_PP_MEM, vout->display_ch); + } + ipu_disable_channel(MEM_PP_MEM, true); + if ((vout->display_ch != MEM_SDC_FG) && + (vout->display_ch != MEM_SDC_BG)) { + ipu_disable_channel(vout->display_ch, true); + ipu_uninit_channel(vout->display_ch); + } else { + fbi->var.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbi->var); + } + + ipu_uninit_channel(MEM_PP_MEM); + if (vout->rotate >= IPU_ROTATE_90_RIGHT) + ipu_uninit_channel(MEM_ROT_PP_MEM); + } else { /* ADC Direct */ + ipu_disable_channel(MEM_PP_ADC, true); + ipu_uninit_channel(MEM_PP_ADC); + } + vout->ready_q.head = vout->ready_q.tail = 0; + vout->done_q.head = vout->done_q.tail = 0; + for (i = 0; i < vout->buffer_cnt; i++) { + vout->v4l2_bufs[i].flags = 0; + vout->v4l2_bufs[i].timestamp.tv_sec = 0; + vout->v4l2_bufs[i].timestamp.tv_usec = 0; + } + + vout->state = STATE_STREAM_OFF; + + if (vout->display_bufs[0] != 0) { + mxc_free_buffers(vout->display_bufs, + vout->display_bufs_vaddr, 2, + vout->display_buf_size); + } +#ifdef CONFIG_FB_MXC_ASYNC_PANEL + if (vout->cur_disp_output < DISP3) { + mxcfb_set_refresh_mode(registered_fb + [vout-> + output_fb_num[vout->cur_disp_output]], + MXCFB_REFRESH_PARTIAL, 0); + } +#endif + + return retval; +} + +/* + * Valid whether the palette is supported + * + * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return 1 if supported, 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + return ((palette == V4L2_PIX_FMT_RGB565) || + (palette == V4L2_PIX_FMT_BGR24) || + (palette == V4L2_PIX_FMT_RGB24) || + (palette == V4L2_PIX_FMT_BGR32) || + (palette == V4L2_PIX_FMT_RGB32) || + (palette == V4L2_PIX_FMT_YUV422P) || + (palette == V4L2_PIX_FMT_YUV420)); +} + +/* + * V4L2 - Handles VIDIOC_G_FMT Ioctl + * + * @param vout structure vout_data * + * + * @param v4l2_format structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2out_g_fmt(vout_data * vout, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + return -EINVAL; + } + *f = vout->v2f; + return 0; +} + +/* + * V4L2 - Handles VIDIOC_S_FMT Ioctl + * + * @param vout structure vout_data * + * + * @param v4l2_format structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f) +{ + int retval = 0; + u32 size = 0; + u32 bytesperline; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + goto err0; + } + if (!valid_mode(f->fmt.pix.pixelformat)) { + dev_err(vout->video_dev->dev, "pixel format not supported\n"); + retval = -EINVAL; + goto err0; + } + + bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) / + 8; + if (f->fmt.pix.bytesperline < bytesperline) { + f->fmt.pix.bytesperline = bytesperline; + } else { + bytesperline = f->fmt.pix.bytesperline; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV422P: + /* byteperline for YUV planar formats is for + Y plane only */ + size = bytesperline * f->fmt.pix.height * 2; + break; + case V4L2_PIX_FMT_YUV420: + size = (bytesperline * f->fmt.pix.height * 3) / 2; + break; + default: + size = bytesperline * f->fmt.pix.height; + break; + } + + /* Return the actual size of the image to the app */ + if (f->fmt.pix.sizeimage < size) { + f->fmt.pix.sizeimage = size; + } else { + size = f->fmt.pix.sizeimage; + } + + vout->v2f.fmt.pix = f->fmt.pix; + if (vout->v2f.fmt.pix.priv != 0) { + if (copy_from_user(&vout->offset, + (void *)vout->v2f.fmt.pix.priv, + sizeof(vout->offset))) { + retval = -EFAULT; + goto err0; + } + } + + retval = 0; + err0: + return retval; +} + +/* + * V4L2 - Handles VIDIOC_G_CTRL Ioctl + * + * @param vout structure vout_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_get_v42lout_control(vout_data * vout, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0; + case V4L2_CID_VFLIP: + return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0; + case (V4L2_CID_PRIVATE_BASE + 1): + return vout->rotate; + default: + return -EINVAL; + } +} + +/* + * V4L2 - Handles VIDIOC_S_CTRL Ioctl + * + * @param vout structure vout_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c) +{ + switch (c->id) { + case V4L2_CID_HFLIP: + vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP : + IPU_ROTATE_NONE; + break; + case V4L2_CID_VFLIP: + vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP : + IPU_ROTATE_NONE; + break; + case V4L2_CID_MXC_ROT: + vout->rotate = c->value; + break; + default: + return -EINVAL; + } + return 0; +} + +/*! + * V4L2 interface - open function + * + * @param inode structure inode * + * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l2out_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + vout_data *vout = video_get_drvdata(dev); + int err; + + dq_intr_cnt = 0; + dq_timeout_cnt = 0; + if (!vout) { + return -ENODEV; + } + + down(&vout->busy_lock); + + err = -EINTR; + if (signal_pending(current)) + goto oops; + + if (vout->open_count++ == 0) { + ipu_request_irq(IPU_IRQ_PP_IN_EOF, + mxc_v4l2out_pp_in_irq_handler, + 0, dev->name, vout); + + init_waitqueue_head(&vout->v4l_bufq); + + init_timer(&vout->output_timer); + vout->output_timer.function = mxc_v4l2out_timer_handler; + vout->output_timer.data = (unsigned long)vout; + + vout->state = STATE_STREAM_OFF; + g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0; + + } + + file->private_data = dev; + + up(&vout->busy_lock); + + return 0; + + oops: + up(&vout->busy_lock); + return err; +} + +/*! + * V4L2 interface - close function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @return 0 success + */ +static int mxc_v4l2out_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + vout_data *vout = video_get_drvdata(dev); + + if (--vout->open_count == 0) { + if (vout->state != STATE_STREAM_OFF) + mxc_v4l2out_streamoff(vout); + + ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout); + + file->private_data = NULL; + + mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr, + vout->buffer_cnt, vout->queue_buf_size); + vout->buffer_cnt = 0; + mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2, + vout->display_buf_size); + + /* capture off */ + wake_up_interruptible(&vout->v4l_bufq); + } + + return 0; +} + +/*! + * V4L2 interface - ioctl function + * + * @param inode struct inode * + * + * @param file struct file * + * + * @param ioctlnr unsigned int + * + * @param arg void * + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static int +mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *vdev = file->private_data; + vout_data *vout = video_get_drvdata(vdev); + int retval = 0; + int i = 0; + + if (!vout) + return -EBADF; + + /* make this _really_ smp-safe */ + if (down_interruptible(&vout->busy_lock)) + return -EBUSY; + + switch (ioctlnr) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + strcpy(cap->driver, "mxc_v4l2_output"); + cap->version = 0; + cap->capabilities = + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->card[0] = '\0'; + cap->bus_info[0] = '\0'; + retval = 0; + break; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *gf = arg; + retval = mxc_v4l2out_g_fmt(vout, gf); + break; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *sf = arg; + if (vout->state != STATE_STREAM_OFF) { + retval = -EBUSY; + break; + } + retval = mxc_v4l2out_s_fmt(vout, sf); + break; + } + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *req = arg; + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || + (req->memory != V4L2_MEMORY_MMAP)) { + dev_dbg(vdev->dev, + "VIDIOC_REQBUFS: incorrect buffer type\n"); + retval = -EINVAL; + break; + } + + if (req->count == 0) + mxc_v4l2out_streamoff(vout); + + if (vout->state == STATE_STREAM_OFF) { + if (vout->queue_buf_paddr[0] != 0) { + mxc_free_buffers(vout->queue_buf_paddr, + vout->queue_buf_vaddr, + vout->buffer_cnt, + vout->queue_buf_size); + dev_dbg(vdev->dev, + "VIDIOC_REQBUFS: freed buffers\n"); + } + vout->buffer_cnt = 0; + } else { + dev_dbg(vdev->dev, + "VIDIOC_REQBUFS: Buffer is in use\n"); + retval = -EBUSY; + break; + } + + if (req->count == 0) + break; + + if (req->count < MIN_FRAME_NUM) { + req->count = MIN_FRAME_NUM; + } else if (req->count > MAX_FRAME_NUM) { + req->count = MAX_FRAME_NUM; + } + vout->buffer_cnt = req->count; + vout->queue_buf_size = + PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); + + retval = mxc_allocate_buffers(vout->queue_buf_paddr, + vout->queue_buf_vaddr, + vout->buffer_cnt, + vout->queue_buf_size); + if (retval < 0) + break; + + /* Init buffer queues */ + vout->done_q.head = 0; + vout->done_q.tail = 0; + vout->ready_q.head = 0; + vout->ready_q.tail = 0; + + for (i = 0; i < vout->buffer_cnt; i++) { + memset(&(vout->v4l2_bufs[i]), 0, + sizeof(vout->v4l2_bufs[i])); + vout->v4l2_bufs[i].flags = 0; + vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP; + vout->v4l2_bufs[i].index = i; + vout->v4l2_bufs[i].type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + vout->v4l2_bufs[i].length = + PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); + vout->v4l2_bufs[i].m.offset = + (unsigned long)vout->queue_buf_paddr[i]; + vout->v4l2_bufs[i].timestamp.tv_sec = 0; + vout->v4l2_bufs[i].timestamp.tv_usec = 0; + } + break; + } + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + u32 type = buf->type; + int index = buf->index; + + if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || + (index >= vout->buffer_cnt)) { + dev_dbg(vdev->dev, + "VIDIOC_QUERYBUFS: incorrect buffer type\n"); + retval = -EINVAL; + break; + } + down(&vout->param_lock); + memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf)); + up(&vout->param_lock); + break; + } + case VIDIOC_QBUF: + { + struct v4l2_buffer *buf = arg; + int index = buf->index; + unsigned long lock_flags; + + if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || + (index >= vout->buffer_cnt)) { + retval = -EINVAL; + break; + } + + dev_dbg(vdev->dev, "VIDIOC_QBUF: %d\n", buf->index); + + /* mmapped buffers are L1 WB cached, + * so we need to clean them */ + if (buf->flags & V4L2_BUF_FLAG_MAPPED) { + flush_cache_all(); + } + + spin_lock_irqsave(&g_lock, lock_flags); + + memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf)); + vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED; + + g_buf_q_cnt++; + queue_buf(&vout->ready_q, index); + if (vout->state == STATE_STREAM_PAUSED) { + unsigned long timeout; + + index = peek_next_buf(&vout->ready_q); + + /* if timestamp is 0, then default to 30fps */ + if ((vout->v4l2_bufs[index].timestamp.tv_sec == + 0) + && (vout->v4l2_bufs[index].timestamp. + tv_usec == 0)) + timeout = + vout->start_jiffies + + vout->frame_count * HZ / 30; + else + timeout = + get_jiffies(&vout->v4l2_bufs[index]. + timestamp); + + if (jiffies >= timeout) { + dev_dbg(vout->video_dev->dev, + "warning: timer timeout already expired.\n"); + } + vout->output_timer.expires = timeout; + dev_dbg(vdev->dev, + "QBUF: frame #%u timeout @ %lu jiffies, current = %lu\n", + vout->frame_count, timeout, jiffies); + add_timer(&vout->output_timer); + vout->state = STATE_STREAM_ON; + } + + spin_unlock_irqrestore(&g_lock, lock_flags); + break; + } + case VIDIOC_DQBUF: + { + struct v4l2_buffer *buf = arg; + int idx; + + if ((queue_size(&vout->done_q) == 0) && + (file->f_flags & O_NONBLOCK)) { + retval = -EAGAIN; + break; + } + + if (!wait_event_interruptible_timeout(vout->v4l_bufq, + queue_size(&vout-> + done_q) + != 0, 10 * HZ)) { + if(dq_timeout_cnt == 0){ + dev_dbg(vdev->dev, "VIDIOC_DQBUF: timeout\n"); + } + dq_timeout_cnt++; + retval = -ETIME; + break; + } else if (signal_pending(current)) { + if (dq_intr_cnt == 0) { + dev_dbg(vdev->dev, + "VIDIOC_DQBUF: interrupt received\n"); + } + dq_intr_cnt++; + retval = -ERESTARTSYS; + break; + } + idx = dequeue_buf(&vout->done_q); + if (idx == -1) { /* No frame free */ + dev_dbg(vdev->dev, + "VIDIOC_DQBUF: no free buffers, returning\n"); + retval = -EAGAIN; + break; + } + if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) == + 0) + dev_dbg(vdev->dev, + "VIDIOC_DQBUF: buffer in done q, but not " + "flagged as done\n"); + + vout->v4l2_bufs[idx].flags = 0; + memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf)); + dev_dbg(vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index); + break; + } + case VIDIOC_STREAMON: + { + retval = mxc_v4l2out_streamon(vout); + break; + } + case VIDIOC_STREAMOFF: + { + retval = mxc_v4l2out_streamoff(vout); + break; + } + case VIDIOC_G_CTRL: + { + retval = mxc_get_v42lout_control(vout, arg); + break; + } + case VIDIOC_S_CTRL: + { + retval = mxc_set_v42lout_control(vout, arg); + break; + } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = arg; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + cap->bounds = vout->crop_bounds[vout->cur_disp_output]; + cap->defrect = vout->crop_bounds[vout->cur_disp_output]; + retval = 0; + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + crop->c = vout->crop_current; + break; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = + &(vout->crop_bounds[vout->cur_disp_output]); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + retval = -EINVAL; + break; + } + if (crop->c.height < 0) { + retval = -EINVAL; + break; + } + if (crop->c.width < 0) { + retval = -EINVAL; + break; + } + + /* only full screen supported for SDC BG */ + if (vout->cur_disp_output == 4) { + crop->c = vout->crop_current; + break; + } + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top >= b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = + b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.left = b->left; + if (crop->c.left >= b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + /* stride line limitation */ + crop->c.height -= crop->c.height % 8; + crop->c.width -= crop->c.width % 8; + + vout->crop_current = crop->c; + break; + } + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *output = arg; + + if ((output->index >= 5) || + (vout->output_enabled[output->index] == false)) { + retval = -EINVAL; + break; + } + + if (output->index < 3) { + *output = mxc_outputs[MXC_V4L2_OUT_2_ADC]; + output->name[4] = '0' + output->index; + } else { + *output = mxc_outputs[MXC_V4L2_OUT_2_SDC]; + } + break; + } + case VIDIOC_G_OUTPUT: + { + int *p_output_num = arg; + + *p_output_num = vout->cur_disp_output; + break; + } + case VIDIOC_S_OUTPUT: + { + int *p_output_num = arg; + + if ((*p_output_num >= 5) || + (vout->output_enabled[*p_output_num] == false)) { + retval = -EINVAL; + break; + } + + if (vout->state != STATE_STREAM_OFF) { + retval = -EBUSY; + break; + } + + vout->cur_disp_output = *p_output_num; + vout->crop_current = + vout->crop_bounds[vout->cur_disp_output]; + break; + } + case VIDIOC_ENUM_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_PARM: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + default: + retval = -EINVAL; + break; + } + + up(&vout->busy_lock); + return retval; +} + +/* + * V4L2 interface - ioctl function + * + * @return None + */ +static int +mxc_v4l2out_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl); +} + +/*! + * V4L2 interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, + * ENOBUFS remap_page error + */ +static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + unsigned long size = vma->vm_end - vma->vm_start; + int res = 0; + int i; + vout_data *vout = video_get_drvdata(vdev); + + dev_dbg(vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* make this _really_ smp-safe */ + if (down_interruptible(&vout->busy_lock)) + return -EINTR; + + for (i = 0; i < vout->buffer_cnt; i++) { + if ((vout->v4l2_bufs[i].m.offset == + (vma->vm_pgoff << PAGE_SHIFT)) && + (vout->v4l2_bufs[i].length >= size)) { + vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED; + break; + } + } + if (i == vout->buffer_cnt) { + res = -ENOBUFS; + goto mxc_mmap_exit; + } + + /* make buffers inner write-back, outer write-thru cacheable */ + vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + vma->vm_pgoff, size, vma->vm_page_prot)) { + dev_dbg(vdev->dev, "mmap remap_pfn_range failed\n"); + res = -ENOBUFS; + goto mxc_mmap_exit; + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + + mxc_mmap_exit: + up(&vout->busy_lock); + return res; +} + +/*! + * V4L2 interface - poll function + * + * @param file structure file * + * + * @param wait structure poll_table * + * + * @return status POLLIN | POLLRDNORM + */ +static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait) +{ + struct video_device *dev = video_devdata(file); + vout_data *vout = video_get_drvdata(dev); + + wait_queue_head_t *queue = NULL; + int res = POLLIN | POLLRDNORM; + + if (down_interruptible(&vout->busy_lock)) + return -EINTR; + + queue = &vout->v4l_bufq; + poll_wait(file, queue, wait); + + up(&vout->busy_lock); + return res; +} + +static struct +file_operations mxc_v4l2out_fops = { + .owner = THIS_MODULE, + .open = mxc_v4l2out_open, + .release = mxc_v4l2out_close, + .ioctl = mxc_v4l2out_ioctl, + .mmap = mxc_v4l2out_mmap, + .poll = mxc_v4l2out_poll, +}; + +static struct video_device mxc_v4l2out_template = { + .owner = THIS_MODULE, + .name = "MXC Video Output", + .type = 0, + .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, + .hardware = 0, + .fops = &mxc_v4l2out_fops, + .release = video_device_release, +}; + +/*! + * Probe routine for the framebuffer driver. It is called during the + * driver binding process. The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, IPU initialization. + * + * @return Appropriate error code to the kernel common code + */ +static int mxc_v4l2out_probe(struct platform_device *pdev) +{ + int i; + vout_data *vout; + + /* + * Allocate sufficient memory for the fb structure + */ + g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL); + + if (!vout) + return 0; + + memset(vout, 0, sizeof(vout_data)); + + vout->video_dev = video_device_alloc(); + if (vout->video_dev == NULL) + return -1; + vout->video_dev->dev = &pdev->dev; + vout->video_dev->minor = -1; + + *(vout->video_dev) = mxc_v4l2out_template; + + /* register v4l device */ + if (video_register_device(vout->video_dev, + VFL_TYPE_GRABBER, video_nr) == -1) { + dev_dbg(&pdev->dev, "video_register_device failed\n"); + return 0; + } + dev_info(&pdev->dev, "Registered device video%d\n", + vout->video_dev->minor & 0x1f); + vout->video_dev->dev = &pdev->dev; + + video_set_drvdata(vout->video_dev, vout); + + init_MUTEX(&vout->param_lock); + init_MUTEX(&vout->busy_lock); + + /* setup outputs and cropping */ + vout->cur_disp_output = -1; + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (strncmp(idstr, "DISP", 4) == 0) { + int disp_num = idstr[4] - '0'; + if ((disp_num == 3) && + (strncmp(idstr, "DISP3 BG", 8) == 0)) { + disp_num = 4; + } + vout->crop_bounds[disp_num].left = 0; + vout->crop_bounds[disp_num].top = 0; + vout->crop_bounds[disp_num].width = + registered_fb[i]->var.xres; + vout->crop_bounds[disp_num].height = + registered_fb[i]->var.yres; + vout->output_enabled[disp_num] = true; + vout->output_fb_num[disp_num] = i; + if (vout->cur_disp_output == -1) { + vout->cur_disp_output = disp_num; + } + } + + } + vout->crop_current = vout->crop_bounds[vout->cur_disp_output]; + + platform_set_drvdata(pdev, vout); + + return 0; +} + +static int mxc_v4l2out_remove(struct platform_device *pdev) +{ + vout_data *vout = platform_get_drvdata(pdev); + + if (vout->video_dev) { + if (-1 != vout->video_dev->minor) + video_unregister_device(vout->video_dev); + else + video_device_release(vout->video_dev); + vout->video_dev = NULL; + } + + platform_set_drvdata(pdev, NULL); + + kfree(vout); + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_v4l2out_driver = { + .driver = { + .name = "MXC Video Output", + }, + .probe = mxc_v4l2out_probe, + .remove = mxc_v4l2out_remove, +}; + +static struct platform_device mxc_v4l2out_device = { + .name = "MXC Video Output", + .id = 0, +}; + +/*! + * mxc v4l2 init function + * + */ +static int mxc_v4l2out_init(void) +{ + u8 err = 0; + + err = platform_driver_register(&mxc_v4l2out_driver); + if (err == 0) { + platform_device_register(&mxc_v4l2out_device); + } + return err; +} + +/*! + * mxc v4l2 cleanup function + * + */ +static void mxc_v4l2out_clean(void) +{ + video_unregister_device(g_vout->video_dev); + + platform_driver_unregister(&mxc_v4l2out_driver); + platform_device_unregister(&mxc_v4l2out_device); + kfree(g_vout); + g_vout = NULL; +} + +module_init(mxc_v4l2out_init); +module_exit(mxc_v4l2out_clean); + +module_param(video_nr, int, 0444); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2-driver for MXC video output"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/media/video/mxc/output/mxc_v4l2_output.h linux-2.6.28-karo/drivers/media/video/mxc/output/mxc_v4l2_output.h --- linux-2.6.28/drivers/media/video/mxc/output/mxc_v4l2_output.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/media/video/mxc/output/mxc_v4l2_output.h 2009-03-11 13:47:25.000000000 +0100 @@ -0,0 +1,169 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup MXC_V4L2_OUTPUT MXC V4L2 Video Output Driver + */ +/*! + * @file mxc_v4l2_output.h + * + * @brief MXC V4L2 Video Output Driver Header file + * + * Video4Linux2 Output Device using MXC IPU Post-processing functionality. + * + * @ingroup MXC_V4L2_OUTPUT + */ +#ifndef __MXC_V4L2_OUTPUT_H__ +#define __MXC_V4L2_OUTPUT_H__ + +#include +#include +#include + +#ifdef __KERNEL__ + +#include +#include + +#define MIN_FRAME_NUM 2 +#define MAX_FRAME_NUM 30 + +#define MXC_V4L2_OUT_NUM_OUTPUTS 5 +#define MXC_V4L2_OUT_2_SDC 0 +#define MXC_V4L2_OUT_2_ADC 1 + +struct dma_buf_desc { + dma_addr_t dma_addr; + void *cpu_addr; + size_t size; +}; + +typedef enum { + BUF_IDLE, + BUF_DONE, + BUF_CANCELED, + BUF_ERROR, + BUF_BUSY, +} v4l_buf_state_t; + +typedef struct v4l_queue { + struct v4l2_buffer buf; + struct list_head head; + v4l_buf_state_t state; + struct dma_buf_desc dma_desc; +} v4l_queue_t; + +/*! + * States for the video stream + */ +typedef enum { + STATE_STREAM_OFF, + STATE_STREAM_ON, + STATE_STREAM_PAUSED, + STATE_STREAM_STOPPING, +} v4lout_state; + +/*! + * States for tearing protection + */ +typedef enum { + TEARING_PROTECTION_INACTIVE, + TEARING_PROTECTION_ACTIVE, + TEARING_PROTECTION_UNSUPPORTED +} v4l_tear_protect; + +/*! + * common v4l2 driver structure. + */ +typedef struct _vout_data { + struct video_device *video_dev; + /*! + * semaphore guard against SMP multithreading + */ +#if 1 + struct semaphore user_lock; +#else + struct mutex user_lock; +#endif + spinlock_t irq_lock; + + struct notifier_block fb_event_notifier; + + /*! + * number of process that have device open + */ + int open_count; + + v4l_tear_protect tear_protection; + + /*! + * params lock for this camera + */ + struct semaphore param_lock; + + struct timer_list output_timer; + unsigned long start_jiffies; + u32 frame_count; + struct list_head ready_q; + struct list_head done_q; + struct list_head free_q; + struct v4l_queue *active; + + s8 next_rdy_ipu_buf; + s8 next_done_ipu_buf; + s8 ipu_buf[2]; + v4lout_state state; + int busy; + + int cur_disp_output; + int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS]; + int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS]; + struct v4l2_framebuffer v4l2_fb; +#ifdef CONFIG_VIDEO_MXC_IPU_OUTPUT + ipu_channel_t display_ch; + ipu_channel_t post_proc_ch; +#endif + int buffer_cnt; + struct list_head display_q; + u32 display_buf_size; + dma_addr_t display_bufs[2]; + + /*! + * Poll wait queue + */ + wait_queue_head_t v4l_bufq; + + /*! + * v4l2 format + */ + struct v4l2_format v2f; + struct v4l2_mxc_offset offset; + struct v4l2_rect crop_rect; + enum ipu_rotate_mode rotate; + + /* crop */ + struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS]; + struct v4l2_rect crop_current; +#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC + int output_fb; + int fb_enabled; + int pp_ready; +#endif +#if 1 + int queued; + struct timeval frame_start; +#endif +} vout_data; + +#endif +#endif /* __MXC_V4L2_OUTPUT_H__ */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mmc/host/Kconfig linux-2.6.28-karo/drivers/mmc/host/Kconfig --- linux-2.6.28/drivers/mmc/host/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mmc/host/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -135,6 +135,16 @@ config MMC_IMX If unsure, say N. +config MMC_MXC + tristate "Freescale i.MX2/3 Multimedia Card Interface support" + depends on ARCH_MXC + help + This selects the Freescale i.MX2/3 Multimedia card Interface. + If you have a i.MX platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + config MMC_TIFM_SD tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" depends on EXPERIMENTAL && PCI diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mmc/host/Makefile linux-2.6.28-karo/drivers/mmc/host/Makefile --- linux-2.6.28/drivers/mmc/host/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mmc/host/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -9,6 +9,7 @@ endif obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_IMX) += imxmmc.o +obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mmc/host/mxcmmc.c linux-2.6.28-karo/drivers/mmc/host/mxcmmc.c --- linux-2.6.28/drivers/mmc/host/mxcmmc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/mmc/host/mxcmmc.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,893 @@ +/* + * linux/drivers/mmc/host/mxcmmc.c - Freescale i.MX MMCI driver + * + * This is a driver for the SDHC controller found in Freescale MX2/MX3 + * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c). + * Unlike the hardware found on MX1, this hardware just works and does + * not need all the quirks found in imxmmc.c, hence the seperate driver. + * + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * derived from pxamci.c by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_MX2 +#include +#define HAS_DMA +#endif + +#define DRIVER_NAME "imx-mmc" + +#define MMC_REG_STR_STP_CLK 0x00 +#define MMC_REG_STATUS 0x04 +#define MMC_REG_CLK_RATE 0x08 +#define MMC_REG_CMD_DAT_CONT 0x0C +#define MMC_REG_RES_TO 0x10 +#define MMC_REG_READ_TO 0x14 +#define MMC_REG_BLK_LEN 0x18 +#define MMC_REG_NOB 0x1C +#define MMC_REG_REV_NO 0x20 +#define MMC_REG_INT_CNTR 0x24 +#define MMC_REG_CMD 0x28 +#define MMC_REG_ARG 0x2C +#define MMC_REG_RES_FIFO 0x34 +#define MMC_REG_BUFFER_ACCESS 0x38 + +#define STR_STP_CLK_RESET (1 << 3) +#define STR_STP_CLK_START_CLK (1 << 1) +#define STR_STP_CLK_STOP_CLK (1 << 0) + +#define STATUS_CARD_INSERTION (1 << 31) +#define STATUS_CARD_REMOVAL (1 << 30) +#define STATUS_YBUF_EMPTY (1 << 29) +#define STATUS_XBUF_EMPTY (1 << 28) +#define STATUS_YBUF_FULL (1 << 27) +#define STATUS_XBUF_FULL (1 << 26) +#define STATUS_BUF_UND_RUN (1 << 25) +#define STATUS_BUF_OVFL (1 << 24) +#define STATUS_SDIO_INT_ACTIVE (1 << 14) +#define STATUS_END_CMD_RESP (1 << 13) +#define STATUS_WRITE_OP_DONE (1 << 12) +#define STATUS_DATA_TRANS_DONE (1 << 11) +#define STATUS_READ_OP_DONE (1 << 11) +#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10) +#define STATUS_CARD_BUS_CLK_RUN (1 << 8) +#define STATUS_BUF_READ_RDY (1 << 7) +#define STATUS_BUF_WRITE_RDY (1 << 6) +#define STATUS_RESP_CRC_ERR (1 << 5) +#define STATUS_CRC_READ_ERR (1 << 3) +#define STATUS_CRC_WRITE_ERR (1 << 2) +#define STATUS_TIME_OUT_RESP (1 << 1) +#define STATUS_TIME_OUT_READ (1 << 0) +#define STATUS_ERR_MASK 0x2f + +#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12) +#define CMD_DAT_CONT_STOP_READWAIT (1 << 11) +#define CMD_DAT_CONT_START_READWAIT (1 << 10) +#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8) +#define CMD_DAT_CONT_INIT (1 << 7) +#define CMD_DAT_CONT_WRITE (1 << 4) +#define CMD_DAT_CONT_DATA_ENABLE (1 << 3) +#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0) +#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0) +#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0) + +#define INT_SDIO_INT_WKP_EN (1 << 18) +#define INT_CARD_INSERTION_WKP_EN (1 << 17) +#define INT_CARD_REMOVAL_WKP_EN (1 << 16) +#define INT_CARD_INSERTION_EN (1 << 15) +#define INT_CARD_REMOVAL_EN (1 << 14) +#define INT_SDIO_IRQ_EN (1 << 13) +#define INT_DAT0_EN (1 << 12) +#define INT_BUF_READ_EN (1 << 4) +#define INT_BUF_WRITE_EN (1 << 3) +#define INT_END_CMD_RES_EN (1 << 2) +#define INT_WRITE_OP_DONE_EN (1 << 1) +#define INT_READ_OP_EN (1 << 0) + +struct mxcmci_host { + struct mmc_host *mmc; + struct resource *res; + void __iomem *base; + int irq; + int detect_irq; + int dma; + int do_dma; + unsigned int power_mode; + struct imxmmc_platform_data *pdata; + + struct mmc_request *req; + struct mmc_command *cmd; + struct mmc_data *data; + + unsigned int dma_nents; + unsigned int datasize; + unsigned int dma_dir; + + u16 rev_no; + unsigned int cmdat; + + struct clk *clk; + + int clock; + + struct work_struct datawork; +}; + +#ifdef HAS_DMA +static inline int mxcmci_use_dma(struct mxcmci_host *host) +{ + return host->do_dma; +} +#else +static inline int mxcmci_use_dma(struct mxcmci_host *host) +{ + return 0; +} +#endif + +static void mxcmci_softreset(struct mxcmci_host *host) +{ + int i; + + /* reset sequence */ + writew(STR_STP_CLK_RESET, host->base + MMC_REG_STR_STP_CLK); + writew(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK, + host->base + MMC_REG_STR_STP_CLK); + + for (i = 0; i < 8; i++) + writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK); + + writew(0xff, host->base + MMC_REG_RES_TO); +} + +static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = data->blksz; + unsigned int datasize = nob * blksz; + struct scatterlist *sg; + int i; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + host->data = data; + data->bytes_xfered = 0; + + writew(nob, host->base + MMC_REG_NOB); + writew(blksz, host->base + MMC_REG_BLK_LEN); + host->datasize = datasize; + +#ifdef HAS_DMA + for_each_sg(data->sg, sg, data->sg_len, i) { + if (sg->offset & 3 || sg->length & 3) { + host->do_dma = 0; + return; + } + } + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize, + host->res->start + MMC_REG_BUFFER_ACCESS, + DMA_MODE_READ); + } else { + host->dma_dir = DMA_TO_DEVICE; + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize, + host->res->start + MMC_REG_BUFFER_ACCESS, + DMA_MODE_WRITE); + } + + wmb(); + + imx_dma_enable(host->dma); +#endif /* HAS_DMA */ +} + +static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd, + unsigned int cmdat) +{ + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_136BIT; + break; + case MMC_RSP_R3: /* short */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT; + break; + case MMC_RSP_NONE: + break; + default: + dev_err(mmc_dev(host->mmc), "unhandled response type 0x%x\n", + mmc_resp_type(cmd)); + cmd->error = -EINVAL; + return -EINVAL; + } + + if (mxcmci_use_dma(host)) + writel(INT_READ_OP_EN | INT_WRITE_OP_DONE_EN | + INT_END_CMD_RES_EN, + host->base + MMC_REG_INT_CNTR); + else + writel(INT_END_CMD_RES_EN, host->base + MMC_REG_INT_CNTR); + + writew(cmd->opcode, host->base + MMC_REG_CMD); + writel(cmd->arg, host->base + MMC_REG_ARG); + writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT); + + return 0; +} + +static void mxcmci_finish_request(struct mxcmci_host *host, + struct mmc_request *req) +{ + writel(0, host->base + MMC_REG_INT_CNTR); + + host->req = NULL; + host->cmd = NULL; + host->data = NULL; + + mmc_request_done(host->mmc, req); +} + +static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + +#ifdef HAS_DMA + if (mxcmci_use_dma(host)) { + imx_dma_disable(host->dma); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents, + host->dma_dir); + } +#endif + + if (stat & STATUS_ERR_MASK) { + dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", + stat); + if (stat & STATUS_CRC_READ_ERR) { + data->error = -EILSEQ; + } else if (stat & STATUS_CRC_WRITE_ERR) { + u32 err_code = (stat >> 9) & 0x3; + if (err_code == 2) /* No CRC response */ + data->error = -ETIMEDOUT; + else + data->error = -EILSEQ; + } else if (stat & STATUS_TIME_OUT_READ) { + data->error = -ETIMEDOUT; + } else { + data->error = -EIO; + } + } else { + data->bytes_xfered = host->datasize; + } + + data_error = data->error; + + host->data = NULL; + + return data_error; +} + +static void imxmmc_read_response(struct mxcmci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 a, b, c; + + if (!cmd) + return; + + if (stat & STATUS_TIME_OUT_RESP) { + dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); + cmd->error = -ETIMEDOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); + cmd->error = -EILSEQ; + } + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + a = readw(host->base + MMC_REG_RES_FIFO); + b = readw(host->base + MMC_REG_RES_FIFO); + cmd->resp[i] = a << 16 | b; + } + } else { + a = readw(host->base + MMC_REG_RES_FIFO); + b = readw(host->base + MMC_REG_RES_FIFO); + c = readw(host->base + MMC_REG_RES_FIFO); + cmd->resp[0] = a << 24 | b << 8 | c >> 8; + } + } +} + +static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) +{ + u32 stat; + + do { + stat = readl(host->base + MMC_REG_STATUS); + if (stat & STATUS_ERR_MASK) + return stat; + } while (!(stat & mask)); + + return 0; +} + +static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) +{ + unsigned int stat; + u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS); + bytes -= 4; + } + + if (bytes) { + u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + tmp = readl(host->base + MMC_REG_BUFFER_ACCESS); + memcpy(b, &tmp, bytes); + } + + return 0; +} + +static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) +{ + unsigned int stat; + u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS); + bytes -= 4; + } + + if (bytes) { + u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + memcpy(&tmp, b, bytes); + writel(tmp, host->base + MMC_REG_BUFFER_ACCESS); + } + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + return 0; +} + +static int mxcmci_transfer_data(struct mxcmci_host *host) +{ + struct mmc_data *data = host->req->data; + struct scatterlist *sg; + unsigned int nob = data->blocks; + int stat, i; + + host->datasize = 0; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + host->data = data; + host->datasize = 0; + + if (data->flags & MMC_DATA_READ) { + for_each_sg(data->sg, sg, data->sg_len, i) { + stat = mxcmci_pull(host, sg_virt(sg), sg->length); + if (stat) + return stat; + host->datasize += sg->length; + } + } else { + for_each_sg(data->sg, sg, data->sg_len, i) { + stat = mxcmci_push(host, sg_virt(sg), sg->length); + if (stat) + return stat; + host->datasize += sg->length; + } + stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); + if (stat) + return stat; + } + return 0; +} + +static void mxcmci_datawork(struct work_struct *work) +{ + struct mxcmci_host *host = container_of(work, struct mxcmci_host, + datawork); + int datastat = mxcmci_transfer_data(host); + mxcmci_finish_data(host, datastat); + + if (host->req->stop) { + if (mxcmci_start_cmd(host, host->req->stop, 0)) { + mxcmci_finish_request(host, host->req); + return; + } + } else { + mxcmci_finish_request(host, host->req); + } +} + +#ifdef HAS_DMA +static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + + if (!data) + return; + + data_error = mxcmci_finish_data(host, stat); + + imxmmc_read_response(host, stat); + host->cmd = NULL; + + if (host->req->stop) { + if (mxcmci_start_cmd(host, host->req->stop, 0)) { + mxcmci_finish_request(host, host->req); + return; + } + } else { + mxcmci_finish_request(host, host->req); + } +} +#endif /* HAS_DMA */ + +static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) +{ + imxmmc_read_response(host, stat); + host->cmd = NULL; + + if (!host->data && host->req) { + mxcmci_finish_request(host, host->req); + return; + } + + /* For the DMA case the DMA engine handles the data transfer + * automatically. For non DMA we have to to it ourselves. + * Don't do it in interrupt context though. + */ + if (!mxcmci_use_dma(host) && host->data) + schedule_work(&host->datawork); + +} + +static irqreturn_t mxcmci_irq(int irq, void *devid) +{ + struct mxcmci_host *host = devid; + u32 stat; + + stat = readl(host->base + MMC_REG_STATUS); + writel(stat, host->base + MMC_REG_STATUS); + + dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); + + if (stat & STATUS_END_CMD_RESP) + mxcmci_cmd_done(host, stat); +#ifdef HAS_DMA + if (mxcmci_use_dma(host) && + (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) + mxcmci_data_done(host, stat); +#endif + return IRQ_HANDLED; +} + +static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mxcmci_host *host = mmc_priv(mmc); + unsigned int cmdat = host->cmdat; + + WARN_ON(host->req != NULL); + + host->req = req; + host->cmdat &= ~CMD_DAT_CONT_INIT; +#ifdef HAS_DMA + host->do_dma = 1; +#endif + if (req->data) { + mxcmci_setup_data(host, req->data); + + cmdat |= CMD_DAT_CONT_DATA_ENABLE; + + if (req->data->flags & MMC_DATA_WRITE) + cmdat |= CMD_DAT_CONT_WRITE; + } + + if (mxcmci_start_cmd(host, req->cmd, cmdat)) + mxcmci_finish_request(host, req); +} + +static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) +{ + unsigned int divider; + int prescaler = 0; + unsigned int clk_in = clk_get_rate(host->clk); + + while (prescaler <= 0x800) { + for (divider = 1; divider <= 0xF; divider++) { + int x; + + x = (clk_in / (divider + 1)); + + if (prescaler) + x /= (prescaler * 2); + + if (x <= clk_ios) + break; + } + if (divider < 0x10) + break; + + if (prescaler == 0) + prescaler = 1; + else + prescaler <<= 1; + } + + writew((prescaler << 4) | divider, host->base + MMC_REG_CLK_RATE); + + dev_dbg(mmc_dev(host->mmc), "scaler: %d divider: %d in: %d out: %d\n", + prescaler, divider, clk_in, clk_ios); +} + +static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mxcmci_host *host = mmc_priv(mmc); +#ifdef HAS_DMA + unsigned int blen; + /* + * use burstlen of 64 in 4 bit mode (--> reg value 0) + * use burstlen of 16 in 1 bit mode (--> reg value 16) + */ + if (ios->bus_width == MMC_BUS_WIDTH_4) + blen = 0; + else + blen = 16; + + imx_dma_config_burstlen(host->dma, blen); +#endif + if (ios->bus_width == MMC_BUS_WIDTH_4) + host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; + else + host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; + + if (host->power_mode != ios->power_mode) { + if (host->pdata->setpower) + host->pdata->setpower(mmc_dev(mmc), ios->vdd); + host->power_mode = ios->power_mode; + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMD_DAT_CONT_INIT; + } + + if (ios->clock) { + mxcmci_set_clk_rate(host, ios->clock); + writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK); + } else { + writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK); + } + + host->clock = ios->clock; +} + +static irqreturn_t mxcmci_detect_irq(int irq, void *data) +{ + struct mmc_host *mmc = data; + + dev_dbg(mmc_dev(mmc), "%s\n", __func__); + + mmc_detect_change(mmc, msecs_to_jiffies(250)); + return IRQ_HANDLED; +} + +static int mxcmci_get_ro(struct mmc_host *mmc) +{ + struct mxcmci_host *host = mmc_priv(mmc); + + if (host->pdata->get_ro) + return !!host->pdata->get_ro(mmc_dev(mmc)); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; +} + + +static const struct mmc_host_ops mxcmci_ops = { + .request = mxcmci_request, + .set_ios = mxcmci_set_ios, + .get_ro = mxcmci_get_ro, +}; + +static int mxcmci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct mxcmci_host *host = NULL; + struct resource *r; + int ret = 0, irq; + + printk(KERN_INFO "i.MX SDHC driver\n"); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENODEV; + + mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev); + if (!mmc) { + return -ENOMEM; + } + + r = request_mem_region(r->start, resource_size(r), pdev->name); + if (!r) { + ret = -EBUSY; + goto out_free; + } + + mmc->ops = &mxcmci_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA; + + /* MMC core transfer sizes tunable parameters */ + mmc->max_hw_segs = 64; + mmc->max_phys_segs = 64; + mmc->max_seg_size = 64 * 512; /* default PAGE_CACHE_SIZE */ + mmc->max_req_size = 64 * 512; /* default PAGE_CACHE_SIZE */ + mmc->max_blk_size = 2048; + mmc->max_blk_count = 65535; + + host = mmc_priv(mmc); + host->base = ioremap(r->start, resource_size(r)); + if (!host->base) { + ret = -ENOMEM; + goto out_release_mem; + } + + host->mmc = mmc; + host->pdata = pdev->dev.platform_data; + if (!host->pdata) { + ret = -ENODEV; + dev_err(&pdev->dev, "No platform data provided!\n"); + goto out_iounmap; + } + + host->res = r; + host->irq = irq; + + host->clk = clk_get(&pdev->dev, "sdhc_clk"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto out_iounmap; + } + clk_enable(host->clk); + + mxcmci_softreset(host); + + host->rev_no = readw(host->base + MMC_REG_REV_NO); + if (host->rev_no != 0x400) { + ret = -ENODEV; + dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", + host->rev_no); + goto out_clk_put; + } + + mmc->f_min = clk_get_rate(host->clk) >> 7; + mmc->f_max = clk_get_rate(host->clk) >> 1; + + /* recommended in data sheet */ + writew(0x2db4, host->base + MMC_REG_READ_TO); + + writel(0, host->base + MMC_REG_INT_CNTR); + +#ifdef HAS_DMA + host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW); + if (host->dma < 0) { + dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n"); + ret = -EBUSY; + goto out_clk_put; + } + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + ret = -ENODEV; + goto out_free_dma; + } + + ret = imx_dma_config_channel(host->dma, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, + r->start, 0); + if (ret) { + dev_err(mmc_dev(host->mmc), "failed to configure DMA channel\n"); + goto out_free_dma; + } +#endif + INIT_WORK(&host->datawork, mxcmci_datawork); + + ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host); + if (ret) + goto out_free_dma; + + platform_set_drvdata(pdev, mmc); + device_set_wakeup_enable(&pdev->dev, 1); + + if (host->pdata->init) { + ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq, + host->mmc); + if (ret) + goto out_free_irq; + } + + mmc_add_host(mmc); + + return 0; + +out_free_irq: + free_irq(host->irq, host); +out_free_dma: +#ifdef HAS_DMA + imx_dma_free(host->dma); +#endif +out_clk_put: + clk_disable(host->clk); + clk_put(host->clk); +out_iounmap: + iounmap(host->base); +out_release_mem: + release_mem_region(host->res->start, resource_size(host->res)); +out_free: + mmc_free_host(mmc); + return ret; +} + +static int mxcmci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mxcmci_host *host = mmc_priv(mmc); + + platform_set_drvdata(pdev, NULL); + + mmc_remove_host(mmc); + + if (host->pdata->exit) + host->pdata->exit(&pdev->dev, mmc); + + free_irq(host->irq, host); + iounmap(host->base); +#ifdef HAS_DMA + imx_dma_free(host->dma); +#endif + clk_disable(host->clk); + clk_put(host->clk); + + release_resource(host->res); + + mmc_free_host(mmc); + + return 0; +} + +#ifdef CONFIG_PM +static int mxcmci_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mxcmci_host *host = mmc_priv(mmc); + int ret = 0; + + if (host->pdata->suspend) + ret = host->pdata->suspend(&pdev->dev, state); + if (ret == 0) { + ret = mmc_suspend_host(mmc, state); + if (ret) + if (host->pdata->resume) + host->pdata->resume(&pdev->dev); + } + + return ret; +} + +static int mxcmci_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mxcmci_host *host = mmc_priv(mmc); + int ret; + + ret = mmc_resume_host(mmc); + if (ret == 0) + if (host->pdata->resume) + ret = host->pdata->resume(&pdev->dev); + + return ret; +} +#else +#define mxcmci_suspend NULL +#define mxcmci_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mxcmci_driver = { + .probe = mxcmci_probe, + .remove = mxcmci_remove, + .suspend = mxcmci_suspend, + .resume = mxcmci_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + } +}; + +static int __init mxcmci_init(void) +{ + return platform_driver_register(&mxcmci_driver); +} + +static void __exit mxcmci_exit(void) +{ + platform_driver_unregister(&mxcmci_driver); +} + +module_init(mxcmci_init); +module_exit(mxcmci_exit); + +MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-mmc"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/mtdblock.c linux-2.6.28-karo/drivers/mtd/mtdblock.c --- linux-2.6.28/drivers/mtd/mtdblock.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/mtdblock.c 2009-03-11 13:16:24.000000000 +0100 @@ -102,9 +102,9 @@ static int write_cached_data (struct mtd if (mtdblk->cache_state != STATE_DIRTY) return 0; - DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" " - "at 0x%lx, size 0x%x\n", mtd->name, - mtdblk->cache_offset, mtdblk->cache_size); + MTD_DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" " + "at 0x%lx, size 0x%x\n", mtd->name, + mtdblk->cache_offset, mtdblk->cache_size); ret = erase_write (mtd, mtdblk->cache_offset, mtdblk->cache_size, mtdblk->cache_data); @@ -131,8 +131,8 @@ static int do_cached_write (struct mtdbl size_t retlen; int ret; - DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", - mtd->name, pos, len); + MTD_DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", + mtd->name, pos, len); if (!sect_size) return mtd->write(mtd, pos, len, &retlen, buf); @@ -201,8 +201,8 @@ static int do_cached_read (struct mtdblk size_t retlen; int ret; - DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", - mtd->name, pos, len); + MTD_DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", + mtd->name, pos, len); if (!sect_size) return mtd->read(mtd, pos, len, &retlen, buf); @@ -268,7 +268,7 @@ static int mtdblock_open(struct mtd_blkt struct mtd_info *mtd = mbd->mtd; int dev = mbd->devnum; - DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); if (mtdblks[dev]) { mtdblks[dev]->count++; @@ -292,7 +292,7 @@ static int mtdblock_open(struct mtd_blkt mtdblks[dev] = mtdblk; - DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); return 0; } @@ -302,7 +302,7 @@ static int mtdblock_release(struct mtd_b int dev = mbd->devnum; struct mtdblk_dev *mtdblk = mtdblks[dev]; - DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); @@ -316,7 +316,7 @@ static int mtdblock_release(struct mtd_b vfree(mtdblk->cache_data); kfree(mtdblk); } - DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); return 0; } diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/mtdchar.c linux-2.6.28-karo/drivers/mtd/mtdchar.c --- linux-2.6.28/drivers/mtd/mtdchar.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/mtdchar.c 2009-03-11 13:16:24.000000000 +0100 @@ -90,7 +90,7 @@ static int mtd_open(struct inode *inode, struct mtd_info *mtd; struct mtd_file_info *mfi; - DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); if (devnum >= MAX_MTD_DEVICES) return -ENODEV; @@ -141,7 +141,7 @@ static int mtd_close(struct inode *inode struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; - DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); /* Only sync if opened RW */ if ((file->f_mode & FMODE_WRITE) && mtd->sync) @@ -169,7 +169,7 @@ static ssize_t mtd_read(struct file *fil int len; char *kbuf; - DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); if (*ppos + count > mtd->size) count = mtd->size - *ppos; @@ -262,7 +262,7 @@ static ssize_t mtd_write(struct file *fi int ret=0; int len; - DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); if (*ppos == mtd->size) return -ENOSPC; @@ -388,7 +388,7 @@ static int mtd_ioctl(struct inode *inode u_long size; struct mtd_info_user info; - DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/mtdcore.c linux-2.6.28-karo/drivers/mtd/mtdcore.c --- linux-2.6.28/drivers/mtd/mtdcore.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/mtdcore.c 2009-03-11 13:16:24.000000000 +0100 @@ -67,7 +67,7 @@ int add_mtd_device(struct mtd_info *mtd) mtd->name); } - DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); + MTD_DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); /* No need to get a refcount on the module containing the notifier, since we hold the mtd_table_mutex */ list_for_each_entry(not, &mtd_notifiers, list) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/mtdsuper.c linux-2.6.28-karo/drivers/mtd/mtdsuper.c --- linux-2.6.28/drivers/mtd/mtdsuper.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/mtdsuper.c 2009-03-11 13:16:24.000000000 +0100 @@ -23,13 +23,13 @@ static int get_sb_mtd_compare(struct sup struct mtd_info *mtd = _mtd; if (sb->s_mtd == mtd) { - DEBUG(2, "MTDSB: Match on device %d (\"%s\")\n", - mtd->index, mtd->name); + MTD_DEBUG(2, "MTDSB: Match on device %d (\"%s\")\n", + mtd->index, mtd->name); return 1; } - DEBUG(2, "MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", - sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); + MTD_DEBUG(2, "MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", + sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); return 0; } @@ -67,8 +67,8 @@ static int get_sb_mtd_aux(struct file_sy goto already_mounted; /* fresh new superblock */ - DEBUG(1, "MTDSB: New superblock for device %d (\"%s\")\n", - mtd->index, mtd->name); + MTD_DEBUG(1, "MTDSB: New superblock for device %d (\"%s\")\n", + mtd->index, mtd->name); sb->s_flags = flags; @@ -85,8 +85,8 @@ static int get_sb_mtd_aux(struct file_sy /* new mountpoint for an already mounted superblock */ already_mounted: - DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n", - mtd->index, mtd->name); + MTD_DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n", + mtd->index, mtd->name); ret = simple_set_mnt(mnt, sb); goto out_put; @@ -109,7 +109,7 @@ static int get_sb_mtd_nr(struct file_sys mtd = get_mtd_device(NULL, mtdnr); if (IS_ERR(mtd)) { - DEBUG(0, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr); + MTD_DEBUG(0, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr); return PTR_ERR(mtd); } @@ -134,7 +134,7 @@ int get_sb_mtd(struct file_system_type * if (!dev_name) return -EINVAL; - DEBUG(2, "MTDSB: dev_name \"%s\"\n", dev_name); + MTD_DEBUG(2, "MTDSB: dev_name \"%s\"\n", dev_name); /* the preferred way of mounting in future; especially when * CONFIG_BLOCK=n - we specify the underlying MTD device by number or @@ -145,8 +145,8 @@ int get_sb_mtd(struct file_system_type * struct mtd_info *mtd; /* mount by MTD device name */ - DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", - dev_name + 4); + MTD_DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", + dev_name + 4); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); @@ -172,8 +172,8 @@ int get_sb_mtd(struct file_system_type * mtdnr = simple_strtoul(dev_name + 3, &endptr, 0); if (!*endptr) { /* It was a valid number */ - DEBUG(1, "MTDSB: mtd%%d, mtdnr %d\n", - mtdnr); + MTD_DEBUG(1, "MTDSB: mtd%%d, mtdnr %d\n", + mtdnr); return get_sb_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super, mnt); @@ -188,10 +188,10 @@ int get_sb_mtd(struct file_system_type * bdev = lookup_bdev(dev_name); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); - DEBUG(1, "MTDSB: lookup_bdev() returned %d\n", ret); + MTD_DEBUG(1, "MTDSB: lookup_bdev() returned %d\n", ret); return ret; } - DEBUG(1, "MTDSB: lookup_bdev() returned 0\n"); + MTD_DEBUG(1, "MTDSB: lookup_bdev() returned 0\n"); ret = -EINVAL; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/nand/Kconfig linux-2.6.28-karo/drivers/mtd/nand/Kconfig --- linux-2.6.28/drivers/mtd/nand/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/nand/Kconfig 2009-03-12 15:04:48.000000000 +0100 @@ -358,6 +358,10 @@ config MTD_NAND_TMIO Support for NAND flash connected to a Toshiba Mobile IO Controller in some PDAs, including the Sharp SL6000x. +config MTD_NAND_MXC_FLASH_BBT + bool "Support a flash based bad block table" + depends on MTD_NAND_MXC + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_PARTITIONS diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/nand/mxc_nand.c linux-2.6.28-karo/drivers/mtd/nand/mxc_nand.c --- linux-2.6.28/drivers/mtd/nand/mxc_nand.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/nand/mxc_nand.c 2009-03-16 11:04:21.000000000 +0100 @@ -141,6 +141,31 @@ static struct nand_ecclayout nand_hw_ecc .oobfree = {{0, 6}, {12, 4}, } }; +#ifdef CONFIG_MTD_NAND_MXC_FLASH_BBT +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = (NAND_BBT_LASTBLOCK | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP), + .offs = 12, + .len = 4, + .veroffs = 11, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = (NAND_BBT_LASTBLOCK | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP), + .offs = 12, + .len = 4, + .veroffs = 11, + .maxblocks = 4, + .pattern = mirror_pattern, +}; +#endif + #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif @@ -193,8 +218,8 @@ static void wait_op_done(struct mxc_nand udelay(1); } if (max_retries <= 0) - DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", - __func__, param); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __func__, param); } } @@ -202,7 +227,7 @@ static void wait_op_done(struct mxc_nand * waits for completion. */ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) { - DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); writew(cmd, host->regs + NFC_FLASH_CMD); writew(NFC_CMD, host->regs + NFC_CONFIG2); @@ -216,7 +241,7 @@ static void send_cmd(struct mxc_nand_hos * a NAND command. */ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) { - DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast); writew(addr, host->regs + NFC_FLASH_ADDR); writew(NFC_ADDR, host->regs + NFC_CONFIG2); @@ -230,7 +255,7 @@ static void send_addr(struct mxc_nand_ho static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, int spare_only) { - DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); @@ -256,7 +281,7 @@ static void send_prog_page(struct mxc_na static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, int spare_only) { - DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); @@ -372,8 +397,8 @@ static int mxc_nand_correct_data(struct uint16_t ecc_status = readw(host->regs + NFC_ECC_STATUS_RESULT); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - DEBUG(MTD_DEBUG_LEVEL0, - "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); return -1; } @@ -427,8 +452,8 @@ static uint16_t mxc_nand_read_word(struc uint16_t col, rd_word, ret; uint16_t __iomem *p; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_word(col = %d)\n", host->col_addr); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", host->col_addr); col = host->col_addr; /* Adjust saved column address */ @@ -465,9 +490,9 @@ static void mxc_nand_write_buf(struct mt struct mxc_nand_host *host = nand_chip->priv; int n, col, i = 0; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, - len); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", + host->col_addr, len); col = host->col_addr; @@ -478,8 +503,8 @@ static void mxc_nand_write_buf(struct mt n = mtd->writesize + mtd->oobsize - col; n = min(len, n); - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); while (n) { void __iomem *p; @@ -490,8 +515,8 @@ static void mxc_nand_write_buf(struct mt p = host->regs + SPARE_AREA0 - mtd->writesize + (col & ~3); - DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, - __LINE__, p); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, + __LINE__, p); if (((col | (int)&buf[i]) & 3) || n < 16) { uint32_t data = 0; @@ -539,9 +564,9 @@ static void mxc_nand_write_buf(struct mt m = min(n, m) & ~3; - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: n = %d, m = %d, i = %d, col = %d\n", - __func__, __LINE__, n, m, i, col); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __func__, __LINE__, n, m, i, col); memcpy(p, &buf[i], m); col += m; @@ -563,8 +588,9 @@ static void mxc_nand_read_buf(struct mtd struct mxc_nand_host *host = nand_chip->priv; int n, col, i = 0; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", + host->col_addr, len); col = host->col_addr; @@ -649,8 +675,9 @@ static void mxc_nand_select_chip(struct #ifdef CONFIG_MTD_NAND_MXC_FORCE_CE if (chip > 0) { - DEBUG(MTD_DEBUG_LEVEL0, - "ERROR: Illegal chip select (chip = %d)\n", chip); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", + chip); return; } @@ -694,9 +721,9 @@ static void mxc_nand_command(struct mtd_ struct mxc_nand_host *host = nand_chip->priv; int useirq = true; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", - command, column, page_addr); + MTD_DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); /* Reset command state information */ host->status_request = false; @@ -831,6 +858,7 @@ static void mxc_nand_command(struct mtd_ break; case NAND_CMD_READID: + host->col_addr = 0; send_read_id(host); break; @@ -879,10 +907,17 @@ static int __init mxcnd_probe(struct pla this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; this->verify_buf = mxc_nand_verify_buf; +#ifdef CONFIG_MTD_NAND_MXC_FLASH_BBT + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + this->options |= NAND_USE_FLASH_BBT; +#endif host->clk = clk_get(&pdev->dev, "nfc_clk"); - if (IS_ERR(host->clk)) + if (IS_ERR(host->clk)) { + err = PTR_ERR(host->clk); goto eclk; + } clk_enable(host->clk); host->clk_act = 1; @@ -895,7 +930,7 @@ static int __init mxcnd_probe(struct pla host->regs = ioremap(res->start, res->end - res->start + 1); if (!host->regs) { - err = -EIO; + err = -ENOMEM; goto eres; } @@ -952,13 +987,17 @@ static int __init mxcnd_probe(struct pla this->ecc.layout = &nand_hw_eccoob_16; } - host->pagesize_2k = 0; - /* Scan to find existence of the device */ - if (nand_scan(mtd, 1)) { - DEBUG(MTD_DEBUG_LEVEL0, - "MXC_ND: Unable to find any NAND device.\n"); - err = -ENXIO; + err = nand_scan_ident(mtd, 1); + if (err) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND: Unable to find any NAND device.\n"); + goto escan; + } + /* this is required before completing the scan */ + host->pagesize_2k = mtd->writesize == 2048; + err = nand_scan_tail(mtd); + if (err) { goto escan; } @@ -1010,43 +1049,51 @@ static int __devexit mxcnd_remove(struct #ifdef CONFIG_PM static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) { - struct mtd_info *info = platform_get_drvdata(pdev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - if (info) - ret = info->suspend(info); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); + if (mtd) + ret = mtd->suspend(mtd); + if (host->clk_act) { /* Disable the NFC clock */ - clk_disable(nfc_clk); /* FIXME */ + clk_disable(host->clk); + } return ret; } static int mxcnd_resume(struct platform_device *pdev) { - struct mtd_info *info = platform_get_drvdata(pdev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - /* Enable the NFC clock */ - clk_enable(nfc_clk); /* FIXME */ + MTD_DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - if (info) - info->resume(info); + if (host->clk_act) { + /* Enable the NFC clock */ + clk_enable(host->clk); + } + if (mtd) + mtd->resume(mtd); return ret; } #else -# define mxcnd_suspend NULL -# define mxcnd_resume NULL +#define mxcnd_suspend NULL +#define mxcnd_resume NULL #endif /* CONFIG_PM */ static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, - }, + }, .remove = __exit_p(mxcnd_remove), .suspend = mxcnd_suspend, .resume = mxcnd_resume, @@ -1054,13 +1101,15 @@ static struct platform_driver mxcnd_driv static int __init mxc_nd_init(void) { + int ret; + /* Register the device driver structure. */ pr_info("MXC MTD nand Driver\n"); - if (platform_driver_probe(&mxcnd_driver, mxcnd_probe) != 0) { + ret = platform_driver_probe(&mxcnd_driver, mxcnd_probe); + if (ret != 0) { printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); - return -ENODEV; } - return 0; + return ret; } static void __exit mxc_nd_cleanup(void) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/nand/nand_base.c linux-2.6.28-karo/drivers/mtd/nand/nand_base.c --- linux-2.6.28/drivers/mtd/nand/nand_base.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/nand/nand_base.c 2009-03-11 13:16:24.000000000 +0100 @@ -1356,8 +1356,8 @@ static int nand_do_read_oob(struct mtd_i int len; uint8_t *buf = ops->oobbuf; - DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", - (unsigned long long)from, readlen); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", + (unsigned long long)from, readlen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1365,8 +1365,8 @@ static int nand_do_read_oob(struct mtd_i len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start read outside oob\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt to start read outside oob\n"); return -EINVAL; } @@ -1374,8 +1374,8 @@ static int nand_do_read_oob(struct mtd_i if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt read beyond end of device\n"); return -EINVAL; } @@ -1449,8 +1449,8 @@ static int nand_read_oob(struct mtd_info /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt read beyond end of device\n"); return -EINVAL; } @@ -1847,8 +1847,8 @@ static int nand_do_write_oob(struct mtd_ int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)ops->ooblen); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1857,14 +1857,14 @@ static int nand_do_write_oob(struct mtd_ /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_write_oob: Attempt to write past end of page\n"); return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start write outside oob\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt to start write outside oob\n"); return -EINVAL; } @@ -1873,8 +1873,8 @@ static int nand_do_write_oob(struct mtd_ ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt write beyond end of device\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt write beyond end of device\n"); return -EINVAL; } @@ -1929,8 +1929,8 @@ static int nand_write_oob(struct mtd_inf /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt read beyond end of device\n"); return -EINVAL; } @@ -2019,26 +2019,24 @@ int nand_erase_nand(struct mtd_info *mtd int rewrite_bbt[NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; - DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", + (unsigned int)instr->addr, (unsigned int)instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Length not block aligned\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Erase past end of device\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } @@ -2059,8 +2057,7 @@ int nand_erase_nand(struct mtd_info *mtd /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Device is write protected!!!\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2113,8 +2110,9 @@ int nand_erase_nand(struct mtd_info *mtd /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Failed erase, page 0x%08x\n", page); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_erase: Failed erase, page 0x%08x\n", + page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << chip->page_shift); goto erase_exit; @@ -2172,9 +2170,10 @@ int nand_erase_nand(struct mtd_info *mtd if (!rewrite_bbt[chipnr]) continue; /* update the BBT for chip */ - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], - chip->bbt_td->pages[chipnr]); + MTD_DEBUG(MTD_DEBUG_LEVEL0, + "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2192,7 +2191,7 @@ static void nand_sync(struct mtd_info *m { struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_SYNCING); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/nand/nand_bbt.c linux-2.6.28-karo/drivers/mtd/nand/nand_bbt.c --- linux-2.6.28/drivers/mtd/nand/nand_bbt.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/nand/nand_bbt.c 2009-03-11 13:16:24.000000000 +0100 @@ -1201,8 +1201,9 @@ int nand_isbad_bbt(struct mtd_info *mtd, block = (int)(offs >> (this->bbt_erase_shift - 1)); res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; - DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + MTD_DEBUG(MTD_DEBUG_LEVEL2, + "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mtd/nand/nand_ecc.c linux-2.6.28-karo/drivers/mtd/nand/nand_ecc.c --- linux-2.6.28/drivers/mtd/nand/nand_ecc.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/mtd/nand/nand_ecc.c 2009-03-11 13:16:24.000000000 +0100 @@ -492,7 +492,6 @@ int nand_correct_data(struct mtd_info *m if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) return 1; /* error in ecc data; no action needed */ - printk(KERN_ERR "uncorrectable error : "); return -1; } EXPORT_SYMBOL(nand_correct_data); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mxc/Kconfig linux-2.6.28-karo/drivers/mxc/Kconfig --- linux-2.6.28/drivers/mxc/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/mxc/Kconfig 2009-03-11 13:39:19.000000000 +0100 @@ -0,0 +1,27 @@ +# drivers/video/mxc/Kconfig + +#if ARCH_MXC + +menuconfig DRIVERS_MXC + bool "MXC support drivers" + depends on ARCH_MXC + +menu "MXC VPU(Video Processing Unit) support" + +config MXC_VPU + tristate "Support for MXC VPU(Video Processing Unit)" + depends on DRIVERS_MXC + depends on MACH_MX27 + help + The VPU codec device provides codec function for H.264/MPEG4/H.263 + +config MXC_VPU_DEBUG + bool "MXC VPU debugging" + depends on MXC_VPU + help + This is an option for the developers; most people should + say N here. This enables MXC VPU driver debugging. + +endmenu + +#endif diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mxc/Makefile linux-2.6.28-karo/drivers/mxc/Makefile --- linux-2.6.28/drivers/mxc/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/mxc/Makefile 2009-03-11 13:40:14.000000000 +0100 @@ -0,0 +1,11 @@ +# +# Makefile for the VPU drivers. +# + +ifeq ($(CONFIG_MXC_VPU_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +vpu-objs := mxc_vpu.o +obj-$(CONFIG_MXC_VPU) += vpu.o + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mxc/mxc_vpu.c linux-2.6.28-karo/drivers/mxc/mxc_vpu.c --- linux-2.6.28/drivers/mxc/mxc_vpu.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/mxc/mxc_vpu.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,540 @@ +/* + * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mxc_vpu.c + * + * @brief VPU system initialization and file operation implementation + * + * @ingroup VPU + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mxc_vpu.h" + + +#define BIT_INT_CLEAR 0x00C +#define BIT_INT_STATUS 0x010 +#define BIT_INT_ENABLE 0x170 + +typedef struct vpu_t { + struct fasync_struct *async_queue; + struct device *dev; +} vpu_t; + +/* To track the allocated memory buffer */ +typedef struct memalloc_record { + struct list_head list; + vpu_mem_desc mem; +} memalloc_record; + +static DEFINE_MUTEX(vpu_list_lock); +static DEFINE_MUTEX(vpu_mutex); +static LIST_HEAD(head); + +static int vpu_major = 0; +static struct class *vpu_class; +static struct vpu_t vpu_data; +static int open_count = 0; +static struct clk *vpu_clk; + +/* implement the blocking ioctl */ +static int codec_done = 0; +static wait_queue_head_t vpu_queue; +static int wait_intr_cnt = 0; + +/*! + * Private function to free buffers + * @return status 0 success. + */ +static int vpu_free_buffers(vpu_t *vpu_data) +{ + struct memalloc_record *rec, *n; + vpu_mem_desc mem; + + mutex_lock(&vpu_list_lock); + list_for_each_entry_safe(rec, n, &head, list) { + mem = rec->mem; + if (mem.cpu_addr != 0) { + dma_free_coherent(vpu_data->dev, PAGE_ALIGN(mem.size), + mem.cpu_addr, mem.phy_addr); + pr_debug("[FREE] freed paddr=0x%08X\n", mem.phy_addr); + + /* delete from list */ + list_del(&rec->list); + kfree(rec); + } + } + mutex_unlock(&vpu_list_lock); + + return 0; +} + +/*! + * @brief vpu interrupt handler + */ +static irqreturn_t vpu_irq_handler(int irq, void *dev_id) +{ + struct vpu_t *dev = dev_id; + + //(void)__raw_readl(IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_STATUS)); + __raw_writel(0x1, IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_CLEAR)); + if (dev->async_queue) + kill_fasync(&dev->async_queue, SIGIO, POLL_IN); + + BUG_ON(codec_done < 0); + codec_done++; + WARN_ON(codec_done > 1); + wake_up_interruptible(&vpu_queue); + + return IRQ_HANDLED; +} + +/*! + * @brief vpu hardware enable function + * + * @return 0 on success or negative error code on error + */ +static int vpu_hardware_enable(void) +{ + u32 tmp; + + clk_enable(vpu_clk); + + /* Let userspace access the codec engine through the + * peripheral access register. + */ +#if 0 + tmp = __raw_readl(IO_ADDRESS(AIPI_BASE_ADDR + 0x00020008)); + __raw_writel(tmp & ~(1 << 3), IO_ADDRESS(AIPI_BASE_ADDR + 0x00020008)); +#endif + codec_done = 0; + + return 0; +} + +/*! + * @brief vpu hardware disable function + * + * @return 0 on success or negative error code on error + */ +static int vpu_hardware_disable(void) +{ + clk_disable(vpu_clk); + return 0; + +} + +/*! + * @brief open function for vpu file operation + * + * @return 0 on success or negative error code on error + */ +static int vpu_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + mutex_lock(&vpu_mutex); + if (open_count == 0) { + open_count++; + filp->private_data = &vpu_data; + vpu_hardware_enable(); + } else { + printk(KERN_ERR "VPU has already been opened.\n"); + ret = -EBUSY; + } + mutex_unlock(&vpu_mutex); + + return ret; +} + +/*! + * @brief IO ctrl function for vpu file operation + * @param cmd IO ctrl command + * @return 0 on success or negative error code on error + */ +static int vpu_ioctl(struct inode *inode, struct file *filp, u_int cmd, + u_long arg) +{ + int ret = 0; + vpu_t *vpu_data = filp->private_data; + + switch (cmd) { + case VPU_IOC_PHYMEM_ALLOC: + { + struct memalloc_record *rec; + + rec = kzalloc(sizeof(*rec), GFP_KERNEL); + if (!rec) + return -ENOMEM; + + ret = copy_from_user(&rec->mem, (const void __user *)arg, + sizeof(vpu_mem_desc)); + if (ret) { + kfree(rec); + return -EFAULT; + } + + pr_debug("[ALLOC] mem alloc size = 0x%x\n", + rec->mem.size); + rec->mem.cpu_addr = dma_alloc_coherent(vpu_data->dev, + PAGE_ALIGN(rec->mem.size), + &rec->mem.phy_addr, + GFP_DMA | GFP_KERNEL); + pr_debug("[ALLOC] mem alloc cpu_addr = 0x%p\n", + rec->mem.cpu_addr); + if (rec->mem.cpu_addr == NULL) { + kfree(rec); + printk(KERN_ERR + "Physical memory allocation error!\n"); + ret = -ENOMEM; + break; + } + ret = copy_to_user((void __user *)arg, &rec->mem, + sizeof(vpu_mem_desc)); + if (ret) { + kfree(rec); + ret = -EFAULT; + break; + } + + mutex_lock(&vpu_list_lock); + list_add(&rec->list, &head); + mutex_unlock(&vpu_list_lock); + + break; + } + case VPU_IOC_PHYMEM_FREE: + { + struct memalloc_record *rec, *n; + vpu_mem_desc vpu_mem; + int found = 0; + + ret = copy_from_user(&vpu_mem, (const void __user *)arg, + sizeof(vpu_mem_desc)); + if (ret) + return -EFAULT; + + pr_debug("[FREE] mem freed cpu_addr = 0x%p\n", + vpu_mem.cpu_addr); + mutex_lock(&vpu_list_lock); + list_for_each_entry_safe(rec, n, &head, list) { + if (rec->mem.cpu_addr == vpu_mem.cpu_addr) { + /* delete from list */ + list_del(&rec->list); + kfree(rec); + found = 1; + break; + } + } + + if (vpu_mem.cpu_addr != NULL) { + dma_free_coherent(vpu_data->dev, + PAGE_ALIGN(vpu_mem.size), + vpu_mem.cpu_addr, + vpu_mem.phy_addr); + } + BUG_ON(!found); + mutex_unlock(&vpu_list_lock); + + break; + } + case VPU_IOC_WAIT4INT: + { + unsigned long flags; + u_long timeout = arg; + + ret = wait_event_interruptible_timeout(vpu_queue, + codec_done, + msecs_to_jiffies(timeout)); + local_irq_save(flags); + if (codec_done) { + codec_done--; + BUG_ON(codec_done < 0); + if (codec_done > 0) { + printk(KERN_DEBUG "%s: WAIT4INT: codec_done=%d\n", + __FUNCTION__, codec_done); + } + ret = 0; + } else if (signal_pending(current)) { + if (wait_intr_cnt == 0) { + printk(KERN_WARNING "wait for VPU interrupt canceled\n"); + } + wait_intr_cnt++; + } else if (ret == 0) { + if (timeout != 0) { + printk(KERN_WARNING "VPU blocking: timeout.\n"); + } + ret = (filp->f_flags & O_NONBLOCK) ? -EAGAIN : -ETIME; + } + local_irq_restore(flags); + break; + } +/* RMW: this is not portable across platforms */ +#ifdef CONFIG_MACH_MX27 + /* set/clear LHD (Latency Hiding Disable) bit in ESDCFG0 reg. + Tends to fix MPEG4 issue on MX27 TO2 */ + case VPU_IOC_LHD: + { + u_int disable = (u_int)arg; + u_int reg; + void __iomem *reg_addr; + + reg_addr = IO_ADDRESS(SDRAMC_BASE_ADDR + 0x10); + reg = __raw_readl(reg_addr); + pr_debug("ESDCFG0: [ 0x%08x ]\n", reg); + + if (disable == 0) { + __raw_writel(reg & ~0x00000020, reg_addr); + pr_debug("Latency Hiding Disable\n"); + } else { + __raw_writel(reg | 0x00000020, reg_addr); + pr_debug("Latency Hiding Enable\n"); + } + + pr_debug("ESDCFG0: [ 0x%08x ]\n", + __raw_readl(reg_addr)); + + break; + } +#endif + case VPU_IOC_VL2CC_FLUSH: + break; + case VPU_IOC_REG_DUMP: + break; + case VPU_IOC_PHYMEM_DUMP: + break; + default: + printk(KERN_ERR "No such IOCTL, cmd is %d\n", cmd); + ret = -EINVAL; + } + return ret; +} + +/*! + * @brief Release function for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct vpu_t *vpu_data = filp->private_data; + + mutex_lock(&vpu_mutex); + if (open_count == 1) { + open_count--; + vpu_free_buffers(vpu_data); + vpu_hardware_disable(); + } else { + ret = -EINVAL; + } + mutex_unlock(&vpu_mutex); + return ret; +} + +/*! + * @brief fasync function for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_fasync(int fd, struct file *filp, int mode) +{ + struct vpu_t *vpu_data = filp->private_data; + return fasync_helper(fd, filp, mode, &vpu_data->async_queue); +} + +/*! + * @brief memory map function of harware registers for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_map_hwregs(struct file *fp, struct vm_area_struct *vm) +{ + unsigned long pfn; + + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); + pfn = VPU_BASE_ADDR >> PAGE_SHIFT; + pr_debug("size=0x%08lx, page no.=0x%08lx\n", + vm->vm_end - vm->vm_start, pfn); + return remap_pfn_range(vm, vm->vm_start, pfn, vm->vm_end - vm->vm_start, + vm->vm_page_prot) ? -EAGAIN : 0; +} + +/*! + * @brief memory map function of memory for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_map_mem(struct file *fp, struct vm_area_struct *vm) +{ + int request_size = vm->vm_end - vm->vm_start; + + pr_debug(" start=0x%08lx, pgoff=0x%08lx, size=0x%08x\n", + vm->vm_start, vm->vm_pgoff, + request_size); + + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); + + return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, + request_size, vm->vm_page_prot) ? -EAGAIN : 0; + +} + +/*! + * @brief memory map interface for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_mmap(struct file *fp, struct vm_area_struct *vm) +{ + if (vm->vm_pgoff) + return vpu_map_mem(fp, vm); + else + return vpu_map_hwregs(fp, vm); +} + +struct file_operations vpu_fops = { + .owner = THIS_MODULE, + .open = vpu_open, + .ioctl = vpu_ioctl, + .release = vpu_release, + .fasync = vpu_fasync, + .mmap = vpu_mmap, +}; + +/*! + * This function is called by the driver framework to initialize the vpu device. + * @param dev The device structure for the vpu passed in by the framework. + * @return 0 on success or negative error code on error + */ +static int vpu_dev_probe(struct platform_device *pdev) +{ + int err = 0; + struct device *temp_device; + + init_waitqueue_head(&vpu_queue); + + vpu_major = register_chrdev(vpu_major, "mxc_vpu", &vpu_fops); + if (vpu_major < 0) { + printk(KERN_ERR "vpu: unable to get a major for VPU\n"); + err = vpu_major; + goto error; + } + + vpu_class = class_create(THIS_MODULE, "mxc_vpu"); + if (IS_ERR(vpu_class)) { + err = PTR_ERR(vpu_class); + goto err_out_chrdev; + } + + temp_device = device_create(vpu_class, NULL, + MKDEV(vpu_major, 0), NULL, "mxc_vpu"); + if (IS_ERR(temp_device)) { + err = PTR_ERR(temp_device); + goto err_out_class; + } + + vpu_data.dev = &pdev->dev; + platform_set_drvdata(pdev, &vpu_data); + + vpu_clk = clk_get(&pdev->dev, "vpu_clk"); + if (IS_ERR(vpu_clk)) { + err = PTR_ERR(vpu_clk); + goto err_out_class; + } + + err = request_irq(MXC_INT_VPU, vpu_irq_handler, 0, "VPU_CODEC_IRQ", + &vpu_data); + if (err) + goto err_out_class; + + printk(KERN_INFO "VPU initialized\n"); + goto out; + +err_out_class: + device_destroy(vpu_class, MKDEV(vpu_major, 0)); + class_destroy(vpu_class); +err_out_chrdev: + unregister_chrdev(vpu_major, "mxc_vpu"); +error: +out: + return err; +} + +static int __devexit vpu_dev_remove(struct platform_device *pdev) +{ + vpu_t *vpu_data = platform_get_drvdata(pdev); + + free_irq(MXC_INT_VPU, vpu_data); + if (vpu_major > 0) { + device_destroy(vpu_class, MKDEV(vpu_major, 0)); + class_destroy(vpu_class); + unregister_chrdev(vpu_major, "mxc_vpu"); + vpu_major = 0; + } + + clk_put(vpu_clk); + return 0; +} + +/*! Driver definition + * + */ +static struct platform_driver mxcvpu_driver = { + .driver = { + .name = "mxc_vpu", + }, + .probe = vpu_dev_probe, + .remove = __devexit_p(vpu_dev_remove), +}; + +static int __init vpu_init(void) +{ + int ret = platform_driver_register(&mxcvpu_driver); + + printk(KERN_DEBUG "%s: platform_driver_register returned %d\n", __FUNCTION__, ret); + + return ret; +} + +static void __exit vpu_exit(void) +{ + platform_driver_unregister(&mxcvpu_driver); + return; +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX27"); +MODULE_LICENSE("GPL"); + +module_init(vpu_init); +module_exit(vpu_exit); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/mxc/mxc_vpu.h linux-2.6.28-karo/drivers/mxc/mxc_vpu.h --- linux-2.6.28/drivers/mxc/mxc_vpu.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/mxc/mxc_vpu.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,22 @@ +typedef struct vpu_mem_desc { + size_t size; + dma_addr_t phy_addr; + void *cpu_addr; /* cpu address to free the dma mem */ + void *virt_uaddr; /* virtual user space address */ +} vpu_mem_desc; + +#define VPU_IOC_MAGIC 'V' + +#define VPU_IOC_PHYMEM_ALLOC _IO(VPU_IOC_MAGIC, 0) +#define VPU_IOC_PHYMEM_FREE _IO(VPU_IOC_MAGIC, 1) +#define VPU_IOC_WAIT4INT _IO(VPU_IOC_MAGIC, 2) +#define VPU_IOC_PHYMEM_DUMP _IO(VPU_IOC_MAGIC, 3) +#define VPU_IOC_REG_DUMP _IO(VPU_IOC_MAGIC, 4) +#define VPU_IOC_LHD _IO(VPU_IOC_MAGIC, 5) +#define VPU_IOC_VL2CC_FLUSH _IO(VPU_IOC_MAGIC, 6) + +int vl2cc_init(u32 vl2cc_hw_base); +void vl2cc_enable(void); +void vl2cc_flush(void); +void vl2cc_disable(void); +void vl2cc_cleanup(void); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/net/Kconfig linux-2.6.28-karo/drivers/net/Kconfig --- linux-2.6.28/drivers/net/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/net/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -1810,11 +1810,11 @@ config 68360_ENET the Motorola 68360 processor. config FEC - bool "FEC ethernet controller (of ColdFire CPUs)" - depends on M523x || M527x || M5272 || M528x || M520x + tristate "FEC ethernet controller" + depends on M523x || M527x || M5272 || M528x || M520x || MACH_MX27 help Say Y here if you want to use the built-in 10/100 Fast ethernet - controller on some Motorola ColdFire processors. + controller on some Motorola/Freescale processors. config FEC2 bool "Second FEC ethernet controller (on some ColdFire CPUs)" diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/net/fec.c linux-2.6.28-karo/drivers/net/fec.c --- linux-2.6.28/drivers/net/fec.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/net/fec.c 2009-03-11 13:16:24.000000000 +0100 @@ -2,6 +2,12 @@ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * + * This version of the driver is specific to the FADS implementation, + * since the board contains control registers external to the processor + * for the control of the LevelOne LXT970 transceiver. The MPC860T manual + * describes connections using the internal parallel port I/O, which + * is basically all of Port D. + * * Right now, I am very wasteful with the buffers. I allocate memory * pages and then divide them into 2K frame buffers. This way I know I * have buffers large enough to hold one frame within one buffer descriptor. @@ -18,48 +24,74 @@ * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) * Copyright (c) 2004-2006 Macq Electronique SA. */ +/* + * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ #include #include -#include -#include +#include #include #include #include #include -#include -#include +#include #include +#include #include #include #include #include -#include -#include +#include +#include +#include +#include #include -#include #include -#include -#include +#define DRV_NAME "fec_enet" + +#ifdef DEBUG +static int debug = 1; +#define dbg_lvl(n) ((n) < debug) +module_param(debug, int, S_IRUGO | S_IWUSR); + +#define DBG(lvl, fmt...) do { if (dbg_lvl(lvl)) printk(KERN_DEBUG fmt); } while (0) +#else +static int debug; +#define dbg_lvl(n) 0 +module_param(debug, int, 0); + +#define DBG(lvl, fmt...) do { } while (0) +#endif + +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \ + defined(CONFIG_M5272) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) #include #include #include "fec.h" - -#if defined(CONFIG_FEC2) -#define FEC_MAX_PORTS 2 +#define FEC_ALIGNMENT (0x03) /*FEC needs 4bytes alignment*/ +#elif defined(CONFIG_ARCH_MXC) +#include +#include +#include "fec.h" +#define FEC_ALIGNMENT (0x0F) /*FEC needs 128bits(32bytes) alignment*/ #else -#define FEC_MAX_PORTS 1 +#include +#include +#include "commproc.h" +#define FEC_ALIGNMENT (0x03) /*FEC needs 4bytes alignment */ #endif -#if defined(CONFIG_M5272) -#define HAVE_mii_link_interrupt -#endif +#define FEC_ADDR_ALIGNMENT(x) ((unsigned char *)(((unsigned long)(x) + (FEC_ALIGNMENT)) & (~FEC_ALIGNMENT))) +#if 0 /* * Define the fixed address of the FEC hardware. */ +/* USE resources provided by platform_device! */ static unsigned int fec_hw[] = { #if defined(CONFIG_M5272) (MCF_MBAR + 0x840), @@ -72,23 +104,26 @@ static unsigned int fec_hw[] = { (MCF_MBAR+0x30000), #elif defined(CONFIG_M532x) (MCF_MBAR+0xfc030000), +#elif defined(CONFIG_ARCH_MXC) + (IO_ADDRESS(FEC_BASE_ADDR)), #else &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec), #endif }; +#endif -static unsigned char fec_mac_default[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - +#if 0 /* * Some hardware gets it MAC address out of local flash memory. * if this is non-zero then assume it is the address to get MAC from. */ +/* implemented using platform_data! */ #if defined(CONFIG_NETtel) #define FEC_FLASHMAC 0xf0006006 #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) #define FEC_FLASHMAC 0xf0006000 +#elif defined (CONFIG_MTD_KeyTechnology) +#define FEC_FLASHMAC 0xffe04000 #elif defined(CONFIG_CANCam) #define FEC_FLASHMAC 0xf0020000 #elif defined (CONFIG_M5272C3) @@ -98,10 +133,13 @@ static unsigned char fec_mac_default[] = #else #define FEC_FLASHMAC 0 #endif +#endif + +#define platform_func(p, args...) ((p) ? (p)(args) : 0) /* Forward declarations of some structures to support different PHYs */ - +#ifndef CONFIG_PHYLIB typedef struct { uint mii_data; void (*funct)(uint mii_reg, struct net_device *dev); @@ -116,6 +154,7 @@ typedef struct { const phy_cmd_t *ack_int; const phy_cmd_t *shutdown; } phy_info_t; +#endif /* The number of Tx and Rx buffers. These are allocated from the page * pool. The code may assume these are power of two, so it it best @@ -129,12 +168,13 @@ typedef struct { #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) #define FEC_ENET_TX_FRSIZE 2048 #define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) -#define TX_RING_SIZE 16 /* Must be power of two */ -#define TX_RING_MOD_MASK 15 /* for this to work */ +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) /* for this to work */ #if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE) #error "FEC: descriptor ring size constants too large" #endif +#define CBD_BUF_SIZE ((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) /* Interrupt events/masks. */ @@ -149,6 +189,12 @@ typedef struct { #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#ifndef CONFIG_ARCH_MXC +#define FEC_ENET_MASK ((uint)0xffc00000) +#else +#define FEC_ENET_MASK ((uint)0xfff80000) +#endif + /* The FEC stores dest/src/type, data, and checksum for receive packets. */ #define PKT_MAXBUF_SIZE 1518 @@ -162,8 +208,8 @@ typedef struct { * account when setting it. */ #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) -#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC) +#define OPT_FRAME_SIZE (RCR_MAX_FL_set(PKT_MAXBUF_SIZE)) #else #define OPT_FRAME_SIZE 0 #endif @@ -178,28 +224,44 @@ typedef struct { */ struct fec_enet_private { /* Hardware registers of the FEC device */ - volatile fec_t *hwp; - - struct net_device *netdev; + volatile void __iomem *reg_base; + struct resource *res_mem1; + struct resource *res_mem2; + int etn_irq; + int mii_irq; +#ifndef CONFIG_PHYLIB + struct timer_list *phy_timer; +#else + struct mii_bus *mii; + int mii_complete; +#endif + u32 msg_enable; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - unsigned char *tx_bounce[TX_RING_SIZE]; + void *tx_bounce[TX_RING_SIZE]; struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct sk_buff* rx_skbuff[RX_RING_SIZE]; ushort skb_cur; ushort skb_dirty; /* CPM dual port RAM relative addresses. */ + struct device *dma_dev; /* pointer to (platform_)device for dma_sync*() functions */ + void *cbd_mem_base; /* save the virtual base address of rx&tx buffer descriptor */ + dma_addr_t cbd_phys_base; /* physical address of buffer descriptor memory for access by FEC HW */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ cbd_t *tx_bd_base; - cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ - cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + struct net_device_stats stats; uint tx_full; - /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ - spinlock_t hw_lock; - /* hold while accessing the mii_list_t() elements */ - spinlock_t mii_lock; + spinlock_t lock; +#ifdef CONFIG_PHYLIB + struct phy_device *phy; + uint phy_speed; +#else uint phy_id; uint phy_id_done; uint phy_status; @@ -209,28 +271,41 @@ struct fec_enet_private { uint sequence_done; uint mii_phy_task_queued; - +#endif uint phy_addr; - int index; - int opened; - int link; - int old_link; - int full_duplex; + unsigned int opened:1; + unsigned int phy_int_enabled:1; + unsigned int linkstatus:1; +#ifndef CONFIG_PHYLIB + unsigned int old_linkstatus:1; +#endif + unsigned int full_duplex:1; + + struct clk *clk; }; -static int fec_enet_open(struct net_device *dev); -static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void fec_enet_mii(struct net_device *dev); -static irqreturn_t fec_enet_interrupt(int irq, void * dev_id); +#ifdef CONFIG_PHYLIB +static int fec_connect_phy(struct net_device *dev, struct fec_enet_private *fep); +#else +static irqreturn_t mii_link_interrupt(int irq, void *dev_id); +#endif +static void fec_restart(struct net_device *dev, int duplex); static void fec_enet_tx(struct net_device *dev); static void fec_enet_rx(struct net_device *dev); -static int fec_enet_close(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void fec_restart(struct net_device *dev, int duplex); +static void fec_enet_mii(struct net_device *dev); static void fec_stop(struct net_device *dev); -static void fec_set_mac_address(struct net_device *dev); +static void _fec_set_mac_address(struct net_device *dev); +/* + * fec_copy_threshold controls the copy when receiving ethernet frame. + * If ethernet header is aligned on a 4byte boundary, the ip header and + * higher level header will not be aligned. + * The reason is, that an ethernet header is 14bytes long. + * And the max size of tcp & ip header is 128bytes. Normally it is 40bytes. + * So I set the default value between 128 to 256. + */ +static int fec_copy_threshold = 192; /* MII processing. We keep this as simple as possible. Requests are * placed on the list (if there is room). When the request is finished @@ -242,14 +317,16 @@ typedef struct mii_list { struct mii_list *mii_next; } mii_list_t; +#ifndef CONFIG_PHYLIB #define NMII 20 static mii_list_t mii_cmds[NMII]; static mii_list_t *mii_free; static mii_list_t *mii_head; static mii_list_t *mii_tail; -static int mii_queue(struct net_device *dev, int request, - void (*func)(uint, struct net_device *)); +static int mii_queue(struct net_device *dev, int request, + void (*func)(uint, struct net_device *)); +#endif /* Make MII read/write commands for the FEC. */ @@ -294,47 +371,147 @@ static int mii_queue(struct net_device * #define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ #define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ +#ifndef DEBUG +static inline unsigned long fec_reg_read(struct fec_enet_private *fep, unsigned int reg) +{ + return readl(fep->reg_base + reg); +} + +static inline void fec_reg_write(struct fec_enet_private *fep, unsigned int reg, unsigned long val) +{ + writel(val, fep->reg_base + reg); +} +#else +#define fec_reg_read(fep, reg) __fec_reg_read(fep, reg, __FUNCTION__, #reg) +#define fec_reg_write(fep, reg, val) __fec_reg_write(fep, reg, val, __FUNCTION__, #reg) + +static inline unsigned long __fec_reg_read(struct fec_enet_private *fep, unsigned int reg, + const char *func, const char *reg_name) +{ + unsigned long val = readl(fep->reg_base + reg); + DBG(3, "%s: Read %08lx from %s(%03x)\n", func, val, reg_name, reg); + return val; +} + +static inline void __fec_reg_write(struct fec_enet_private *fep, unsigned int reg, + unsigned long val, const char *func, const char *reg_name) +{ + DBG(3, "%s: Writing %08lx to %s(%03x)\n", func, val, reg_name, reg); + writel(val, fep->reg_base + reg); +} +#endif + +static inline void fec_enet_cbd_get(struct fec_enet_private *fep) +{ + DBG(2, "%s: Requesting cbd area: %08lx\n", __FUNCTION__, (ulong)fep->cbd_phys_base); + dma_sync_single_for_cpu(fep->dma_dev, fep->cbd_phys_base, CBD_BUF_SIZE, DMA_BIDIRECTIONAL); +} + +static inline void fec_enet_cbd_put(struct fec_enet_private *fep) +{ + DBG(2, "%s: Flushing changes to cbd area\n", __FUNCTION__); + dma_sync_single_for_device(fep->dma_dev, fep->cbd_phys_base, + CBD_BUF_SIZE, DMA_BIDIRECTIONAL); +} + +static inline void fec_enet_rxbuf_get(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Requesting RX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + dma_sync_single_for_cpu(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_FROM_DEVICE); +} + +static inline void fec_enet_rxbuf_put(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Releasing RX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + dma_sync_single_for_device(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_FROM_DEVICE); +} + +static inline void fec_enet_rxbuf_map(struct fec_enet_private *fep, cbd_t *bdp, + void *buf, ushort len) +{ + BUG_ON(!dma_mapping_error(fep->dma_dev, bdp->cbd_bufaddr)); + bdp->cbd_bufaddr = dma_map_single(fep->dma_dev, buf, len, DMA_FROM_DEVICE); + DBG(2, "%s: RX buffer %p(%u) mapped to %08lx\n", __FUNCTION__, buf, len, + (ulong)bdp->cbd_bufaddr); +} + +static inline void fec_enet_rxbuf_unmap(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Unmapping RX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + BUG_ON(dma_mapping_error(fep->dma_dev, bdp->cbd_bufaddr)); + dma_unmap_single(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_FROM_DEVICE); + bdp->cbd_bufaddr = ~0; +} + +static inline void fec_enet_txbuf_map(struct fec_enet_private *fep, cbd_t *bdp, + void *buf, ushort len) +{ + BUG_ON(!dma_mapping_error(fep->dma_dev, bdp->cbd_bufaddr)); + bdp->cbd_bufaddr = dma_map_single(fep->dma_dev, buf, len, DMA_TO_DEVICE); + DBG(2, "%s: TX buffer %p(%u) mapped to %08lx\n", __FUNCTION__, buf, len, + (ulong)bdp->cbd_bufaddr); +} + +static inline void fec_enet_txbuf_unmap(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Unmapping TX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + BUG_ON(dma_mapping_error(fep->dma_dev, bdp->cbd_bufaddr)); + dma_unmap_single(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_TO_DEVICE); + bdp->cbd_bufaddr = ~0; +} + +static inline void fec_enet_txbuf_get(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Requesting TX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + dma_sync_single_for_cpu(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_TO_DEVICE); +} + +static inline void fec_enet_txbuf_put(struct fec_enet_private *fep, cbd_t *bdp, ushort len) +{ + DBG(2, "%s: Releasing TX buffer %08lx(%u)\n", __FUNCTION__, (ulong)bdp->cbd_bufaddr, len); + dma_sync_single_for_device(fep->dma_dev, bdp->cbd_bufaddr, len, DMA_TO_DEVICE); +} static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct fec_enet_private *fep; - volatile fec_t *fecp; - volatile cbd_t *bdp; - unsigned short status; + struct fec_enet_private *fep = netdev_priv(dev); + cbd_t *bdp; + unsigned short status; unsigned long flags; - fep = netdev_priv(dev); - fecp = (volatile fec_t*)dev->base_addr; - - if (!fep->link) { + if (!fep->linkstatus) { + DBG(0, "%s: Cannot send packet; link is down\n", __FUNCTION__); /* Link is down or autonegotiation is in progress. */ return 1; } - spin_lock_irqsave(&fep->hw_lock, flags); + spin_lock_irqsave(&fep->lock, flags); + + //WARN_ON(fec_reg_read(fep, FEC_TDAR) & TDAR_BUSY); + fec_enet_cbd_get(fep); + /* Fill in a Tx ring entry */ bdp = fep->cur_tx; status = bdp->cbd_sc; -#ifndef final_version +#ifdef DEBUG if (status & BD_ENET_TX_READY) { /* Ooops. All transmit buffers are full. Bail out. * This should not happen, since dev->tbusy should be set. */ printk("%s: tx queue full!.\n", dev->name); - spin_unlock_irqrestore(&fep->hw_lock, flags); + fec_enet_cbd_put(fep); + spin_unlock_irqrestore(&fep->lock, flags); return 1; } #endif - /* Clear all of the status flags. */ status &= ~BD_ENET_TX_STATS; /* Set buffer length and buffer pointer. */ - bdp->cbd_bufaddr = __pa(skb->data); bdp->cbd_datlen = skb->len; /* @@ -342,30 +519,25 @@ fec_enet_start_xmit(struct sk_buff *skb, * 4-byte boundaries. Use bounce buffers to copy data * and get it aligned. Ugh. */ - if (bdp->cbd_bufaddr & 0x3) { + if (unlikely((bdp->cbd_bufaddr) & FEC_ALIGNMENT)) { unsigned int index; index = bdp - fep->tx_bd_base; - memcpy(fep->tx_bounce[index], (void *) bdp->cbd_bufaddr, bdp->cbd_datlen); - bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]); + memcpy(fep->tx_bounce[index], skb->data, skb->len); + fec_enet_txbuf_map(fep, bdp, fep->tx_bounce[index], skb->len); + } else { + fec_enet_txbuf_map(fep, bdp, skb->data, skb->len); } /* Save skb pointer. */ fep->tx_skbuff[fep->skb_cur] = skb; - dev->stats.tx_bytes += skb->len; - fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK; - - /* Push the data cache so the CPM does not get stale memory - * data. - */ - flush_dcache_range((unsigned long)skb->data, - (unsigned long)skb->data + skb->len); + fep->stats.tx_bytes += skb->len; + fep->skb_cur = (fep->skb_cur + 1) & TX_RING_MOD_MASK; /* Send it on its way. Tell FEC it's ready, interrupt when done, * it's the last BD of the frame, and to put the CRC on the end. */ - status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); bdp->cbd_sc = status; @@ -373,7 +545,7 @@ fec_enet_start_xmit(struct sk_buff *skb, dev->trans_start = jiffies; /* Trigger transmission start */ - fecp->fec_x_des_active = 0; + fec_reg_write(fep, FEC_TDAR, DONT_CARE); /* If this was the last BD in the ring, start at the beginning again. */ @@ -385,12 +557,14 @@ fec_enet_start_xmit(struct sk_buff *skb, if (bdp == fep->dirty_tx) { fep->tx_full = 1; + DBG(0, "TX ring full, stopping netif queue\n"); netif_stop_queue(dev); } - fep->cur_tx = (cbd_t *)bdp; + fep->cur_tx = bdp; + fec_enet_cbd_put(fep); - spin_unlock_irqrestore(&fep->hw_lock, flags); + spin_unlock_irqrestore(&fep->lock, flags); return 0; } @@ -400,68 +574,73 @@ fec_timeout(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - printk("%s: transmit timed out.\n", dev->name); - dev->stats.tx_errors++; -#ifndef final_version + printk(KERN_WARNING "%s: transmit timed out.\n", dev->name); + fep->stats.tx_errors++; +#ifdef DEBUG { - int i; - cbd_t *bdp; + int i; + cbd_t *bdp; - printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n", - (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "", - (unsigned long)fep->dirty_tx, - (unsigned long)fep->cur_rx); + fec_enet_cbd_get(fep); - bdp = fep->tx_bd_base; - printk(" tx: %u buffers\n", TX_RING_SIZE); - for (i = 0 ; i < TX_RING_SIZE; i++) { - printk(" %08x: %04x %04x %08x\n", - (uint) bdp, - bdp->cbd_sc, - bdp->cbd_datlen, - (int) bdp->cbd_bufaddr); - bdp++; - } + printk(KERN_DEBUG "%s: Ring data dump: cur_tx %p%s, dirty_tx %p cur_rx: %p\n", + __FUNCTION__, + fep->cur_tx, fep->tx_full ? " (full)" : "", + fep->dirty_tx, + fep->cur_rx); - bdp = fep->rx_bd_base; - printk(" rx: %lu buffers\n", (unsigned long) RX_RING_SIZE); - for (i = 0 ; i < RX_RING_SIZE; i++) { - printk(" %08x: %04x %04x %08x\n", - (uint) bdp, - bdp->cbd_sc, - bdp->cbd_datlen, - (int) bdp->cbd_bufaddr); - bdp++; - } + bdp = fep->tx_bd_base; + printk(" tx: %u buffers\n", TX_RING_SIZE); + for (i = 0 ; i < TX_RING_SIZE; i++) { + printk(" %p: %04x %04x %08x\n", + bdp, + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp++; + } + + bdp = fep->rx_bd_base; + printk(" rx: %lu buffers\n", RX_RING_SIZE); + for (i = 0 ; i < RX_RING_SIZE; i++) { + printk(" %p: %04x %04x %08x\n", + bdp, + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp++; + } + fec_enet_cbd_put(fep); } #endif fec_restart(dev, fep->full_duplex); - netif_wake_queue(dev); + DBG(0, "%s: Scheduling netif queue\n", __FUNCTION__); + //netif_schedule(dev); } /* The interrupt handler. * This is called from the MPC core interrupt. */ static irqreturn_t -fec_enet_interrupt(int irq, void * dev_id) +fec_enet_interrupt(int irq, void *dev_id) { - struct net_device *dev = dev_id; - volatile fec_t *fecp; - uint int_events; - irqreturn_t ret = IRQ_NONE; - - fecp = (volatile fec_t*)dev->base_addr; + struct net_device *dev = dev_id; + struct fec_enet_private *fep = netdev_priv(dev); + uint int_events; + int handled = 0; + DBG(2, "%s: %08lx:%08lx\n", __FUNCTION__, + fec_reg_read(fep, FEC_EIR), fec_reg_read(fep, FEC_EIMR)); /* Get the interrupt events that caused us to be here. */ - do { - int_events = fecp->fec_ievent; - fecp->fec_ievent = int_events; + while ((int_events = fec_reg_read(fep, FEC_EIR)) != 0) { + fec_reg_write(fep, FEC_EIR, int_events); /* Handle receive event in its own function. */ - if (int_events & FEC_ENET_RXF) { - ret = IRQ_HANDLED; + if (int_events & (FEC_ENET_RXF | FEC_ENET_RXB)) { + DBG(2, "%s: Handling RX Interrupt\n", __FUNCTION__); + handled = 1; fec_enet_rx(dev); } @@ -469,32 +648,43 @@ fec_enet_interrupt(int irq, void * dev_i descriptors. FEC handles all errors, we just discover them as part of the transmit process. */ - if (int_events & FEC_ENET_TXF) { - ret = IRQ_HANDLED; + if (int_events & (FEC_ENET_TXF | FEC_ENET_TXB)) { + DBG(2, "%s: Handling TX Interrupt\n", __FUNCTION__); + handled = 1; fec_enet_tx(dev); } - if (int_events & FEC_ENET_MII) { - ret = IRQ_HANDLED; + if (int_events & (FEC_ENET_MII | FEC_ENET_HBERR)) { + DBG(2, "%s: Handling MII Interrupt\n", __FUNCTION__); + handled = 1; fec_enet_mii(dev); } - - } while (int_events); - - return ret; + } + return IRQ_RETVAL(handled); } +static void fec_free_skb(struct fec_enet_private *fep, cbd_t *bdp, struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + if (!dma_mapping_error(fep->dma_dev, bdp->cbd_bufaddr)) { + fec_enet_txbuf_unmap(fep, bdp, skb->len); + } + dev_kfree_skb_any(skb); + *pskb = NULL; +} static void fec_enet_tx(struct net_device *dev) { - struct fec_enet_private *fep; - volatile cbd_t *bdp; + struct fec_enet_private *fep = netdev_priv(dev); + cbd_t *bdp; unsigned short status; - struct sk_buff *skb; + struct sk_buff *skb; - fep = netdev_priv(dev); - spin_lock_irq(&fep->hw_lock); + spin_lock(&fep->lock); + + //WARN_ON(fec_reg_read(fep, FEC_TDAR) & TDAR_BUSY); + fec_enet_cbd_get(fep); bdp = fep->dirty_tx; while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { @@ -505,22 +695,22 @@ fec_enet_tx(struct net_device *dev) if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN | BD_ENET_TX_CSL)) { - dev->stats.tx_errors++; + fep->stats.tx_errors++; if (status & BD_ENET_TX_HB) /* No heartbeat */ - dev->stats.tx_heartbeat_errors++; + fep->stats.tx_heartbeat_errors++; if (status & BD_ENET_TX_LC) /* Late collision */ - dev->stats.tx_window_errors++; + fep->stats.tx_window_errors++; if (status & BD_ENET_TX_RL) /* Retrans limit */ - dev->stats.tx_aborted_errors++; + fep->stats.tx_aborted_errors++; if (status & BD_ENET_TX_UN) /* Underrun */ - dev->stats.tx_fifo_errors++; + fep->stats.tx_fifo_errors++; if (status & BD_ENET_TX_CSL) /* Carrier lost */ - dev->stats.tx_carrier_errors++; + fep->stats.tx_carrier_errors++; } else { - dev->stats.tx_packets++; + fep->stats.tx_packets++; } -#ifndef final_version +#ifdef DEBUG if (status & BD_ENET_TX_READY) printk("HEY! Enet xmit interrupt and TX_READY.\n"); #endif @@ -528,12 +718,11 @@ fec_enet_tx(struct net_device *dev) * but we eventually sent the packet OK. */ if (status & BD_ENET_TX_DEF) - dev->stats.collisions++; + fep->stats.collisions++; /* Free the sk buffer associated with this last transmit. */ - dev_kfree_skb_any(skb); - fep->tx_skbuff[fep->skb_dirty] = NULL; + fec_free_skb(fep, bdp, &fep->tx_skbuff[fep->skb_dirty]); fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; /* Update pointer to next buffer descriptor to be transmitted. @@ -548,12 +737,15 @@ fec_enet_tx(struct net_device *dev) */ if (fep->tx_full) { fep->tx_full = 0; - if (netif_queue_stopped(dev)) + if (netif_queue_stopped(dev)) { + DBG(0, "%s: Waking up netif queue\n", __FUNCTION__); netif_wake_queue(dev); + } } } - fep->dirty_tx = (cbd_t *)bdp; - spin_unlock_irq(&fep->hw_lock); + fec_enet_cbd_put(fep); + fep->dirty_tx = bdp; + spin_unlock(&fep->lock); } @@ -565,22 +757,22 @@ fec_enet_tx(struct net_device *dev) static void fec_enet_rx(struct net_device *dev) { - struct fec_enet_private *fep; - volatile fec_t *fecp; - volatile cbd_t *bdp; + struct fec_enet_private *fep = netdev_priv(dev); + cbd_t *bdp; unsigned short status; - struct sk_buff *skb; - ushort pkt_len; - __u8 *data; + struct sk_buff *skb; + ushort pkt_len; + int rx_index; #ifdef CONFIG_M532x + /* This is probably nonsense + Proper use of dma-mapping functions should make this obsolete + */ flush_cache_all(); #endif - - fep = netdev_priv(dev); - fecp = (volatile fec_t*)dev->base_addr; - - spin_lock_irq(&fep->hw_lock); + /* reserve the dual port memory area for our use */ + //WARN_ON(fec_reg_read(fep, FEC_RDAR) & RDAR_BUSY); + fec_enet_cbd_get(fep); /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. @@ -588,8 +780,8 @@ fec_enet_rx(struct net_device *dev) bdp = fep->cur_rx; while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { - -#ifndef final_version + rx_index = bdp - fep->rx_bd_base; +#ifdef DEBUG /* Since we have allocated space to hold a complete frame, * the last indicator should be set. */ @@ -597,23 +789,24 @@ while (!((status = bdp->cbd_sc) & BD_ENE printk("FEC ENET: rcv is not +last\n"); #endif - if (!fep->opened) + if (!fep->opened) { + DBG(0, "%s: Driver not opened; ignoring packet\n", __FUNCTION__); goto rx_processing_done; - + } /* Check for errors. */ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) { - dev->stats.rx_errors++; + fep->stats.rx_errors++; if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { /* Frame too long or too short. */ - dev->stats.rx_length_errors++; + fep->stats.rx_length_errors++; } if (status & BD_ENET_RX_NO) /* Frame alignment */ - dev->stats.rx_frame_errors++; + fep->stats.rx_frame_errors++; if (status & BD_ENET_RX_CR) /* CRC Error */ - dev->stats.rx_crc_errors++; + fep->stats.rx_crc_errors++; if (status & BD_ENET_RX_OV) /* FIFO overrun */ - dev->stats.rx_fifo_errors++; + fep->stats.rx_fifo_errors++; } /* Report late collisions as a frame error. @@ -621,36 +814,63 @@ while (!((status = bdp->cbd_sc) & BD_ENE * have in the buffer. So, just drop this frame on the floor. */ if (status & BD_ENET_RX_CL) { - dev->stats.rx_errors++; - dev->stats.rx_frame_errors++; + fep->stats.rx_errors++; + fep->stats.rx_frame_errors++; + DBG(0, "%s: Collision detected; dropping packet\n", __FUNCTION__); goto rx_processing_done; } /* Process the incoming frame. */ - dev->stats.rx_packets++; + fep->stats.rx_packets++; pkt_len = bdp->cbd_datlen; - dev->stats.rx_bytes += pkt_len; - data = (__u8*)__va(bdp->cbd_bufaddr); + fep->stats.rx_bytes += pkt_len; /* This does 16 byte alignment, exactly what we need. * The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up * bridging applications. */ - skb = dev_alloc_skb(pkt_len-4); + if ((pkt_len - 4) < fec_copy_threshold) { + skb = dev_alloc_skb(pkt_len); + } else { + skb = dev_alloc_skb(FEC_ENET_RX_FRSIZE); + } if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); - dev->stats.rx_dropped++; + fep->stats.rx_dropped++; } else { - skb_put(skb,pkt_len-4); /* Make room */ - skb_copy_to_linear_data(skb, data, pkt_len-4); - skb->protocol=eth_type_trans(skb,dev); + if ((pkt_len - 4) < fec_copy_threshold) { + /* skip 2 bytes, so IP header is on a 4 bytes boundary */ + skb_reserve(skb, 2); + skb_put(skb, pkt_len - 4); /* Make room */ + fec_enet_rxbuf_get(fep, bdp, pkt_len - 4); + skb_copy_to_linear_data(skb, + fep->rx_skbuff[rx_index]->data, + pkt_len - 4); + fec_enet_rxbuf_put(fep, bdp, pkt_len - 4); + } else { + struct sk_buff *pskb = fep->rx_skbuff[rx_index]; + + /* unmap the skb we are going to hand down to the network layer */ + fec_enet_rxbuf_unmap(fep, bdp, FEC_ENET_RX_FRSIZE); + + /* init the newly allocated skb */ + fep->rx_skbuff[rx_index] = skb; + skb->data = FEC_ADDR_ALIGNMENT(skb->data); + /* map the newly allocated skb's data buffer for DMA */ + fec_enet_rxbuf_map(fep, bdp, skb->data, FEC_ENET_RX_FRSIZE); + + skb_put(pskb, pkt_len - 4); /* Make room */ + skb = pskb; + + } + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); } rx_processing_done: - /* Clear the status flags for this buffer. */ status &= ~BD_ENET_RX_STATS; @@ -660,6 +880,9 @@ while (!((status = bdp->cbd_sc) & BD_ENE status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; + /* release the dual port memory area for use by the FEC hardware */ + fec_enet_cbd_put(fep); + /* Update BD pointer to next entry. */ if (status & BD_ENET_RX_WRAP) @@ -672,10 +895,10 @@ while (!((status = bdp->cbd_sc) & BD_ENE * incoming frames. On a heavily loaded network, we should be * able to keep up at the expense of system resources. */ - fecp->fec_r_des_active = 0; + fec_reg_write(fep, FEC_RDAR, DONT_CARE); #endif } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */ - fep->cur_rx = (cbd_t *)bdp; + fep->cur_rx = bdp; #if 0 /* Doing this here will allow us to process all frames in the @@ -685,27 +908,28 @@ while (!((status = bdp->cbd_sc) & BD_ENE * our way back to the interrupt return only to come right back * here. */ - fecp->fec_r_des_active = 0; + fec_reg_write(fep, FEC_RDAR, DONT_CARE); #endif - - spin_unlock_irq(&fep->hw_lock); } - +#ifdef CONFIG_PHYLIB /* called from interrupt context */ +static void fec_enet_mii(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + fep->mii_complete = 1; +} +#else static void fec_enet_mii(struct net_device *dev) { - struct fec_enet_private *fep; - volatile fec_t *ep; + struct fec_enet_private *fep = netdev_priv(dev); mii_list_t *mip; uint mii_reg; - fep = netdev_priv(dev); - spin_lock_irq(&fep->mii_lock); + mii_reg = fec_reg_read(fep, FEC_MMFR); - ep = fep->hwp; - mii_reg = ep->fec_mii_data; + spin_lock(&fep->lock); if ((mip = mii_head) == NULL) { printk("MII and no head!\n"); @@ -720,27 +944,27 @@ fec_enet_mii(struct net_device *dev) mii_free = mip; if ((mip = mii_head) != NULL) - ep->fec_mii_data = mip->mii_regval; + fec_reg_write(fep, FEC_MMFR, mip->mii_regval); unlock: - spin_unlock_irq(&fep->mii_lock); + spin_unlock(&fep->lock); } static int mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) { - struct fec_enet_private *fep; + struct fec_enet_private *fep = netdev_priv(dev); unsigned long flags; mii_list_t *mip; int retval; + retval = 0; + + spin_lock_irqsave(&fep->lock,flags); + /* Add PHY address to register command. */ - fep = netdev_priv(dev); - spin_lock_irqsave(&fep->mii_lock, flags); - regval |= fep->phy_addr << 23; - retval = 0; if ((mip = mii_free) != NULL) { mii_free = mip->mii_next; @@ -752,32 +976,32 @@ mii_queue(struct net_device *dev, int re mii_tail = mip; } else { mii_head = mii_tail = mip; - fep->hwp->fec_mii_data = regval; + fec_reg_write(fep, FEC_MMFR, regval); } } else { retval = 1; } - spin_unlock_irqrestore(&fep->mii_lock, flags); - return retval; + spin_unlock_irqrestore(&fep->lock,flags); + + return(retval); } static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) { - if(!c) - return; + int k; - for (; c->mii_data != mk_mii_end; c++) - mii_queue(dev, c->mii_data, c->funct); + for (k = 0; c != NULL && c[k].mii_data != mk_mii_end; k++) { + mii_queue(dev, c[k].mii_data, c[k].funct); + } } static void mii_parse_sr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + status = fep->phy_status & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); if (mii_reg & 0x0004) status |= PHY_STAT_LINK; @@ -785,31 +1009,30 @@ static void mii_parse_sr(uint mii_reg, s status |= PHY_STAT_FAULT; if (mii_reg & 0x0020) status |= PHY_STAT_ANC; - *s = status; + + fep->phy_status = status; } static void mii_parse_cr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP); + status = fep->phy_status & ~(PHY_CONF_ANE | PHY_CONF_LOOP); if (mii_reg & 0x1000) status |= PHY_CONF_ANE; if (mii_reg & 0x4000) status |= PHY_CONF_LOOP; - *s = status; + fep->phy_status = status; } static void mii_parse_anar(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_CONF_SPMASK); + status = fep->phy_status & ~(PHY_CONF_SPMASK); if (mii_reg & 0x0020) status |= PHY_CONF_10HDX; @@ -819,7 +1042,7 @@ static void mii_parse_anar(uint mii_reg, status |= PHY_CONF_100HDX; if (mii_reg & 0x00100) status |= PHY_CONF_100FDX; - *s = status; + fep->phy_status = status; } /* ------------------------------------------------------------------------- */ @@ -834,10 +1057,9 @@ static void mii_parse_anar(uint mii_reg, static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_STAT_SPMASK); + status = fep->phy_status & ~(PHY_STAT_SPMASK); if (mii_reg & 0x0800) { if (mii_reg & 0x1000) status |= PHY_STAT_100FDX; @@ -849,7 +1071,7 @@ static void mii_parse_lxt970_csr(uint mi else status |= PHY_STAT_10HDX; } - *s = status; + fep->phy_status = status; } static phy_cmd_t const phy_cmd_lxt970_config[] = { @@ -905,16 +1127,15 @@ static phy_info_t const phy_info_lxt970 static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); + status = fep->phy_status & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); if (mii_reg & 0x0400) { - fep->link = 1; + fep->linkstatus = 1; status |= PHY_STAT_LINK; } else { - fep->link = 0; + fep->linkstatus = 0; } if (mii_reg & 0x0080) status |= PHY_STAT_ANC; @@ -932,7 +1153,7 @@ static void mii_parse_lxt971_sr2(uint mi if (mii_reg & 0x0008) status |= PHY_STAT_FAULT; - *s = status; + fep->phy_status = status; } static phy_cmd_t const phy_cmd_lxt971_config[] = { @@ -989,10 +1210,9 @@ static phy_info_t const phy_info_lxt971 static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_STAT_SPMASK); + status = fep->phy_status & ~(PHY_STAT_SPMASK); switch((mii_reg >> 2) & 7) { case 1: status |= PHY_STAT_10HDX; break; @@ -1001,7 +1221,7 @@ static void mii_parse_qs6612_pcr(uint mi case 6: status |= PHY_STAT_100FDX; break; } - *s = status; + fep->phy_status = status; } static phy_cmd_t const phy_cmd_qs6612_config[] = { @@ -1059,10 +1279,9 @@ static phy_info_t const phy_info_qs6612 static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); uint status; - status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC); + status = fep->phy_status & ~(PHY_STAT_SPMASK | PHY_STAT_ANC); if (mii_reg & 0x0080) status |= PHY_STAT_ANC; @@ -1071,7 +1290,7 @@ static void mii_parse_am79c874_dr(uint m else status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX); - *s = status; + fep->phy_status = status; } static phy_cmd_t const phy_cmd_am79c874_config[] = { @@ -1155,33 +1374,32 @@ static phy_info_t const phy_info_ks8721b static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev) { - struct fec_enet_private *fep = dev->priv; - volatile uint *s = &(fep->phy_status); + struct fec_enet_private *fep = netdev_priv(dev); - *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); + fep->phy_status &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); /* Link up */ if (mii_reg & 0x0001) { - fep->link = 1; - *s |= PHY_STAT_LINK; + fep->linkstatus = 1; + fep->phy_status |= PHY_STAT_LINK; } else - fep->link = 0; + fep->linkstatus = 0; /* Status of link */ if (mii_reg & 0x0010) /* Autonegotioation complete */ - *s |= PHY_STAT_ANC; + fep->phy_status |= PHY_STAT_ANC; if (mii_reg & 0x0002) { /* 10MBps? */ if (mii_reg & 0x0004) /* Full Duplex? */ - *s |= PHY_STAT_10FDX; + fep->phy_status |= PHY_STAT_10FDX; else - *s |= PHY_STAT_10HDX; + fep->phy_status |= PHY_STAT_10HDX; } else { /* 100 Mbps? */ if (mii_reg & 0x0004) /* Full Duplex? */ - *s |= PHY_STAT_100FDX; + fep->phy_status |= PHY_STAT_100FDX; else - *s |= PHY_STAT_100HDX; + fep->phy_status |= PHY_STAT_100HDX; } if (mii_reg & 0x0008) - *s |= PHY_STAT_FAULT; + fep->phy_status |= PHY_STAT_FAULT; } static phy_info_t phy_info_dp83848= { @@ -1218,660 +1436,249 @@ static phy_info_t const * const phy_info &phy_info_dp83848, NULL }; - -/* ------------------------------------------------------------------------- */ -#ifdef HAVE_mii_link_interrupt -static irqreturn_t -mii_link_interrupt(int irq, void * dev_id); #endif -#if defined(CONFIG_M5272) /* - * Code specific to Coldfire 5272 setup. + * do some initializtion based architecture of this chip +MOVED to platform_data hooks! */ -static void __inline__ fec_request_intrs(struct net_device *dev) -{ - volatile unsigned long *icrp; - static const struct idesc { - char *name; - unsigned short irq; - irq_handler_t handler; - } *idp, id[] = { - { "fec(RX)", 86, fec_enet_interrupt }, - { "fec(TX)", 87, fec_enet_interrupt }, - { "fec(OTHER)", 88, fec_enet_interrupt }, - { "fec(MII)", 66, mii_link_interrupt }, - { NULL }, - }; - - /* Setup interrupt handlers. */ - for (idp = id; idp->name; idp++) { - if (request_irq(idp->irq, idp->handler, IRQF_DISABLED, idp->name, dev) != 0) - printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, idp->irq); - } - /* Unmask interrupt at ColdFire 5272 SIM */ - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3); - *icrp = 0x00000ddd; - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1); - *icrp = 0x0d000000; -} +#define PHY_POLL_LINK_ON (1 * HZ) +#define PHY_POLL_LINK_OFF (HZ / 5) -static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) +#ifdef CONFIG_PHYLIB +static void fec_link_change(struct net_device *dev) { - volatile fec_t *fecp; - - fecp = fep->hwp; - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04; - fecp->fec_x_cntrl = 0x00; - - /* - * Set MII speed to 2.5 MHz - * See 5272 manual section 11.5.8: MSCR - */ - fep->phy_speed = ((((MCF_CLK / 4) / (2500000 / 10)) + 5) / 10) * 2; - fecp->fec_mii_speed = fep->phy_speed; + struct fec_enet_private *fep = netdev_priv(dev); + struct phy_device *phydev = fep->phy; - fec_restart(dev, 0); + if (phydev->link != fep->linkstatus || + phydev->duplex != fep->full_duplex) { + DBG(0, "%s: link status changed from %d to %d %s -> %s duplex\n", __FUNCTION__, + fep->linkstatus, phydev->link, fep->full_duplex ? "full" : "half", + phydev->duplex ? "full" : "half"); + if (phydev->link) { + fec_restart(dev, phydev->duplex); + } else { + fec_stop(dev); + } + if (fep->linkstatus != phydev->link && netif_msg_link(fep)) { + phy_print_status(phydev); + } + fep->linkstatus = phydev->link; + } } - -static void __inline__ fec_get_mac(struct net_device *dev) +#else +static void fec_link_change(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile fec_t *fecp; - unsigned char *iap, tmpaddr[ETH_ALEN]; - fecp = fep->hwp; + DBG(0, "%s: link status changed from %d to %d\n", __FUNCTION__, + fep->old_linkstatus, fep->linkstatus); + if (fep->linkstatus) { + int duplex; - if (FEC_FLASHMAC) { - /* - * Get MAC address from FLASH. - * If it is all 1's or 0's, use the default. - */ - iap = (unsigned char *)FEC_FLASHMAC; - if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && - (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) - iap = fec_mac_default; - if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) && - (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff)) - iap = fec_mac_default; + duplex = 0; + if (fep->phy_status & (PHY_STAT_100FDX | PHY_STAT_10FDX)) { + duplex = 1; + } + fec_restart(dev, duplex); + if (fep->phy_timer) { + mod_timer(fep->phy_timer, jiffies + PHY_POLL_LINK_ON); + } } else { - *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low; - *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16); - iap = &tmpaddr[0]; + fec_stop(dev); + if (fep->phy_timer) { + mod_timer(fep->phy_timer, jiffies + PHY_POLL_LINK_OFF); + } } - memcpy(dev->dev_addr, iap, ETH_ALEN); - - /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) - dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; -} - -static void __inline__ fec_enable_phy_intr(void) -{ -} - -static void __inline__ fec_disable_phy_intr(void) -{ - volatile unsigned long *icrp; - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1); - *icrp = 0x08000000; -} - -static void __inline__ fec_phy_ack_intr(void) -{ - volatile unsigned long *icrp; - /* Acknowledge the interrupt */ - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1); - *icrp = 0x0d000000; + fep->old_linkstatus = fep->linkstatus; } -static void __inline__ fec_localhw_setup(void) +static void fec_phy_timer(unsigned long data) { -} + struct net_device *dev = (struct net_device *)data; + struct fec_enet_private *fep = netdev_priv(dev); + int link_poll_interval = fep->linkstatus ? PHY_POLL_LINK_ON : PHY_POLL_LINK_OFF; -/* - * Do not need to make region uncached on 5272. - */ -static void __inline__ fec_uncache(unsigned long addr) -{ + if (fep->old_linkstatus != fep->linkstatus) { + fec_link_change(dev); + } + mod_timer(fep->phy_timer, link_poll_interval); } - -/* ------------------------------------------------------------------------- */ - -#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) +#endif /* - * Code specific to Coldfire 5230/5231/5232/5234/5235, - * the 5270/5271/5274/5275 and 5280/5282 setups. + * Code specific to Freescale i.MXC */ -static void __inline__ fec_request_intrs(struct net_device *dev) +static int fec_request_intrs(struct platform_device *pdev, struct net_device *dev) { - struct fec_enet_private *fep; - int b; - static const struct idesc { - char *name; - unsigned short irq; - } *idp, id[] = { - { "fec(TXF)", 23 }, - { "fec(RXF)", 27 }, - { "fec(MII)", 29 }, - { NULL }, - }; + int ret; + struct fec_enet_private *fep = netdev_priv(dev); - fep = netdev_priv(dev); - b = (fep->index) ? 128 : 64; + fep->etn_irq = platform_get_irq(pdev, 0); + fep->mii_irq = platform_get_irq(pdev, 1); /* Setup interrupt handlers. */ - for (idp = id; idp->name; idp++) { - if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name, dev) != 0) - printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq); - } - - /* Unmask interrupts at ColdFire 5280/5282 interrupt controller */ - { - volatile unsigned char *icrp; - volatile unsigned long *imrp; - int i, ilip; - - b = (fep->index) ? MCFICM_INTC1 : MCFICM_INTC0; - icrp = (volatile unsigned char *) (MCF_IPSBAR + b + - MCFINTC_ICR0); - for (i = 23, ilip = 0x28; (i < 36); i++) - icrp[i] = ilip--; - - imrp = (volatile unsigned long *) (MCF_IPSBAR + b + - MCFINTC_IMRH); - *imrp &= ~0x0000000f; - imrp = (volatile unsigned long *) (MCF_IPSBAR + b + - MCFINTC_IMRL); - *imrp &= ~0xff800001; - } - -#if defined(CONFIG_M528x) - /* Set up gpio outputs for MII lines */ - { - volatile u16 *gpio_paspar; - volatile u8 *gpio_pehlpar; - - gpio_paspar = (volatile u16 *) (MCF_IPSBAR + 0x100056); - gpio_pehlpar = (volatile u16 *) (MCF_IPSBAR + 0x100058); - *gpio_paspar |= 0x0f00; - *gpio_pehlpar = 0xc0; + ret = request_irq(fep->etn_irq, fec_enet_interrupt, 0, "fec", dev); + if (ret != 0) { + printk(KERN_ERR "FEC: Could not allocate FEC IRQ(%d)!\n", fep->etn_irq); + return ret; + } +#ifndef CONFIG_PHYLIB + if (fep->mii_irq >= 0) { + /* TODO: disable now due to CPLD issue */ + ret = request_irq(fep->mii_irq, mii_link_interrupt, 0, "fec(MII)", dev); + if (ret != 0) { + printk(KERN_ERR "FEC: Could not allocate FEC(MII) IRQ(%d)!\n", + fep->mii_irq); + free_irq(fep->etn_irq, dev); + return ret; + } + /* + * board specific workaround should be done in board specific code + * This is unsafe anyway. An interrupt might have been asserted + * already. Use IRQ_NOAUTOEN with request_irq() to have irq initially disabled. + */ + fep->phy_int_enabled = 1; + } else { + fep->phy_timer = kzalloc(sizeof(struct timer_list), GFP_KERNEL); + if (fep->phy_timer == NULL) { + free_irq(fep->etn_irq, dev); + return -ENOMEM; + } + init_timer(fep->phy_timer); + fep->phy_timer->function = fec_phy_timer; + fep->phy_timer->data = (unsigned long)dev; + fec_link_change(dev); } #endif -#if defined(CONFIG_M527x) - /* Set up gpio outputs for MII lines */ - { - volatile u8 *gpio_par_fec; - volatile u16 *gpio_par_feci2c; - - gpio_par_feci2c = (volatile u16 *)(MCF_IPSBAR + 0x100082); - /* Set up gpio outputs for FEC0 MII lines */ - gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100078); - - *gpio_par_feci2c |= 0x0f00; - *gpio_par_fec |= 0xc0; + return 0; +} -#if defined(CONFIG_FEC2) - /* Set up gpio outputs for FEC1 MII lines */ - gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100079); +static void __inline__ fec_release_intrs(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); - *gpio_par_feci2c |= 0x00a0; - *gpio_par_fec |= 0xc0; -#endif /* CONFIG_FEC2 */ + free_irq(fep->etn_irq, dev); +#ifndef CONFIG_PHYLIB + if (fep->mii_irq >= 0) { + free_irq(fep->mii_irq, dev); } -#endif /* CONFIG_M527x */ +#endif } static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) { - volatile fec_t *fecp; + u32 rate; + struct clk *clk; - fecp = fep->hwp; - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04; - fecp->fec_x_cntrl = 0x00; + fec_reg_write(fep, FEC_RCR, OPT_FRAME_SIZE | 0x04); + fec_reg_write(fep, FEC_TCR, 0x00); - /* + /* * Set MII speed to 2.5 MHz - * See 5282 manual section 17.5.4.7: MSCR */ - fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; - fecp->fec_mii_speed = fep->phy_speed; + clk = clk_get(fep->dma_dev, "fec_clk"); + rate = clk_get_rate(clk); + clk_put(clk); - fec_restart(dev, 0); + fep->phy_speed = ((((rate / 2 + 4999999) / 2500000) / 2) & 0x3F) << 1; + fec_reg_write(fep, FEC_MSCR, fep->phy_speed); } -static void __inline__ fec_get_mac(struct net_device *dev) +static const unsigned char default_mac[ETH_ALEN] = { + 0x00, 0x04, 0x9f, 0x00, 0x74, 0x4a, +}; + +#define FEC_IIM_BASE IO_ADDRESS(IIM_BASE_ADDR) +static void fec_get_mac(struct net_device *dev) { +#if 1 + // keep bootloader assigned MAC address struct fec_enet_private *fep = netdev_priv(dev); - volatile fec_t *fecp; - unsigned char *iap, tmpaddr[ETH_ALEN]; - - fecp = fep->hwp; + unsigned long eth_addr = fec_reg_read(fep, FEC_PALR); + dev->dev_addr[0] = eth_addr >> 24; + dev->dev_addr[1] = eth_addr >> 16; + dev->dev_addr[2] = eth_addr >> 8; + dev->dev_addr[3] = eth_addr >> 0; + eth_addr = fec_reg_read(fep, FEC_PAUR); + dev->dev_addr[5] = eth_addr >> 16; + dev->dev_addr[4] = eth_addr >> 24; +#else + int i; + unsigned long fec_mac_base = FEC_IIM_BASE + MXC_IIMKEY0; - if (FEC_FLASHMAC) { - /* - * Get MAC address from FLASH. - * If it is all 1's or 0's, use the default. - */ - iap = FEC_FLASHMAC; - if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && - (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) - iap = fec_mac_default; - if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) && - (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff)) - iap = fec_mac_default; - } else { - *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low; - *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16); - iap = &tmpaddr[0]; + if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) { + fec_mac_base = FEC_IIM_BASE + MXC_IIMMAC; } - memcpy(dev->dev_addr, iap, ETH_ALEN); - - /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) - dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; + DBG(0, "%s: Reading MAC address from %08lx\n", __FUNCTION__, fec_mac_base); + for (i = 0; i < ETH_ALEN; i++) { + dev->dev_addr[ETH_ALEN - 1 - i] = __raw_readb(fec_mac_base + i * 4); + } + //memcpy(dev->dev_addr, default_mac, ETH_ALEN); +#endif } -static void __inline__ fec_enable_phy_intr(void) +#ifdef CONFIG_PHYLIB +static void __inline__ fec_enable_phy_intr(struct fec_enet_private *fep) { } - -static void __inline__ fec_disable_phy_intr(void) +static void __inline__ fec_disable_phy_intr(struct fec_enet_private *fep) { } - -static void __inline__ fec_phy_ack_intr(void) +static void __inline__ fec_phy_ack_intr(struct fec_enet_private *fep) { } - -static void __inline__ fec_localhw_setup(void) +#else +static void __inline__ fec_enable_phy_intr(struct fec_enet_private *fep) { -} - -/* - * Do not need to make region uncached on 5272. - */ -static void __inline__ fec_uncache(unsigned long addr) -{ -} - -/* ------------------------------------------------------------------------- */ - -#elif defined(CONFIG_M520x) - -/* - * Code specific to Coldfire 520x - */ -static void __inline__ fec_request_intrs(struct net_device *dev) -{ - struct fec_enet_private *fep; - int b; - static const struct idesc { - char *name; - unsigned short irq; - } *idp, id[] = { - { "fec(TXF)", 23 }, - { "fec(RXF)", 27 }, - { "fec(MII)", 29 }, - { NULL }, - }; - - fep = netdev_priv(dev); - b = 64 + 13; - - /* Setup interrupt handlers. */ - for (idp = id; idp->name; idp++) { - if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name,dev) != 0) - printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq); - } - - /* Unmask interrupts at ColdFire interrupt controller */ - { - volatile unsigned char *icrp; - volatile unsigned long *imrp; - - icrp = (volatile unsigned char *) (MCF_IPSBAR + MCFICM_INTC0 + - MCFINTC_ICR0); - for (b = 36; (b < 49); b++) - icrp[b] = 0x04; - imrp = (volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + - MCFINTC_IMRH); - *imrp &= ~0x0001FFF0; + if (!fep->phy_int_enabled) { + fep->phy_int_enabled = 1; + enable_irq(fep->mii_irq); } - *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FEC) |= 0xf0; - *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C) |= 0x0f; } -static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) +static void __inline__ fec_disable_phy_intr(struct fec_enet_private *fep) { - volatile fec_t *fecp; - - fecp = fep->hwp; - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04; - fecp->fec_x_cntrl = 0x00; - - /* - * Set MII speed to 2.5 MHz - * See 5282 manual section 17.5.4.7: MSCR - */ - fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; - fecp->fec_mii_speed = fep->phy_speed; - - fec_restart(dev, 0); -} - -static void __inline__ fec_get_mac(struct net_device *dev) -{ - struct fec_enet_private *fep = netdev_priv(dev); - volatile fec_t *fecp; - unsigned char *iap, tmpaddr[ETH_ALEN]; - - fecp = fep->hwp; - - if (FEC_FLASHMAC) { - /* - * Get MAC address from FLASH. - * If it is all 1's or 0's, use the default. - */ - iap = FEC_FLASHMAC; - if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && - (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) - iap = fec_mac_default; - if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) && - (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff)) - iap = fec_mac_default; - } else { - *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low; - *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16); - iap = &tmpaddr[0]; + if (fep->phy_int_enabled) { + disable_irq(fep->mii_irq); + fep->phy_int_enabled = 0; } - - memcpy(dev->dev_addr, iap, ETH_ALEN); - - /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) - dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; -} - -static void __inline__ fec_enable_phy_intr(void) -{ -} - -static void __inline__ fec_disable_phy_intr(void) -{ -} - -static void __inline__ fec_phy_ack_intr(void) -{ -} - -static void __inline__ fec_localhw_setup(void) -{ } -static void __inline__ fec_uncache(unsigned long addr) +static void __inline__ fec_phy_ack_intr(struct fec_enet_private *fep) { -} - -/* ------------------------------------------------------------------------- */ - -#elif defined(CONFIG_M532x) -/* - * Code specific for M532x - */ -static void __inline__ fec_request_intrs(struct net_device *dev) -{ - struct fec_enet_private *fep; - int b; - static const struct idesc { - char *name; - unsigned short irq; - } *idp, id[] = { - { "fec(TXF)", 36 }, - { "fec(RXF)", 40 }, - { "fec(MII)", 42 }, - { NULL }, - }; - - fep = netdev_priv(dev); - b = (fep->index) ? 128 : 64; - - /* Setup interrupt handlers. */ - for (idp = id; idp->name; idp++) { - if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name,dev) != 0) - printk("FEC: Could not allocate %s IRQ(%d)!\n", - idp->name, b+idp->irq); - } - - /* Unmask interrupts */ - MCF_INTC0_ICR36 = 0x2; - MCF_INTC0_ICR37 = 0x2; - MCF_INTC0_ICR38 = 0x2; - MCF_INTC0_ICR39 = 0x2; - MCF_INTC0_ICR40 = 0x2; - MCF_INTC0_ICR41 = 0x2; - MCF_INTC0_ICR42 = 0x2; - MCF_INTC0_ICR43 = 0x2; - MCF_INTC0_ICR44 = 0x2; - MCF_INTC0_ICR45 = 0x2; - MCF_INTC0_ICR46 = 0x2; - MCF_INTC0_ICR47 = 0x2; - MCF_INTC0_ICR48 = 0x2; - - MCF_INTC0_IMRH &= ~( - MCF_INTC_IMRH_INT_MASK36 | - MCF_INTC_IMRH_INT_MASK37 | - MCF_INTC_IMRH_INT_MASK38 | - MCF_INTC_IMRH_INT_MASK39 | - MCF_INTC_IMRH_INT_MASK40 | - MCF_INTC_IMRH_INT_MASK41 | - MCF_INTC_IMRH_INT_MASK42 | - MCF_INTC_IMRH_INT_MASK43 | - MCF_INTC_IMRH_INT_MASK44 | - MCF_INTC_IMRH_INT_MASK45 | - MCF_INTC_IMRH_INT_MASK46 | - MCF_INTC_IMRH_INT_MASK47 | - MCF_INTC_IMRH_INT_MASK48 ); - - /* Set up gpio outputs for MII lines */ - MCF_GPIO_PAR_FECI2C |= (0 | - MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC | - MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO); - MCF_GPIO_PAR_FEC = (0 | - MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC | - MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC); -} - -static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) -{ - volatile fec_t *fecp; - - fecp = fep->hwp; - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04; - fecp->fec_x_cntrl = 0x00; - - /* - * Set MII speed to 2.5 MHz - */ - fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; - fecp->fec_mii_speed = fep->phy_speed; - - fec_restart(dev, 0); -} - -static void __inline__ fec_get_mac(struct net_device *dev) -{ - struct fec_enet_private *fep = netdev_priv(dev); - volatile fec_t *fecp; - unsigned char *iap, tmpaddr[ETH_ALEN]; - - fecp = fep->hwp; - - if (FEC_FLASHMAC) { - /* - * Get MAC address from FLASH. - * If it is all 1's or 0's, use the default. - */ - iap = FEC_FLASHMAC; - if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && - (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) - iap = fec_mac_default; - if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) && - (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff)) - iap = fec_mac_default; - } else { - *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low; - *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16); - iap = &tmpaddr[0]; + if (fep->phy_int_enabled) { + disable_irq(fep->mii_irq); + fep->phy_int_enabled = 0; } - - memcpy(dev->dev_addr, iap, ETH_ALEN); - - /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) - dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; } - -static void __inline__ fec_enable_phy_intr(void) -{ -} - -static void __inline__ fec_disable_phy_intr(void) -{ -} - -static void __inline__ fec_phy_ack_intr(void) -{ -} - -static void __inline__ fec_localhw_setup(void) -{ -} - -/* - * Do not need to make region uncached on 532x. - */ -static void __inline__ fec_uncache(unsigned long addr) -{ -} - -/* ------------------------------------------------------------------------- */ - - -#else - -/* - * Code specific to the MPC860T setup. - */ -static void __inline__ fec_request_intrs(struct net_device *dev) -{ - volatile immap_t *immap; - - immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ - - if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) - panic("Could not allocate FEC IRQ!"); -} - -static void __inline__ fec_get_mac(struct net_device *dev) -{ - bd_t *bd; - - bd = (bd_t *)__res; - memcpy(dev->dev_addr, bd->bi_enetaddr, ETH_ALEN); -} - -static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) -{ - extern uint _get_IMMR(void); - volatile immap_t *immap; - volatile fec_t *fecp; - - fecp = fep->hwp; - immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ - - /* Configure all of port D for MII. - */ - immap->im_ioport.iop_pdpar = 0x1fff; - - /* Bits moved from Rev. D onward. - */ - if ((_get_IMMR() & 0xffff) < 0x0501) - immap->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ - else - immap->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ - - /* Set MII speed to 2.5 MHz - */ - fecp->fec_mii_speed = fep->phy_speed = - ((bd->bi_busfreq * 1000000) / 2500000) & 0x7e; -} - -static void __inline__ fec_enable_phy_intr(void) -{ - volatile fec_t *fecp; - - fecp = fep->hwp; - - /* Enable MII command finished interrupt - */ - fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; -} - -static void __inline__ fec_disable_phy_intr(void) -{ -} - -static void __inline__ fec_phy_ack_intr(void) -{ -} - -static void __inline__ fec_localhw_setup(void) -{ - volatile fec_t *fecp; - - fecp = fep->hwp; - fecp->fec_r_hash = PKT_MAXBUF_SIZE; - /* Enable big endian and don't care about SDMA FC. - */ - fecp->fec_fun_code = 0x78000000; -} - -static void __inline__ fec_uncache(unsigned long addr) -{ - pte_t *pte; - pte = va_to_pte(mem_addr); - pte_val(*pte) |= _PAGE_NO_CACHE; - flush_tlb_page(init_mm.mmap, mem_addr); -} - #endif /* ------------------------------------------------------------------------- */ +#ifndef CONFIG_PHYLIB static void mii_display_status(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); - if (!fep->link && !fep->old_link) { + if (!fep->linkstatus && !fep->old_linkstatus) { /* Link is still down - don't print anything */ return; } printk("%s: status: ", dev->name); - if (!fep->link) { + if (!fep->linkstatus) { printk("link down"); } else { printk("link up"); - switch(*s & PHY_STAT_SPMASK) { + switch(fep->phy_status & PHY_STAT_SPMASK) { case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break; case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break; case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break; @@ -1880,20 +1687,19 @@ static void mii_display_status(struct ne printk(", Unknown speed/duplex"); } - if (*s & PHY_STAT_ANC) + if (fep->phy_status & PHY_STAT_ANC) printk(", auto-negotiation complete"); } - if (*s & PHY_STAT_FAULT) + if (fep->phy_status & PHY_STAT_FAULT) printk(", remote fault"); printk(".\n"); } -static void mii_display_config(struct work_struct *work) +static void mii_display_config(struct work_struct *w) { - struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task); - struct net_device *dev = fep->netdev; + struct fec_enet_private *fep = container_of(w, struct fec_enet_private, phy_task); uint status = fep->phy_status; /* @@ -1901,7 +1707,7 @@ static void mii_display_config(struct wo ** the workqueue. It is thus safe to allow to reuse it. */ fep->mii_phy_task_queued = 0; - printk("%s: config: auto-negotiation ", dev->name); + //printk("%s: config: auto-negotiation ", dev->name); if (status & PHY_CONF_ANE) printk("on"); @@ -1926,11 +1732,21 @@ static void mii_display_config(struct wo fep->sequence_done = 1; } +#endif + +#ifndef CONFIG_PHYLIB +static inline void *priv_netdev(struct fec_enet_private *fep) +{ + /* ugly hack, stolen from include linux/netdevice.h */ + return (char *)fep - ((sizeof(struct net_device) + + NETDEV_ALIGN_CONST) + & ~NETDEV_ALIGN_CONST); +} -static void mii_relink(struct work_struct *work) +static void mii_relink(struct work_struct *w) { - struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task); - struct net_device *dev = fep->netdev; + struct fec_enet_private *fep = container_of(w, struct fec_enet_private, phy_task); + struct net_device *dev = priv_netdev(fep); int duplex; /* @@ -1938,23 +1754,19 @@ static void mii_relink(struct work_struc ** the workqueue. It is thus safe to allow to reuse it. */ fep->mii_phy_task_queued = 0; - fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; + fep->linkstatus = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; mii_display_status(dev); - fep->old_link = fep->link; + fep->old_linkstatus = fep->linkstatus; - if (fep->link) { + if (fep->linkstatus) { duplex = 0; - if (fep->phy_status - & (PHY_STAT_100FDX | PHY_STAT_10FDX)) + if (fep->phy_status & (PHY_STAT_100FDX | PHY_STAT_10FDX)) { duplex = 1; + } fec_restart(dev, duplex); - } else + } else { fec_stop(dev); - -#if 0 - enable_irq(fep->mii_irq); -#endif - + } } /* mii_queue_relink is called in interrupt context from mii_link_interrupt */ @@ -2004,15 +1816,14 @@ phy_cmd_t const phy_cmd_config[] = { static void mii_discover_phy3(uint mii_reg, struct net_device *dev) { - struct fec_enet_private *fep; + struct fec_enet_private *fep = netdev_priv(dev); int i; - fep = netdev_priv(dev); fep->phy_id |= (mii_reg & 0xffff); printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id); - for(i = 0; phy_info[i]; i++) { - if(phy_info[i]->id == (fep->phy_id >> 4)) + for (i = 0; phy_info[i]; i++) { + if (phy_info[i]->id == (fep->phy_id >> 4)) break; } @@ -2031,13 +1842,9 @@ mii_discover_phy3(uint mii_reg, struct n static void mii_discover_phy(uint mii_reg, struct net_device *dev) { - struct fec_enet_private *fep; - volatile fec_t *fecp; + struct fec_enet_private *fep = netdev_priv(dev); uint phytype; - fep = netdev_priv(dev); - fecp = fep->hwp; - if (fep->phy_addr < 32) { if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) { @@ -2045,37 +1852,41 @@ mii_discover_phy(uint mii_reg, struct ne */ fep->phy_id = phytype << 16; mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), - mii_discover_phy3); + mii_discover_phy3); } else { fep->phy_addr++; mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), - mii_discover_phy); + mii_discover_phy); } } else { printk("FEC: No PHY device found.\n"); /* Disable external MII interface */ - fecp->fec_mii_speed = fep->phy_speed = 0; - fec_disable_phy_intr(); + fep->phy_speed = 0; + fec_reg_write(fep, FEC_MSCR, fep->phy_speed); + fec_disable_phy_intr(fep); } } +#endif -/* This interrupt occurs when the PHY detects a link change. -*/ -#ifdef HAVE_mii_link_interrupt +#ifndef CONFIG_PHYLIB static irqreturn_t -mii_link_interrupt(int irq, void * dev_id) +mii_link_interrupt(int irq, void *dev_id) { - struct net_device *dev = dev_id; + struct net_device *dev = dev_id; struct fec_enet_private *fep = netdev_priv(dev); - fec_phy_ack_intr(); + DBG(0, "%s: \n", __FUNCTION__); -#if 0 - disable_irq(fep->mii_irq); /* disable now, enable later */ -#endif + fec_phy_ack_intr(fep); - mii_do_cmd(dev, fep->phy->ack_int); - mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ + /* + * Some board will trigger phy interrupt before phy enable. + * And at that moment , fep->phy is not initialized. + */ + if (fep->phy) { + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ + } return IRQ_HANDLED; } @@ -2084,16 +1895,29 @@ mii_link_interrupt(int irq, void * dev_i static int fec_enet_open(struct net_device *dev) { + int ret = 0; struct fec_enet_private *fep = netdev_priv(dev); /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */ - fec_set_mac_address(dev); + DBG(0, "%s: \n", __FUNCTION__); + _fec_set_mac_address(dev); - fep->sequence_done = 0; - fep->link = 0; +#ifdef CONFIG_PHYLIB + ret = fec_connect_phy(dev, fep); + if (ret != 0) { + DBG(0, "%s: Failed to connect to PHY: %d\n", __FUNCTION__, ret); + return ret; + } + phy_start(fep->phy); + fep->linkstatus = fep->phy->link; + fec_restart(dev, 0); + DBG(0, "%s: Link status is: %d\n", __FUNCTION__, fep->linkstatus); +#else + fep->linkstatus = 0; + fep->sequence_done = 0; if (fep->phy) { mii_do_cmd(dev, fep->phy->ack_int); mii_do_cmd(dev, fep->phy->config); @@ -2115,16 +1939,16 @@ fec_enet_open(struct net_device *dev) * based on this device does not implement a PHY interrupt, * so we are never notified of link change. */ - fep->link = 1; + fep->linkstatus = 1; } else { - fep->link = 1; /* lets just try it and see */ + fep->linkstatus = 1; /* lets just try it and see */ /* no phy, go full duplex, it's most likely a hub chip */ fec_restart(dev, 1); } - - netif_start_queue(dev); + fep->old_linkstatus = fep->linkstatus; +#endif fep->opened = 1; - return 0; /* Success */ + return ret; } static int @@ -2132,15 +1956,30 @@ fec_enet_close(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - /* Don't know what to do yet. - */ - fep->opened = 0; - netif_stop_queue(dev); - fec_stop(dev); +#ifdef CONFIG_PHYLIB + if (fep->phy) { + DBG(0, "%s: Stopping PHY %p\n", __FUNCTION__, fep->phy); + phy_stop(fep->phy); + DBG(0, "%s: Disconnecting PHY %p\n", __FUNCTION__, fep->phy); + phy_disconnect(fep->phy); + fep->phy = NULL; + } +#endif + fep->opened = 0; + if (fep->linkstatus) { + fec_stop(dev); + } return 0; } +static struct net_device_stats *fec_enet_get_stats(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + + return &fep->stats; +} + /* Set or clear the multicast filter for this adaptor. * Skeleton taken from sunlance driver. * The CPM Ethernet implementation allows Multicast as well as individual @@ -2156,37 +1995,32 @@ fec_enet_close(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { - struct fec_enet_private *fep; - volatile fec_t *ep; + struct fec_enet_private *fep = netdev_priv(dev); struct dev_mc_list *dmi; unsigned int i, j, bit, data, crc; unsigned char hash; - fep = netdev_priv(dev); - ep = fep->hwp; - - if (dev->flags&IFF_PROMISC) { - ep->fec_r_cntrl |= 0x0008; + if (dev->flags & IFF_PROMISC) { + fec_reg_write(fep, FEC_RCR, fec_reg_read(fep, FEC_RCR) | 0x0008); } else { - ep->fec_r_cntrl &= ~0x0008; + fec_reg_write(fep, FEC_RCR, fec_reg_read(fep, FEC_RCR) & ~0x0008); if (dev->flags & IFF_ALLMULTI) { /* Catch all multicast addresses, so set the * filter to all 1's. */ - ep->fec_grp_hash_table_high = 0xffffffff; - ep->fec_grp_hash_table_low = 0xffffffff; + fec_reg_write(fep, FEC_IAUR, 0xffffffff); + fec_reg_write(fep, FEC_IALR, 0xffffffff); } else { /* Clear filter and add the addresses in hash register. */ - ep->fec_grp_hash_table_high = 0; - ep->fec_grp_hash_table_low = 0; + fec_reg_write(fep, FEC_IAUR, 0); + fec_reg_write(fep, FEC_IALR, 0); dmi = dev->mc_list; - for (j = 0; j < dev->mc_count; j++, dmi = dmi->next) - { + for (j = 0; j < dev->mc_count; j++, dmi = dmi->next) { /* Only support group multicast for now. */ if (!(dmi->dmi_addr[0] & 1)) @@ -2196,11 +2030,9 @@ static void set_multicast_list(struct ne */ crc = 0xffffffff; - for (i = 0; i < dmi->dmi_addrlen; i++) - { + for (i = 0; i < dmi->dmi_addrlen; i++) { data = dmi->dmi_addr[i]; - for (bit = 0; bit < 8; bit++, data >>= 1) - { + for (bit = 0; bit < 8; bit++, data >>= 1) { crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CRC32_POLY : 0); } @@ -2212,9 +2044,13 @@ static void set_multicast_list(struct ne hash = (crc >> (32 - HASH_BITS)) & 0x3f; if (hash > 31) - ep->fec_grp_hash_table_high |= 1 << (hash - 32); + fec_reg_write(fep, FEC_IAUR, + fec_reg_read(fep, FEC_IAUR) | + (1 << (hash - 32))); else - ep->fec_grp_hash_table_low |= 1 << hash; + fec_reg_write(fep, FEC_IALR, + fec_reg_read(fep, FEC_IALR) | + (1 << hash)); } } } @@ -2223,61 +2059,211 @@ static void set_multicast_list(struct ne /* Set a MAC change in hardware. */ static void -fec_set_mac_address(struct net_device *dev) +_fec_set_mac_address(struct net_device *dev) { - volatile fec_t *fecp; - - fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp; + struct fec_enet_private *fep = netdev_priv(dev); /* Set station address. */ - fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) | - (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24); - fecp->fec_addr_high = (dev->dev_addr[5] << 16) | - (dev->dev_addr[4] << 24); + fec_reg_write(fep, FEC_PALR, dev->dev_addr[3] | (dev->dev_addr[2] << 8) | + (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24)); + fec_reg_write(fep, FEC_PAUR, (dev->dev_addr[5] << 16) | + (dev->dev_addr[4] << 24)); +} + +static int +fec_set_mac_address(struct net_device *dev, void *_addr) +{ + struct sockaddr *addr = _addr; + if (!is_valid_ether_addr((const char *)&addr->sa_data)) { + printk(KERN_WARNING "Bad ethernet address: %02x:%02x:%02x:%02x:%02x:%02x\n", + addr->sa_data[0], addr->sa_data[1], addr->sa_data[2], addr->sa_data[3], + addr->sa_data[4], addr->sa_data[5]); + return -EINVAL; + } + printk(KERN_DEBUG "Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", + addr->sa_data[0], addr->sa_data[1], addr->sa_data[2], addr->sa_data[3], + addr->sa_data[4], addr->sa_data[5]); + + memcpy(&dev->dev_addr, &addr->sa_data, ETH_ALEN); + + _fec_set_mac_address(dev); + + return 0; } -/* Initialize the FEC Ethernet on 860T (or ColdFire 5272). - */ - /* - * XXX: We need to clean up on failure exits here. - */ -int __init fec_enet_init(struct net_device *dev) +static void fec_enet_free_buffers(struct fec_enet_private *fep) { + cbd_t *bdp = fep->rx_bd_base; + int i; + + DBG(0, "%s: Freeing TX bounce buffers %p\n", __FUNCTION__, fep->tx_bounce[0]); + kfree(fep->tx_bounce[0]); + memset(fep->tx_bounce, 0, TX_RING_SIZE * sizeof(void*)); + for (i = 0; i < RX_RING_SIZE; i++, bdp++) { + if (fep->rx_skbuff[i] != NULL) { + DBG(0, "%s: Freeing RX skb %p\n", __FUNCTION__, fep->rx_skbuff[i]); + fec_enet_rxbuf_unmap(fep, bdp, FEC_ENET_RX_FRSIZE); + kfree_skb(fep->rx_skbuff[i]); + fep->rx_skbuff[i] = NULL; + } + } +} + +#ifdef CONFIG_PHYLIB +/* called by the generic PHY layer in interrupt context */ +static int fec_mii_read(struct mii_bus *bus, int phy_id, int regnum) +{ + int ret; + struct net_device *dev = bus->priv; struct fec_enet_private *fep = netdev_priv(dev); - unsigned long mem_addr; - volatile cbd_t *bdp; - cbd_t *cbd_base; - volatile fec_t *fecp; - int i, j; - static int index = 0; + unsigned long regval = mk_mii_read(regnum) | phy_id << 23; + unsigned long flags; + static int regs[32] = { [0 ... ARRAY_SIZE(regs) - 1] = -1}; - /* Only allow us to be probed once. */ - if (index >= FEC_MAX_PORTS) - return -ENXIO; + spin_lock_irqsave(&fep->lock, flags); + fep->mii_complete = 0; + fec_reg_write(fep, FEC_MMFR, regval); + spin_unlock_irqrestore(&fep->lock, flags); + + while (!fep->mii_complete) { + cpu_relax(); + } + + ret = fec_reg_read(fep, FEC_MMFR) & 0xffff; + if (ret < 0) { + DBG(0, "%s: Failed to read PHY[%02x] reg %02x: %d\n", __FUNCTION__, + phy_id, regnum, ret); + } else if (regs[regnum] != ret) { + DBG(1, "%s: Read %04x from PHY[%02x] reg %02x\n", __FUNCTION__, + ret, phy_id, regnum); + regs[regnum] = ret; + } + return ret; +} - /* Allocate memory for buffer descriptors. - */ - mem_addr = __get_free_page(GFP_KERNEL); - if (mem_addr == 0) { - printk("FEC: allocate descriptor memory failed?\n"); +static int fec_mii_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) +{ + struct net_device *dev = bus->priv; + struct fec_enet_private *fep = netdev_priv(dev); + unsigned long regval = mk_mii_write(regnum, val) | phy_id << 23; + unsigned long flags; + + spin_lock_irqsave(&fep->lock, flags); + fep->mii_complete = 0; + fec_reg_write(fep, FEC_MMFR, regval); + spin_unlock_irqrestore(&fep->lock, flags); + + while (!fep->mii_complete) { + cpu_relax(); + } + DBG(1, "%s: Wrote %04x to PHY[%02x] reg %02x\n", __FUNCTION__, val, phy_id, regnum); + return 0; +} + +static int fec_mii_reset(struct mii_bus *bus) +{ + return 0; +} + +static int fec_init_phy(struct net_device *dev, struct fec_enet_private *fep) +{ + int ret; + int i; + struct mii_bus *mii; + + mii = mdiobus_alloc(); + if (mii == NULL) { return -ENOMEM; } + mii->name = "fec mii"; + mii->read = fec_mii_read; + mii->write = fec_mii_write; + mii->reset = fec_mii_reset; + mii->priv = dev; + snprintf(mii->id, MII_BUS_ID_SIZE, "%x", 0); + mii->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + for (i = 0; i < PHY_MAX_ADDR; i++) { + mii->irq[i] = fep->mii_irq >= 0 ? fep->mii_irq : PHY_POLL; + } + + ret = mdiobus_register(mii); + if (ret != 0) { + DBG(0, "%s: Failed to register MII bus: %d\n", __FUNCTION__, ret); + kfree(mii->irq); + mdiobus_free(mii); + return ret; + } + fep->phy_addr = -1; + DBG(0, "%s: MII bus registered\n", __FUNCTION__); + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (mii->phy_map[i] != NULL) { + fep->phy_addr = i; + break; + } + } + if (fep->phy_addr == -1) { + DBG(0, "%s: No PHY found\n", __FUNCTION__); + return -ENODEV; + } + DBG(0, "%s: Using PHY at addr %02x\n", __FUNCTION__, fep->phy_addr); + fep->mii = mii; - spin_lock_init(&fep->hw_lock); - spin_lock_init(&fep->mii_lock); + return 0; +} - /* Create an Ethernet device instance. - */ - fecp = (volatile fec_t *) fec_hw[index]; +static int fec_connect_phy(struct net_device *dev, struct fec_enet_private *fep) +{ + struct mii_bus *mii = fep->mii; + + fep->phy = phy_connect(dev, mii->phy_map[fep->phy_addr]->dev.bus_id, + fec_link_change, 0, mii->phy_map[fep->phy_addr]->interface); + if (IS_ERR(fep->phy)) { + int ret = PTR_ERR(fep->phy); + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + fep->phy = NULL; + return ret; + } + DBG(0, "%s: Registered PHY %s[%02x] IRQ %d with %s\n", __FUNCTION__, + fep->phy->dev.bus_id, fep->phy_addr, fep->phy->irq, dev->name); + + return 0; +} +#else +static int fec_init_phy(struct net_device *dev, struct fec_enet_private *fep) +{ + /* Queue up command to detect the PHY and initialize the + * remainder of the interface. + */ + fep->phy_id_done = 0; + fep->phy_addr = 0; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); + + return 0; +} +#endif - fep->index = index; - fep->hwp = fecp; - fep->netdev = dev; +/* Initialize the FEC Ethernet on 860T (or ColdFire 5272). + */ + /* + * XXX: We need to clean up on failure exits here. + */ +#define res_len(r) ((r)->end - (r)->start + 1) + +int __devinit fec_enet_init(struct platform_device *pdev, struct net_device *dev) +{ + int ret; + struct fec_enet_private *fep = netdev_priv(dev); + cbd_t *bdp; + struct sk_buff *pskb; + int i; + void *mem; + + spin_lock_init(&fep->lock); /* Whack a reset. We should wait for this. */ - fecp->fec_ecntrl = 1; + fec_reg_write(fep, FEC_ECR, 1); udelay(10); /* Set the Ethernet address. If using multiple Enets on the 8xx, @@ -2288,41 +2274,36 @@ int __init fec_enet_init(struct net_devi */ fec_get_mac(dev); - cbd_base = (cbd_t *)mem_addr; - /* XXX: missing check for allocation failure */ - - fec_uncache(mem_addr); - - /* Set receive and transmit descriptor base. - */ - fep->rx_bd_base = cbd_base; - fep->tx_bd_base = cbd_base + RX_RING_SIZE; - fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; fep->skb_cur = fep->skb_dirty = 0; - /* Initialize the receive buffer descriptors. - */ - bdp = fep->rx_bd_base; - for (i=0; itx_bd_base; + + DBG(0, "%s: Allocated %d byte of TX buffer memory @ %p\n", __FUNCTION__, + TX_RING_SIZE * FEC_ENET_TX_FRSIZE, mem); + for (i = 0; i < TX_RING_SIZE; i++) { + fep->tx_bounce[i] = mem; + DBG(0, "%s: TX bounce buffer[%d]=%p\n", __FUNCTION__, i, fep->tx_bounce[i]); + mem = (void *)((unsigned long)(mem + FEC_ENET_TX_FRSIZE)); /* Initialize the BD for every fragment in the page. */ - for (j=0; jcbd_sc = BD_ENET_RX_EMPTY; - bdp->cbd_bufaddr = __pa(mem_addr); - mem_addr += FEC_ENET_RX_FRSIZE; - bdp++; - } + /* already zeroed by kzalloc */ + //bdp->cbd_sc = 0; + bdp->cbd_bufaddr = ~0; + bdp++; } /* Set the last buffer to wrap. @@ -2330,80 +2311,77 @@ int __init fec_enet_init(struct net_devi bdp--; bdp->cbd_sc |= BD_SC_WRAP; - /* ...and the same for transmmit. + /* ...and the same for receive. */ - bdp = fep->tx_bd_base; - for (i=0, j=FEC_ENET_TX_FRPPG; i= FEC_ENET_TX_FRPPG) { - mem_addr = __get_free_page(GFP_KERNEL); - j = 1; - } else { - mem_addr += FEC_ENET_TX_FRSIZE; - j++; + bdp = fep->rx_bd_base; + for (i = 0; i < RX_RING_SIZE; i++, bdp++) { + pskb = __dev_alloc_skb(FEC_ENET_RX_FRSIZE, GFP_KERNEL); + if (pskb == NULL) { + DBG(0, "%s: Failed to allocate RX skb; cleaning up\n", __FUNCTION__); + fec_enet_free_buffers(fep); + fec_enet_cbd_put(fep); + return -ENOMEM; } - fep->tx_bounce[i] = (unsigned char *) mem_addr; - - /* Initialize the BD for every fragment in the page. - */ - bdp->cbd_sc = 0; - bdp->cbd_bufaddr = 0; - bdp++; + DBG(0, "%s: RX skb allocated @ %p\n", __FUNCTION__, pskb); + fep->rx_skbuff[i] = pskb; + pskb->data = FEC_ADDR_ALIGNMENT(pskb->data); + bdp->cbd_sc = BD_ENET_RX_EMPTY; + bdp->cbd_bufaddr = ~0; + fec_enet_rxbuf_map(fep, bdp, pskb->data, FEC_ENET_RX_FRSIZE); } - /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; + fec_enet_cbd_put(fep); /* Set receive and transmit descriptor base. */ - fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); - fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); - + fec_reg_write(fep, FEC_ERDSR, fep->cbd_phys_base); + fec_reg_write(fep, FEC_ETDSR, fep->cbd_phys_base + RX_RING_SIZE * sizeof(cbd_t)); + /* Install our interrupt handlers. This varies depending on * the architecture. */ - fec_request_intrs(dev); - - fecp->fec_grp_hash_table_high = 0; - fecp->fec_grp_hash_table_low = 0; - fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; - fecp->fec_ecntrl = 2; - fecp->fec_r_des_active = 0; -#ifndef CONFIG_M5272 - fecp->fec_hash_table_high = 0; - fecp->fec_hash_table_low = 0; -#endif - - dev->base_addr = (unsigned long)fecp; - + ret = fec_request_intrs(pdev, dev); + if (ret != 0) { + return ret; + } + /* Clear and enable interrupts */ + fec_reg_write(fep, FEC_EIR, fec_reg_read(fep, FEC_EIR)); + fec_reg_write(fep, FEC_EIMR, FEC_ENET_TXF | FEC_ENET_TXB | + FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); + + fec_reg_write(fep, FEC_IAUR, 0); + fec_reg_write(fep, FEC_IALR, 0); + fec_reg_write(fep, FEC_EMRBR, PKT_MAXBLR_SIZE); + fec_reg_write(fep, FEC_ECR, 2); + fec_reg_write(fep, FEC_RDAR, DONT_CARE); + /* The FEC Ethernet specific entries in the device structure. */ dev->open = fec_enet_open; dev->hard_start_xmit = fec_enet_start_xmit; dev->tx_timeout = fec_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->stop = fec_enet_close; + dev->get_stats = fec_enet_get_stats; dev->set_multicast_list = set_multicast_list; + dev->set_mac_address = fec_set_mac_address; - for (i=0; ifec_ievent = 0xffc00000; - fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII); - - /* Queue up command to detect the PHY and initialize the - * remainder of the interface. - */ - fep->phy_id_done = 0; - fep->phy_addr = 0; - mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); - - index++; + ret = fec_init_phy(dev, fep); + if (ret) { + DBG(0, "%s: Failed to initialize PHY: %d\n", __FUNCTION__, ret); + return ret; + } return 0; } @@ -2414,62 +2392,67 @@ int __init fec_enet_init(struct net_devi static void fec_restart(struct net_device *dev, int duplex) { - struct fec_enet_private *fep; - volatile cbd_t *bdp; - volatile fec_t *fecp; + struct fec_enet_private *fep = netdev_priv(dev); + cbd_t *bdp; int i; + u32 rcr = OPT_FRAME_SIZE | RCR_MII_MODE; /* MII enable */ + u32 tcr = TCR_HBC; - fep = netdev_priv(dev); - fecp = fep->hwp; - + DBG(0, "%s: Restarting FEC in %s-duplex mode\n", __FUNCTION__, + duplex ? "full" : "half"); /* Whack a reset. We should wait for this. */ - fecp->fec_ecntrl = 1; + fec_reg_write(fep, FEC_ECR, 1); udelay(10); + /* Enable interrupts we wish to service. + */ + fec_reg_write(fep, FEC_EIMR, FEC_ENET_TXF | FEC_ENET_TXB | + FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); + /* Clear any outstanding interrupt. - */ - fecp->fec_ievent = 0xffc00000; - fec_enable_phy_intr(); + * + */ + fec_reg_write(fep, FEC_EIR, FEC_ENET_MASK); + + fec_enable_phy_intr(fep); /* Set station address. */ - fec_set_mac_address(dev); + _fec_set_mac_address(dev); /* Reset all multicast. */ - fecp->fec_grp_hash_table_high = 0; - fecp->fec_grp_hash_table_low = 0; + fec_reg_write(fep, FEC_IAUR, 0); + fec_reg_write(fep, FEC_IALR, 0); /* Set maximum receive buffer size. */ - fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; - - fec_localhw_setup(); + fec_reg_write(fep, FEC_EMRBR, PKT_MAXBLR_SIZE); /* Set receive and transmit descriptor base. */ - fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); - fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); - + fec_reg_write(fep, FEC_ERDSR, fep->cbd_phys_base); + fec_reg_write(fep, FEC_ETDSR, fep->cbd_phys_base + RX_RING_SIZE * sizeof(cbd_t)); + fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; /* Reset SKB transmit buffers. */ fep->skb_cur = fep->skb_dirty = 0; - for (i=0; i<=TX_RING_MOD_MASK; i++) { + bdp = fep->tx_bd_base; + for (i = 0; i <= TX_RING_MOD_MASK; i++) { if (fep->tx_skbuff[i] != NULL) { - dev_kfree_skb_any(fep->tx_skbuff[i]); - fep->tx_skbuff[i] = NULL; + fec_free_skb(fep, bdp, &fep->tx_skbuff[i]); + bdp++; } } /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; - for (i=0; icbd_sc = BD_ENET_RX_EMPTY; @@ -2484,12 +2467,11 @@ fec_restart(struct net_device *dev, int /* ...and the same for transmmit. */ bdp = fep->tx_bd_base; - for (i=0; icbd_sc = 0; - bdp->cbd_bufaddr = 0; + bdp->cbd_bufaddr = ~0; bdp++; } @@ -2501,92 +2483,321 @@ fec_restart(struct net_device *dev, int /* Enable MII mode. */ if (duplex) { - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;/* MII enable */ - fecp->fec_x_cntrl = 0x04; /* FD enable */ + tcr |= TCR_FDEN; /* FD enable */ } else { - /* MII enable|No Rcv on Xmit */ - fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x06; - fecp->fec_x_cntrl = 0x00; + rcr |= RCR_DRT; /* No Rcv on Xmit */ } + fec_reg_write(fep, FEC_RCR, rcr); + fec_reg_write(fep, FEC_TCR, tcr); fep->full_duplex = duplex; /* Set MII speed. */ - fecp->fec_mii_speed = fep->phy_speed; + fec_reg_write(fep, FEC_MSCR, fep->phy_speed); /* And last, enable the transmit and receive processing. */ - fecp->fec_ecntrl = 2; - fecp->fec_r_des_active = 0; - - /* Enable interrupts we wish to service. - */ - fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII); + fec_reg_write(fep, FEC_ECR, 2); + fec_reg_write(fep, FEC_RDAR, DONT_CARE); + + DBG(0, "%s: Starting netif queue\n", __FUNCTION__); + netif_start_queue(dev); } static void fec_stop(struct net_device *dev) { - volatile fec_t *fecp; - struct fec_enet_private *fep; + struct fec_enet_private *fep = netdev_priv(dev); - fep = netdev_priv(dev); - fecp = fep->hwp; + DBG(0, "%s: Stopping netif queue\n", __FUNCTION__); + netif_stop_queue(dev); /* ** We cannot expect a graceful transmit stop without link !!! */ - if (fep->link) - { - fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */ + if (fep->linkstatus) { + fec_reg_write(fep, FEC_TCR, 0x01); /* Graceful transmit stop */ udelay(10); - if (!(fecp->fec_ievent & FEC_ENET_GRA)) + if (!(fec_reg_read(fep, FEC_EIR) & FEC_ENET_GRA)) printk("fec_stop : Graceful transmit stop did not complete !\n"); - } + } /* Whack a reset. We should wait for this. */ - fecp->fec_ecntrl = 1; + fec_reg_write(fep, FEC_ECR, 1); udelay(10); - /* Clear outstanding MII command interrupts. + /* Mask and clear outstanding MII command interrupts. */ - fecp->fec_ievent = FEC_ENET_MII; - fec_enable_phy_intr(); + fec_reg_write(fep, FEC_EIMR, FEC_ENET_MII); + fec_reg_write(fep, FEC_EIR, FEC_ENET_MII); + fec_enable_phy_intr(fep); - fecp->fec_imask = FEC_ENET_MII; - fecp->fec_mii_speed = fep->phy_speed; + fec_reg_write(fep, FEC_MSCR, fep->phy_speed); } -static int __init fec_enet_module_init(void) +static int __devinit fec_enet_probe(struct platform_device *pdev) { + int ret; + struct fec_enet_private *fep; struct net_device *dev; - int i, err; - DECLARE_MAC_BUF(mac); + struct fec_enet_platform_data *pdata = pdev->dev.platform_data; + struct resource *res_mem1; + struct resource *res_mem2; + + res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_mem1 == NULL) { + return -ENODEV; + } + + res_mem1 = request_mem_region(res_mem1->start, res_len(res_mem1), DRV_NAME); + if (res_mem1 == NULL) { + return -EBUSY; + } + res_mem2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res_mem2 != NULL) { + res_mem2 = request_mem_region(res_mem2->start, res_len(res_mem2), DRV_NAME); + if (res_mem2 == NULL) { + ret = -EBUSY; + goto release1; + } + } + + dev = alloc_etherdev(sizeof(struct fec_enet_private)); + if (dev == NULL) { + ret = -ENOMEM; + goto release2; + } + platform_set_drvdata(pdev, dev); + fep = netdev_priv(dev); + fep->res_mem1 = res_mem1; + fep->res_mem2 = res_mem2; + + fep->reg_base = ioremap(res_mem1->start, res_len(res_mem1)); + if (fep->reg_base == NULL) { + printk("FEC: Mapping FEC registers failed\n"); + ret = -ENOMEM; + goto free_netdev; + } + DBG(0, "%s: FEC registers @ %08lx mapped to %p\n", __FUNCTION__, + (unsigned long)res_mem1->start, fep->reg_base); + + /* Allocate memory for buffer descriptors. */ + fep->cbd_mem_base = dma_alloc_coherent(&pdev->dev, CBD_BUF_SIZE, &fep->cbd_phys_base, + GFP_KERNEL); + if (fep->cbd_mem_base == NULL) { + printk("FEC: allocate descriptor memory failed\n"); + ret = -ENOMEM; + goto unmap; + } + DBG(0, "%s: Allocated %lu [(%u + %lu) * %d] byte for CBD buffer @ %p[%08lx]\n", + __FUNCTION__, CBD_BUF_SIZE, TX_RING_SIZE, RX_RING_SIZE, sizeof(cbd_t), + fep->cbd_mem_base, (unsigned long)fep->cbd_phys_base); + + /* Set receive and transmit descriptor base. + */ + fep->rx_bd_base = fep->cbd_mem_base; + fep->tx_bd_base = fep->rx_bd_base + RX_RING_SIZE; printk("FEC ENET Version 0.2\n"); + ret = platform_func(pdata->arch_init, pdev); + if (ret != 0) { + printk(KERN_ERR "%s: platform init failed: %d\n", __FUNCTION__, ret); + goto free_dma; + } + + ret = fec_enet_init(pdev, dev); + if (ret != 0) { + goto fec_disable; + } + + /* Enable most messages by default */ + fep->msg_enable = (NETIF_MSG_IFUP << 1) - 1; + ret = register_netdev(dev); + if (ret != 0) { + /* XXX: missing cleanup here */ + goto free_buffers; + } + + printk(KERN_INFO "%s: ethernet %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); - for (i = 0; (i < FEC_MAX_PORTS); i++) { - dev = alloc_etherdev(sizeof(struct fec_enet_private)); - if (!dev) - return -ENOMEM; - err = fec_enet_init(dev); - if (err) { - free_netdev(dev); - continue; - } - if (register_netdev(dev) != 0) { - /* XXX: missing cleanup here */ - free_netdev(dev); - return -EIO; + return 0; + + free_buffers: + fec_enet_free_buffers(fep); + + fec_disable: + platform_func(pdata->arch_exit, pdev); + + free_dma: + dma_free_coherent(&pdev->dev, CBD_BUF_SIZE, fep->cbd_mem_base, fep->cbd_phys_base); + + unmap: + iounmap(fep->reg_base); + + free_netdev: + free_netdev(dev); + + release2: + if (res_mem2 != NULL) { + release_resource(res_mem2); + } + + release1: + release_resource(res_mem1); + + return ret; +} + +static int __devexit fec_enet_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct fec_enet_private *fep = netdev_priv(dev); + + unregister_netdev(dev); + free_netdev(dev); + +#ifdef CONFIG_PHYLIB + if (fep->mii != NULL) { + kfree(fep->mii->irq); + mdiobus_unregister(fep->mii); + } + mdiobus_free(fep->mii); +#endif + fec_release_intrs(dev); + + DBG(0, "%s: Unmapping FEC registers %p\n", __FUNCTION__, fep->reg_base); + iounmap(fep->reg_base); + + fec_enet_free_buffers(fep); + + DBG(0, "%s: Freeing CBD buffer area %p[%08lx]\n", __FUNCTION__, + fep->cbd_mem_base, (unsigned long)fep->cbd_phys_base); + dma_free_coherent(&pdev->dev, CBD_BUF_SIZE, fep->cbd_mem_base, fep->cbd_phys_base); + + release_resource(fep->res_mem1); + if (fep->res_mem2 != NULL) { + release_resource(fep->res_mem2); + } + return 0; +} + +static void fec_enet_shutdown(struct platform_device *pdev) +{ + struct fec_enet_platform_data *pdata = pdev->dev.platform_data; + + DBG(0, "%s: Shutting down FEC Hardware\n", __FUNCTION__); + platform_func(pdata->arch_exit, pdev); +} + +#ifdef CONFIG_PM +static int fec_enet_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret; + struct fec_enet_platform_data *pdata = pdev->dev.platform_data; + struct net_device *ndev = platform_get_drvdata(pdev); + struct fec_enet_private *fep = netdev_priv(ndev); + + if (netif_running(ndev)) { + DBG(0, "%s: Detaching netif\n", __FUNCTION__); + netif_device_detach(ndev); +#ifdef CONFIG_PHYLIB + DBG(0, "%s: Disconnecting PHY %p\n", __FUNCTION__, fep->phy); + phy_disconnect(fep->phy); + fep->phy = NULL; +#endif + } +#ifndef CONFIG_PHYLIB + if (fep->phy_timer) { + ret = del_timer_sync(fep->phy_timer); + if (ret != 0) { + DBG(0, "%s: Failed to delete PHY timer: %d\n", __FUNCTION__, ret); + return ret; } + } +#endif + DBG(0, "%s: Shutting down FEC Hardware %d\n", __FUNCTION__, + netif_running(ndev)); + ret = platform_func(pdata->suspend, pdev); + if (ret != 0 && netif_running(ndev)) { + DBG(0, "%s: Failed to suspend: %d\n", __FUNCTION__, ret); + /* Undo suspend */ +#ifdef CONFIG_PHYLIB + DBG(0, "%s: Reconnecting PHY\n", __FUNCTION__); + if (fec_connect_phy(ndev, fep) != 0) { + DBG(0, "%s: Failed to connect to PHY\n", __FUNCTION__); + return ret; + } + phy_start(fep->phy); +#endif + fec_link_change(ndev); + netif_device_attach(ndev); + } + return ret; +} - printk("%s: ethernet %s\n", - dev->name, print_mac(mac, dev->dev_addr)); +static int fec_enet_resume(struct platform_device *pdev) +{ + int ret; + struct fec_enet_platform_data *pdata = pdev->dev.platform_data; + struct net_device *ndev = platform_get_drvdata(pdev); + + DBG(0, "%s: Powering up FEC Hardware %d\n", __FUNCTION__, + netif_running(ndev)); + ret = platform_func(pdata->resume, pdev); + if (ret != 0) { + DBG(0, "%s: Failed to resume: %d\n", __FUNCTION__, ret); + return ret; + } + if (netif_running(ndev)) { +#ifdef CONFIG_PHYLIB + struct fec_enet_private *fep = netdev_priv(ndev); + + DBG(0, "%s: Reconnecting PHY\n", __FUNCTION__); + ret = fec_connect_phy(ndev, fep); + if (ret != 0) { + DBG(0, "%s: Failed to connect to PHY: %d\n", __FUNCTION__, ret); + return ret; + } + phy_start(fep->phy); +#endif + fec_link_change(ndev); + netif_device_attach(ndev); } return 0; } +#else +#define fec_enet_suspend NULL +#define fec_enet_resume NULL +#endif + +static struct platform_driver fec_enet_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = fec_enet_probe, + .remove = __devexit_p(fec_enet_remove), + .shutdown = fec_enet_shutdown, + .suspend = fec_enet_suspend, + .resume = fec_enet_resume, +}; + +static int __init fec_enet_module_init(void) +{ + int ret; + ret = platform_driver_register(&fec_enet_driver); + + return ret; +} module_init(fec_enet_module_init); +static void __exit fec_enet_module_cleanup(void) +{ + platform_driver_unregister(&fec_enet_driver); +} +module_exit(fec_enet_module_cleanup); + MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/net/fec.h linux-2.6.28-karo/drivers/net/fec.h --- linux-2.6.28/drivers/net/fec.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/net/fec.h 2009-03-11 13:16:24.000000000 +0100 @@ -13,13 +13,50 @@ #define FEC_H /****************************************************************************/ +/* + * dummy value to write into RDAR,TDAR. FEC hardware will scan the TX/RX + * descriptors in memory upon any write access to those registers. + * The actual value written to those registers does not matter. +*/ +#define DONT_CARE 0 +#define RDAR_BUSY (1 << 24) +#define TDAR_BUSY (1 << 24) + #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC) /* * Just figures, Motorola would have to change the offsets for * registers in the same peripheral device on different models * of the ColdFire! */ +// relying on structure alignment for hardware register is just evil +#ifndef GARBAGE +#define FEC_EIR 0x004 +#define FEC_EIMR 0x008 +#define FEC_RDAR 0x010 +#define FEC_TDAR 0x014 +#define FEC_ECR 0x024 +#define FEC_MMFR 0x040 +#define FEC_MSCR 0x044 +#define FEC_MIBC 0x064 +#define FEC_RCR 0x084 +#define FEC_TCR 0x0c4 +#define FEC_PALR 0x0e4 +#define FEC_PAUR 0x0e8 +#define FEC_OPD 0x0ec +#define FEC_IAUR 0x118 +#define FEC_IALR 0x11c +#define FEC_GAUR 0x120 +#define FEC_GALR 0x124 +#define FEC_TFWR 0x144 +#define FEC_FRBR 0x14c +#define FEC_FRSR 0x150 +#define FEC_ERDSR 0x180 +#define FEC_ETDSR 0x184 +#define FEC_EMRBR 0x188 + +#else + typedef struct fec { unsigned long fec_reserved0; unsigned long fec_ievent; /* Interrupt event reg */ @@ -57,6 +94,7 @@ typedef struct fec { unsigned long fec_x_des_start; /* Transmit descriptor ring */ unsigned long fec_r_buff_size; /* Maximum receive buff size */ } fec_t; +#endif #else @@ -88,8 +126,8 @@ typedef struct fec { unsigned long fec_reserved7[158]; unsigned long fec_addr_low; /* Low 32bits MAC address */ unsigned long fec_addr_high; /* High 16bits MAC address */ - unsigned long fec_grp_hash_table_high;/* High 32bits hash table */ - unsigned long fec_grp_hash_table_low; /* Low 32bits hash table */ + unsigned long fec_hash_table_high; /* High 32bits hash table */ + unsigned long fec_hash_table_low; /* Low 32bits hash table */ unsigned long fec_r_des_start; /* Receive descriptor ring */ unsigned long fec_x_des_start; /* Transmit descriptor ring */ unsigned long fec_r_buff_size; /* Maximum receive buff size */ @@ -103,18 +141,28 @@ typedef struct fec { /* * Define the buffer descriptor structure. */ +/* Please see "Receive Buffer Descriptor Field Definitions" in Specification. + * It's LE. + */ +#ifdef CONFIG_ARCH_MXC +typedef struct bufdesc { + unsigned short cbd_datlen; /* Data length */ + unsigned short cbd_sc; /* Control and status info */ + dma_addr_t cbd_bufaddr; /* Buffer address as seen by FEC Hardware */ +} cbd_t; +#else typedef struct bufdesc { unsigned short cbd_sc; /* Control and status info */ unsigned short cbd_datlen; /* Data length */ - unsigned long cbd_bufaddr; /* Buffer address */ + dma_addr_t cbd_bufaddr; /* Buffer address */ } cbd_t; - +#endif /* * The following definitions courtesy of commproc.h, which where * Copyright (c) 1997 Dan Malek (dmalek@jlc.net). */ -#define BD_SC_EMPTY ((ushort)0x8000) /* Recieve is empty */ +#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */ #define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ #define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ #define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ @@ -161,5 +209,22 @@ typedef struct bufdesc { #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ +#define RCR_LOOP (1 << 0) +#define RCR_DRT (1 << 1) +#define RCR_MII_MODE (1 << 2) +#define RCR_PROM (1 << 3) +#define RCR_BC_REJ (1 << 4) +#define RCR_FCE (1 << 5) +#define RCR_MAX_FL_SHIFT 16 +#define RCR_MAX_FL_MASK (0x7ff << (RCR_MAX_FL_SHIFT)) +#define RCR_MAX_FL_set(n) (((n) << (RCR_MAX_FL_SHIFT)) & (RCR_MAX_FL_MASK)) +#define RCR_MAX_FL_get(n) (((n) & (RCR_MAX_FL_MASK)) >> (RCR_MAX_FL_SHIFT)) + +#define TCR_GTS (1 << 0) +#define TCR_HBC (1 << 1) +#define TCR_FDEN (1 << 2) +#define TCR_TFCPAUSE (1 << 3) +#define TCR_RFCPAUSE (1 << 4) + /****************************************************************************/ #endif /* FEC_H */ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/power/Kconfig linux-2.6.28-karo/drivers/power/Kconfig --- linux-2.6.28/drivers/power/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/power/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -68,4 +68,16 @@ config BATTERY_BQ27x00 help Say Y here to enable support for batteries with BQ27200(I2C) chip. +config LP3972 + tristate "National Semiconductor LP3972 Power Management Unit" + depends on EXPERIMENTAL + depends on I2C + default n + help + If you say yes here you get support for the National Semiconductor + Power Management Chip. + + This driver can also be built as a module. If so, the module + will be called lp3972. + endif # POWER_SUPPLY diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/power/Makefile linux-2.6.28-karo/drivers/power/Makefile --- linux-2.6.28/drivers/power/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/power/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -23,3 +23,5 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_batte obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o + +obj-$(CONFIG_LP3972) += lp3972.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/power/lp3972.c linux-2.6.28-karo/drivers/power/lp3972.c --- linux-2.6.28/drivers/power/lp3972.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/power/lp3972.c 2009-03-11 13:52:22.000000000 +0100 @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2008 Lothar Wassmann + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 + */ + +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x34, + I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD_1(lp3972); + +struct lp3972_data { + struct i2c_client client; +}; + +static int lp3972_attach_adapter(struct i2c_adapter *adapter); +static int lp3972_detect(struct i2c_adapter *adapter, int address, int kind); +static int lp3972_detach_client(struct i2c_client *client); +static void lp3972_init_client(struct i2c_client *client); + +static struct i2c_driver lp3972_driver = { + .driver = { + .name = "lp3972", + }, + //.id = I2C_DRIVERID_LP3972, + .attach_adapter = lp3972_attach_adapter, + .detach_client = lp3972_detach_client, +}; + +struct lp3972_dev_attr { + struct device_attribute dev_attr; + u8 reg; + u8 val; +}; + +#define to_lp3972_attr(d) container_of(d, struct lp3972_dev_attr, dev_attr) + +static ssize_t lp3972_get_val(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct lp3972_dev_attr *lp3972_attr = to_lp3972_attr(attr); + + ret = i2c_smbus_read_byte_data(client, lp3972_attr->reg); + if (ret < 0) { + return ret; + } + lp3972_attr->val = ret; + return sprintf(buf, "0x%02x\n", ret); +} + +static ssize_t lp3972_set_val(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct lp3972_dev_attr *lp3972_attr = to_lp3972_attr(attr); + unsigned long val = simple_strtoul(buf, NULL, 0); + + if (val > 0xff) { + printk(KERN_WARNING "value %ld is out of range\n", val); + return -EINVAL; + } + if (val != lp3972_attr->val) { + lp3972_attr->val = val; + ret = i2c_smbus_write_byte_data(client, lp3972_attr->reg, lp3972_attr->val); + if (ret < 0) { + return ret; + } + } + return count; +} + +#define LP3972_DEV_ATTR(_name, _mode, _reg, _read, _write) \ + struct lp3972_dev_attr lp3972_dev_attr_##_name = { \ + .dev_attr = __ATTR(_name,_mode,_read,_write), \ + .reg = _reg, \ + } + +static LP3972_DEV_ATTR(scr, S_IWUSR | S_IRUGO, 0x07, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(over1, S_IWUSR | S_IRUGO, 0x10, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(ovsr1, S_IRUGO, 0x11, lp3972_get_val, NULL); +static LP3972_DEV_ATTR(over2, S_IWUSR | S_IRUGO, 0x12, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(ovsr2, S_IRUGO, 0x13, lp3972_get_val, NULL); +static LP3972_DEV_ATTR(vcc1, S_IWUSR | S_IRUGO, 0x20, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(adtv1, S_IWUSR | S_IRUGO, 0x23, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(adtv2, S_IWUSR | S_IRUGO, 0x24, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(avrc, S_IWUSR | S_IRUGO, 0x25, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(cdtc1, S_IWUSR, 0x26, NULL, lp3972_set_val); +static LP3972_DEV_ATTR(cdtc2, S_IWUSR, 0x27, NULL, lp3972_set_val); +static LP3972_DEV_ATTR(sdtv1, S_IWUSR | S_IRUGO, 0x29, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(sdtv2, S_IWUSR | S_IRUGO, 0x2a, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(mdtv1, S_IWUSR | S_IRUGO, 0x32, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(mdtv2, S_IWUSR | S_IRUGO, 0x33, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(l12vcr, S_IWUSR | S_IRUGO, 0x39, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(l34vcr, S_IWUSR | S_IRUGO, 0x3a, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(scr1, S_IWUSR | S_IRUGO, 0x80, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(scr2, S_IWUSR | S_IRUGO, 0x81, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(oen3, S_IWUSR | S_IRUGO, 0x82, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(osr3, S_IWUSR | S_IRUGO, 0x83, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(loer, S_IWUSR | S_IRUGO, 0x84, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(b2tv, S_IWUSR | S_IRUGO, 0x85, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(b3tv, S_IWUSR | S_IRUGO, 0x86, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(c32rc, S_IWUSR | S_IRUGO, 0x87, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(isra, S_IRUGO, 0x88, lp3972_get_val, NULL); +static LP3972_DEV_ATTR(bccr, S_IWUSR | S_IRUGO, 0x89, lp3972_get_val, lp3972_set_val); +static LP3972_DEV_ATTR(ii1rr, S_IRUGO, 0x8e, lp3972_get_val, NULL); +static LP3972_DEV_ATTR(ii2rr, S_IRUGO, 0x8f, lp3972_get_val, NULL); + +static struct attribute *lp3972_attributes[] = { + &lp3972_dev_attr_scr.dev_attr.attr, + &lp3972_dev_attr_over1.dev_attr.attr, + &lp3972_dev_attr_ovsr1.dev_attr.attr, + &lp3972_dev_attr_over2.dev_attr.attr, + &lp3972_dev_attr_ovsr2.dev_attr.attr, + &lp3972_dev_attr_vcc1.dev_attr.attr, + &lp3972_dev_attr_adtv1.dev_attr.attr, + &lp3972_dev_attr_adtv2.dev_attr.attr, + &lp3972_dev_attr_avrc.dev_attr.attr, + &lp3972_dev_attr_cdtc1.dev_attr.attr, + &lp3972_dev_attr_cdtc2.dev_attr.attr, + &lp3972_dev_attr_sdtv1.dev_attr.attr, + &lp3972_dev_attr_sdtv2.dev_attr.attr, + &lp3972_dev_attr_mdtv1.dev_attr.attr, + &lp3972_dev_attr_mdtv2.dev_attr.attr, + &lp3972_dev_attr_l12vcr.dev_attr.attr, + &lp3972_dev_attr_l34vcr.dev_attr.attr, + &lp3972_dev_attr_scr1.dev_attr.attr, + &lp3972_dev_attr_scr2.dev_attr.attr, + &lp3972_dev_attr_oen3.dev_attr.attr, + &lp3972_dev_attr_osr3.dev_attr.attr, + &lp3972_dev_attr_loer.dev_attr.attr, + &lp3972_dev_attr_b2tv.dev_attr.attr, + &lp3972_dev_attr_b3tv.dev_attr.attr, + &lp3972_dev_attr_c32rc.dev_attr.attr, + &lp3972_dev_attr_isra.dev_attr.attr, + &lp3972_dev_attr_bccr.dev_attr.attr, + &lp3972_dev_attr_ii1rr.dev_attr.attr, + &lp3972_dev_attr_ii2rr.dev_attr.attr, + NULL +}; + +static const struct attribute_group lp3972_attr_group = { + .attrs = lp3972_attributes, +}; + +/* + * Real code + */ + +static int lp3972_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, lp3972_detect); +} + +/* This function is called by i2c_probe */ +static int lp3972_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int ret; + struct i2c_client *new_client; + struct lp3972_data *data; + const char *client_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { + return -ENOTSUPP; + } + + data = kzalloc(sizeof(struct lp3972_data), GFP_KERNEL); + if (data == NULL) { + return -ENOMEM; + } + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lp3972_driver; + new_client->flags = 0; + + /* Now, we would do the remaining detection. But the LP3972 is plainly + impossible to detect! Stupid chip. */ + + /* Determine the chip type */ + if (kind <= 0) { + kind = lp3972; + } + + client_name = "lp3972"; + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + + /* Tell the I2C layer a new client has arrived */ + ret = i2c_attach_client(new_client); + if (ret != 0) { + goto free; + } + + /* Initialize the LP3972 chip */ + lp3972_init_client(new_client); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&new_client->dev.kobj, &lp3972_attr_group); + if (ret == 0) { + return ret; + } + + i2c_detach_client(new_client); + free: + kfree(data); + return ret; +} + +static int lp3972_detach_client(struct i2c_client *client) +{ + int ret; + + sysfs_remove_group(&client->dev.kobj, &lp3972_attr_group); + + ret = i2c_detach_client(client); + if (ret != 0) { + return ret; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* Called when we have found a new LP3972. */ +static void lp3972_init_client(struct i2c_client *client) +{ +} + +static int __init lp3972_init(void) +{ + return i2c_add_driver(&lp3972_driver); +} + +static void __exit lp3972_exit(void) +{ + i2c_del_driver(&lp3972_driver); +} + + +MODULE_AUTHOR("Lothar Wassmann "); +MODULE_DESCRIPTION("LP3972 I2C PMIC driver"); +MODULE_LICENSE("GPL v2"); + +module_init(lp3972_init); +module_exit(lp3972_exit); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/rtc/Kconfig linux-2.6.28-karo/drivers/rtc/Kconfig --- linux-2.6.28/drivers/rtc/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/rtc/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -141,6 +141,24 @@ config RTC_DRV_DS1307 This driver can also be built as a module. If so, the module will be called rtc-ds1307. +config RTC_DRV_DS13XX + tristate "Dallas/Maxim DS13xx I2C RTC chips" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for various compatible RTC + chips (often with battery backup) connected with I2C. This driver + should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00, + and probably other chips. Platform specific code may provide a + callback function to properly initialize the chip. + + The first seven registers on these chips hold an RTC, and other + registers may add features such as NVRAM, a trickle charger for + the RTC/NVRAM backup power, and alarms. This driver may not + expose all those available chip features. + + This driver can also be built as a module. If so, the module + will be called rtc-ds13xx. + config RTC_DRV_DS1374 tristate "Dallas/Maxim DS1374" depends on RTC_CLASS && I2C @@ -649,6 +667,13 @@ config RTC_DRV_RS5C313 help If you say yes here you get support for the Ricoh RS5C313 RTC chips. +config RTC_MXC + tristate "Freescale MXC Real Time Clock" + depends on ARCH_MXC + depends on RTC_CLASS + help + If you say yes here you get support for the Freescale i.MX RTC + config RTC_DRV_PARISC tristate "PA-RISC firmware RTC support" depends on PARISC diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/rtc/Makefile linux-2.6.28-karo/drivers/rtc/Makefile --- linux-2.6.28/drivers/rtc/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/rtc/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds13 obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o +obj-$(CONFIG_RTC_DRV_DS13XX) += rtc-ds13xx.o obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o @@ -62,6 +63,7 @@ obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx85 obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o +obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/rtc/rtc-ds13xx.c linux-2.6.28-karo/drivers/rtc/rtc-ds13xx.c --- linux-2.6.28/drivers/rtc/rtc-ds13xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/rtc/rtc-ds13xx.c 2009-03-11 18:38:58.000000000 +0100 @@ -0,0 +1,590 @@ +/* + * rtc-ds13xx.c - RTC driver for some mostly-compatible I2C chips. + * + * Copyright (C) 2005 James Chapman (ds1337 core) + * Copyright (C) 2006 David Brownell + * Copyright (C) 2007 Lothar Wassmann + * rewritten to support initialization via platform_device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* We can't determine type by probing, but if we expect pre-Linux code + * to have set the chip up as a clock (turning on the oscillator and + * setting the date and time), Linux can ignore the non-clock features. + * That's a natural job for a factory or repair bench. + * + * If the I2C "force" mechanism is used, we assume the chip is a ds1337. + * (Much better would be board-specific tables of I2C devices, along with + * the platform_data drivers would use to sort such issues out.) + */ + +static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +// the RTC epoch starts at 2000 +#define CENTURY 2000 + +/* RTC registers don't differ much, except for the century flag */ +#define DS13XX_REG_SECS 0x00 /* 00-59 */ +# define DS1307_BIT_CH (1 << 7) +#define DS13XX_REG_MIN 0x01 /* 00-59 */ +#define DS13XX_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ +# define DS1340_BIT_CENTURY_EN (1 << 7) /* in REG_HOUR */ +# define DS1340_BIT_CENTURY (1 << 6) /* in REG_HOUR */ +#define DS13XX_REG_WDAY 0x03 /* 01-07 */ +#define DS13XX_REG_MDAY 0x04 /* 01-31 */ +#define DS13XX_REG_MONTH 0x05 /* 01-12 */ +# define DS133X_BIT_CENTURY (1 << 7) /* in REG_MONTH */ +#define DS13XX_REG_YEAR 0x06 /* 00-99 */ + +/* Other registers (control, status, alarms, trickle charge, NVRAM, etc) + * start at 7, and they differ a lot. Only control and status matter for RTC; + * be careful using them. + */ +#define DS1307_REG_CONTROL 0x07 +#define DS1339_REG_AL1SEC 0x07 +#define DS1339_REG_AL1MIN 0x08 +#define DS1339_REG_AL1HRS 0x09 +#define DS1339_REG_AL1DAY 0x0a +#define DS1339_REG_AL2MIN 0x0b +#define DS1339_REG_AL2HRS 0x0c +#define DS1339_REG_AL2DAY 0x0d +# define DS1339_BIT_ALMSK (1 << 7) +# define DS1339_BIT_DYDT (1 << 6) +#define DS133X_REG_CONTROL 0x0e +# define DS1338_BIT_OSF (1 << 5) +# define DS133X_BIT_nEOSC (1 << 7) +# define DS133X_BIT_A1IE (1 << 0) +# define DS133X_BIT_A2IE (1 << 1) +#define DS133X_REG_STATUS 0x0f +# define DS133X_BIT_OSF (1 << 7) +# define DS133X_BIT_A1I (1 << 0) +# define DS133X_BIT_A2I (1 << 1) +#define DS1339_REG_TRC 0x10 +# define DS1339_TRC_MAGIC 0xa0 +#define DS1340_REG_STATUS 0x09 +# define DS1340_BIT_OSF (1 << 7) + +struct ds13xx { + u8 reg_addr; + u8 regs[8]; + enum ds13xx_type type; + struct i2c_msg msg[2]; + struct i2c_client client; + struct rtc_device *rtc; + int valid_time; +}; + +static const struct i2c_device_id ds13xx_id[] = { + { "ds1307", ds_1307 }, + { "ds1337", ds_1337 }, + { "ds1338", ds_1338 }, + { "ds1339", ds_1339 }, + { "ds1340", ds_1340 }, + { "m41t00", m41t00 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds13xx_id); + +static inline int ds13xx_write_reg(struct i2c_client *client, u8 regno, u8 val) +{ + dev_dbg(&client->dev, "Writing %02x to reg %02x\n", val, regno); + return i2c_smbus_write_byte_data(client, regno, val); +} + +static inline int ds13xx_read_reg(struct i2c_client *client, u8 regno) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, regno); + + return ret; +} + +static int __devinit ds13xx_i2c_init(struct platform_device *pdev, struct i2c_client *client) +{ + int ret = 0; + struct ds13xx_platform_data *pdata = pdev->dev.platform_data; + struct ds13xx *ds13xx = i2c_get_clientdata(client); + + if (pdata != NULL && pdata->type > 0) { + ds13xx->type = pdata->type; + switch (ds13xx->type) { + case unknown: + strlcpy(client->name, "unknown", I2C_NAME_SIZE); + break; + case ds_1307: + strlcpy(client->name, "ds1307", I2C_NAME_SIZE); + break; + case ds_1337: + strlcpy(client->name, "ds1337", I2C_NAME_SIZE); + break; + case ds_1339: + strlcpy(client->name, "ds1339", I2C_NAME_SIZE); + break; + case ds_1340: + strlcpy(client->name, "ds1340", I2C_NAME_SIZE); + break; + default: + return -ENODEV; + } + } else { + dev_dbg(&client->dev, "No platform_data\n"); + } + return ret; +} + +static int __devinit ds13xx_rtc_init(struct ds13xx_platform_data *pdata, struct i2c_client *client) +{ + int ret = 0; + struct ds13xx *ds13xx = i2c_get_clientdata(client); + int oscstart = 0; + + ds13xx->valid_time = 1; + dev_dbg(&client->dev, "Inititialzing RTC %s\n", client->name); + switch (ds13xx->type) { + case ds_1307: + case m41t00: + if (ds13xx->regs[DS13XX_REG_SECS] & DS1307_BIT_CH) { + oscstart = 1; + ds13xx_write_reg(client, DS13XX_REG_SECS, 0); + } + if (pdata && pdata->ctrl >= 0) { + ds13xx_write_reg(client, DS1307_REG_CONTROL, pdata->ctrl); + } + break; + case ds_1337: + ret = ds13xx_read_reg(client, DS133X_REG_CONTROL); + if (ret > 0 && ret & DS133X_BIT_nEOSC) { + oscstart = 1; + } + if (pdata && pdata->ctrl >= 0) { + ds13xx_write_reg(client, DS133X_REG_CONTROL, pdata->ctrl); + } + break; + case ds_1338: + /* clock halted? turn it on, so clock can tick. */ + if (ds13xx->regs[DS13XX_REG_SECS] & DS1307_BIT_CH) { + ds13xx_write_reg(client, DS13XX_REG_SECS, 0); + } + /* oscillator fault? clear flag, and warn */ + if (ds13xx->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { + ret = ds13xx_write_reg(client, DS1307_REG_CONTROL, + ds13xx->regs[DS1307_REG_CONTROL] & + ~DS1338_BIT_OSF); + ds13xx->valid_time = 0; + } + break; + case ds_1339: + ret = ds13xx_read_reg(client, DS133X_REG_CONTROL); + if (ret < 0) { + dev_warn(&client->dev, "failed to read DS1339 control register: %d\n", + ret); + break; + } else if (ret & DS133X_BIT_nEOSC) { + oscstart = 1; + } + if (pdata && pdata->ctrl >= 0) { + ds13xx_write_reg(client, DS133X_REG_CONTROL, pdata->ctrl); + } + if (pdata && pdata->trc >= 0) { + ds13xx_write_reg(client, DS1339_REG_TRC, pdata->trc); + } + ret = ds13xx_read_reg(&ds13xx->client, DS133X_REG_STATUS); + if (ret < 0) { + dev_warn(&client->dev, "failed to read DS1339 status: %d\n", + ret); + break; + } else { + dev_dbg(&client->dev, "DS1339 status: %02x\n", ret); + } + if (ret > 0 && ret & DS133X_BIT_OSF) { + ds13xx->valid_time = 0; + } + break; + case ds_1340: + if (ds13xx->regs[DS13XX_REG_SECS] & DS133X_BIT_nEOSC) { + oscstart = 1; + ds13xx_write_reg(client, DS13XX_REG_SECS, 0); + } + if (pdata && pdata->ctrl >= 0) { + ds13xx_write_reg(client, DS133X_REG_CONTROL, pdata->ctrl); + } + ret = ds13xx_read_reg(&ds13xx->client, DS1340_REG_STATUS); + if (ret > 0 && ret & DS1340_BIT_OSF) { + ds13xx->valid_time = 0; + } + break; + default: + dev_warn(&client->dev, "Unknown DS13xx chip type: %d\n", ds13xx->type); + break; + } + if (!ds13xx->valid_time) { + dev_warn(&client->dev, "%s oscillator failure; RTC time must be set!\n", + client->name); + } else if (oscstart) { + ds13xx->valid_time = 0; + dev_warn(&client->dev, "%s oscillator has been started; RTC time must be set!\n", + client->name); + } + + return ret < 0 ? ret : 0; +} + +static int ds13xx_get_time(struct device *dev, struct rtc_time *t) +{ + int ret; + struct ds13xx *ds13xx = dev_get_drvdata(dev); + static int once = 1; + + if (!ds13xx->valid_time) { + if (once) { + dev_warn(dev, "RTC Oscillator failure; RTC time must be set!\n"); + once = 0; + } + } + + /* read the RTC registers all at once */ + ds13xx->msg[1].flags = I2C_M_RD; + ds13xx->msg[1].len = 7; + + ret = i2c_transfer(ds13xx->client.adapter, ds13xx->msg, 2); + if (ret != 2) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret < 0 ? ret : -EIO; + } + + dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", + "read", + ds13xx->regs[0], ds13xx->regs[1], + ds13xx->regs[2], ds13xx->regs[3], + ds13xx->regs[4], ds13xx->regs[5], + ds13xx->regs[6]); + + t->tm_sec = bcd2bin(ds13xx->regs[DS13XX_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(ds13xx->regs[DS13XX_REG_MIN] & 0x7f); + ret = ds13xx->regs[DS13XX_REG_HOUR] & 0x3f; + t->tm_hour = bcd2bin(ret); + t->tm_wday = bcd2bin(ds13xx->regs[DS13XX_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(ds13xx->regs[DS13XX_REG_MDAY] & 0x3f); + ret = ds13xx->regs[DS13XX_REG_MONTH] & 0x1f; + t->tm_mon = bcd2bin(ret) - 1; + + t->tm_year = bcd2bin(ds13xx->regs[DS13XX_REG_YEAR]) + 100; + switch (ds13xx->type) { + case ds_1339: + if (ds13xx->regs[DS13XX_REG_MONTH] & DS133X_BIT_CENTURY) { + t->tm_year += 100; + } + default: + break; + } + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year - 100 + CENTURY, t->tm_wday); + + return 0; +} + +static int ds13xx_set_time(struct device *dev, struct rtc_time *t) +{ + int ret; + struct ds13xx *ds13xx = dev_get_drvdata(dev); + u8 *buf = ds13xx->regs; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year - 100 + CENTURY, t->tm_wday); + + *buf++ = 0; /* first register addr */ + buf[DS13XX_REG_SECS] = bin2bcd(t->tm_sec); + buf[DS13XX_REG_MIN] = bin2bcd(t->tm_min); + buf[DS13XX_REG_HOUR] = bin2bcd(t->tm_hour); + buf[DS13XX_REG_WDAY] = bin2bcd(t->tm_wday + 1); + buf[DS13XX_REG_MDAY] = bin2bcd(t->tm_mday); + buf[DS13XX_REG_MONTH] = bin2bcd(t->tm_mon + 1); + + ret = t->tm_year - 100; + buf[DS13XX_REG_YEAR] = bin2bcd(ret); + + if (!ds13xx->valid_time) { + switch (ds13xx->type) { + case ds_1307: + break; + case ds_1337: + case ds_1339: + dev_dbg(dev, "Clearing OSF\n"); + // clear oscillator fail flag, in case it was set + ret = ds13xx_write_reg(&ds13xx->client, DS133X_REG_STATUS, + (u8)~DS133X_BIT_OSF); + break; + case ds_1340: + buf[DS13XX_REG_HOUR] |= DS1340_BIT_CENTURY_EN; + // clear oscillator fail flag, in case it was set + ret = ds13xx_write_reg(&ds13xx->client, DS1340_REG_STATUS, + (u8)~DS1340_BIT_OSF); + break; + default: + BUG(); + } + } + ds13xx->msg[1].flags = 0; + ds13xx->msg[1].len = sizeof(ds13xx->regs); + + dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", + "write", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + ret = i2c_transfer(ds13xx->client.adapter, &ds13xx->msg[1], 1); + if (ret != 1) { + dev_err(dev, "%s error %d\n", "write", ret); + return ret < 0 ? ret : -EIO; + } + ds13xx->valid_time = 1; + return 0; +} + +static const struct rtc_class_ops ds13xx_rtc_ops = { + .read_time = ds13xx_get_time, + .set_time = ds13xx_set_time, +}; + +static struct i2c_driver ds13xx_i2c_driver; +static struct platform_device *ds13xx_dev; + +static int __devinit ds13xx_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct ds13xx *ds13xx; + int err = -ENODEV; + struct i2c_client *client; + int ret; + + if (ds13xx_dev == NULL) { + return -ENODEV; + } + + ds13xx = kzalloc(sizeof(struct ds13xx), GFP_KERNEL); + if (ds13xx == NULL) { + err = -ENOMEM; + goto exit; + } + + client = &ds13xx->client; + client->addr = address; + client->adapter = adapter; + client->driver = &ds13xx_i2c_driver; + client->flags = 0; + + i2c_set_clientdata(client, ds13xx); + + ds13xx->msg[0].addr = client->addr; + ds13xx->msg[0].flags = 0; + ds13xx->msg[0].len = 1; + ds13xx->msg[0].buf = &ds13xx->reg_addr; + + ds13xx->msg[1].addr = client->addr; + ds13xx->msg[1].flags = I2C_M_RD; + ds13xx->msg[1].len = sizeof(ds13xx->regs); + ds13xx->msg[1].buf = ds13xx->regs; + + /* HACK: "force" implies "needs ds1337-style-oscillator setup" */ + if (kind >= 0) { + ds13xx->type = ds_1337; + + ds13xx->reg_addr = DS133X_REG_CONTROL; + ds13xx->msg[1].len = 2; + + ret = i2c_transfer(client->adapter, ds13xx->msg, 2); + if (ret != 2) { + pr_debug("read error %d\n", ret); + err = ret < 0 ? ret : -EIO; + goto exit_free; + } + + ds13xx->reg_addr = 0; + ds13xx->msg[1].len = sizeof(ds13xx->regs); + + /* oscillator is off; need to turn it on */ + if ((ds13xx->regs[0] & DS133X_BIT_nEOSC) + || (ds13xx->regs[1] & DS133X_BIT_OSF)) { + dev_err(&ds13xx_dev->dev, "no ds1337 oscillator code\n"); + goto exit_free; + } + } else { + ds13xx->type = ds_1307; + } + + err = ds13xx_i2c_init(ds13xx_dev, client); + if (err) { + goto exit_free; + } + +read_rtc: + /* read RTC registers */ + + ret = i2c_transfer(client->adapter, ds13xx->msg, 2); + if (ret != 2) { + dev_dbg(&ds13xx_dev->dev, "read error %d\n", ret); + err = ret < 0 ? ret : -EIO; + goto exit_free; + } + + err = ds13xx_rtc_init(ds13xx_dev->dev.platform_data, client); + if (err) { + goto exit_free; + } + + /* minimal sanity checking; some chips (like DS1340) don't + * specify the extra bits as must-be-zero, but there are + * still a few values that are clearly out-of-range. + */ + ret = ds13xx->regs[DS13XX_REG_SECS]; + if (ret & DS1307_BIT_CH) { + if (ds13xx->type && ds13xx->type != ds_1307) { + pr_debug("not a ds1307?\n"); + goto exit_free; + } + ds13xx->type = ds_1307; + + /* this partial initialization should work for ds13xx, + * ds1338, ds1340, st m41t00, and more. + */ + dev_warn(&client->dev, "oscillator started; SET TIME!\n"); + ds13xx_write_reg(client, DS13XX_REG_SECS, 0); + goto read_rtc; + } + ret = bcd2bin(ret & 0x7f); + if (ret > 60) + goto exit_free; + ret = bcd2bin(ds13xx->regs[DS13XX_REG_MIN] & 0x7f); + if (ret > 60) + goto exit_free; + + ret = bcd2bin(ds13xx->regs[DS13XX_REG_MDAY] & 0x3f); + if (ret == 0 || ret > 31) + goto exit_free; + + ret = bcd2bin(ds13xx->regs[DS13XX_REG_MONTH] & 0x1f); + if (ret == 0 || ret > 12) + goto exit_free; + + /* force into in 24 hour mode (most chips) or + * disable century bit (ds1340) + */ + ret = ds13xx->regs[DS13XX_REG_HOUR]; + if (ret & (1 << 6)) { + if (ret & (1 << 5)) + ret = bcd2bin(ret & 0x1f) + 12; + else + ret = bcd2bin(ret); + ds13xx_write_reg(client, + DS13XX_REG_HOUR, + bin2bcd(ret)); + } + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(client))) + goto exit_free; + + dev_dbg(&client->dev, "Registering RTC %s\n", client->name); + ds13xx->rtc = rtc_device_register(client->name, &client->dev, + &ds13xx_rtc_ops, THIS_MODULE); + if (IS_ERR(ds13xx->rtc)) { + err = PTR_ERR(ds13xx->rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_detach; + } + + return 0; + +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(ds13xx); +exit: + return err; +} + +static int __devinit ds13xx_attach_adapter(struct i2c_adapter *adapter) +{ + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return 0; + return i2c_probe(adapter, &addr_data, ds13xx_detect); +} + +static int __devexit ds13xx_detach_client(struct i2c_client *client) +{ + int err; + struct ds13xx *ds13xx = i2c_get_clientdata(client); + + rtc_device_unregister(ds13xx->rtc); + if ((err = i2c_detach_client(client))) + return err; + kfree(ds13xx); + return 0; +} + +static struct i2c_driver ds13xx_i2c_driver = { + .driver = { + .name = "ds13xx", + .owner = THIS_MODULE, + }, + .id_table = ds13xx_id, + .attach_adapter = ds13xx_attach_adapter, + .detach_client = __devexit_p(ds13xx_detach_client), +}; + +static int __devinit ds13xx_probe(struct platform_device *pdev) +{ + ds13xx_dev = pdev; + return i2c_add_driver(&ds13xx_i2c_driver); +} + +static int __devexit ds13xx_remove(struct platform_device *pdev) +{ + i2c_del_driver(&ds13xx_i2c_driver); + ds13xx_dev = NULL; + return 0; +} + +static struct platform_driver ds13xx_driver = { + .probe = ds13xx_probe, + .remove = __devexit_p(ds13xx_remove), + .driver = { + .owner = THIS_MODULE, + .name = "rtc-ds13xx", + }, +}; + +static int __init ds13xx_init(void) +{ + return platform_driver_register(&ds13xx_driver); +} +module_init(ds13xx_init); + +static void __exit ds13xx_exit(void) +{ + platform_driver_unregister(&ds13xx_driver); +} +module_exit(ds13xx_exit); + +MODULE_DESCRIPTION("RTC driver for DS1307,1337,1339,1340 and similar chips"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/rtc/rtc-mxc.c linux-2.6.28-karo/drivers/rtc/rtc-mxc.c --- linux-2.6.28/drivers/rtc/rtc-mxc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/rtc/rtc-mxc.c 2009-03-11 18:47:09.000000000 +0100 @@ -0,0 +1,835 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* + * Implementation based on rtc-ds1553.c + */ + +/*! + * @defgroup RTC Real Time Clock (RTC) Driver + */ +/*! + * @file rtc-mxc.c + * @brief Real Time Clock interface + * + * This file contains Real Time Clock interface for Linux. + * + * @ingroup RTC + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define RTC_INPUT_CLK_32768HZ (0x00 << 5) +#define RTC_INPUT_CLK_32000HZ (0x01 << 5) +#define RTC_INPUT_CLK_38400HZ (0x02 << 5) + +#define RTC_SW_BIT (1 << 0) +#define RTC_ALM_BIT (1 << 2) +#define RTC_1HZ_BIT (1 << 4) +#define RTC_2HZ_BIT (1 << 7) +#define RTC_SAM0_BIT (1 << 8) +#define RTC_SAM1_BIT (1 << 9) +#define RTC_SAM2_BIT (1 << 10) +#define RTC_SAM3_BIT (1 << 11) +#define RTC_SAM4_BIT (1 << 12) +#define RTC_SAM5_BIT (1 << 13) +#define RTC_SAM6_BIT (1 << 14) +#define RTC_SAM7_BIT (1 << 15) +#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \ + RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \ + RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT) + +#define RTC_ENABLE_BIT (1 << 7) + +#define MAX_PIE_NUM 9 +#define MAX_PIE_FREQ 512 +const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { + {2, RTC_2HZ_BIT}, + {4, RTC_SAM0_BIT}, + {8, RTC_SAM1_BIT}, + {16, RTC_SAM2_BIT}, + {32, RTC_SAM3_BIT}, + {64, RTC_SAM4_BIT}, + {128, RTC_SAM5_BIT}, + {256, RTC_SAM6_BIT}, + {MAX_PIE_FREQ, RTC_SAM7_BIT}, +}; + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 /* Periodic interrupt */ +#define RTC_AF 0x20 /* Alarm interrupt */ +#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */ + +#define MXC_RTC_TIME 0 +#define MXC_RTC_ALARM 1 + +#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */ +#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */ +#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */ +#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */ +#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */ +#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */ +#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */ +#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */ +#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */ +#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */ +#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */ +#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */ +#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */ + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + unsigned long baseaddr; + int irq; + struct clk *clk; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; +}; + +/*! + * @defgroup RTC Real Time Clock (RTC) Driver + */ +/*! + * @file rtc-mxc.c + * @brief Real Time Clock interface + * + * This file contains Real Time Clock interface for Linux. + * + * @ingroup RTC + */ + +#if defined(CONFIG_MXC_MC13783_RTC) +#include +#else +#define pmic_rtc_get_time(args) MXC_EXTERNAL_RTC_NONE +#define pmic_rtc_set_time(args) MXC_EXTERNAL_RTC_NONE +#define pmic_rtc_loaded() 0 +#endif + +#define RTC_VERSION "1.0" +#define MXC_EXTERNAL_RTC_OK 0 +#define MXC_EXTERNAL_RTC_ERR -1 +#define MXC_EXTERNAL_RTC_NONE -2 + +/*! + * This function reads the RTC value from some external source. + * + * @param second pointer to the returned value in second + * + * @return 0 if successful; non-zero otherwise + */ +int get_ext_rtc_time(u32 * second) +{ + int ret = 0; + struct timeval tmp; + if (!pmic_rtc_loaded()) { + return MXC_EXTERNAL_RTC_NONE; + } + + ret = pmic_rtc_get_time(&tmp); + + if (0 == ret) + *second = tmp.tv_sec; + else + ret = MXC_EXTERNAL_RTC_ERR; + + return ret; +} + +/*! + * This function sets external RTC + * + * @param second value in second to be set to external RTC + * + * @return 0 if successful; non-zero otherwise + */ +int set_ext_rtc_time(u32 second) +{ + int ret = 0; + struct timeval tmp; + + if (!pmic_rtc_loaded()) { + return MXC_EXTERNAL_RTC_NONE; + } + + tmp.tv_sec = second; + + ret = pmic_rtc_set_time(&tmp); + + if (0 != ret) + ret = MXC_EXTERNAL_RTC_ERR; + + return ret; +} + +static u32 rtc_freq = 2; /* minimun value for PIE */ +static unsigned long rtc_status; + +static struct rtc_time g_rtc_alarm = { + .tm_year = 0, + .tm_mon = 0, + .tm_mday = 0, + .tm_hour = 0, + .tm_mon = 0, + .tm_sec = 0, +}; + +static DEFINE_SPINLOCK(rtc_lock); + +/*! + * This function is used to obtain the RTC time or the alarm value in + * second. + * + * @param time_alarm use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value + * + * @return The RTC time or alarm time in second. + */ +static u32 get_alarm_or_time(struct device *dev, int time_alarm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 day, hr, min, sec, hr_min; + if (time_alarm == MXC_RTC_TIME) { + day = readw(ioaddr + RTC_DAYR); + hr_min = readw(ioaddr + RTC_HOURMIN); + sec = readw(ioaddr + RTC_SECOND); + } else if (time_alarm == MXC_RTC_ALARM) { + day = readw(ioaddr + RTC_DAYALARM); + hr_min = (0x0000FFFF) & readw(ioaddr + RTC_ALRM_HM); + sec = readw(ioaddr + RTC_ALRM_SEC); + } else { + panic("wrong value for time_alarm=%d\n", time_alarm); + } + + hr = hr_min >> 8; + min = hr_min & 0x00FF; + + return ((((day * 24 + hr) * 60) + min) * 60 + sec); +} + +/*! + * This function sets the RTC alarm value or the time value. + * + * @param time_alarm the new alarm value to be updated in the RTC + * @param time use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value + */ +static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) +{ + u32 day, hr, min, sec, temp; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + day = time / 86400; + time -= day * 86400; + /* time is within a day now */ + hr = time / 3600; + time -= hr * 3600; + /* time is within an hour now */ + min = time / 60; + sec = time - min * 60; + + temp = (hr << 8) + min; + + if (time_alarm == MXC_RTC_TIME) { + writew(day, ioaddr + RTC_DAYR); + writew(sec, ioaddr + RTC_SECOND); + writew(temp, ioaddr + RTC_HOURMIN); + } else if (time_alarm == MXC_RTC_ALARM) { + writew(day, ioaddr + RTC_DAYALARM); + writew(sec, ioaddr + RTC_ALRM_SEC); + writew(temp, ioaddr + RTC_ALRM_HM); + } else { + panic("wrong value for time_alarm=%d\n", time_alarm); + } +} + +/*! + * This function updates the RTC alarm registers and then clears all the + * interrupt status bits. + * + * @param alrm the new alarm value to be updated in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + now = get_alarm_or_time(dev, MXC_RTC_TIME); + rtc_time_to_tm(now, &now_tm); + alarm_tm.tm_year = now_tm.tm_year; + alarm_tm.tm_mon = now_tm.tm_mon; + alarm_tm.tm_mday = now_tm.tm_mday; + alarm_tm.tm_hour = alrm->tm_hour; + alarm_tm.tm_min = alrm->tm_min; + alarm_tm.tm_sec = alrm->tm_sec; + rtc_tm_to_time(&now_tm, &now); + rtc_tm_to_time(&alarm_tm, &time); + if (time < now) { + time += 60 * 60 * 24; + rtc_time_to_tm(time, &alarm_tm); + } + ret = rtc_tm_to_time(&alarm_tm, &time); + + /* clear all the interrupt status bits */ + writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); + + set_alarm_or_time(dev, MXC_RTC_ALARM, time); + + return ret; +} + +/*! + * This function is the RTC interrupt service routine. + * + * @param irq RTC IRQ number + * @param dev_id device ID which is not used + * + * @return IRQ_HANDLED as defined in the include/linux/interrupt.h file. + */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 status; + u32 events = 0; + spin_lock(&rtc_lock); + status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR); + /* clear interrupt sources */ + writew(status, ioaddr + RTC_RTCISR); + + /* clear alarm interrupt if it has occurred */ + if (status & RTC_ALM_BIT) { + status &= ~RTC_ALM_BIT; + } + + /* update irq data & counter */ + if (status & RTC_ALM_BIT) { + events |= (RTC_AF | RTC_IRQF); + } + if (status & RTC_1HZ_BIT) { + events |= (RTC_UF | RTC_IRQF); + } + if (status & PIT_ALL_ON) { + events |= (RTC_PF | RTC_IRQF); + } + + if ((status & RTC_ALM_BIT) && rtc_valid_tm(&g_rtc_alarm)) { + rtc_update_alarm(&pdev->dev, &g_rtc_alarm); + } + + spin_unlock(&rtc_lock); + rtc_update_irq(pdata->rtc, 1, events); + return IRQ_HANDLED; +} + +/*! + * This function is used to open the RTC driver by registering the RTC + * interrupt service routine. + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_open(struct device *dev) +{ + if (test_and_set_bit(1, &rtc_status)) + return -EBUSY; + return 0; +} + +/*! + * clear all interrupts and release the IRQ + */ +static void mxc_rtc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + spin_lock_irq(&rtc_lock); + writew(0, ioaddr + RTC_RTCIENR); /* Disable all rtc interrupts */ + writew(0xFFFFFFFF, ioaddr + RTC_RTCISR); /* Clear all interrupt status */ + spin_unlock_irq(&rtc_lock); + rtc_status = 0; +} + +/*! + * This function is used to support some ioctl calls directly. + * Other ioctl calls are supported indirectly through the + * arm/common/rtctime.c file. + * + * @param cmd ioctl command as defined in include/linux/rtc.h + * @param arg value for the ioctl command + * + * @return 0 if successful or negative value otherwise. + */ +static int mxc_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + int i; + switch (cmd) { + case RTC_PIE_OFF: + writew((readw(ioaddr + RTC_RTCIENR) & ~PIT_ALL_ON), + ioaddr + RTC_RTCIENR); + return 0; + case RTC_IRQP_SET: + if (arg < 2 || arg > MAX_PIE_FREQ || (arg % 2) != 0) + return -EINVAL; /* Also make sure a power of 2Hz */ + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (u32 *) arg); + case RTC_PIE_ON: + for (i = 0; i < MAX_PIE_NUM; i++) { + if (PIE_BIT_DEF[i][0] == rtc_freq) { + break; + } + } + if (i == MAX_PIE_NUM) { + return -EACCES; + } + spin_lock_irq(&rtc_lock); + writew((readw(ioaddr + RTC_RTCIENR) | PIE_BIT_DEF[i][1]), + ioaddr + RTC_RTCIENR); + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_ALM_BIT), + ioaddr + RTC_RTCIENR); + spin_unlock_irq(&rtc_lock); + return 0; + + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + writew((readw(ioaddr + RTC_RTCIENR) | RTC_ALM_BIT), + ioaddr + RTC_RTCIENR); + spin_unlock_irq(&rtc_lock); + return 0; + + case RTC_UIE_OFF: /* UIE is for the 1Hz interrupt */ + spin_lock_irq(&rtc_lock); + writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_1HZ_BIT), + ioaddr + RTC_RTCIENR); + spin_unlock_irq(&rtc_lock); + return 0; + + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + writew((readw(ioaddr + RTC_RTCIENR) | RTC_1HZ_BIT), + ioaddr + RTC_RTCIENR); + spin_unlock_irq(&rtc_lock); + return 0; + } + return -ENOIOCTLCMD; +} + +/*! + * This function reads the current RTC time into tm in Gregorian date. + * + * @param tm contains the RTC time value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u32 val; + + /* Avoid roll-over from reading the different registers */ + do { + val = get_alarm_or_time(dev, MXC_RTC_TIME); + } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); + + rtc_time_to_tm(val, tm); + return 0; +} + +/*! + * This function sets the internal RTC time based on tm in Gregorian date. + * + * @param tm the time value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + int ret; + ret = rtc_tm_to_time(tm, &time); + if (ret != 0) { + return ret; + } + + /* Avoid roll-over from reading the different registers */ + do { + set_alarm_or_time(dev, MXC_RTC_TIME, time); + } while (time != get_alarm_or_time(dev, MXC_RTC_TIME)); + + ret = set_ext_rtc_time(time); + + if (ret != MXC_EXTERNAL_RTC_OK) { + if (ret == MXC_EXTERNAL_RTC_NONE) { + pr_info("No external RTC\n"); + ret = 0; + } else + pr_info("Failed to set external RTC\n"); + } + + return ret; +} + +/*! + * This function reads the current alarm value into the passed in \b alrm + * argument. It updates the \b alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + * + * @param alrm contains the RTC alarm value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); + alrm->enabled = !!(readw(ioaddr + RTC_RTCIENR) & RTC_ALM_BIT); + alrm->pending = !!(readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT); + return 0; +} + +/*! + * This function sets the RTC alarm based on passed in alrm. + * + * @param alrm the alarm value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + int ret; + + spin_lock_irq(&rtc_lock); + if (rtc_valid_tm(&alrm->time)) { + if (alrm->time.tm_sec > 59 || + alrm->time.tm_hour > 23 || alrm->time.tm_min > 59) { + ret = -EINVAL; + goto out; + } + ret = rtc_update_alarm(dev, &alrm->time); + } else { + if ((ret = rtc_valid_tm(&alrm->time))) + goto out; + ret = rtc_update_alarm(dev, &alrm->time); + } + + if (ret == 0) { + memcpy(&g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + + if (alrm->enabled) { + writew((readw(ioaddr + RTC_RTCIENR) | RTC_ALM_BIT), + ioaddr + RTC_RTCIENR); + } else { + writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_ALM_BIT), + ioaddr + RTC_RTCIENR); + } + device_set_wakeup_enable(dev, alrm->enabled); + } + out: + spin_unlock_irq(&rtc_lock); + + return ret; +} + +/*! + * This function is used to provide the content for the /proc/driver/rtc + * file. + * + * @param buf the buffer to hold the information that the driver wants to write + * + * @return The number of bytes written into the rtc file. + */ +static int mxc_rtc_proc(struct device *dev, struct seq_file *sq) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + char *p = sq->buf; + + p += sprintf(p, "alarm_IRQ\t: %s\n", + (((readw(ioaddr + RTC_RTCIENR)) & RTC_ALM_BIT) != + 0) ? "yes" : "no"); + p += sprintf(p, "update_IRQ\t: %s\n", + (((readw(ioaddr + RTC_RTCIENR)) & RTC_1HZ_BIT) != + 0) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", + (((readw(ioaddr + RTC_RTCIENR)) & PIT_ALL_ON) != + 0) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %d\n", rtc_freq); + + return p - (sq->buf); +} + +/*! + * The RTC driver structure + */ +static struct rtc_class_ops mxc_rtc_ops = { + .open = mxc_rtc_open, + .release = mxc_rtc_release, + .ioctl = mxc_rtc_ioctl, + .read_time = mxc_rtc_read_time, + .set_time = mxc_rtc_set_time, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .proc = mxc_rtc_proc, +}; + +/*! MXC RTC Power management control */ + +static struct timespec mxc_rtc_delta; + +static int mxc_rtc_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct timespec tv; + struct resource *res; + struct rtc_time temp_time; + struct rtc_device *rtc; + struct rtc_plat_data *pdata = NULL; + u32 sec, reg; + int ret; + int rtc_input_clk; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->clk = clk_get(&pdev->dev, "rtc_clk"); + clk_enable(pdata->clk); + + pdata->baseaddr = res->start; + pdata->ioaddr = ((void *)(IO_ADDRESS(pdata->baseaddr))); + /* Configure and enable the RTC */ + rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + if (pdata->irq >= 0) + free_irq(pdata->irq, pdev); + kfree(pdata); + return ret; + } + pdata->rtc = rtc; + platform_set_drvdata(pdev, pdata); + + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq >= 0) { + if (request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED, + pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + } + device_set_wakeup_capable(&pdev->dev, 1); + + ret = get_ext_rtc_time(&sec); + if (ret == MXC_EXTERNAL_RTC_OK) { + rtc_time_to_tm(sec, &temp_time); + mxc_rtc_set_time(&pdev->dev, &temp_time); + + } else if (ret == MXC_EXTERNAL_RTC_NONE) { + pr_info("No external RTC device\n"); + } else { + pr_info("Reading external RTC device failed\n"); + } + tv.tv_nsec = 0; + tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME); + clk = clk_get(NULL, "ckil"); + rtc_input_clk = clk_get_rate(clk); + if (rtc_input_clk == 32768) + reg = RTC_INPUT_CLK_32768HZ; + else if (rtc_input_clk == 32000) + reg = RTC_INPUT_CLK_32000HZ; + else if (rtc_input_clk == 38400) + reg = RTC_INPUT_CLK_38400HZ; + else { + printk(KERN_ALERT "rtc clock is not valid"); + return -EINVAL; + } + clk_put(clk); + reg |= RTC_ENABLE_BIT; + writew(reg, (pdata->ioaddr + RTC_RTCCTL)); + if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) { + printk(KERN_ALERT "rtc : hardware module can't be enabled!\n"); + return -EPERM; + } + printk("Real Time clock Driver v%s %ukHz base clock\n", RTC_VERSION, + rtc_input_clk); + return 0; +} + +static int __exit mxc_rtc_remove(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + rtc_device_unregister(pdata->rtc); + if (pdata->irq >= 0) { + free_irq(pdata->irq, pdev); + } + clk_disable(pdata->clk); + clk_put(pdata->clk); + kfree(pdata); + mxc_rtc_release(NULL); + return 0; +} + +/*! + * This function is called to save the system time delta relative to + * the MXC RTC when enterring a low power state. This time delta is + * then used on resume to adjust the system time to account for time + * loss while suspended. + * + * @param pdev not used + * @param state Power state to enter. + * + * @return The function always returns 0. + */ +static int mxc_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + struct timespec tv; + + /* calculate time delta for suspend */ + /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ + tv.tv_nsec = NSEC_PER_SEC >> 1; + tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME); + set_normalized_timespec(&mxc_rtc_delta, + xtime.tv_sec - tv.tv_sec, + xtime.tv_nsec - tv.tv_nsec); + + if (device_may_wakeup(&pdev->dev)) { + int ret; + + ret = enable_irq_wake(pdata->irq); + if (ret != 0) { + dev_warn(&pdev->dev, "Failed to enable IRQ wake for IRQ %d: %d\n", + pdata->irq, ret); + return ret; + } + } + return 0; +} + +/*! + * This function is called to correct the system time based on the + * current MXC RTC time relative to the time delta saved during + * suspend. + * + * @param pdev not used + * + * @return The function always returns 0. + */ +static int mxc_rtc_resume(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + struct timespec tv; + struct timespec ts; + + tv.tv_nsec = 0; + tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME); + + /* restore wall clock using delta against this RTC; + * adjust again for avg 1/2 second RTC sampling error + */ + set_normalized_timespec(&ts, + tv.tv_sec + mxc_rtc_delta.tv_sec, + (NSEC_PER_SEC >> 1) + mxc_rtc_delta.tv_nsec); + do_settimeofday(&ts); + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(pdata->irq); + return 0; +} + +/*! + * Contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc", + }, + .probe = mxc_rtc_probe, + .remove = __exit_p(mxc_rtc_remove), + .suspend = mxc_rtc_suspend, + .resume = mxc_rtc_resume, +}; + +/*! + * This function creates the /proc/driver/rtc file and registers the device RTC + * in the /dev/misc directory. It also reads the RTC value from external source + * and setup the internal RTC properly. + * + * @return -1 if RTC is failed to initialize; 0 is successful. + */ +static int __init mxc_rtc_init(void) +{ + return platform_driver_register(&mxc_rtc_driver); +} + +/*! + * This function removes the /proc/driver/rtc file and un-registers the + * device RTC from the /dev/misc directory. + */ +static void __exit mxc_rtc_exit(void) +{ + platform_driver_unregister(&mxc_rtc_driver); + +} + +module_init(mxc_rtc_init); +module_exit(mxc_rtc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/serial/imx.c linux-2.6.28-karo/drivers/serial/imx.c --- linux-2.6.28/drivers/serial/imx.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/serial/imx.c 2009-03-11 13:16:24.000000000 +0100 @@ -31,6 +31,7 @@ #endif #include +#include #include #include #include @@ -42,7 +43,6 @@ #include #include -#include #include #include #include @@ -66,7 +66,7 @@ #define ONEMS 0xb0 /* One Millisecond register */ #define UTS 0xb4 /* UART Test Register */ #endif -#ifdef CONFIG_ARCH_IMX +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) #define BIPR1 0xb0 /* Incremental Preset Register 1 */ #define BIPR2 0xb4 /* Incremental Preset Register 2 */ #define BIPR3 0xb8 /* Incremental Preset Register 3 */ @@ -79,105 +79,105 @@ #endif /* UART Control Register Bit Fields.*/ -#define URXD_CHARRDY (1<<15) -#define URXD_ERR (1<<14) -#define URXD_OVRRUN (1<<13) -#define URXD_FRMERR (1<<12) -#define URXD_BRK (1<<11) -#define URXD_PRERR (1<<10) -#define UCR1_ADEN (1<<15) /* Auto dectect interrupt */ -#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ -#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ -#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ -#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ -#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ -#define UCR1_IREN (1<<7) /* Infrared interface enable */ -#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ -#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ -#define UCR1_SNDBRK (1<<4) /* Send break */ -#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ -#ifdef CONFIG_ARCH_IMX -#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ +#define URXD_CHARRDY (1 << 15) +#define URXD_ERR (1 << 14) +#define URXD_OVRRUN (1 << 13) +#define URXD_FRMERR (1 << 12) +#define URXD_BRK (1 << 11) +#define URXD_PRERR (1 << 10) +#define UCR1_ADEN (1 << 15) /* Auto dectect interrupt */ +#define UCR1_ADBR (1 << 14) /* Auto detect baud rate */ +#define UCR1_TRDYEN (1 << 13) /* Transmitter ready interrupt enable */ +#define UCR1_IDEN (1 << 12) /* Idle condition interrupt */ +#define UCR1_RRDYEN (1 << 9) /* Recv ready interrupt enable */ +#define UCR1_RDMAEN (1 << 8) /* Recv ready DMA enable */ +#define UCR1_IREN (1 << 7) /* Infrared interface enable */ +#define UCR1_TXMPTYEN (1 << 6) /* Transimitter empty interrupt enable */ +#define UCR1_RTSDEN (1 << 5) /* RTS delta interrupt enable */ +#define UCR1_SNDBRK (1 << 4) /* Send break */ +#define UCR1_TDMAEN (1 << 3) /* Transmitter ready DMA enable */ +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) +#define UCR1_UARTCLKEN (1 << 2) /* UART clock enabled */ #endif #if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 -#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */ +#define UCR1_UARTCLKEN 0 /* not present on mx2/mx3 */ #endif -#define UCR1_DOZE (1<<1) /* Doze */ -#define UCR1_UARTEN (1<<0) /* UART enabled */ -#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ -#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ -#define UCR2_CTSC (1<<13) /* CTS pin control */ -#define UCR2_CTS (1<<12) /* Clear to send */ -#define UCR2_ESCEN (1<<11) /* Escape enable */ -#define UCR2_PREN (1<<8) /* Parity enable */ -#define UCR2_PROE (1<<7) /* Parity odd/even */ -#define UCR2_STPB (1<<6) /* Stop */ -#define UCR2_WS (1<<5) /* Word size */ -#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ -#define UCR2_TXEN (1<<2) /* Transmitter enabled */ -#define UCR2_RXEN (1<<1) /* Receiver enabled */ -#define UCR2_SRST (1<<0) /* SW reset */ -#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ -#define UCR3_PARERREN (1<<12) /* Parity enable */ -#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ -#define UCR3_DSR (1<<10) /* Data set ready */ -#define UCR3_DCD (1<<9) /* Data carrier detect */ -#define UCR3_RI (1<<8) /* Ring indicator */ -#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ -#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ -#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ -#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ +#define UCR1_DOZE (1 << 1) /* Doze */ +#define UCR1_UARTEN (1 << 0) /* UART enabled */ +#define UCR2_ESCI (1 << 15) /* Escape seq interrupt enable */ +#define UCR2_IRTS (1 << 14) /* Ignore RTS pin */ +#define UCR2_CTSC (1 << 13) /* CTS pin control */ +#define UCR2_CTS (1 << 12) /* Clear to send */ +#define UCR2_ESCEN (1 << 11) /* Escape enable */ +#define UCR2_PREN (1 << 8) /* Parity enable */ +#define UCR2_PROE (1 << 7) /* Parity odd/even */ +#define UCR2_STPB (1 << 6) /* Stop */ +#define UCR2_WS (1 << 5) /* Word size */ +#define UCR2_RTSEN (1 << 4) /* Request to send interrupt enable */ +#define UCR2_TXEN (1 << 2) /* Transmitter enabled */ +#define UCR2_RXEN (1 << 1) /* Receiver enabled */ +#define UCR2_SRST (1 << 0) /* SW reset */ +#define UCR3_DTREN (1 << 13) /* DTR interrupt enable */ +#define UCR3_PARERREN (1 << 12) /* Parity enable */ +#define UCR3_FRAERREN (1 << 11) /* Frame error interrupt enable */ +#define UCR3_DSR (1 << 10) /* Data set ready */ +#define UCR3_DCD (1 << 9) /* Data carrier detect */ +#define UCR3_RI (1 << 8) /* Ring indicator */ +#define UCR3_TIMEOUTEN (1 << 7) /* Timeout interrupt enable */ +#define UCR3_RXDSEN (1 << 6) /* Receive status interrupt enable */ +#define UCR3_AIRINTEN (1 << 5) /* Async IR wake interrupt enable */ +#define UCR3_AWAKEN (1 << 4) /* Async wake interrupt enable */ #ifdef CONFIG_ARCH_IMX -#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ -#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ +#define UCR3_REF25 (1 << 3) /* Ref freq 25 MHz, only on mx1 */ +#define UCR3_REF30 (1 << 2) /* Ref Freq 30 MHz, only on mx1 */ #endif #if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 -#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ +#define UCR3_RXDMUXSEL (1 << 2) /* RXD Muxed Input Select, on mx2/mx3 */ #endif -#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ -#define UCR3_BPEN (1<<0) /* Preset registers enable */ -#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ -#define UCR4_INVR (1<<9) /* Inverted infrared reception */ -#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ -#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ -#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ -#define UCR4_IRSC (1<<5) /* IR special case */ -#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ -#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ -#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ -#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ -#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ -#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ -#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ -#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ -#define USR1_RTSS (1<<14) /* RTS pin status */ -#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ -#define USR1_RTSD (1<<12) /* RTS delta */ -#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ -#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ -#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ -#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ -#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ -#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ -#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ -#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ -#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ -#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ -#define USR2_IDLE (1<<12) /* Idle condition */ -#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ -#define USR2_WAKE (1<<7) /* Wake */ -#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ -#define USR2_TXDC (1<<3) /* Transmitter complete */ -#define USR2_BRCD (1<<2) /* Break condition */ -#define USR2_ORE (1<<1) /* Overrun error */ -#define USR2_RDR (1<<0) /* Recv data ready */ -#define UTS_FRCPERR (1<<13) /* Force parity error */ -#define UTS_LOOP (1<<12) /* Loop tx and rx */ -#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ -#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ -#define UTS_TXFULL (1<<4) /* TxFIFO full */ -#define UTS_RXFULL (1<<3) /* RxFIFO full */ -#define UTS_SOFTRST (1<<0) /* Software reset */ +#define UCR3_INVT (1 << 1) /* Inverted Infrared transmission */ +#define UCR3_BPEN (1 << 0) /* Preset registers enable */ +#define UCR4_CTSTL_32 (32 << 10) /* CTS trigger level (32 chars) */ +#define UCR4_INVR (1 << 9) /* Inverted infrared reception */ +#define UCR4_ENIRI (1 << 8) /* Serial infrared interrupt enable */ +#define UCR4_WKEN (1 << 7) /* Wake interrupt enable */ +#define UCR4_REF16 (1 << 6) /* Ref freq 16 MHz */ +#define UCR4_IRSC (1 << 5) /* IR special case */ +#define UCR4_TCEN (1 << 3) /* Transmit complete interrupt enable */ +#define UCR4_BKEN (1 << 2) /* Break condition interrupt enable */ +#define UCR4_OREN (1 << 1) /* Receiver overrun interrupt enable */ +#define UCR4_DREN (1 << 0) /* Recv data ready interrupt enable */ +#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ +#define UFCR_RFDIV (7 << 7) /* Reference freq divider mask */ +#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ +#define USR1_PARITYERR (1 << 15) /* Parity error interrupt flag */ +#define USR1_RTSS (1 << 14) /* RTS pin status */ +#define USR1_TRDY (1 << 13) /* Transmitter ready interrupt/dma flag */ +#define USR1_RTSD (1 << 12) /* RTS delta */ +#define USR1_ESCF (1 << 11) /* Escape seq interrupt flag */ +#define USR1_FRAMERR (1 << 10) /* Frame error interrupt flag */ +#define USR1_RRDY (1 << 9) /* Receiver ready interrupt/dma flag */ +#define USR1_TIMEOUT (1 << 7) /* Receive timeout interrupt status */ +#define USR1_RXDS (1 << 6) /* Receiver idle interrupt flag */ +#define USR1_AIRINT (1 << 5) /* Async IR wake interrupt flag */ +#define USR1_AWAKE (1 << 4) /* Aysnc wake interrupt flag */ +#define USR2_ADET (1 << 15) /* Auto baud rate detect complete */ +#define USR2_TXFE (1 << 14) /* Transmit buffer FIFO empty */ +#define USR2_DTRF (1 << 13) /* DTR edge interrupt flag */ +#define USR2_IDLE (1 << 12) /* Idle condition */ +#define USR2_IRINT (1 << 8) /* Serial infrared interrupt flag */ +#define USR2_WAKE (1 << 7) /* Wake */ +#define USR2_RTSF (1 << 4) /* RTS edge interrupt flag */ +#define USR2_TXDC (1 << 3) /* Transmitter complete */ +#define USR2_BRCD (1 << 2) /* Break condition */ +#define USR2_ORE (1 << 1) /* Overrun error */ +#define USR2_RDR (1 << 0) /* Recv data ready */ +#define UTS_FRCPERR (1 << 13) /* Force parity error */ +#define UTS_LOOP (1 << 12) /* Loop tx and rx */ +#define UTS_TXEMPTY (1 << 6) /* TxFIFO empty */ +#define UTS_RXEMPTY (1 << 5) /* RxFIFO empty */ +#define UTS_TXFULL (1 << 4) /* TxFIFO full */ +#define UTS_RXFULL (1 << 3) /* RxFIFO full */ +#define UTS_SOFTRST (1 << 0) /* Software reset */ /* We've been assigned a range on the "Low-density serial ports" major */ #ifdef CONFIG_ARCH_IMX @@ -187,9 +187,9 @@ #define MAX_INTERNAL_IRQ IMX_IRQS #endif -#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 -#define SERIAL_IMX_MAJOR 207 -#define MINOR_START 16 +#ifdef CONFIG_ARCH_MXC +#define SERIAL_IMX_MAJOR 207 +#define MINOR_START 16 #define DEV_NAME "ttymxc" #define MAX_INTERNAL_IRQ MXC_MAX_INT_LINES #endif @@ -200,7 +200,7 @@ * so we have to poll them. We also check immediately before * filling the TX fifo incase CTS has been dropped. */ -#define MCTRL_TIMEOUT (250*HZ/1000) +#define MCTRL_TIMEOUT (250 * HZ / 1000) #define DRIVER_NAME "IMX-uart" @@ -210,7 +210,7 @@ struct imx_port { struct uart_port port; struct timer_list timer; unsigned int old_status; - int txirq,rxirq,rtsirq; + int txirq, rxirq, rtsirq; int have_rtscts:1; struct clk *clk; }; @@ -268,8 +268,8 @@ static void imx_stop_tx(struct uart_port struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR1); - writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); + temp = __raw_readl(sport->port.membase + UCR1); + __raw_writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); } /* @@ -280,8 +280,8 @@ static void imx_stop_rx(struct uart_port struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR2); - writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2); + temp = __raw_readl(sport->port.membase + UCR2); + __raw_writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); } /* @@ -298,12 +298,12 @@ static inline void imx_transmit_buffer(s { struct circ_buf *xmit = &sport->port.info->xmit; - while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) { + while (!(__raw_readl(sport->port.membase + UTS) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ - writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); + __raw_writel(xmit->buf[xmit->tail], + sport->port.membase + URTX0); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; if (uart_circ_empty(xmit)) break; @@ -321,22 +321,22 @@ static void imx_start_tx(struct uart_por struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR1); - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + temp = __raw_readl(sport->port.membase + UCR1); + __raw_writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); - if (readl(sport->port.membase + UTS) & UTS_TXEMPTY) + if (__raw_readl(sport->port.membase + UTS) & UTS_TXEMPTY) imx_transmit_buffer(sport); } static irqreturn_t imx_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; + unsigned int val = __raw_readl(sport->port.membase + USR1) & USR1_RTSS; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - writel(USR1_RTSD, sport->port.membase + USR1); + __raw_writel(USR1_RTSD, sport->port.membase + USR1); uart_handle_cts_change(&sport->port, !!val); wake_up_interruptible(&sport->port.info->delta_msr_wait); @@ -350,11 +350,10 @@ static irqreturn_t imx_txint(int irq, vo struct circ_buf *xmit = &sport->port.info->xmit; unsigned long flags; - spin_lock_irqsave(&sport->port.lock,flags); - if (sport->port.x_char) - { + spin_lock_irqsave(&sport->port.lock, flags); + if (sport->port.x_char) { /* Send next char */ - writel(sport->port.x_char, sport->port.membase + URTX0); + __raw_writel(sport->port.x_char, sport->port.membase + URTX0); goto out; } @@ -369,37 +368,37 @@ static irqreturn_t imx_txint(int irq, vo uart_write_wakeup(&sport->port); out: - spin_unlock_irqrestore(&sport->port.lock,flags); + spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int rx,flg,ignored = 0; + unsigned int rx, flg, ignored = 0; struct tty_struct *tty = sport->port.info->port.tty; unsigned long flags, temp; - spin_lock_irqsave(&sport->port.lock,flags); + spin_lock_irqsave(&sport->port.lock, flags); - while (readl(sport->port.membase + USR2) & USR2_RDR) { + while (__raw_readl(sport->port.membase + USR2) & USR2_RDR) { flg = TTY_NORMAL; sport->port.icount.rx++; - rx = readl(sport->port.membase + URXD0); + rx = __raw_readl(sport->port.membase + URXD0); - temp = readl(sport->port.membase + USR2); + temp = __raw_readl(sport->port.membase + USR2); if (temp & USR2_BRCD) { - writel(temp | USR2_BRCD, sport->port.membase + USR2); + __raw_writel(temp | USR2_BRCD, + sport->port.membase + USR2); if (uart_handle_break(&sport->port)) continue; } - if (uart_handle_sysrq_char - (&sport->port, (unsigned char)rx)) + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) continue; - if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { + if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR)) { if (rx & URXD_PRERR) sport->port.icount.parity++; else if (rx & URXD_FRMERR) @@ -431,7 +430,7 @@ static irqreturn_t imx_rxint(int irq, vo } out: - spin_unlock_irqrestore(&sport->port.lock,flags); + spin_unlock_irqrestore(&sport->port.lock, flags); tty_flip_buffer_push(tty); return IRQ_HANDLED; } @@ -441,13 +440,13 @@ static irqreturn_t imx_int(int irq, void struct imx_port *sport = dev_id; unsigned int sts; - sts = readl(sport->port.membase + USR1); + sts = __raw_readl(sport->port.membase + USR1); if (sts & USR1_RRDY) imx_rxint(irq, dev_id); if (sts & USR1_TRDY && - readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) + __raw_readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) imx_txint(irq, dev_id); if (sts & USR1_RTSD) @@ -463,7 +462,8 @@ static unsigned int imx_tx_empty(struct { struct imx_port *sport = (struct imx_port *)port; - return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; + return (__raw_readl(sport->port.membase + USR2) & USR2_TXDC) ? + TIOCSER_TEMT : 0; } /* @@ -471,29 +471,29 @@ static unsigned int imx_tx_empty(struct */ static unsigned int imx_get_mctrl(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; - unsigned int tmp = TIOCM_DSR | TIOCM_CAR; + struct imx_port *sport = (struct imx_port *)port; + unsigned int tmp = TIOCM_DSR | TIOCM_CAR; - if (readl(sport->port.membase + USR1) & USR1_RTSS) - tmp |= TIOCM_CTS; + if (__raw_readl(sport->port.membase + USR1) & USR1_RTSS) + tmp |= TIOCM_CTS; - if (readl(sport->port.membase + UCR2) & UCR2_CTS) - tmp |= TIOCM_RTS; + if (__raw_readl(sport->port.membase + UCR2) & UCR2_CTS) + tmp |= TIOCM_RTS; - return tmp; + return tmp; } static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; + temp = __raw_readl(sport->port.membase + UCR2) & ~UCR2_CTS; - if (mctrl & TIOCM_RTS) + if (mctrl & TIOCM_RTS) temp |= UCR2_CTS; - writel(temp, sport->port.membase + UCR2); + __raw_writel(temp, sport->port.membase + UCR2); } /* @@ -506,12 +506,12 @@ static void imx_break_ctl(struct uart_po spin_lock_irqsave(&sport->port.lock, flags); - temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; + temp = __raw_readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; - if ( break_state != 0 ) + if (break_state != 0) temp |= UCR1_SNDBRK; - writel(temp, sport->port.membase + UCR1); + __raw_writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -531,17 +531,17 @@ static int imx_setup_ufcr(struct imx_por ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2) / sport->port.uartclk; - if(!ufcr_rfdiv) + if (!ufcr_rfdiv) ufcr_rfdiv = 1; - if(ufcr_rfdiv >= 7) + if (ufcr_rfdiv >= 7) ufcr_rfdiv = 6; else ufcr_rfdiv = 6 - ufcr_rfdiv; val |= UFCR_RFDIV & (ufcr_rfdiv << 7); - writel(val, sport->port.membase + UFCR); + __raw_writel(val, sport->port.membase + UFCR); return 0; } @@ -557,8 +557,8 @@ static int imx_startup(struct uart_port /* disable the DREN bit (Data Ready interrupt enable) before * requesting IRQs */ - temp = readl(sport->port.membase + UCR4); - writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + temp = __raw_readl(sport->port.membase + UCR4); + __raw_writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); /* * Allocate the IRQ(s) i.MX1 has three interrupts whereas later @@ -576,8 +576,8 @@ static int imx_startup(struct uart_port goto error_out2; retval = request_irq(sport->rtsirq, imx_rtsint, - (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, DRIVER_NAME, sport); if (retval) goto error_out3; @@ -593,28 +593,28 @@ static int imx_startup(struct uart_port /* * Finally, clear and enable interrupts */ - writel(USR1_RTSD, sport->port.membase + USR1); + __raw_writel(USR1_RTSD, sport->port.membase + USR1); - temp = readl(sport->port.membase + UCR1); + temp = __raw_readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; - writel(temp, sport->port.membase + UCR1); + __raw_writel(temp, sport->port.membase + UCR1); - temp = readl(sport->port.membase + UCR2); + temp = __raw_readl(sport->port.membase + UCR2); temp |= (UCR2_RXEN | UCR2_TXEN); - writel(temp, sport->port.membase + UCR2); + __raw_writel(temp, sport->port.membase + UCR2); #if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 - temp = readl(sport->port.membase + UCR3); + temp = __raw_readl(sport->port.membase + UCR3); temp |= UCR3_RXDMUXSEL; - writel(temp, sport->port.membase + UCR3); + __raw_writel(temp, sport->port.membase + UCR3); #endif /* * Enable modem status interrupts */ - spin_lock_irqsave(&sport->port.lock,flags); + spin_lock_irqsave(&sport->port.lock, flags); imx_enable_ms(&sport->port); - spin_unlock_irqrestore(&sport->port.lock,flags); + spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -652,14 +652,14 @@ static void imx_shutdown(struct uart_por * Disable all interrupts, port and break condition. */ - temp = readl(sport->port.membase + UCR1); + temp = __raw_readl(sport->port.membase + UCR1); temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); - writel(temp, sport->port.membase + UCR1); + __raw_writel(temp, sport->port.membase + UCR1); } static void imx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + struct ktermios *old) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; @@ -680,7 +680,7 @@ imx_set_termios(struct uart_port *port, * We only support CS7 and CS8. */ while ((termios->c_cflag & CSIZE) != CS7 && - (termios->c_cflag & CSIZE) != CS8) { + (termios->c_cflag & CSIZE) != CS8) { termios->c_cflag &= ~CSIZE; termios->c_cflag |= old_csize; old_csize = CS8; @@ -692,7 +692,7 @@ imx_set_termios(struct uart_port *port, ucr2 = UCR2_SRST | UCR2_IRTS; if (termios->c_cflag & CRTSCTS) { - if( sport->have_rtscts ) { + if (sport->have_rtscts) { ucr2 &= ~UCR2_IRTS; ucr2 |= UCR2_CTSC; } else { @@ -748,17 +748,17 @@ imx_set_termios(struct uart_port *port, /* * disable interrupts and drain transmitter */ - old_ucr1 = readl(sport->port.membase + UCR1); - writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), + old_ucr1 = __raw_readl(sport->port.membase + UCR1); + __raw_writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), sport->port.membase + UCR1); - while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) + while (!(__raw_readl(sport->port.membase + USR2) & USR2_TXDC)) barrier(); /* then, disable everything */ - old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), - sport->port.membase + UCR2); + old_txrxen = __raw_readl(sport->port.membase + UCR2); + __raw_writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN), + sport->port.membase + UCR2); old_txrxen &= (UCR2_TXEN | UCR2_RXEN); div = sport->port.uartclk / (baud * 16); @@ -780,27 +780,27 @@ imx_set_termios(struct uart_port *port, if (denom > 0) denom -= 1; - writel(num, sport->port.membase + UBIR); - writel(denom, sport->port.membase + UBMR); + __raw_writel(num, sport->port.membase + UBIR); + __raw_writel(denom, sport->port.membase + UBMR); if (div == 7) div = 6; /* 6 in RFDIV means divide by 7 */ else div = 6 - div; - ufcr = readl(sport->port.membase + UFCR); - ufcr = (ufcr & (~UFCR_RFDIV)) | - (div << 7); - writel(ufcr, sport->port.membase + UFCR); + ufcr = __raw_readl(sport->port.membase + UFCR); + ufcr = (ufcr & ~UFCR_RFDIV) | (div << 7); + __raw_writel(ufcr, sport->port.membase + UFCR); #ifdef ONEMS - writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS); + __raw_writel(sport->port.uartclk / div / 1000, + sport->port.membase + ONEMS); #endif - writel(old_ucr1, sport->port.membase + UCR1); + __raw_writel(old_ucr1, sport->port.membase + UCR1); /* set the parity, stop bits and data size */ - writel(ucr2 | old_txrxen, sport->port.membase + UCR2); + __raw_writel(ucr2 | old_txrxen, sport->port.membase + UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); @@ -854,7 +854,7 @@ static void imx_config_port(struct uart_ struct imx_port *sport = (struct imx_port *)port; if (flags & UART_CONFIG_TYPE && - imx_request_port(&sport->port) == 0) + imx_request_port(&sport->port) == 0) sport->port.type = PORT_IMX; } @@ -912,10 +912,10 @@ static void imx_console_putchar(struct u { struct imx_port *sport = (struct imx_port *)port; - while (readl(sport->port.membase + UTS) & UTS_TXFULL) + while (__raw_readl(sport->port.membase + UTS) & UTS_TXFULL) barrier(); - writel(ch, sport->port.membase + URTX0); + __raw_writel(ch, sport->port.membase + URTX0); } /* @@ -930,14 +930,14 @@ imx_console_write(struct console *co, co /* * First, save UCR1/2 and then disable interrupts */ - old_ucr1 = readl(sport->port.membase + UCR1); - old_ucr2 = readl(sport->port.membase + UCR2); + old_ucr1 = __raw_readl(sport->port.membase + UCR1); + old_ucr2 = __raw_readl(sport->port.membase + UCR2); - writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) & + __raw_writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), sport->port.membase + UCR1); - writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); + __raw_writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); uart_console_write(&sport->port, s, count, imx_console_putchar); @@ -945,10 +945,11 @@ imx_console_write(struct console *co, co * Finally, wait for transmitter to become empty * and restore UCR1/2 */ - while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); + while (!(__raw_readl(sport->port.membase + USR2) & USR2_TXDC)) + ; - writel(old_ucr1, sport->port.membase + UCR1); - writel(old_ucr2, sport->port.membase + UCR2); + __raw_writel(old_ucr1, sport->port.membase + UCR1); + __raw_writel(old_ucr2, sport->port.membase + UCR2); } /* @@ -957,16 +958,15 @@ imx_console_write(struct console *co, co */ static void __init imx_console_get_options(struct imx_port *sport, int *baud, - int *parity, int *bits) + int *parity, int *bits) { - - if ( readl(sport->port.membase + UCR1) | UCR1_UARTEN ) { + if (__raw_readl(sport->port.membase + UCR1) | UCR1_UARTEN) { /* ok, the port was enabled */ - unsigned int ucr2, ubir,ubmr, uartclk; + unsigned int ucr2, ubir, ubmr, uartclk; unsigned int baud_raw; unsigned int ucfr_rfdiv; - ucr2 = readl(sport->port.membase + UCR2); + ucr2 = __raw_readl(sport->port.membase + UCR2); *parity = 'n'; if (ucr2 & UCR2_PREN) { @@ -981,10 +981,11 @@ imx_console_get_options(struct imx_port else *bits = 7; - ubir = readl(sport->port.membase + UBIR) & 0xffff; - ubmr = readl(sport->port.membase + UBMR) & 0xffff; + ubir = __raw_readl(sport->port.membase + UBIR) & 0xffff; + ubmr = __raw_readl(sport->port.membase + UBMR) & 0xffff; - ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7; + ucfr_rfdiv = (__raw_readl(sport->port.membase + UFCR) & + UFCR_RFDIV) >> 7; if (ucfr_rfdiv == 6) ucfr_rfdiv = 7; else @@ -993,7 +994,8 @@ imx_console_get_options(struct imx_port uartclk = clk_get_rate(sport->clk); uartclk /= ucfr_rfdiv; - { /* + { + /* * The next code provides exact computation of * baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1)) * without need of float support or long long division, @@ -1008,7 +1010,7 @@ imx_console_get_options(struct imx_port *baud = (baud_raw + 50) / 100 * 100; } - if(*baud != baud_raw) + if (*baud != baud_raw) printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n", baud_raw, *baud); } @@ -1028,9 +1030,13 @@ imx_console_setup(struct console *co, ch * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) + if (co->index < 0 || co->index >= ARRAY_SIZE(imx_ports)) co->index = 0; - sport = imx_ports[co->index]; + do { + sport = imx_ports[co->index]; + } while (sport == NULL && ++co->index < ARRAY_SIZE(imx_ports)); + if (!sport) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -1070,22 +1076,22 @@ static struct uart_driver imx_reg = { static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) { - struct imx_port *sport = platform_get_drvdata(dev); + struct imx_port *sport = platform_get_drvdata(dev); - if (sport) - uart_suspend_port(&imx_reg, &sport->port); + if (sport) + uart_suspend_port(&imx_reg, &sport->port); - return 0; + return 0; } static int serial_imx_resume(struct platform_device *dev) { - struct imx_port *sport = platform_get_drvdata(dev); + struct imx_port *sport = platform_get_drvdata(dev); - if (sport) - uart_resume_port(&imx_reg, &sport->port); + if (sport) + uart_resume_port(&imx_reg, &sport->port); - return 0; + return 0; } static int serial_imx_probe(struct platform_device *pdev) @@ -1141,7 +1147,7 @@ static int serial_imx_probe(struct platf imx_ports[pdev->id] = sport; pdata = pdev->dev.platform_data; - if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) + if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) sport->have_rtscts = 1; if (pdata->init) { @@ -1150,13 +1156,13 @@ static int serial_imx_probe(struct platf goto clkput; } - uart_add_one_port(&imx_reg, &sport->port); platform_set_drvdata(pdev, &sport->port); + uart_add_one_port(&imx_reg, &sport->port); return 0; clkput: - clk_put(sport->clk); clk_disable(sport->clk); + clk_put(sport->clk); unmap: iounmap(sport->port.membase); free: @@ -1172,15 +1178,12 @@ static int serial_imx_remove(struct plat pdata = pdev->dev.platform_data; - platform_set_drvdata(pdev, NULL); - - if (sport) { - uart_remove_one_port(&imx_reg, &sport->port); - clk_put(sport->clk); - } + uart_remove_one_port(&imx_reg, &sport->port); clk_disable(sport->clk); + clk_put(sport->clk); + platform_set_drvdata(pdev, NULL); if (pdata->exit) pdata->exit(pdev); @@ -1191,13 +1194,13 @@ static int serial_imx_remove(struct plat } static struct platform_driver serial_imx_driver = { - .probe = serial_imx_probe, - .remove = serial_imx_remove, + .probe = serial_imx_probe, + .remove = serial_imx_remove, .suspend = serial_imx_suspend, .resume = serial_imx_resume, .driver = { - .name = "imx-uart", + .name = "imx-uart", .owner = THIS_MODULE, }, }; @@ -1209,14 +1212,14 @@ static int __init imx_serial_init(void) printk(KERN_INFO "Serial: IMX driver\n"); ret = uart_register_driver(&imx_reg); - if (ret) + if (ret != 0) return ret; ret = platform_driver_register(&serial_imx_driver); if (ret != 0) uart_unregister_driver(&imx_reg); - return 0; + return ret; } static void __exit imx_serial_exit(void) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/spi/Kconfig linux-2.6.28-karo/drivers/spi/Kconfig --- linux-2.6.28/drivers/spi/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/spi/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -135,6 +135,55 @@ config SPI_MPC83xx technology. This driver uses a simple set of shift registers for data (opposed to the CPM based descriptor model). +config SPI_MXC + tristate "MXC CSPI controller as SPI Master" + depends on ARCH_MXC && SPI_MASTER + select SPI_BITBANG + help + This implements the SPI master mode using MXC CSPI. + +config SPI_MXC_TEST_LOOPBACK + bool "LOOPBACK Testing of CSPIs" + depends on SPI_MXC + default n + +config SPI_MXC_SELECT1 + bool "CSPI1" + depends on SPI_MXC + default y + +config SPI_MXC_SELECT2 + bool "CSPI2" + depends on SPI_MXC + default n + +config SPI_MXC_SELECT3 + bool "CSPI3" + depends on SPI_MXC && (ARCH_MX3 || ARCH_MX2) + default n + +choice + prompt "SPI controller hardware revision" + default SPI_MXC_REV0 + depends on SPI_MXC + config SPI_MXC_REV0 + bool "Revision 0" + help + hardware revision 0, mostly on MX27 + config SPI_MXC_REV4 + bool "Revision 4" + help + hardware revision 4, mostly on MX31 + config SPI_MXC_REV5 + bool "Revision 5" + help + hardware revision 5 + config SPI_MXC_REV7 + bool "Revision 7" + help + hardware revision 7 +endchoice + config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" depends on ARCH_OMAP1 diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/spi/Makefile linux-2.6.28-karo/drivers/spi/Makefile --- linux-2.6.28/drivers/spi/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/spi/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -24,6 +24,7 @@ obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcs obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o +obj-$(CONFIG_SPI_MXC) += mxc_spi.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/spi/mxc_spi.c linux-2.6.28-karo/drivers/spi/mxc_spi.c --- linux-2.6.28/drivers/spi/mxc_spi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/spi/mxc_spi.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,748 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 Juergen Beisert + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK +struct spi_chip_info { + int lb_enable; +}; + +static struct spi_chip_info lb_chip_info = { + .lb_enable = 1, +}; + +static struct spi_board_info loopback_info[] = { +#ifdef CONFIG_SPI_MXC_SELECT1 + { + .modalias = "loopback_spi", + .controller_data = &lb_chip_info, + .irq = 0, + .max_speed_hz = 4000000, + .bus_num = 0, + .chip_select = 4, + }, +#endif +#ifdef CONFIG_SPI_MXC_SELECT2 + { + .modalias = "loopback_spi", + .controller_data = &lb_chip_info, + .irq = 0, + .max_speed_hz = 4000000, + .bus_num = 1, + .chip_select = 4, + }, +#endif +#ifdef CONFIG_SPI_MXC_SELECT3 + { + .modalias = "loopback_spi", + .controller_data = &lb_chip_info, + .irq = 0, + .max_speed_hz = 4000000, + .bus_num = 2, + .chip_select = 4, + }, +#endif +}; +#endif + +static struct mxc_spi_unique_def spi_ver_0_7 = { + .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_7, + .cs_shift = MXC_CSPICTRL_CSSHIFT_0_7, + .bc_shift = MXC_CSPICTRL_BCSHIFT_0_7, + .bc_mask = MXC_CSPICTRL_BCMASK_0_7, + .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_7, + .xfer_complete = MXC_CSPISTAT_TC_0_7, + .bc_overflow = MXC_CSPISTAT_BO_0_7, +}; + +static struct mxc_spi_unique_def spi_ver_0_5 = { + .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_5, + .cs_shift = MXC_CSPICTRL_CSSHIFT_0_5, + .bc_shift = MXC_CSPICTRL_BCSHIFT_0_5, + .bc_mask = MXC_CSPICTRL_BCMASK_0_5, + .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_5, + .xfer_complete = MXC_CSPISTAT_TC_0_5, + .bc_overflow = MXC_CSPISTAT_BO_0_5, +}; + +static struct mxc_spi_unique_def spi_ver_0_4 = { + .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_4, + .cs_shift = MXC_CSPICTRL_CSSHIFT_0_4, + .bc_shift = MXC_CSPICTRL_BCSHIFT_0_4, + .bc_mask = MXC_CSPICTRL_BCMASK_0_4, + .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_4, + .xfer_complete = MXC_CSPISTAT_TC_0_4, + .bc_overflow = MXC_CSPISTAT_BO_0_4, +}; + +static struct mxc_spi_unique_def spi_ver_0_0 = { + .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_0, + .cs_shift = MXC_CSPICTRL_CSSHIFT_0_0, + .bc_shift = MXC_CSPICTRL_BCSHIFT_0_0, + .bc_mask = MXC_CSPICTRL_BCMASK_0_0, + .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_0, + .xfer_complete = MXC_CSPISTAT_TC_0_0, + .bc_overflow = MXC_CSPISTAT_BO_0_0, +}; + +struct mxc_spi; +/* + * Structure to group together all the data buffers and functions + * used in data transfers. + */ +struct mxc_spi_xfer { + const void *tx_buf; /* Transmit buffer. */ + void *rx_buf; /* Receive buffer. */ + unsigned int count; /* Data transfered count. */ + void (*rx_get) (struct mxc_spi *, u32 val); /* Function to read the FIFO data to rx_buf. */ + u32(*tx_get) (struct mxc_spi *); /* Function to get the data to be written to FIFO. */ +}; + +/* + * This structure is a way for the low level driver to define their own + * spi_master structure. This structure includes the core spi_master + * structure that is provided by Linux SPI Framework/driver as an + * element and has other elements that are specifically required by this + * low-level driver. + */ +struct mxc_spi { + struct spi_bitbang mxc_bitbang; /* SPI Master and a simple I/O queue runner. */ + struct completion xfer_done; /* Completion flags used in data transfers. */ + struct mxc_spi_xfer transfer; /* Data transfer structure. */ + struct resource *res; /* Resource structure, which will maintain base addresses and IRQs. */ + void *base; /* Base address of CSPI, used in readl and writel. */ + int irq; /* CSPI IRQ number. */ + struct clk *clk; /* CSPI Clock id. */ + /*! + * CSPI input clock SCLK. + */ + unsigned long spi_ipg_clk; + /*! + * CSPI registers' bit pattern. + */ + struct mxc_spi_unique_def *spi_ver_def; +}; + +#define MXC_SPI_BUF_RX(type) \ +void mxc_spi_buf_rx_##type(struct mxc_spi *master_drv_data, u32 val)\ +{\ + type *rx = master_drv_data->transfer.rx_buf;\ + *rx++ = (type)val;\ + master_drv_data->transfer.rx_buf = rx;\ +} + +#define MXC_SPI_BUF_TX(type) \ +u32 mxc_spi_buf_tx_##type(struct mxc_spi *master_drv_data)\ +{\ + u32 val;\ + const type *tx = master_drv_data->transfer.tx_buf;\ + val = *tx++;\ + master_drv_data->transfer.tx_buf = tx;\ + return val;\ +} +MXC_SPI_BUF_RX(u8) + MXC_SPI_BUF_TX(u8) + MXC_SPI_BUF_RX(u16) + MXC_SPI_BUF_TX(u16) + MXC_SPI_BUF_RX(u32) + MXC_SPI_BUF_TX(u32) + +static int spi_enable_interrupt(struct mxc_spi *master_data, unsigned int irqs) +{ + if (irqs & ~((1 << master_data->spi_ver_def->intr_bit_shift) - 1)) { + return -1; + } + + writel((irqs | readl(master_data->base + MXC_CSPIINT)), + master_data->base + MXC_CSPIINT); + + return 0; +} + +static int spi_disable_interrupt(struct mxc_spi *master_data, unsigned int irqs) +{ + if (irqs & ~((1 << master_data->spi_ver_def->intr_bit_shift) - 1)) { + return -1; + } + + writel((~irqs & readl(master_data->base + MXC_CSPIINT)), + master_data->base + MXC_CSPIINT); + return 0; +} + +static unsigned int spi_find_baudrate(struct mxc_spi *master_data, + unsigned int baud) +{ + unsigned int divisor; + unsigned int shift = 0; + + /* Calculate required divisor (rounded) */ + divisor = (master_data->spi_ipg_clk + baud / 2) / baud; + while (divisor >>= 1) + shift++; + MXC_CSPICTRL_ADJUST_SHIFT(shift); + if (shift > MXC_CSPICTRL_MAXDATRATE) + shift = MXC_CSPICTRL_MAXDATRATE; + + return shift << MXC_CSPICTRL_DATASHIFT; +} + +static unsigned int spi_get_rx_data(void *base) +{ + return readl(base + MXC_CSPIRXDATA); +} + +static void spi_put_tx_data(void *base, unsigned int val) +{ + unsigned int ctrl_reg; + + writel(val, base + MXC_CSPITXDATA); + + ctrl_reg = readl(base + MXC_CSPICTRL); + + ctrl_reg |= MXC_CSPICTRL_XCH; + + writel(ctrl_reg, base + MXC_CSPICTRL); + + return; +} + +void mxc_spi_chipselect(struct spi_device *spi, int is_active) +{ + struct mxc_spi *master_drv_data; + struct mxc_spi_xfer *ptransfer; + struct mxc_spi_unique_def *spi_ver_def; + unsigned int ctrl_reg; + unsigned int ctrl_mask; + unsigned int xfer_len; + + if (is_active == BITBANG_CS_INACTIVE) { + /*Need to deselect the slave */ + return; + } + + /* Get the master controller driver data from spi device's master */ + + master_drv_data = spi_master_get_devdata(spi->master); + spi_ver_def = master_drv_data->spi_ver_def; + + xfer_len = spi->bits_per_word; + + /* Control Register Settings for transfer to this slave */ + + ctrl_reg = readl(master_drv_data->base + MXC_CSPICTRL); + + ctrl_mask = + (MXC_CSPICTRL_LOWPOL | MXC_CSPICTRL_PHA | MXC_CSPICTRL_HIGHSSPOL | + MXC_CSPICTRL_CSMASK << spi_ver_def->cs_shift | + MXC_CSPICTRL_DATAMASK << MXC_CSPICTRL_DATASHIFT | + spi_ver_def->bc_mask << spi_ver_def->bc_shift); + ctrl_reg &= ~ctrl_mask; + + ctrl_reg |= + ((spi->chip_select & MXC_CSPICTRL_CSMASK) << spi_ver_def->cs_shift); + ctrl_reg |= spi_find_baudrate(master_drv_data, spi->max_speed_hz); + ctrl_reg |= + (((xfer_len - 1) & spi_ver_def->bc_mask) << spi_ver_def->bc_shift); + if (spi->mode & SPI_CPHA) + ctrl_reg |= MXC_CSPICTRL_PHA; + if (!(spi->mode & SPI_CPOL)) + ctrl_reg |= MXC_CSPICTRL_LOWPOL; + if (spi->mode & SPI_CS_HIGH) + ctrl_reg |= MXC_CSPICTRL_HIGHSSPOL; + + writel(ctrl_reg, master_drv_data->base + MXC_CSPICTRL); + + /* Initialize the functions for transfer */ + ptransfer = &master_drv_data->transfer; + if (xfer_len <= 8) { + ptransfer->rx_get = mxc_spi_buf_rx_u8; + ptransfer->tx_get = mxc_spi_buf_tx_u8; + } else if (xfer_len <= 16) { + ptransfer->rx_get = mxc_spi_buf_rx_u16; + ptransfer->tx_get = mxc_spi_buf_tx_u16; + } else if (xfer_len <= 32) { + ptransfer->rx_get = mxc_spi_buf_rx_u32; + ptransfer->tx_get = mxc_spi_buf_tx_u32; + } +#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK + { + struct spi_chip_info *lb_chip = + spi->controller_data; + if (!lb_chip) + writel(0, master_drv_data->base + MXC_CSPITEST); + else if (lb_chip->lb_enable) + writel(MXC_CSPITEST_LBC, + master_drv_data->base + MXC_CSPITEST); + } +#endif + return; +} + +static irqreturn_t mxc_spi_isr(int irq, void *dev_id) +{ + struct mxc_spi *master_drv_data = dev_id; + irqreturn_t ret = IRQ_NONE; + unsigned int status; + + /* Read the interrupt status register to determine the source */ + status = readl(master_drv_data->base + MXC_CSPISTAT); + + /* Rx is given higher priority - Handle it first */ + if (status & MXC_CSPISTAT_RR) { + u32 rx_tmp = spi_get_rx_data(master_drv_data->base); + + if (master_drv_data->transfer.rx_buf) + master_drv_data->transfer.rx_get(master_drv_data, + rx_tmp); + + ret = IRQ_HANDLED; + } + + master_drv_data->transfer.count--; + /* Handle the Tx now */ + if (master_drv_data->transfer.count) { + if (master_drv_data->transfer.tx_buf) { + u32 tx_tmp = + master_drv_data->transfer.tx_get(master_drv_data); + + spi_put_tx_data(master_drv_data->base, tx_tmp); + } + } else { + complete(&master_drv_data->xfer_done); + } + + return ret; +} + +int mxc_spi_setup(struct spi_device *spi) +{ + struct mxc_spi *master_data = spi_master_get_devdata(spi->master); + + if ((spi->max_speed_hz < 0) + || (spi->max_speed_hz > (master_data->spi_ipg_clk / 4))) { + dev_err(&spi->dev, "Cannot set required SPI clock speed." + " Requested: %uHz, possible: %luHz\n", + spi->max_speed_hz, master_data->spi_ipg_clk / 4); + return -EINVAL; + } + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + pr_debug("%s: mode %d, %u bpw, %d hz\n", __FUNCTION__, + spi->mode, spi->bits_per_word, spi->max_speed_hz); + + return 0; +} + +int mxc_spi_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct mxc_spi *master_drv_data = NULL; + + /* Get the master controller driver data from spi device's master */ + + master_drv_data = spi_master_get_devdata(spi->master); + + /* Modify the Tx, Rx, Count */ + + master_drv_data->transfer.tx_buf = t->tx_buf; + master_drv_data->transfer.rx_buf = t->rx_buf; + master_drv_data->transfer.count = t->len; + INIT_COMPLETION(master_drv_data->xfer_done); + + /* Enable the Rx Interrupts */ + + spi_enable_interrupt(master_drv_data, MXC_CSPIINT_RREN); + + /* Perform single Tx transaction */ + + spi_put_tx_data(master_drv_data->base, + master_drv_data->transfer.tx_get(master_drv_data)); + + /* Wait for transfer completion */ + + wait_for_completion(&master_drv_data->xfer_done); + + /* Disable the Rx Interrupts */ + + spi_disable_interrupt(master_drv_data, MXC_CSPIINT_RREN); + + return t->len - master_drv_data->transfer.count; +} + +void mxc_spi_cleanup(struct spi_device *spi) +{ + return; +} + +static int mxc_spi_probe(struct platform_device *pdev) +{ + struct mxc_spi_master *mxc_platform_info; + struct spi_master *master; + struct mxc_spi *master_drv_data; + unsigned int spi_ver; + int ret = -ENODEV; + + dev_dbg(&pdev->dev, "Enter: %s\n", __FUNCTION__); + + /* Get the platform specific data for this master device */ + + mxc_platform_info = pdev->dev.platform_data; + if (!mxc_platform_info) { + dev_err(&pdev->dev, "can't get the platform data for CSPI\n"); + return -ENODEV; + } + + /* Allocate SPI master controller */ + + master = spi_alloc_master(&pdev->dev, sizeof(struct mxc_spi)); + if (!master) { + dev_err(&pdev->dev, "can't alloc for spi_master\n"); + return -ENOMEM; + } + + /* Set this device's driver data to master */ + + platform_set_drvdata(pdev, master); + + /* Set this master's data from platform_info */ + + master->bus_num = pdev->id; + master->num_chipselect = mxc_platform_info->maxchipselect; + + /* Set the master controller driver data for this master */ + + master_drv_data = spi_master_get_devdata(master); + if (master_drv_data == NULL) + goto err; + master_drv_data->mxc_bitbang.master = spi_master_get(master); + + /* Set the master bitbang data */ + + master_drv_data->mxc_bitbang.chipselect = mxc_spi_chipselect; + master_drv_data->mxc_bitbang.txrx_bufs = mxc_spi_transfer; + master_drv_data->mxc_bitbang.master->setup = mxc_spi_setup; + master_drv_data->mxc_bitbang.master->cleanup = mxc_spi_cleanup; + + /* Initialize the completion object */ + + init_completion(&master_drv_data->xfer_done); + + /* Set the master controller register addresses and irqs */ + + master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!master_drv_data->res) { + dev_err(&pdev->dev, "can't get platform resource for CSPI%d\n", + master->bus_num); + ret = -ENOMEM; + goto err; + } + + if (!request_mem_region(master_drv_data->res->start, + master_drv_data->res->end - + master_drv_data->res->start + 1, pdev->name)) { + dev_err(&pdev->dev, "request_mem_region failed for CSPI%d\n", + master->bus_num); + ret = -EBUSY; + goto err; + } + + master_drv_data->base = ioremap(master_drv_data->res->start, + master_drv_data->res->end - + master_drv_data->res->start + 1); + + if (!master_drv_data->base) { + dev_err(&pdev->dev, "Cannot map iomem for CSPI%d\n", + master->bus_num); + ret = -EINVAL; + goto err1; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "can't get IRQ for CSPI%d\n", + master->bus_num); + goto err2; + } + master_drv_data->irq = ret; + + /* Register for SPI Interrupt */ + + ret = request_irq(master_drv_data->irq, mxc_spi_isr, + 0, "CSPI_IRQ", master_drv_data); + if (ret != 0) { + dev_err(&pdev->dev, "request_irq failed for CSPI%d\n", + master->bus_num); + goto err2; + } + + /* Setup any GPIO active */ + if (mxc_platform_info->init) + mxc_platform_info->init(pdev); + + /* Identify SPI version */ + + spi_ver = mxc_platform_info->spi_version; + if (spi_ver == 7) { + master_drv_data->spi_ver_def = &spi_ver_0_7; + } else if (spi_ver == 5) { + master_drv_data->spi_ver_def = &spi_ver_0_5; + } else if (spi_ver == 4) { + master_drv_data->spi_ver_def = &spi_ver_0_4; + } else if (spi_ver == 0) { + master_drv_data->spi_ver_def = &spi_ver_0_0; + } + + /* Enable the CSPI Clock, CSPI Module, set as a master */ + + master_drv_data->clk = clk_get(&pdev->dev, "cspi_clk"); + clk_enable(master_drv_data->clk); + master_drv_data->spi_ipg_clk = clk_get_rate(master_drv_data->clk); + + writel(0x00008000, master_drv_data->base + MXC_CSPICTRL); /* reset default */ + writel(MXC_CSPICTRL_ENABLE | MXC_CSPICTRL_MASTER, + master_drv_data->base + MXC_CSPICTRL); + writel(MXC_CSPIPERIOD_32KHZ, + master_drv_data->base + MXC_CSPIPERIOD); + writel(0, master_drv_data->base + MXC_CSPIINT); + + /* Start the SPI Master Controller driver */ + + ret = spi_bitbang_start(&master_drv_data->mxc_bitbang); + if (ret != 0) { + dev_err(&pdev->dev, "Cannot start bitbang driver\n"); + goto err3; + } + + printk(KERN_INFO "CSPI: %s-%d probed\n", pdev->name, pdev->id); + +#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK + { + int i; + struct spi_board_info *bi = &loopback_info[0]; + for (i = 0; i < ARRAY_SIZE(loopback_info); i++, bi++) { + if (bi->bus_num != master->bus_num) + continue; + + dev_info(&pdev->dev, + "registering loopback device '%s'\n", + bi->modalias); + + spi_new_device(master, bi); + } + } +#endif + return ret; + +err3: + clk_disable(master_drv_data->clk); + clk_put(master_drv_data->clk); + if (mxc_platform_info->exit) + mxc_platform_info->exit(pdev); + free_irq(master_drv_data->irq, master_drv_data); +err2: + iounmap(master_drv_data->base); +err1: + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); +err: + spi_master_put(master); + kfree(master); + platform_set_drvdata(pdev, NULL); + return ret; +} + +static int mxc_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct mxc_spi_master *mxc_platform_info = + pdev->dev.platform_data; + + if (master) { + struct mxc_spi *master_drv_data = + spi_master_get_devdata(master); + + if (mxc_platform_info->exit) + mxc_platform_info->exit(pdev); + clk_disable(master_drv_data->clk); + /* clk_put(master_drv_data->clk); */ /* FIXME required? */ + + /* Disable the CSPI module */ + + writel(MXC_CSPICTRL_DISABLE, + master_drv_data->base + MXC_CSPICTRL); + + spi_bitbang_stop(&master_drv_data->mxc_bitbang); + + /* free resources */ + spi_master_put(master); + free_irq(master_drv_data->irq, master_drv_data); + iounmap(master_drv_data->base); + release_mem_region(master_drv_data->res->start, + master_drv_data->res->end - + master_drv_data->res->start + 1); + } + + printk(KERN_INFO "CSPI: %s-%d removed\n", pdev->name, pdev->id); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int suspend_devices(struct device *dev, void *pm_message) +{ + pm_message_t *state = pm_message; + + if (dev->power.power_state.event != state->event) { + dev_warn(dev, "mismatch in pm state request\n"); + return -1; + } + + return 0; +} + +static int spi_bitbang_suspend(struct spi_bitbang *bitbang) +{ + unsigned long flags; + unsigned limit = 500; + + spin_lock_irqsave(&bitbang->lock, flags); + //bitbang->shutdown = 0; + while (!list_empty(&bitbang->queue) && limit--) { + spin_unlock_irqrestore(&bitbang->lock, flags); + + dev_dbg(&bitbang->master->dev, "wait for queue\n"); + msleep(10); + + spin_lock_irqsave(&bitbang->lock, flags); + } + if (!list_empty(&bitbang->queue)) { + dev_err(&bitbang->master->dev, "queue didn't empty\n"); + return -EBUSY; + } + spin_unlock_irqrestore(&bitbang->lock, flags); + + //bitbang->shutdown = 1; + + return 0; +} + +static void spi_bitbang_resume(struct spi_bitbang *bitbang) +{ + spin_lock_init(&bitbang->lock); + INIT_LIST_HEAD(&bitbang->queue); + + bitbang->busy = 0; + //bitbang->shutdown = 0; +} + +static int mxc_spi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct mxc_spi *master_drv_data = spi_master_get_devdata(master); + struct mxc_spi_master *mxc_platform_info = + pdev->dev.platform_data; + int ret = 0; + + if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) { + dev_warn(&pdev->dev, "suspend aborted\n"); + return -EINVAL; + } + + spi_bitbang_suspend(&master_drv_data->mxc_bitbang); + writel(MXC_CSPICTRL_DISABLE, + master_drv_data->base + MXC_CSPICTRL); + + clk_disable(master_drv_data->clk); + if (mxc_platform_info->exit) + mxc_platform_info->exit(pdev); + + return ret; +} + +static int mxc_spi_resume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct mxc_spi *master_drv_data = spi_master_get_devdata(master); + struct mxc_spi_master *mxc_platform_info = + pdev->dev.platform_data; + + if (mxc_platform_info->init) + mxc_platform_info->init(pdev); + clk_enable(master_drv_data->clk); + + spi_bitbang_resume(&master_drv_data->mxc_bitbang); + writel(MXC_CSPICTRL_ENABLE | MXC_CSPICTRL_MASTER, + master_drv_data->base + MXC_CSPICTRL); + + return 0; +} +#else +# define mxc_spi_suspend NULL +# define mxc_spi_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mxc_spi_driver = { + .driver = { + .name = "mxc_spi", + .owner = THIS_MODULE, + }, + .probe = mxc_spi_probe, + .remove = mxc_spi_remove, + .suspend = mxc_spi_suspend, + .resume = mxc_spi_resume, +}; + +static int __init mxc_spi_init(void) +{ + pr_debug("Registering the SPI Controller Driver\n"); + return platform_driver_register(&mxc_spi_driver); +} + +static void __exit mxc_spi_exit(void) +{ + pr_debug("Unregistering the SPI Controller Driver\n"); + platform_driver_unregister(&mxc_spi_driver); +} + +subsys_initcall(mxc_spi_init); +module_exit(mxc_spi_exit); + +MODULE_DESCRIPTION("SPI Master Controller driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/usb/Kconfig linux-2.6.28-karo/drivers/usb/Kconfig --- linux-2.6.28/drivers/usb/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/usb/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -56,6 +56,7 @@ config USB_ARCH_HAS_EHCI default y if PPC_83xx default y if SOC_AU1200 default y if ARCH_IXP4XX + default y if ARCH_MXC default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/usb/host/Kconfig linux-2.6.28-karo/drivers/usb/host/Kconfig --- linux-2.6.28/drivers/usb/host/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/usb/host/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -96,6 +96,13 @@ config USB_EHCI_HCD_PPC_OF Enables support for the USB controller present on the PowerPC OpenFirmware platform bus. +config USB_EHCI_MXC + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MXC + select USB_EHCI_ROOT_HUB_TT + ---help--- + Variation of ARC USB block used in some Freescale chips. + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/usb/host/ehci-hcd.c linux-2.6.28-karo/drivers/usb/host/ehci-hcd.c --- linux-2.6.28/drivers/usb/host/ehci-hcd.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/usb/host/ehci-hcd.c 2009-03-11 13:16:24.000000000 +0100 @@ -1009,6 +1009,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_fsl_driver #endif +#ifdef CONFIG_USB_EHCI_MXC +#include "ehci-mxc.c" +#define PLATFORM_DRIVER ehci_mxc_driver +#endif + #ifdef CONFIG_SOC_AU1200 #include "ehci-au1xxx.c" #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/usb/host/ehci-mxc.c linux-2.6.28-karo/drivers/usb/host/ehci-mxc.c --- linux-2.6.28/drivers/usb/host/ehci-mxc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/usb/host/ehci-mxc.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2008 Sascha Hauer , Pengutronix + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +/* called during probe() after chip reset completes */ +static int ehci_mxc_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + ehci_port_power(ehci, 0); + return 0; +} + +static const struct hc_driver ehci_mxc_hc_driver = { + .description = hcd_name, + .product_desc = "Freescale On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ehci_mxc_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ehci_mxc_drv_probe(struct platform_device *pdev) +{ + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct usb_hcd *hcd; + struct resource *res; + int irq, ret, temp; + struct clk *usbclk, *ahbclk; + + dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); + + /* Need platform data for setup */ + if (!pdata) { + dev_err(&pdev->dev, + "No platform data for %s.\n", pdev->dev.bus_id); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + + hcd = usb_create_hcd(&ehci_mxc_hc_driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + ret = -ENOMEM; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + pdev->dev.bus_id); + ret = -ENODEV; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto err2; + } + + ahbclk = clk_get(NULL, "usb_ahb_clk"); + if (IS_ERR(ahbclk)) { + ret = PTR_ERR(ahbclk); + printk(KERN_ERR "Failed to get usb_ahb_clk: %d\n", ret); + goto err3; + } + clk_enable(ahbclk); + + usbclk = clk_get(NULL, "usb_clk"); + if (IS_ERR(usbclk)) { + ret = PTR_ERR(usbclk); + printk(KERN_ERR "Failed to get usb_clk: %d\n", ret); + goto err4; + } + clk_enable(usbclk); + + if (pdata->init) { + ret = pdata->init(pdev); + if (ret) { + dev_err(&pdev->dev, "platform init failed\n"); + goto err5; + } + } + + /* Set to Host mode */ + temp = readl(hcd->regs + 0x1a8); + writel(temp | 0x3, hcd->regs + 0x1a8); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) + goto err6; + + platform_set_drvdata(pdev, hcd); + + return 0; +err6: + if (pdata->exit) + pdata->exit(pdev); +err5: + clk_disable(usbclk); + clk_put(usbclk); +err4: + clk_disable(ahbclk); + clk_put(ahbclk); +err3: + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return ret; +} + +static int ehci_mxc_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct clk *usbclk, *ahbclk; + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + if (pdata->exit) + pdata->exit(pdev); + + usbclk = clk_get(NULL, "usb_clk"); + if (!IS_ERR(usbclk)) { + clk_disable(usbclk); + clk_put(usbclk); + } + ahbclk = clk_get(NULL, "usb_ahb_clk"); + if (!IS_ERR(ahbclk)) { + clk_disable(ahbclk); + clk_put(ahbclk); + } + return 0; +} + +MODULE_ALIAS("platform:mxc-ehci"); + +static struct platform_driver ehci_mxc_driver = { + .probe = ehci_mxc_drv_probe, + .remove = ehci_mxc_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "mxc-ehci", + }, +}; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/video/Kconfig linux-2.6.28-karo/drivers/video/Kconfig --- linux-2.6.28/drivers/video/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/video/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -397,7 +397,7 @@ config FB_SA1100 config FB_IMX tristate "Motorola i.MX LCD support" - depends on FB && ARM && ARCH_IMX + depends on FB && ARM && (ARCH_IMX || ARCH_MX2) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2115,6 +2115,18 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. +config FB_MXC + tristate "MXC Framebuffer support" + depends on FB && MXC_IPU + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + help + This is a framebuffer device for the i.MX31 LCD Controller. So + far only synchronous displays are supported. If you plan to use + an LCD display with your i.MX31 system, say Y here. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/video/Makefile linux-2.6.28-karo/drivers/video/Makefile --- linux-2.6.28/drivers/video/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/video/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -132,6 +132,7 @@ obj-$(CONFIG_FB_VGA16) += vga obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o +obj-$(CONFIG_FB_MXC) += mxcfb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/video/imxfb.c linux-2.6.28-karo/drivers/video/imxfb.c --- linux-2.6.28/drivers/video/imxfb.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/video/imxfb.c 2009-03-11 13:16:24.000000000 +0100 @@ -1,6 +1,4 @@ /* - * linux/drivers/video/imxfb.c - * * Freescale i.MX Frame Buffer device driver * * Copyright (C) 2004 Sascha Hauer, Pengutronix @@ -16,8 +14,6 @@ * linux-arm-kernel@lists.arm.linux.org.uk */ -//#define DEBUG 1 - #include #include #include @@ -30,11 +26,11 @@ #include #include #include +#include #include #include +#include -#include -#include #include /* @@ -42,23 +38,170 @@ */ #define DEBUG_VAR 1 -#include "imxfb.h" +#define DRIVER_NAME "imx-fb" + +#define LCDC_SSA 0x00 + +#define LCDC_SIZE 0x04 +#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20) +#ifdef CONFIG_ARCH_IMX +#define SIZE_YMAX(y) ((y) & 0x1ff) +#else +#define SIZE_YMAX(y) ((y) & 0x3ff) +#endif + +#define LCDC_VPW 0x08 +#define VPW_VPW(x) ((x) & 0x3ff) + +#define LCDC_CPOS 0x0C +#define CPOS_CC1 (1<<31) +#define CPOS_CC0 (1<<30) +#define CPOS_OP (1<<28) +#define CPOS_CXP(x) (((x) & 3ff) << 16) +#ifdef CONFIG_ARCH_IMX +#define CPOS_CYP(y) ((y) & 0x1ff) +#else +#define CPOS_CYP(y) ((y) & 0x3ff) +#endif + +#define LCDC_LCWHB 0x10 +#define LCWHB_BK_EN (1<<31) +#define LCWHB_CW(w) (((w) & 0x1f) << 24) +#define LCWHB_CH(h) (((h) & 0x1f) << 16) +#define LCWHB_BD(x) ((x) & 0xff) + +#define LCDC_LCHCC 0x14 +#ifdef CONFIG_ARCH_IMX +#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11) +#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5) +#define LCHCC_CUR_COL_B(b) ((b) & 0x1f) +#else +#define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12) +#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6) +#define LCHCC_CUR_COL_B(b) ((b) & 0x3f) +#endif + +#define LCDC_PCR 0x18 + +#define LCDC_HCR 0x1C +#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26) +#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8) +#define HCR_H_WAIT_2(x) ((x) & 0xff) + +#define LCDC_VCR 0x20 +#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26) +#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8) +#define VCR_V_WAIT_2(x) ((x) & 0xff) + +#define LCDC_POS 0x24 +#define POS_POS(x) ((x) & 1f) + +#define LCDC_LSCR1 0x28 +/* bit fields in imxfb.h */ + +#define LCDC_PWMR 0x2C +/* bit fields in imxfb.h */ + +#define LCDC_DMACR 0x30 +/* bit fields in imxfb.h */ + +#define LCDC_RMCR 0x34 +#define RMCR_LCDC_EN (1<<1) +#define RMCR_SELF_REF (1<<0) + +#define LCDC_LCDICR 0x38 +#define LCDICR_INT_SYN (1<<2) +#define LCDICR_INT_CON (1) + +#define LCDC_LCDISR 0x40 +#define LCDISR_UDR_ERR (1<<3) +#define LCDISR_ERR_RES (1<<2) +#define LCDISR_EOF (1<<1) +#define LCDISR_BOF (1<<0) -static struct imxfb_rgb def_rgb_16 = { - .red = { .offset = 8, .length = 4, }, - .green = { .offset = 4, .length = 4, }, - .blue = { .offset = 0, .length = 4, }, - .transp = { .offset = 0, .length = 0, }, +/* + * These are the bitfields for each + * display depth that we support. + */ +struct imxfb_rgb { + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; }; -static struct imxfb_rgb def_rgb_8 = { - .red = { .offset = 0, .length = 8, }, - .green = { .offset = 0, .length = 8, }, - .blue = { .offset = 0, .length = 8, }, - .transp = { .offset = 0, .length = 0, }, +struct imxfb_info { + struct platform_device *pdev; + void __iomem *regs; + struct clk *clk; + char enabled; + + u_int max_bpp; + u_int max_xres; + u_int max_yres; + + /* + * These are the addresses we mapped + * the framebuffer memory region to. + */ + dma_addr_t map_dma; + u_char *map_cpu; + u_int map_size; + + u_char *screen_cpu; + dma_addr_t screen_dma; + u_int palette_size; + + dma_addr_t dbar1; + dma_addr_t dbar2; + + u_int pcr; + u_int pwmr; + u_int lscr1; + u_int dmacr; + u_int cmap_inverse:1, + cmap_static:1, + unused:30; + + void (*lcd_power)(int); + void (*backlight_power)(int); }; -static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info); +#define IMX_NAME "IMX" + +/* + * Minimum X and Y resolutions + */ +#define MIN_XRES 64 +#define MIN_YRES 64 + +static struct imxfb_rgb def_rgb_18 = { + .red = {.offset = 18, .length = 6,}, + .green = {.offset = 10, .length = 6,}, + .blue = {.offset = 2, .length = 6,}, + .transp = {.offset = 0, .length = 0,}, +}; + +static struct imxfb_rgb def_rgb_16_tft = { + .red = {.offset = 11, .length = 5,}, + .green = {.offset = 5, .length = 6,}, + .blue = {.offset = 0, .length = 5,}, + .transp = {.offset = 0, .length = 0,}, +}; + +static struct imxfb_rgb def_rgb_16_stn = { + .red = {.offset = 8, .length = 4,}, + .green = {.offset = 4, .length = 4,}, + .blue = {.offset = 0, .length = 4,}, + .transp = {.offset = 0, .length = 0,}, +}; + +static struct imxfb_rgb def_rgb_8 = { + .red = {.offset = 0, .length = 8,}, + .green = {.offset = 0, .length = 8,}, + .blue = {.offset = 0, .length = 8,}, + .transp = {.offset = 0, .length = 0,}, +}; static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) { @@ -67,10 +210,8 @@ static inline u_int chan_to_field(u_int return chan << bf->offset; } -#define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2) -static int -imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, - u_int trans, struct fb_info *info) +static int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, + u_int trans, struct fb_info *info) { struct imxfb_info *fbi = info->par; u_int val, ret = 1; @@ -81,14 +222,13 @@ imxfb_setpalettereg(u_int regno, u_int r (CNVT_TOHW(green,4) << 4) | CNVT_TOHW(blue, 4); - LCDC_PALETTE(regno) = val; + writel(val, fbi->regs + 0x800 + (regno << 2)); ret = 0; } return ret; } -static int -imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, +static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info) { struct imxfb_info *fbi = info->par; @@ -148,11 +288,10 @@ imxfb_setcolreg(u_int regno, u_int red, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. */ -static int -imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct imxfb_info *fbi = info->par; - int rgbidx; + struct imxfb_rgb *rgb; if (var->xres < MIN_XRES) var->xres = MIN_XRES; @@ -167,24 +306,29 @@ imxfb_check_var(struct fb_var_screeninfo pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); switch (var->bits_per_pixel) { + case 32: + rgb = &def_rgb_18; + break; case 16: - rgbidx = RGB_16; + default: + if (readl(fbi->regs + LCDC_PCR) & PCR_TFT) + rgb = &def_rgb_16_tft; + else + rgb = &def_rgb_16_stn; break; case 8: - rgbidx = RGB_8; + rgb = &def_rgb_8; break; - default: - rgbidx = RGB_16; } /* * Copy the RGB parameters for this display * from the machine specific parameters. */ - var->red = fbi->rgb[rgbidx]->red; - var->green = fbi->rgb[rgbidx]->green; - var->blue = fbi->rgb[rgbidx]->blue; - var->transp = fbi->rgb[rgbidx]->transp; + var->red = rgb->red; + var->green = rgb->green; + var->blue = rgb->blue; + var->transp = rgb->transp; pr_debug("RGBT length = %d:%d:%d:%d\n", var->red.length, var->green.length, var->blue.length, @@ -206,9 +350,7 @@ static int imxfb_set_par(struct fb_info struct imxfb_info *fbi = info->par; struct fb_var_screeninfo *var = &info->var; - pr_debug("set_par\n"); - - if (var->bits_per_pixel == 16) + if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32) info->fix.visual = FB_VISUAL_TRUECOLOR; else if (!fbi->cmap_static) info->fix.visual = FB_VISUAL_PSEUDOCOLOR; @@ -221,49 +363,72 @@ static int imxfb_set_par(struct fb_info info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; } - info->fix.line_length = var->xres_virtual * - var->bits_per_pixel / 8; + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; - imxfb_activate_var(var, info); + if (var->bits_per_pixel == 16) + fb_dealloc_cmap(&info->cmap); + else + fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0); return 0; } static void imxfb_enable_controller(struct imxfb_info *fbi) { - pr_debug("Enabling LCD controller\n"); - - /* initialize LCDC */ - LCDC_RMCR &= ~RMCR_LCDC_EN; /* just to be safe... */ - - LCDC_SSA = fbi->screen_dma; - /* physical screen start address */ - LCDC_VPW = VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4); + struct imx_fb_platform_data *pdata = fbi->pdev->dev.platform_data; + int ret; - LCDC_POS = 0x00000000; /* panning offset 0 (0 pixel offset) */ + if (!fbi->enabled) { + if (clk_enable(fbi->clk) != 0) + printk("Warning: Could not enable clock!"); + pr_debug("LCD clock enabled\n"); + + if (pdata->init) { + ret = (pdata->init)(fbi->pdev); + pr_debug("GPIOs enabled\n"); + if (ret != 0) + pr_err("Failing to enable LCD pins\n"); + } - /* disable hardware cursor */ - LCDC_CPOS &= ~(CPOS_CC0 | CPOS_CC1); + if (pdata->lcd_power) { + pdata->lcd_power(1); + pr_debug("Display's power enabled\n"); + } - LCDC_RMCR = RMCR_LCDC_EN; + if (pdata->backlight_power) { + pdata->backlight_power(1); + pr_debug("Backlight's power enabled\n"); + } - if(fbi->backlight_power) - fbi->backlight_power(1); - if(fbi->lcd_power) - fbi->lcd_power(1); + fbi->enabled = 1; + } } static void imxfb_disable_controller(struct imxfb_info *fbi) { - pr_debug("Disabling LCD controller\n"); + struct imx_fb_platform_data *pdata = fbi->pdev->dev.platform_data; + int ret; + + if (fbi->enabled) { + if (pdata->backlight_power) + pdata->backlight_power(0); + if (pdata->lcd_power) + pdata->lcd_power(0); - if(fbi->backlight_power) - fbi->backlight_power(0); - if(fbi->lcd_power) - fbi->lcd_power(0); +#ifdef CONFIG_ARCH_IMX + writel(0, fbi->regs + LCDC_RMCR); /* for IMX only */ +#endif + clk_disable(fbi->clk); + + if (pdata->exit) { + ret = (pdata->exit)(fbi->pdev); + if (ret != 0) + pr_err("Failed to disable LCD pins\n"); + } - LCDC_RMCR = 0; + fbi->enabled = 0; + } } static int imxfb_blank(int blank, struct fb_info *info) @@ -298,114 +463,68 @@ static struct fb_ops imxfb_ops = { .fb_blank = imxfb_blank, }; -/* - * imxfb_activate_var(): - * Configures LCD Controller based on entries in var parameter. Settings are - * only written to the controller if changes were made. - */ -static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) -{ - struct imxfb_info *fbi = info->par; - pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", - var->xres, var->hsync_len, - var->left_margin, var->right_margin); - pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n", - var->yres, var->vsync_len, - var->upper_margin, var->lower_margin); - -#if DEBUG_VAR - if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", - info->fix.id, var->xres); - if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", - info->fix.id, var->hsync_len); - if (var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", - info->fix.id, var->left_margin); - if (var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", - info->fix.id, var->right_margin); - if (var->yres < 1 || var->yres > 511) - printk(KERN_ERR "%s: invalid yres %d\n", - info->fix.id, var->yres); - if (var->vsync_len > 100) - printk(KERN_ERR "%s: invalid vsync_len %d\n", - info->fix.id, var->vsync_len); - if (var->upper_margin > 63) - printk(KERN_ERR "%s: invalid upper_margin %d\n", - info->fix.id, var->upper_margin); - if (var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", - info->fix.id, var->lower_margin); -#endif - - LCDC_HCR = HCR_H_WIDTH(var->hsync_len) | - HCR_H_WAIT_1(var->left_margin) | - HCR_H_WAIT_2(var->right_margin); - - LCDC_VCR = VCR_V_WIDTH(var->vsync_len) | - VCR_V_WAIT_1(var->upper_margin) | - VCR_V_WAIT_2(var->lower_margin); - - LCDC_SIZE = SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres); - LCDC_PCR = fbi->pcr; - LCDC_PWMR = fbi->pwmr; - LCDC_LSCR1 = fbi->lscr1; - LCDC_DMACR = fbi->dmacr; - - return 0; -} - -static void imxfb_setup_gpio(struct imxfb_info *fbi) +/* enable the LCD controller with basic setup for the connected display. */ +static void imxfb_setup_display(struct imxfb_info *fbi) { - int width; + u32 pixel_clk, pcr; + struct imx_fb_platform_data *pinfo = fbi->pdev->dev.platform_data; - LCDC_RMCR &= ~(RMCR_LCDC_EN | RMCR_SELF_REF); +#ifdef CONFIG_ARCH_IMX + writel(readl(fbi->regs + LCDC_RMCR) & ~RMCR_LCDC_EN, + fbi->regs + LCDC_RMCR); /* just to be safe... */ +#endif - if( fbi->pcr & PCR_TFT ) - width = 16; - else - width = 1 << ((fbi->pcr >> 28) & 0x3); + /* physical screen start address */ + writel(fbi->screen_dma, fbi->regs + LCDC_SSA); - switch(width) { - case 16: - imx_gpio_mode(PD30_PF_LD15); - imx_gpio_mode(PD29_PF_LD14); - imx_gpio_mode(PD28_PF_LD13); - imx_gpio_mode(PD27_PF_LD12); - imx_gpio_mode(PD26_PF_LD11); - imx_gpio_mode(PD25_PF_LD10); - imx_gpio_mode(PD24_PF_LD9); - imx_gpio_mode(PD23_PF_LD8); - case 8: - imx_gpio_mode(PD22_PF_LD7); - imx_gpio_mode(PD21_PF_LD6); - imx_gpio_mode(PD20_PF_LD5); - imx_gpio_mode(PD19_PF_LD4); - case 4: - imx_gpio_mode(PD18_PF_LD3); - imx_gpio_mode(PD17_PF_LD2); - case 2: - imx_gpio_mode(PD16_PF_LD1); - case 1: - imx_gpio_mode(PD15_PF_LD0); - } - - /* initialize GPIOs */ - imx_gpio_mode(PD6_PF_LSCLK); - imx_gpio_mode(PD11_PF_CONTRAST); - imx_gpio_mode(PD14_PF_FLM_VSYNC); - imx_gpio_mode(PD13_PF_LP_HSYNC); - imx_gpio_mode(PD12_PF_ACD_OE); - - /* These are only needed for Sharp HR TFT displays */ - if (fbi->pcr & PCR_SHARP) { - imx_gpio_mode(PD7_PF_REV); - imx_gpio_mode(PD8_PF_CLS); - imx_gpio_mode(PD9_PF_PS); - imx_gpio_mode(PD10_PF_SPL_SPR); - } + /* dimension of this display */ + writel(SIZE_XMAX(pinfo->xres) | SIZE_YMAX(pinfo->yres), + fbi->regs + LCDC_SIZE); + + /* virtual page width */ + writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4), + fbi->regs + LCDC_VPW); + + /* setup the pixel clock */ + pixel_clk = clk_get_rate(fbi->clk); + + pcr = (pixel_clk + ((pinfo->pixclock * 1000UL) / 2)) / (pinfo->pixclock * 1000UL); + if (--pcr > 0x3F) { + pcr = 0x3F; + printk("Must limit pixel clock to %luHz\n", clk_get_rate(fbi->clk) / pcr); + } + + /* add sync polarities */ + pcr |= fbi->pcr & ~0x3F; + writel(pcr, fbi->regs + LCDC_PCR); + + /* setup sync behaviour */ + writel(HCR_H_WIDTH(pinfo->hsync_len - 1) | + HCR_H_WAIT_1(pinfo->right_margin - 1) | + HCR_H_WAIT_2(pinfo->left_margin - 3), + fbi->regs + LCDC_HCR); + + writel(VCR_V_WIDTH(pinfo->vsync_len) | + VCR_V_WAIT_1(pinfo->lower_margin) | + VCR_V_WAIT_2(pinfo->upper_margin), + fbi->regs + LCDC_VCR); + + /* PWM contrast control register */ + writel(fbi->pwmr, fbi->regs + LCDC_PWMR); + + /* special setup for Sharp displays */ + writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); + + /* dma watermarks for this mode */ + writel(fbi->dmacr, fbi->regs + LCDC_DMACR); + + /* activate refresh */ +#ifdef CONFIG_ARCH_IMX + writel(readl(fbi->regs + LCDC_RMCR) | RMCR_LCDC_EN, + fbi->regs + LCDC_RMCR); +#else + writel(0x00000000, fbi->regs + LCDC_RMCR); +#endif } #ifdef CONFIG_PM @@ -413,19 +532,23 @@ static void imxfb_setup_gpio(struct imxf * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int imxfb_suspend(struct platform_device *dev, pm_message_t state) +static int imxfb_suspend(struct platform_device *pdev, pm_message_t state) { - struct imxfb_info *fbi = platform_get_drvdata(dev); - pr_debug("%s\n",__func__); + struct fb_info *info = platform_get_drvdata(pdev); + struct imxfb_info *fbi = info->par; + + pr_debug("%s\n", __func__); imxfb_disable_controller(fbi); return 0; } -static int imxfb_resume(struct platform_device *dev) +static int imxfb_resume(struct platform_device *pdev) { - struct imxfb_info *fbi = platform_get_drvdata(dev); - pr_debug("%s\n",__func__); + struct fb_info *info = platform_get_drvdata(pdev); + struct imxfb_info *fbi = info->par; + + pr_debug("%s\n", __func__); imxfb_enable_controller(fbi); return 0; @@ -435,149 +558,148 @@ static int imxfb_resume(struct platform_ #define imxfb_resume NULL #endif -static int __init imxfb_init_fbinfo(struct device *dev) +static int __init imxfb_init_fbinfo(struct platform_device *pdev) { - struct imxfb_mach_info *inf = dev->platform_data; - struct fb_info *info = dev_get_drvdata(dev); + struct imx_fb_platform_data *pdata = pdev->dev.platform_data; + struct fb_info *info = dev_get_drvdata(&pdev->dev); struct imxfb_info *fbi = info->par; - pr_debug("%s\n",__func__); + pr_debug("%s\n", __func__); - info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL); + info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); if (!info->pseudo_palette) return -ENOMEM; memset(fbi, 0, sizeof(struct imxfb_info)); - fbi->dev = dev; strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); - info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.type_aux = 0; info->fix.xpanstep = 0; info->fix.ypanstep = 0; info->fix.ywrapstep = 0; - info->fix.accel = FB_ACCEL_NONE; + info->fix.accel = FB_ACCEL_NONE; info->var.nonstd = 0; info->var.activate = FB_ACTIVATE_NOW; info->var.height = -1; info->var.width = -1; info->var.accel_flags = 0; - info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.vmode = FB_VMODE_NONINTERLACED; info->fbops = &imxfb_ops; - info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST; - - fbi->rgb[RGB_16] = &def_rgb_16; - fbi->rgb[RGB_8] = &def_rgb_8; + info->flags = FBINFO_FLAG_DEFAULT | + FBINFO_READS_FAST; - fbi->max_xres = inf->xres; - info->var.xres = inf->xres; - info->var.xres_virtual = inf->xres; - fbi->max_yres = inf->yres; - info->var.yres = inf->yres; - info->var.yres_virtual = inf->yres; - fbi->max_bpp = inf->bpp; - info->var.bits_per_pixel = inf->bpp; - info->var.nonstd = inf->nonstd; - info->var.pixclock = inf->pixclock; - info->var.hsync_len = inf->hsync_len; - info->var.left_margin = inf->left_margin; - info->var.right_margin = inf->right_margin; - info->var.vsync_len = inf->vsync_len; - info->var.upper_margin = inf->upper_margin; - info->var.lower_margin = inf->lower_margin; - info->var.sync = inf->sync; - info->var.grayscale = inf->cmap_greyscale; - fbi->cmap_inverse = inf->cmap_inverse; - fbi->cmap_static = inf->cmap_static; - fbi->pcr = inf->pcr; - fbi->lscr1 = inf->lscr1; - fbi->dmacr = inf->dmacr; - fbi->pwmr = inf->pwmr; - fbi->lcd_power = inf->lcd_power; - fbi->backlight_power = inf->backlight_power; + fbi->max_xres = pdata->xres; + info->var.xres = pdata->xres; + info->var.xres_virtual = pdata->xres; + fbi->max_yres = pdata->yres; + info->var.yres = pdata->yres; + info->var.yres_virtual = pdata->yres; + fbi->max_bpp = pdata->bpp; + info->var.bits_per_pixel = pdata->bpp; + info->var.nonstd = pdata->nonstd; + info->var.pixclock = pdata->pixclock; + info->var.hsync_len = pdata->hsync_len; + info->var.left_margin = pdata->left_margin; + info->var.right_margin = pdata->right_margin; + info->var.vsync_len = pdata->vsync_len; + info->var.upper_margin = pdata->upper_margin; + info->var.lower_margin = pdata->lower_margin; + info->var.sync = pdata->sync; + info->var.grayscale = pdata->cmap_greyscale; + fbi->cmap_inverse = pdata->cmap_inverse; + fbi->cmap_static = pdata->cmap_static; + fbi->pcr = pdata->pcr; + fbi->lscr1 = pdata->lscr1; + fbi->dmacr = pdata->dmacr; + fbi->pwmr = pdata->pwmr; + fbi->lcd_power = pdata->lcd_power; + fbi->backlight_power = pdata->backlight_power; info->fix.smem_len = fbi->max_xres * fbi->max_yres * fbi->max_bpp / 8; return 0; } -/* - * Allocates the DRAM memory for the frame buffer. This buffer is - * remapped into a non-cached, non-buffered, memory region to - * allow pixel writes to occur without flushing the cache. - * Once this area is remapped, all virtual memory access to the - * video memory should occur at the new region. - */ -static int __init imxfb_map_video_memory(struct fb_info *info) -{ - struct imxfb_info *fbi = info->par; - - fbi->map_size = PAGE_ALIGN(info->fix.smem_len); - fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, - &fbi->map_dma,GFP_KERNEL); - - if (fbi->map_cpu) { - info->screen_base = fbi->map_cpu; - fbi->screen_cpu = fbi->map_cpu; - fbi->screen_dma = fbi->map_dma; - info->fix.smem_start = fbi->screen_dma; - } - - return fbi->map_cpu ? 0 : -ENOMEM; -} - static int __init imxfb_probe(struct platform_device *pdev) { struct imxfb_info *fbi; struct fb_info *info; - struct imxfb_mach_info *inf; + struct imx_fb_platform_data *pdata; struct resource *res; int ret; printk("i.MX Framebuffer driver\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if(!res) + if (!res) return -ENODEV; - inf = pdev->dev.platform_data; - if(!inf) { + pdata = pdev->dev.platform_data; + if (!pdata) { dev_err(&pdev->dev,"No platform_data available\n"); return -ENOMEM; } info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); - if(!info) + if (!info) return -ENOMEM; fbi = info->par; platform_set_drvdata(pdev, info); - ret = imxfb_init_fbinfo(&pdev->dev); - if( ret < 0 ) + ret = imxfb_init_fbinfo(pdev); + if (ret < 0) goto failed_init; - res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB"); + fbi->pdev = pdev; + + res = request_mem_region(res->start, resource_size(res), + DRIVER_NAME); if (!res) { ret = -EBUSY; - goto failed_regs; + goto failed_req; + } + + fbi->regs = ioremap(res->start, resource_size(res)); + if (fbi->regs == NULL) { + printk(KERN_ERR"Cannot map frame buffer registers\n"); + goto failed_ioremap; } - if (!inf->fixed_screen_cpu) { - ret = imxfb_map_video_memory(info); - if (ret) { - dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret); + fbi->clk = clk_get(&pdev->dev, "lcdc_clk"); + if (IS_ERR(fbi->clk)) { + ret = PTR_ERR(fbi->clk); + dev_err(&pdev->dev, + "Cannot get the clock for IMX LCD unit %d: %d\n", + pdev->id, ret); + goto failed_getclock; + } + + if (!pdata->fixed_screen_cpu) { + fbi->map_size = PAGE_ALIGN(info->fix.smem_len); + fbi->map_cpu = dma_alloc_writecombine(&pdev->dev, + fbi->map_size, &fbi->map_dma, GFP_KERNEL); + + if (!fbi->map_cpu) { + dev_err(&pdev->dev, "Failed to allocate %u byte for video RAM\n", + fbi->map_size); ret = -ENOMEM; goto failed_map; } + + info->screen_base = fbi->map_cpu; + fbi->screen_cpu = fbi->map_cpu; + fbi->screen_dma = fbi->map_dma; + info->fix.smem_start = fbi->screen_dma; } else { /* Fixed framebuffer mapping enables location of the screen in eSRAM */ - fbi->map_cpu = inf->fixed_screen_cpu; - fbi->map_dma = inf->fixed_screen_dma; + fbi->map_cpu = pdata->fixed_screen_cpu; + fbi->map_dma = pdata->fixed_screen_dma; info->screen_base = fbi->map_cpu; fbi->screen_cpu = fbi->map_cpu; fbi->screen_dma = fbi->map_dma; @@ -588,17 +710,26 @@ static int __init imxfb_probe(struct pla * This makes sure that our colour bitfield * descriptors are correctly initialised. */ - imxfb_check_var(&info->var, info); + imxfb_setup_display(fbi); + ret = imxfb_check_var(&info->var, info); + if (ret) { + dev_err(&pdev->dev, "failed to get suitable mode\n"); + goto failed_cmap; + } - ret = fb_alloc_cmap(&info->cmap, 1<var.bits_per_pixel, 0); - if (ret < 0) + ret = imxfb_set_par(info); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); goto failed_cmap; + } - imxfb_setup_gpio(fbi); + ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0); + if (ret < 0) + goto failed_cmap; imxfb_set_par(info); ret = register_framebuffer(info); - if (ret < 0) { + if (ret) { dev_err(&pdev->dev, "failed to register framebuffer\n"); goto failed_register; } @@ -610,20 +741,24 @@ static int __init imxfb_probe(struct pla failed_register: fb_dealloc_cmap(&info->cmap); failed_cmap: - if (!inf->fixed_screen_cpu) + if (!pdata->fixed_screen_cpu) dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu, - fbi->map_dma); + fbi->map_dma); failed_map: - kfree(info->pseudo_palette); -failed_regs: + clk_put(fbi->clk); +failed_getclock: + iounmap(fbi->regs); +failed_ioremap: release_mem_region(res->start, res->end - res->start); +failed_req: + kfree(info->pseudo_palette); failed_init: platform_set_drvdata(pdev, NULL); framebuffer_release(info); return ret; } -static int imxfb_remove(struct platform_device *pdev) +static int __devexit imxfb_remove(struct platform_device *pdev) { struct fb_info *info = platform_get_drvdata(pdev); struct imxfb_info *fbi = info->par; @@ -639,7 +774,12 @@ static int imxfb_remove(struct platform_ kfree(info->pseudo_palette); framebuffer_release(info); + iounmap(fbi->regs); release_mem_region(res->start, res->end - res->start + 1); + + clk_disable(fbi->clk); + clk_put(fbi->clk); + platform_set_drvdata(pdev, NULL); return 0; @@ -653,19 +793,18 @@ void imxfb_shutdown(struct platform_dev } static struct platform_driver imxfb_driver = { - .probe = imxfb_probe, .suspend = imxfb_suspend, .resume = imxfb_resume, - .remove = imxfb_remove, + .remove = __devexit_p(imxfb_remove), .shutdown = imxfb_shutdown, .driver = { - .name = "imx-fb", + .name = DRIVER_NAME, }, }; int __init imxfb_init(void) { - return platform_driver_register(&imxfb_driver); + return platform_driver_probe(&imxfb_driver, imxfb_probe); } static void __exit imxfb_cleanup(void) diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/video/imxfb.h linux-2.6.28-karo/drivers/video/imxfb.h --- linux-2.6.28/drivers/video/imxfb.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/video/imxfb.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,73 +0,0 @@ -/* - * linux/drivers/video/imxfb.h - * - * Freescale i.MX Frame Buffer device driver - * - * Copyright (C) 2004 S.Hauer, Pengutronix - * - * Copyright (C) 1999 Eric A. Thomas - * Based on acornfb.c Copyright (C) Russell King. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -/* - * These are the bitfields for each - * display depth that we support. - */ -struct imxfb_rgb { - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; -}; - -#define RGB_16 (0) -#define RGB_8 (1) -#define NR_RGB 2 - -struct imxfb_info { - struct device *dev; - struct imxfb_rgb *rgb[NR_RGB]; - - u_int max_bpp; - u_int max_xres; - u_int max_yres; - - /* - * These are the addresses we mapped - * the framebuffer memory region to. - */ - dma_addr_t map_dma; - u_char * map_cpu; - u_int map_size; - - u_char * screen_cpu; - dma_addr_t screen_dma; - u_int palette_size; - - dma_addr_t dbar1; - dma_addr_t dbar2; - - u_int pcr; - u_int pwmr; - u_int lscr1; - u_int dmacr; - u_int cmap_inverse:1, - cmap_static:1, - unused:30; - - void (*lcd_power)(int); - void (*backlight_power)(int); -}; - -#define IMX_NAME "IMX" - -/* - * Minimum X and Y resolutions - */ -#define MIN_XRES 64 -#define MIN_YRES 64 - diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/w1/masters/Kconfig linux-2.6.28-karo/drivers/w1/masters/Kconfig --- linux-2.6.28/drivers/w1/masters/Kconfig 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/w1/masters/Kconfig 2009-03-11 13:16:24.000000000 +0100 @@ -34,6 +34,12 @@ config W1_MASTER_DS2482 This driver can also be built as a module. If so, the module will be called ds2482. +config W1_MASTER_MXC + tristate "Freescale MXC 1-wire busmaster" + depends on W1 && ARCH_MXC + help + Say Y here to enable MXC 1-wire host + config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" depends on W1 && ARM && HAVE_CLK diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/w1/masters/Makefile linux-2.6.28-karo/drivers/w1/masters/Makefile --- linux-2.6.28/drivers/w1/masters/Makefile 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/drivers/w1/masters/Makefile 2009-03-11 13:16:24.000000000 +0100 @@ -5,6 +5,8 @@ obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o +obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o + obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/drivers/w1/masters/mxc_w1.c linux-2.6.28-karo/drivers/w1/masters/mxc_w1.c --- linux-2.6.28/drivers/w1/masters/mxc_w1.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/drivers/w1/masters/mxc_w1.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,211 @@ +/* + * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Luotao Fu, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_log.h" + +/* According to the mx27 Datasheet the reset procedure should take up to about + * 1350us. We set the timeout to 500*100us = 50ms for sure */ +#define MXC_W1_RESET_TIMEOUT 500 + +/* + * MXC W1 Register offsets + */ +#define MXC_W1_CONTROL 0x00 +#define MXC_W1_TIME_DIVIDER 0x02 +#define MXC_W1_RESET 0x04 +#define MXC_W1_COMMAND 0x06 +#define MXC_W1_TXRX 0x08 +#define MXC_W1_INTERRUPT 0x0A +#define MXC_W1_INTERRUPT_EN 0x0C + +struct mxc_w1_device { + void __iomem *regs; + unsigned int clkdiv; + struct clk *clk; + struct w1_bus_master bus_master; +}; + +/* + * this is the low level routine to + * reset the device on the One Wire interface + * on the hardware + */ +static u8 mxc_w1_ds2_reset_bus(void *data) +{ + u8 reg_val; + unsigned int timeout_cnt = 0; + struct mxc_w1_device *dev = data; + + __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); + + while (1) { + reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); + + if (((reg_val >> 7) & 0x1) == 0 || + timeout_cnt > MXC_W1_RESET_TIMEOUT) + break; + else + timeout_cnt++; + + udelay(100); + } + return (reg_val >> 7) & 0x1; +} + +/* + * this is the low level routine to read/write a bit on the One Wire + * interface on the hardware. It does write 0 if parameter bit is set + * to 0, otherwise a write 1/read. + */ +static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) +{ + struct mxc_w1_device *mdev = data; + void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL; + unsigned int timeout_cnt = 400; /* Takes max. 120us according to + * datasheet. + */ + + __raw_writeb((1 << (5 - bit)), ctrl_addr); + + while (timeout_cnt--) { + if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) + break; + + udelay(1); + } + + return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; +} + +static int __init mxc_w1_probe(struct platform_device *pdev) +{ + struct mxc_w1_device *mdev; + struct resource *res; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->clk = clk_get(&pdev->dev, "owire_clk"); + if (!mdev->clk) { + err = -ENODEV; + goto failed_clk; + } + + mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; + + res = request_mem_region(res->start, resource_size(res), + "mxc_w1"); + if (!res) { + err = -EBUSY; + goto failed_req; + } + + mdev->regs = ioremap(res->start, resource_size(res)); + if (!mdev->regs) { + printk(KERN_ERR "Cannot map frame buffer registers\n"); + goto failed_ioremap; + } + + clk_enable(mdev->clk); + __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); + + mdev->bus_master.data = mdev; + mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; + mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; + + err = w1_add_master_device(&mdev->bus_master); + + if (err) + goto failed_add; + + platform_set_drvdata(pdev, mdev); + return 0; + +failed_add: + iounmap(mdev->regs); +failed_ioremap: + release_mem_region(res->start, resource_size(res)); +failed_req: + clk_put(mdev->clk); +failed_clk: + kfree(mdev); + return err; +} + +/* + * disassociate the w1 device from the driver + */ +static int mxc_w1_remove(struct platform_device *pdev) +{ + struct mxc_w1_device *mdev = platform_get_drvdata(pdev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + w1_remove_master_device(&mdev->bus_master); + + iounmap(mdev->regs); + release_mem_region(res->start, resource_size(res)); + clk_disable(mdev->clk); + clk_put(mdev->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxc_w1_driver = { + .driver = { + .name = "mxc_w1", + }, + .probe = mxc_w1_probe, + .remove = mxc_w1_remove, +}; + +static int __init mxc_w1_init(void) +{ + return platform_driver_register(&mxc_w1_driver); +} + +static void mxc_w1_exit(void) +{ + platform_driver_unregister(&mxc_w1_driver); +} + +module_init(mxc_w1_init); +module_exit(mxc_w1_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductors Inc"); +MODULE_DESCRIPTION("Driver for One-Wire on MXC"); diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/include/linux/dmaengine.h linux-2.6.28-karo/include/linux/dmaengine.h --- linux-2.6.28/include/linux/dmaengine.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/include/linux/dmaengine.h 2009-03-11 13:16:24.000000000 +0100 @@ -285,6 +285,7 @@ struct dma_async_tx_descriptor { struct list_head tx_list; struct dma_chan *chan; dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx); + void (*tx_free)(struct dma_async_tx_descriptor *tx); dma_async_tx_callback callback; void *callback_param; struct dma_async_tx_descriptor *next; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/include/linux/fec_enet.h linux-2.6.28-karo/include/linux/fec_enet.h --- linux-2.6.28/include/linux/fec_enet.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/include/linux/fec_enet.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2007 Lothar Wassmann + * + * platform_data definitions for fec_enet device + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 + */ + +struct fec_enet_platform_data { + /* callback for platform specific initialization */ + int (*arch_init)(struct platform_device *dev); + void (*arch_exit)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); +}; diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/include/linux/mtd/mtd.h linux-2.6.28-karo/include/linux/mtd/mtd.h --- linux-2.6.28/include/linux/mtd/mtd.h 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/include/linux/mtd/mtd.h 2009-03-11 13:16:24.000000000 +0100 @@ -268,13 +268,13 @@ static inline void mtd_erase_callback(st #define MTD_DEBUG_LEVEL3 (3) /* Noisy */ #ifdef CONFIG_MTD_DEBUG -#define DEBUG(n, args...) \ +#define MTD_DEBUG(n, args...) \ do { \ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ printk(KERN_INFO args); \ } while(0) #else /* CONFIG_MTD_DEBUG */ -#define DEBUG(n, args...) \ +#define MTD_DEBUG(n, args...) \ do { \ if (0) \ printk(KERN_INFO args); \ diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/include/linux/rtc/ds13xx.h linux-2.6.28-karo/include/linux/rtc/ds13xx.h --- linux-2.6.28/include/linux/rtc/ds13xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/include/linux/rtc/ds13xx.h 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,73 @@ +/* + * include/linux/rtc/rtc-ds1339.h + * + * platform specific definitions for DS1339 RTC driver + * + * + * Copyright (C) 2007 Lothar Wassmann + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +enum ds13xx_type { + unknown, + ds_1307, /* or ds1338, ... */ + ds_1337, + ds_1338, + ds_1339, + ds_1340, + m41t00, + // rs5c372 too? different address... +}; + +/* DS1307 control reg (0x07) bit definitions */ +#define DS1307_BIT_OUT 0x80 +#define DS1307_BIT_SQWE 0x10 +#define DS1307_BIT_RS1 0x02 +#define DS1307_BIT_RS0 0x01 +/* DS1337/DS1339 control reg (0x0e) bit definitions */ +#define DS133X_BIT_INTCN 0x04 +#define DS133X_BIT_RS1 0x08 +#define DS133X_BIT_RS2 0x10 +#define DS1339_BIT_BBSQI 0x20 +/* DS1339 trickle charge reg (0x10) bit definitions */ +#define DS1339_TRC_ENABLE 0xa0 +#define DS1339_TRC_250R 0x01 +#define DS1339_TRC_2K 0x02 +#define DS1339_TRC_4K 0x03 +#define DS1339_DIODE_DISABLE 0x04 +#define DS1339_DIODE_ENABLE 0x08 +/* DS1340 trickle charge reg (0x08) bit definitions */ +#define DS1340_TRC_ENABLE 0xa0 +#define DS1340_TRC_250R 0x01 +#define DS1340_TRC_2K 0x02 +#define DS1340_TRC_4K 0x03 +#define DS1340_DIODE_DISABLE 0x04 +#define DS1340_DIODE_ENABLE 0x08 +/* DS1340 control reg (0x09) bit definitions */ +#define DS1340_BIT_OUT 0x80 +#define DS1340_BIT_FT 0x0 +#define DS1340_CAL_SIGN 0x0 +#define DS1340_CAL_MASK 0x1f +#define DS1340_CAL(v) ((v) & (DS1340_CAL_MASK)) + +struct ds13xx_platform_data { + /* type of DS13XX chip */ + enum ds13xx_type type; + /* value for Control register on DS13xx; < 0 means don't care */ + short ctrl; + /* value for Trickle charge register on DS1339; < 0 means don't care */ + short trc; +}; + diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/kernel/printk.c linux-2.6.28-karo/kernel/printk.c --- linux-2.6.28/kernel/printk.c 2008-12-25 00:26:37.000000000 +0100 +++ linux-2.6.28-karo/kernel/printk.c 2009-03-11 13:16:24.000000000 +0100 @@ -622,6 +622,9 @@ static int recursion_bug; static int new_text_line = 1; static char printk_buf[1024]; +#ifdef CONFIG_DEBUG_LL +extern void asmlinkage printascii(const char *); +#endif asmlinkage int vprintk(const char *fmt, va_list args) { int printed_len = 0; @@ -669,6 +672,9 @@ asmlinkage int vprintk(const char *fmt, sizeof(printk_buf) - printed_len, fmt, args); +#ifdef CONFIG_DEBUG_LL + printascii(printk_buf); +#endif /* * Copy the output into log_buf. If the caller didn't provide * appropriate log level tags, we insert them here diff -purN -X linux-2.6.28/Documentation/dontdiff linux-2.6.28/sound/arm/soc-wrapper.c linux-2.6.28-karo/sound/arm/soc-wrapper.c --- linux-2.6.28/sound/arm/soc-wrapper.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28-karo/sound/arm/soc-wrapper.c 2009-03-11 13:16:24.000000000 +0100 @@ -0,0 +1,222 @@ + +#define SOC_SHIFT_LEFT 8 +#define SOC_SHIFT_RIGHT 13 +#define SOC_SHIFT_MAX 18 +#define SOC_SHIFT_INVERT 26 + +#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&xenum } + +#define SOC_ENUM_SINGLE_EXT(xmask, xtexts) \ +{ .mask = xmask, .texts = xtexts } + +#define SOC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ + .private_value = (unsigned long)&xenum } + +#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ +{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ + .mask = xmask, .texts = xtexts } +#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \ + SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts) + +#define SOC_SINGLE_VALUE(reg, shift, max, invert) ((reg) | ((shift) << SOC_SHIFT_LEFT) |\ + ((shift) << SOC_SHIFT_RIGHT) | ((max) << SOC_SHIFT_MAX) | ((invert) << SOC_SHIFT_INVERT)) + +#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ + .put = snd_soc_put_volsw, \ + .private_value = (reg) | ((shift_left) << SOC_SHIFT_LEFT) | \ + ((shift_right) << SOC_SHIFT_RIGHT) | ((max) << SOC_SHIFT_MAX) | ((invert) << SOC_SHIFT_INVERT) } + +#define SOC_SINGLE_EXT(xname, xreg, xshift, xmask, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) } + +#define SOC_SINGLE(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = snd_soc_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +struct soc_enum { + unsigned short reg; + unsigned short reg2; + unsigned char shift_l; + unsigned char shift_r; + unsigned int mask; + const char **texts; + void *dapm; +}; + +static unsigned int snd_soc_read(int reg); +static void snd_soc_write(int reg, int val); + +static int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->mask; + + if (uinfo->value.enumerated.item > e->mask - 1) + uinfo->value.enumerated.item = e->mask - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int max = (kcontrol->private_value >> SOC_SHIFT_MAX) & 0xff; + int shift = (kcontrol->private_value >> SOC_SHIFT_LEFT) & 0x1f; + int rshift = (kcontrol->private_value >> SOC_SHIFT_RIGHT) & 0x1f; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = shift == rshift ? 1 : 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max; + return 0; +} + +static int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> SOC_SHIFT_LEFT) & 0x1f; + int rshift = (kcontrol->private_value >> SOC_SHIFT_RIGHT) & 0x1f; + int max = (kcontrol->private_value >> SOC_SHIFT_MAX) & 0xff; + int mask = (1 << fls(max)) - 1; + int invert = (kcontrol->private_value >> SOC_SHIFT_INVERT) & 0x01; + + ucontrol->value.integer.value[0] = + (snd_soc_read(reg) >> shift) & mask; + if (shift != rshift) + ucontrol->value.integer.value[1] = + (snd_soc_read(reg) >> rshift) & mask; + if (invert) { + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + if (shift != rshift) + ucontrol->value.integer.value[1] = + max - ucontrol->value.integer.value[1]; + } + + return 0; +} + +static int snd_soc_update_bits(unsigned short reg, + unsigned int mask, unsigned int value) +{ + int change; + unsigned int old, new; + +// mutex_lock(&io_mutex); + old = snd_soc_read(reg); + new = (old & ~mask) | value; + change = old != new; + if (change) + snd_soc_write(reg, new); + +// mutex_unlock(&io_mutex); + return change; +} + +static int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> SOC_SHIFT_LEFT) & 0x1f; + int rshift = (kcontrol->private_value >> SOC_SHIFT_RIGHT) & 0x1f; + int max = (kcontrol->private_value >> SOC_SHIFT_MAX) & 0xff; + int mask = (1 << fls(max)) - 1; + int invert = (kcontrol->private_value >> SOC_SHIFT_INVERT) & 0x01; + unsigned int val, val2, val_mask; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + val_mask = mask << shift; + val = val << shift; + if (shift != rshift) { + val2 = (ucontrol->value.integer.value[1] & mask); + if (invert) + val2 = max - val2; + val_mask |= mask << rshift; + val |= val2 << rshift; + } + return snd_soc_update_bits(reg, val_mask, val); +} + +static int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = e->shift_l == e->shift_r ? 1 : 2; + uinfo->value.enumerated.items = e->mask; + + if (uinfo->value.enumerated.item > e->mask - 1) + uinfo->value.enumerated.item = e->mask - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + val = snd_soc_read(e->reg); + ucontrol->value.enumerated.item[0] + = (val >> e->shift_l) & (bitmask - 1); + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + (val >> e->shift_r) & (bitmask - 1); + + return 0; +} + +static int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned int mask, bitmask; + + for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) + ; + if (ucontrol->value.enumerated.item[0] > e->mask - 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << e->shift_l; + mask = (bitmask - 1) << e->shift_l; + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->mask - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= (bitmask - 1) << e->shift_r; + } + + return snd_soc_update_bits(e->reg, mask, val); +}