summaryrefslogtreecommitdiff
path: root/target/linux/ep93xx
diff options
context:
space:
mode:
authorflorian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-11-25 23:43:48 +0000
committerflorian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-11-25 23:43:48 +0000
commit288fe380457a72eaf1a6b6201326e5a51b82976c (patch)
tree4e11bba416827d9797e6dac0e98bafd030a6ef0f /target/linux/ep93xx
parentbd245df66fd2d2474807fcfb636119dd782f8da4 (diff)
[ep93xx] add support for the Simplemachines Sim.One board
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@18540 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/ep93xx')
-rw-r--r--target/linux/ep93xx/Makefile21
-rw-r--r--target/linux/ep93xx/base-files/etc/inittab5
-rw-r--r--target/linux/ep93xx/config-2.6.30239
-rw-r--r--target/linux/ep93xx/image/Makefile35
-rw-r--r--target/linux/ep93xx/patches-2.6.30/001-ep93xx-regs.patch479
-rw-r--r--target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch4342
-rw-r--r--target/linux/ep93xx/patches-2.6.30/003-ep93xx-i2c.patch225
-rw-r--r--target/linux/ep93xx/patches-2.6.30/004-simone-rtc.patch78
-rw-r--r--target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch3622
-rw-r--r--target/linux/ep93xx/patches-2.6.30/006-ep93xx-touchscreen.patch1319
-rw-r--r--target/linux/ep93xx/patches-2.6.30/007-ep93xx-eth.patch405
-rw-r--r--target/linux/ep93xx/patches-2.6.30/008-ep93xx-spi.patch714
-rw-r--r--target/linux/ep93xx/patches-2.6.30/009-ep93xx-fb.patch3565
-rw-r--r--target/linux/ep93xx/patches-2.6.30/010-ep93xx-snd-ac97.patch3808
-rw-r--r--target/linux/ep93xx/patches-2.6.30/011-simone-board-def.patch1658
-rw-r--r--target/linux/ep93xx/patches-2.6.30/012-ep93xx-cpuinfo.patch32
16 files changed, 20547 insertions, 0 deletions
diff --git a/target/linux/ep93xx/Makefile b/target/linux/ep93xx/Makefile
new file mode 100644
index 0000000000..a43855ed18
--- /dev/null
+++ b/target/linux/ep93xx/Makefile
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=arm
+BOARD:=ep93xx
+BOARDNAME:=Cirrus Logic EP93xx SoC
+FEATURES:=squashfs jffs2 ext2 tgz
+CFLAGS:=-Os -pipe -march=armv4t -funit-at-a-time
+
+LINUX_VERSION:=2.6.30.9
+
+include $(INCLUDE_DIR)/target.mk
+
+KERNELNAME:="uImage"
+
+$(eval $(call BuildTarget))
diff --git a/target/linux/ep93xx/base-files/etc/inittab b/target/linux/ep93xx/base-files/etc/inittab
new file mode 100644
index 0000000000..ea83e79ccd
--- /dev/null
+++ b/target/linux/ep93xx/base-files/etc/inittab
@@ -0,0 +1,5 @@
+::sysinit:/etc/init.d/rcS S boot
+::shutdown:/etc/init.d/rcS K stop
+tts/0::askfirst:/bin/ash --login
+ttyAM0::askfirst:/bin/ash --login
+tty1::askfirst:/bin/ash --login
diff --git a/target/linux/ep93xx/config-2.6.30 b/target/linux/ep93xx/config-2.6.30
new file mode 100644
index 0000000000..3e3e205c40
--- /dev/null
+++ b/target/linux/ep93xx/config-2.6.30
@@ -0,0 +1,239 @@
+CONFIG_AEABI=y
+CONFIG_ALIGNMENT_TRAP=y
+CONFIG_ARCH_EP93XX=y
+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARM=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_VIC=y
+# CONFIG_ARPD is not set
+# CONFIG_BINARY_PRINTF is not set
+CONFIG_BITREVERSE=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_CFG80211=y
+# CONFIG_CFG80211_REG_DEBUG is not set
+CONFIG_CMDLINE="console=ttyAM0,57600 init=/etc/preinit"
+CONFIG_COMMON_CLKDEV=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_CPU_32=y
+CONFIG_CPU_32v4T=y
+CONFIG_CPU_ABRT_EV4T=y
+CONFIG_CPU_ARM920T=y
+CONFIG_CPU_CACHE_V4WT=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CRC7=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRUNCH=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_PCBC=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_DEBUG_USER=y
+CONFIG_DECOMPRESS_LZMA=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_DM9000 is not set
+CONFIG_DNOTIFY=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_ELF_CORE=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+CONFIG_EP93XX_EARLY_UART1=y
+# CONFIG_EP93XX_EARLY_UART2 is not set
+# CONFIG_EP93XX_EARLY_UART3 is not set
+CONFIG_EP93XX_ETH=y
+CONFIG_EP93XX_WATCHDOG=y
+CONFIG_FB=y
+# CONFIG_FB_ARMCLCD is not set
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_EP93XX=y
+# CONFIG_FB_EP93XX_MONO is not set
+# CONFIG_FIRMWARE_EDID is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_10x18 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+CONFIG_FONT_8x16=y
+CONFIG_FONT_8x8=y
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_FPE_NWFPE=y
+CONFIG_FPE_NWFPE_XP=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+# CONFIG_GENERIC_TIME is not set
+CONFIG_GPIOLIB=y
+# CONFIG_HAMRADIO is not set
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAVE_AOUT=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_ALGOBIT=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_EP93XX=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_INPUT=y
+# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+# CONFIG_JFFS2_SUMMARY is not set
+CONFIG_LCD_HD44780=m
+CONFIG_LCD_LINUX=m
+CONFIG_LIBCRC32C=y
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_MAC80211=m
+# CONFIG_MAC80211_DEBUGFS is not set
+# CONFIG_MAC80211_DEBUG_MENU is not set
+# CONFIG_MAC80211_HWSIM is not set
+# CONFIG_MAC80211_LEDS is not set
+# CONFIG_MAC80211_MESH is not set
+CONFIG_MAC80211_RC_DEFAULT="pid"
+# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_PID=y
+# CONFIG_MACH_ADSSPHERE is not set
+# CONFIG_MACH_EDB9302 is not set
+# CONFIG_MACH_EDB9302A is not set
+# CONFIG_MACH_EDB9307 is not set
+# CONFIG_MACH_EDB9307A is not set
+# CONFIG_MACH_EDB9312 is not set
+# CONFIG_MACH_EDB9315 is not set
+# CONFIG_MACH_EDB9315A is not set
+# CONFIG_MACH_GESBC9312 is not set
+# CONFIG_MACH_MICRO9 is not set
+# CONFIG_MACH_MICRO9H is not set
+# CONFIG_MACH_MICRO9L is not set
+# CONFIG_MACH_MICRO9M is not set
+CONFIG_MACH_SIM_ONE=y
+# CONFIG_MACH_TS72XX is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_SPI=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+# CONFIG_MTD_CFI_GEOMETRY is not set
+CONFIG_MTD_CFI_STAA=y
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_RAM=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETFILTER is not set
+CONFIG_NET_KEY=y
+# CONFIG_NEW_LEDS is not set
+CONFIG_NLS=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_OABI_COMPAT=y
+# CONFIG_OUTER_CACHE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PCI_SYSCALL is not set
+CONFIG_PREEMPT=y
+# CONFIG_ROMFS_BACKED_BY_BLOCK is not set
+# CONFIG_ROMFS_BACKED_BY_BOTH is not set
+# CONFIG_ROMFS_BACKED_BY_MTD is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_AMBA_PL010=y
+CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
+# CONFIG_SERIAL_AMBA_PL011 is not set
+# CONFIG_SLOW_WORK is not set
+CONFIG_SPI=y
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_EP93XX=y
+# CONFIG_SPI_GPIO is not set
+CONFIG_SPI_MASTER=y
+# CONFIG_SPI_SPIDEV is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_TOUCHSCREEN_EP93XX=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_UID16=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/ep93xx/image/Makefile b/target/linux/ep93xx/image/Makefile
new file mode 100644
index 0000000000..129db3f381
--- /dev/null
+++ b/target/linux/ep93xx/image/Makefile
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+define Image/Prepare
+ cp $(LINUX_DIR)/arch/arm/boot/uImage $(KDIR)/uImage
+endef
+
+define Image/BuildKernel
+endef
+
+define Image/Build/jffs2-64k
+ dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=64k conv=sync
+endef
+
+define Image/Build/jffs2-128k
+ dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=128k conv=sync
+endef
+
+define Image/Build/squashfs
+ $(call prepare_generic_squashfs,$(KDIR)/root.squashfs)
+ dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=128k conv=sync
+endef
+
+define Image/Build
+ cp $(KDIR)/uImage $(BIN_DIR)/uImage-$(board)
+ $(call Image/Build/$(1),$(1))
+endef
+
+$(eval $(call BuildImage))
diff --git a/target/linux/ep93xx/patches-2.6.30/001-ep93xx-regs.patch b/target/linux/ep93xx/patches-2.6.30/001-ep93xx-regs.patch
new file mode 100644
index 0000000000..9b3128bfcf
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/001-ep93xx-regs.patch
@@ -0,0 +1,479 @@
+--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
++++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+@@ -57,6 +57,33 @@
+ #define EP93XX_APB_SIZE 0x00200000
+
+
++/* 8081_0000 - 8081_ffff: Timers */
++#define TIMERS_OFFSET 0x010000
++#define TIMERS_BASE (EP93XX_APB_VIRT_BASE|TIMERS_OFFSET)
++
++#define TIMER1LOAD (TIMERS_BASE+0x00)
++#define TIMER1VALUE (TIMERS_BASE+0x04)
++#define TIMER1CONTROL (TIMERS_BASE+0x08)
++#define TIMER1CLEAR (TIMERS_BASE+0x0C)
++#define TIMER1TEST (TIMERS_BASE+0x10)
++
++#define TIMER2LOAD (TIMERS_BASE+0x20)
++#define TIMER2VALUE (TIMERS_BASE+0x24)
++#define TIMER2CONTROL (TIMERS_BASE+0x28)
++#define TIMER2CLEAR (TIMERS_BASE+0x2C)
++#define TIMER2TEST (TIMERS_BASE+0x30)
++
++#define TIMER3LOAD (TIMERS_BASE+0x80)
++#define TIMER3VALUE (TIMERS_BASE+0x84)
++#define TIMER3CONTROL (TIMERS_BASE+0x88)
++#define TIMER3CLEAR (TIMERS_BASE+0x8C)
++#define TIMER3TEST (TIMERS_BASE+0x90)
++
++#define TTIMERBZCONT (TIMERS_BASE+0x40)
++
++#define TIMER4VALUELOW (TIMERS_BASE+0x60)
++#define TIMER4VALUEHIGH (TIMERS_BASE+0x64)
++
+ /* AHB peripherals */
+ #define EP93XX_DMA_BASE ((void __iomem *) \
+ (EP93XX_AHB_VIRT_BASE + 0x00000000))
+@@ -105,6 +132,8 @@
+ #define EP93XX_I2S_BASE (EP93XX_APB_VIRT_BASE + 0x00020000)
+
+ #define EP93XX_SECURITY_BASE (EP93XX_APB_VIRT_BASE + 0x00030000)
++#define EP93XX_SECURITY_REG(x) (EP93XX_SECURITY_BASE + (x))
++#define EP93XX_SECURITY_UNIQID EP93XX_SECURITY_REG(0x2440)
+
+ #define EP93XX_GPIO_BASE (EP93XX_APB_VIRT_BASE + 0x00040000)
+ #define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x))
+@@ -127,6 +156,7 @@
+ #define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000)
+
+ #define EP93XX_SPI_BASE (EP93XX_APB_VIRT_BASE + 0x000a0000)
++#define EP93XX_SPI_BASE_PHYS (EP93XX_APB_PHYS_BASE + 0x000a0000)
+
+ #define EP93XX_IRDA_BASE (EP93XX_APB_VIRT_BASE + 0x000b0000)
+
+@@ -164,8 +194,425 @@
+ #define EP93XX_SYSCON_DEVICE_CONFIG_U2EN (1<<20)
+ #define EP93XX_SYSCON_DEVICE_CONFIG_U1EN (1<<18)
+ #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0)
++#define EP93XX_SYSCON_CHIP_ID EP93XX_SYSCON_REG(0x94)
++#define EP93XX_SYSCON_BMAR EP93XX_SYSCON_REG(0x54)
++#define EP93XX_SYSCON_I2SDIV EP93XX_SYSCON_REG(0x8C)
++#define EP93XX_SYSCON_DEVCFG_CONFIG_Mong 0x02000000
++#define EP93XX_SYSCON_DEVCFG_CONFIG_Tong 0x04000000
++#define EP93XX_SYSCON_DEVCFG_CONFIG_I2SONSSP 0x00000080
++#define EP93XX_SYSCON_DEVCFG_CONFIG_I2SONAC97 0x00000040
++#define EP93XX_SYSCON_DEVCFG_RasOnP3 0x00000010
++#define EP93XX_SYSCON_DEVCFG_A1onG 0x00200000
++#define EP93XX_SYSCON_DEVCFG_A2onG 0x00400000
++#define EP93XX_SYSCON_DEVCFG_U1EN 0x00040000
++#define EP93XX_SYSCON_DEVCFG_TIN 0x00020000
+
+ #define EP93XX_WATCHDOG_BASE (EP93XX_APB_VIRT_BASE + 0x00140000)
+
+
++#define SYSCON_PWRCNT (EP93XX_SYSCON_BASE+0x0004)
++#define SYSCON_VIDDIV (EP93XX_SYSCON_BASE+0x0084)
++#define SYSCON_MIRDIV (EP93XX_SYSCON_BASE+0x0088)
++#define SYSCON_KTDIV (EP93XX_SYSCON_BASE+0x0090)
++#define SYSCON_KTDIV_TSEN 0x80000000
++//-----------------------------------------------------------------------------
++// SYSCON_CLKSET1
++//-----------------------------------------------------------------------------
++#define SYSCON_CLKSET1_PLL1_X2IPD_SHIFT 0
++#define SYSCON_CLKSET1_PLL1_X2IPD_MASK 0x0000001f
++#define SYSCON_CLKSET1_PLL1_X2FBD2_SHIFT 5
++#define SYSCON_CLKSET1_PLL1_X2FBD2_MASK 0x000007e0
++#define SYSCON_CLKSET1_PLL1_X1FBD1_SHIFT 11
++#define SYSCON_CLKSET1_PLL1_X1FBD1_MASK 0x0000f800
++#define SYSCON_CLKSET1_PLL1_PS_SHIFT 16
++#define SYSCON_CLKSET1_PLL1_PS_MASK 0x00030000
++#define SYSCON_CLKSET1_PCLKDIV_SHIFT 18
++#define SYSCON_CLKSET1_PCLKDIV_MASK 0x000c0000
++#define SYSCON_CLKSET1_HCLKDIV_SHIFT 20
++#define SYSCON_CLKSET1_HCLKDIV_MASK 0x00700000
++#define SYSCON_CLKSET1_nBYP1 0x00800000
++#define SYSCON_CLKSET1_SMCROM 0x01000000
++#define SYSCON_CLKSET1_FCLKDIV_SHIFT 25
++#define SYSCON_CLKSET1_FCLKDIV_MASK 0x0e000000
++
++#define SYSCON_CLKSET1_HSEL 0x00000001
++#define SYSCON_CLKSET1_PLL1_EXCLKSEL 0x00000002
++
++#define SYSCON_CLKSET1_PLL1_P_MASK 0x0000007C
++#define SYSCON_CLKSET1_PLL1_P_SHIFT 2
++
++#define SYSCON_CLKSET1_PLL1_M1_MASK 0x00000780
++#define SYSCON_CLKSET1_PLL1_M1_SHIFT 7
++#define SYSCON_CLKSET1_PLL1_M2_MASK 0x0000F800
++#define SYSCON_CLKSET1_PLL1_M2_SHIFT 11
++#define SYSCON_CLKSET1_PLL1_PS_MASK 0x00030000
++#define SYSCON_CLKSET1_PLL1_PS_SHIFT 16
++#define SYSCON_CLKSET1_PCLK_DIV_MASK 0x000C0000
++#define SYSCON_CLKSET1_PCLK_DIV_SHIFT 18
++#define SYSCON_CLKSET1_HCLK_DIV_MASK 0x00700000
++#define SYSCON_CLKSET1_HCLK_DIV_SHIFT 20
++#define SYSCON_CLKSET1_SMCROM 0x01000000
++#define SYSCON_CLKSET1_FCLK_DIV_MASK 0x0E000000
++#define SYSCON_CLKSET1_FCLK_DIV_SHIFT 25
++
++#define SYSCON_CLKSET2_PLL2_EN 0x00000001
++#define SYSCON_CLKSET2_PLL2EXCLKSEL 0x00000002
++#define SYSCON_CLKSET2_PLL2_P_MASK 0x0000007C
++#define SYSCON_CLKSET2_PLL2_P_SHIFT 2
++#define SYSCON_CLKSET2_PLL2_M2_MASK 0x00000F80
++#define SYSCON_CLKSET2_PLL2_M2_SHIFT 7
++#define SYSCON_CLKSET2_PLL2_M1_MASK 0x0001F000
++#define SYSCON_CLKSET2_PLL2_M1 12
++#define SYSCON_CLKSET2_PLL2_PS_MASK 0x000C0000
++#define SYSCON_CLKSET2_PLL2_PS_SHIFT 18
++#define SYSCON_CLKSET2_USBDIV_MASK 0xF0000000
++#define SYSCON_CLKSET2_USBDIV_SHIFT 28
++
++//-----------------------------------------------------------------------------
++// I2SDIV Register Defines
++//-----------------------------------------------------------------------------
++#define SYSCON_I2SDIV_MDIV_MASK 0x0000007f
++#define SYSCON_I2SDIV_MDIV_SHIFT 0
++#define SYSCON_I2SDIV_PDIV_MASK 0x00000300
++#define SYSCON_I2SDIV_PDIV_SHIFT 8
++#define SYSCON_I2SDIV_PSEL 0x00002000
++#define SYSCON_I2SDIV_ESEL 0x00004000
++#define SYSCON_I2SDIV_MENA 0x00008000
++#define SYSCON_I2SDIV_SDIV 0x00010000
++#define SYSCON_I2SDIV_LRDIV_MASK 0x00060000
++#define SYSCON_I2SDIV_LRDIV_SHIFT 17
++#define SYSCON_I2SDIV_SPOL 0x00080000
++#define SYSCON_I2SDIV_DROP 0x00100000
++#define SYSCON_I2SDIV_ORIDE 0x20000000
++#define SYSCON_I2SDIV_SLAVE 0x40000000
++#define SYSCON_I2SDIV_SENA 0x80000000
++
++#define SYSCON_I2SDIV_PDIV_OFF 0x00000000
++#define SYSCON_I2SDIV_PDIV_2 0x00000100
++#define SYSCON_I2SDIV_PDIV_25 0x00000200
++#define SYSCON_I2SDIV_PDIV_3 0x00000300
++
++#define SYSCON_I2SDIV_LRDIV_32 0x00000000
++#define SYSCON_I2SDIV_LRDIV_64 0x00020000
++#define SYSCON_I2SDIV_LRDIV_128 0x00040000
++
++//-----------------------------------------------------------------------------
++// VIDDIV Register Defines
++//-----------------------------------------------------------------------------
++#define SYSCON_VIDDIV_VDIV_MASK 0x0000007f
++#define SYSCON_VIDDIV_VDIV_SHIFT 0
++#define SYSCON_VIDDIV_PDIV_MASK 0x00000300
++#define SYSCON_VIDDIV_PDIV_SHIFT 8
++#define SYSCON_VIDDIV_PSEL 0x00002000
++#define SYSCON_VIDDIV_ESEL 0x00004000
++#define SYSCON_VIDDIV_VENA 0x00008000
++
++//-----------------------------------------------------------------------------
++// MIRDIV Register Defines
++//-----------------------------------------------------------------------------
++#define SYSCON_MIRDIV_MDIV_MASK 0x0000003f
++#define SYSCON_MIRDIV_MDIV_SHIFT 0
++#define SYSCON_MIRDIV_PDIV_MASK 0x00000300
++#define SYSCON_MIRDIV_PDIV_SHIFT 8
++#define SYSCON_MIRDIV_PSEL 0x00002000
++#define SYSCON_MIRDIV_ESEL 0x00004000
++#define SYSCON_MIRDIV_MENA 0x00008000
++
++/* 8082_0000 - 8082_ffff: I2S */
++#define I2S_OFFSET 0x020000
++#define I2S_BASE (EP93XX_APB_VIRT_BASE|I2S_OFFSET)
++#define I2S_PHYS_BASE (EP93XX_APB_PHYS_BASE + I2S_OFFSET)
++
++
++
++#define I2STxClkCfg (I2S_BASE+0x00) /* 8082.0000 R/W Transmitter clock config register */
++#define I2SRxClkCfg (I2S_BASE+0x04) /* 8082.0004 R/W Receiver clock config register */
++#define I2SGlSts (I2S_BASE+0x08) /* 8082.0008 R/W SAI Global Status register. */
++#define I2SGlCtrl (I2S_BASE+0x0C) /* 8082.000C R/W SAI Global Control register */
++
++#define I2STX0Lft (I2S_BASE+0x10) /* 8082.0010 R/W Left TX data reg for channel 0 */
++#define I2STX0Rt (I2S_BASE+0x14) /* 8082.0014 R/W Right TX data reg for channel 0 */
++#define I2STX1Lft (I2S_BASE+0x18) /* 8082.0018 R/W Left TX data reg for channel 1 */
++#define I2STX1Rt (I2S_BASE+0x1C) /* 8082.001C R/W Right TX data reg for channel 1 */
++#define I2STX2Lft (I2S_BASE+0x20) /* 8082.0020 R/W Left TX data reg for channel 2 */
++#define I2STX2Rt (I2S_BASE+0x24) /* 8082.0024 R/W Right TX data reg for channel 2 */
++
++#define I2STXLinCtrlData (I2S_BASE+0x28) /* 8082.0028 R/W TX Line Control data register */
++#define I2STXCtrl (I2S_BASE+0x2C) /* 8082.002C R/W TX Control register */
++#define I2STXWrdLen (I2S_BASE+0x30) /* 8082.0030 R/W TX Word Length */
++#define I2STX0En (I2S_BASE+0x34) /* 8082.0034 R/W TX0 Channel Enable */
++#define I2STX1En (I2S_BASE+0x38) /* 8082.0038 R/W TX1 Channel Enable */
++#define I2STX2En (I2S_BASE+0x3C) /* 8082.003C R/W TX2 Channel Enable */
++
++#define I2SRX0Lft (I2S_BASE+0x40) /* 8082.0040 R Left RX data reg for channel 0 */
++#define I2SRX0Rt (I2S_BASE+0x44) /* 8082.0044 R Right RX data reg for channel 0 */
++#define I2SRX1Lft (I2S_BASE+0x48) /* 8082.0048 R Left RX data reg for channel 1 */
++#define I2SRX1Rt (I2S_BASE+0x4C) /* 8082.004c R Right RX data reg for channel 1 */
++#define I2SRX2Lft (I2S_BASE+0x50) /* 8082.0050 R Left RX data reg for channel 2 */
++#define I2SRX2Rt (I2S_BASE+0x54) /* 8082.0054 R Right RX data reg for channel 2 */
++
++#define I2SRXLinCtrlData (I2S_BASE+0x58) /* 8082.0058 R/W RX Line Control data register */
++#define I2SRXCtrl (I2S_BASE+0x5C) /* 8082.005C R/W RX Control register */
++#define I2SRXWrdLen (I2S_BASE+0x60) /* 8082.0060 R/W RX Word Length */
++#define I2SRX0En (I2S_BASE+0x64) /* 8082.0064 R/W RX0 Channel Enable */
++#define I2SRX1En (I2S_BASE+0x68) /* 8082.0068 R/W RX1 Channel Enable */
++#define I2SRX2En (I2S_BASE+0x6C) /* 8082.006C R/W RX2 Channel Enable */
++
++/* 8084_0000 - 8084_ffff: GPIO */
++#define GPIO_OFFSET 0x040000
++#define GPIO_BASE (EP93XX_APB_VIRT_BASE|GPIO_OFFSET)
++#define GPIO_PADR (GPIO_BASE+0x00)
++#define GPIO_PBDR (GPIO_BASE+0x04)
++#define GPIO_PCDR (GPIO_BASE+0x08)
++#define GPIO_PDDR (GPIO_BASE+0x0C)
++#define GPIO_PADDR (GPIO_BASE+0x10)
++#define GPIO_PBDDR (GPIO_BASE+0x14)
++#define GPIO_PCDDR (GPIO_BASE+0x18)
++#define GPIO_PDDDR (GPIO_BASE+0x1C)
++#define GPIO_PEDR (GPIO_BASE+0x20)
++#define GPIO_PEDDR (GPIO_BASE+0x24)
++// #define 0x8084.0028 Reserved
++// #define 0x8084.002C Reserved
++#define GPIO_PFDR (GPIO_BASE+0x30)
++#define GPIO_PFDDR (GPIO_BASE+0x34)
++#define GPIO_PGDR (GPIO_BASE+0x38)
++#define GPIO_PGDDR (GPIO_BASE+0x3C)
++#define GPIO_PHDR (GPIO_BASE+0x40)
++#define GPIO_PHDDR (GPIO_BASE+0x44)
++// #define 0x8084.0048 RAZ RAZ
++#define GPIO_FINTTYPE1 (GPIO_BASE+0x4C)
++#define GPIO_FINTTYPE2 (GPIO_BASE+0x50)
++#define GPIO_FEOI (GPIO_BASE+0x54) /* WRITE ONLY - READ UNDEFINED */
++#define GPIO_FINTEN (GPIO_BASE+0x58)
++#define GPIO_INTSTATUSF (GPIO_BASE+0x5C)
++#define GPIO_RAWINTSTASUSF (GPIO_BASE+0x60)
++#define GPIO_FDB (GPIO_BASE+0x64)
++#define GPIO_PAPINDR (GPIO_BASE+0x68)
++#define GPIO_PBPINDR (GPIO_BASE+0x6C)
++#define GPIO_PCPINDR (GPIO_BASE+0x70)
++#define GPIO_PDPINDR (GPIO_BASE+0x74)
++#define GPIO_PEPINDR (GPIO_BASE+0x78)
++#define GPIO_PFPINDR (GPIO_BASE+0x7C)
++#define GPIO_PGPINDR (GPIO_BASE+0x80)
++#define GPIO_PHPINDR (GPIO_BASE+0x84)
++#define GPIO_AINTTYPE1 (GPIO_BASE+0x90)
++#define GPIO_AINTTYPE2 (GPIO_BASE+0x94)
++#define GPIO_AEOI (GPIO_BASE+0x98) /* WRITE ONLY - READ UNDEFINED */
++#define GPIO_AINTEN (GPIO_BASE+0x9C)
++#define GPIO_INTSTATUSA (GPIO_BASE+0xA0)
++#define GPIO_RAWINTSTSTISA (GPIO_BASE+0xA4)
++#define GPIO_ADB (GPIO_BASE+0xA8)
++#define GPIO_BINTTYPE1 (GPIO_BASE+0xAC)
++#define GPIO_BINTTYPE2 (GPIO_BASE+0xB0)
++#define GPIO_BEOI (GPIO_BASE+0xB4) /* WRITE ONLY - READ UNDEFINED */
++#define GPIO_BINTEN (GPIO_BASE+0xB8)
++#define GPIO_INTSTATUSB (GPIO_BASE+0xBC)
++#define GPIO_RAWINTSTSTISB (GPIO_BASE+0xC0)
++#define GPIO_BDB (GPIO_BASE+0xC4)
++#define GPIO_EEDRIVE (GPIO_BASE+0xC8)
++//#define Reserved (GPIO_BASE+0xCC)
++#define GPIO_TCR (GPIO_BASE+0xD0) /* Test Registers */
++#define GPIO_TISRA (GPIO_BASE+0xD4) /* Test Registers */
++#define GPIO_TISRB (GPIO_BASE+0xD8) /* Test Registers */
++#define GPIO_TISRC (GPIO_BASE+0xDC) /* Test Registers */
++#define GPIO_TISRD (GPIO_BASE+0xE0) /* Test Registers */
++#define GPIO_TISRE (GPIO_BASE+0xE4) /* Test Registers */
++#define GPIO_TISRF (GPIO_BASE+0xE8) /* Test Registers */
++#define GPIO_TISRG (GPIO_BASE+0xEC) /* Test Registers */
++#define GPIO_TISRH (GPIO_BASE+0xF0) /* Test Registers */
++#define GPIO_TCER (GPIO_BASE+0xF4) /* Test Registers */
++
++
++/* 8088_0000 - 8088_ffff: Ac97 Controller (AAC) */
++#define AC97_OFFSET 0x080000
++#define AC97_BASE (EP93XX_APB_VIRT_BASE|AC97_OFFSET)
++#define EP93XX_AC97_PHY_BASE (EP93XX_APB_PHYS_BASE|AC97_OFFSET)
++#define AC97DR1 (AC97_BASE+0x00) /* 8088.0000 R/W Data read or written from/to FIFO1 */
++#define AC97RXCR1 (AC97_BASE+0x04) /* 8088.0004 R/W Control register for receive */
++#define AC97TXCR1 (AC97_BASE+0x08) /* 8088.0008 R/W Control register for transmit */
++#define AC97SR1 (AC97_BASE+0x0C) /* 8088.000C R Status register */
++#define AC97RISR1 (AC97_BASE+0x10) /* 8088.0010 R Raw interrupt status register */
++#define AC97ISR1 (AC97_BASE+0x14) /* 8088.0014 R Interrupt Status */
++#define AC97IE1 (AC97_BASE+0x18) /* 8088.0018 R/W Interrupt Enable */
++ /* 8088.001C Reserved - RAZ */
++#define AC97DR2 (AC97_BASE+0x20) /* 8088.0020 R/W Data read or written from/to FIFO2 */
++#define AC97RXCR2 (AC97_BASE+0x24) /* 8088.0024 R/W Control register for receive */
++#define AC97TXCR2 (AC97_BASE+0x28) /* 8088.0028 R/W Control register for transmit */
++#define AC97SR2 (AC97_BASE+0x2C) /* 8088.002C R Status register */
++#define AC97RISR2 (AC97_BASE+0x30) /* 8088.0030 R Raw interrupt status register */
++#define AC97ISR2 (AC97_BASE+0x34) /* 8088.0034 R Interrupt Status */
++#define AC97IE2 (AC97_BASE+0x38) /* 8088.0038 R/W Interrupt Enable */
++ /* 8088.003C Reserved - RAZ */
++#define AC97DR3 (AC97_BASE+0x40) /* 8088.0040 R/W Data read or written from/to FIFO3. */
++#define AC97RXCR3 (AC97_BASE+0x44) /* 8088.0044 R/W Control register for receive */
++#define AC97TXCR3 (AC97_BASE+0x48) /* 8088.0048 R/W Control register for transmit */
++#define AC97SR3 (AC97_BASE+0x4C) /* 8088.004C R Status register */
++#define AC97RISR3 (AC97_BASE+0x50) /* 8088.0050 R Raw interrupt status register */
++#define AC97ISR3 (AC97_BASE+0x54) /* 8088.0054 R Interrupt Status */
++#define AC97IE3 (AC97_BASE+0x58) /* 8088.0058 R/W Interrupt Enable */
++ /* 8088.005C Reserved - RAZ */
++#define AC97DR2 (AC97_BASE+0x20) /* 8088.0020 R/W Data read or written from/to FIFO2 */
++#define AC97RXCR2 (AC97_BASE+0x24) /* 8088.0024 R/W Control register for receive */
++#define AC97TXCR2 (AC97_BASE+0x28) /* 8088.0028 R/W Control register for transmit */
++#define AC97SR2 (AC97_BASE+0x2C) /* 8088.002C R Status register */
++#define AC97RISR2 (AC97_BASE+0x30) /* 8088.0030 R Raw interrupt status register */
++#define AC97ISR2 (AC97_BASE+0x34) /* 8088.0034 R Interrupt Status */
++#define AC97IE2 (AC97_BASE+0x38) /* 8088.0038 R/W Interrupt Enable */
++ /* 8088.003C Reserved - RAZ */
++#define AC97DR3 (AC97_BASE+0x40) /* 8088.0040 R/W Data read or written from/to FIFO3. */
++#define AC97RXCR3 (AC97_BASE+0x44) /* 8088.0044 R/W Control register for receive */
++#define AC97TXCR3 (AC97_BASE+0x48) /* 8088.0048 R/W Control register for transmit */
++#define AC97SR3 (AC97_BASE+0x4C) /* 8088.004C R Status register */
++#define AC97RISR3 (AC97_BASE+0x50) /* 8088.0050 R Raw interrupt status register */
++#define AC97ISR3 (AC97_BASE+0x54) /* 8088.0054 R Interrupt Status */
++#define AC97IE3 (AC97_BASE+0x58) /* 8088.0058 R/W Interrupt Enable */
++ /* 8088.005C Reserved - RAZ */
++#define AC97DR4 (AC97_BASE+0x60) /* 8088.0060 R/W Data read or written from/to FIFO4. */
++#define AC97RXCR4 (AC97_BASE+0x64) /* 8088.0064 R/W Control register for receive */
++#define AC97TXCR4 (AC97_BASE+0x68) /* 8088.0068 R/W Control register for transmit */
++#define AC97SR4 (AC97_BASE+0x6C) /* 8088.006C R Status register */
++#define AC97RISR4 (AC97_BASE+0x70) /* 8088.0070 R Raw interrupt status register */
++#define AC97ISR4 (AC97_BASE+0x74) /* 8088.0074 R Interrupt Status */
++#define AC97IE4 (AC97_BASE+0x78) /* 8088.0078 R/W Interrupt Enable */
++ /* 8088.007C Reserved - RAZ */
++#define AC97S1DATA (AC97_BASE+0x80) /* 8088.0080 R/W Data received/transmitted on SLOT1 */
++#define AC97S2DATA (AC97_BASE+0x84) /* 8088.0084 R/W Data received/transmitted on SLOT2 */
++#define AC97S12DATA (AC97_BASE+0x88) /* 8088.0088 R/W Data received/transmitted on SLOT12 */
++#define AC97RGIS (AC97_BASE+0x8C) /* 8088.008C R/W Raw Global interrupt status register*/
++#define AC97GIS (AC97_BASE+0x90) /* 8088.0090 R Global interrupt status register */
++#define AC97IM (AC97_BASE+0x94) /* 8088.0094 R/W Interrupt mask register */
++#define AC97EOI (AC97_BASE+0x98) /* 8088.0098 W Interrupt clear register */
++#define AC97GCR (AC97_BASE+0x9C) /* 8088.009C R/W Main Control register */
++#define AC97RESET (AC97_BASE+0xA0) /* 8088.00A0 R/W RESET control register. */
++#define AC97SYNC (AC97_BASE+0xA4) /* 8088.00A4 R/W SYNC control register. */
++#define AC97GCIS (AC97_BASE+0xA8) /* 8088.00A8 R Global chan FIFO int status register */
++
++
++/* 800B_0000 - 800B_FFFF: VIC 0 */
++#define VIC0_OFFSET 0x0B0000
++#define VIC0_BASE (EP93XX_AHB_VIRT_BASE|VIC0_OFFSET)
++#define VIC0 (VIC0_BASE+0x000)
++#define VIC0IRQSTATUS (VIC0_BASE+0x000) /* R IRQ status register */
++#define VIC0FIQSTATUS (VIC0_BASE+0x004) /* R FIQ status register */
++#define VIC0RAWINTR (VIC0_BASE+0x008) /* R Raw interrupt status register */
++#define VIC0INTSELECT (VIC0_BASE+0x00C) /* R/W Interrupt select register */
++#define VIC0INTENABLE (VIC0_BASE+0x010) /* R/W Interrupt enable register */
++#define VIC0INTENCLEAR (VIC0_BASE+0x014) /* W Interrupt enable clear register */
++
++/* 8003_0000 - 8003_ffff: Raster */
++#define RASTER_OFFSET 0x030000
++#define RASTER_BASE (EP93XX_AHB_VIRT_BASE|RASTER_OFFSET)
++#define VLINESTOTAL (RASTER_BASE+0x00)
++#define VSYNCSTRTSTOP (RASTER_BASE+0x04)
++#define VACTIVESTRTSTOP (RASTER_BASE+0x08)
++#define VCLKSTRTSTOP (RASTER_BASE+0x0C)
++#define HCLKSTOTAL (RASTER_BASE+0x10)
++#define HSYNCSTRTSTOP (RASTER_BASE+0x14)
++#define HACTIVESTRTSTOP (RASTER_BASE+0x18)
++#define HCLKSTRTSTOP (RASTER_BASE+0x1C)
++#define BRIGHTNESS (RASTER_BASE+0x20)
++#define VIDEOATTRIBS (RASTER_BASE+0x24)
++#define VIDSCRNPAGE (RASTER_BASE+0x28)
++#define VIDSCRNHPG (RASTER_BASE+0x2C)
++#define SCRNLINES (RASTER_BASE+0x30)
++#define LINELENGTH (RASTER_BASE+0x34)
++#define VLINESTEP (RASTER_BASE+0x38)
++#define LINECARRY (RASTER_BASE+0x3C)
++#define BLINKRATE (RASTER_BASE+0x40)
++#define BLINKMASK (RASTER_BASE+0x44)
++#define BLINKPATTRN (RASTER_BASE+0x48)
++#define PATTRNMASK (RASTER_BASE+0x4C)
++#define BG_OFFSET (RASTER_BASE+0x50)
++#define PIXELMODE (RASTER_BASE+0x54)
++#define PARLLIFOUT (RASTER_BASE+0x58)
++#define PARLLIFIN (RASTER_BASE+0x5C)
++#define CURSOR_ADR_START (RASTER_BASE+0x60)
++#define CURSOR_ADR_RESET (RASTER_BASE+0x64)
++#define CURSORSIZE (RASTER_BASE+0x68)
++#define CURSORCOLOR1 (RASTER_BASE+0x6C)
++#define CURSORCOLOR2 (RASTER_BASE+0x70)
++#define CURSORXYLOC (RASTER_BASE+0x74)
++#define CURSOR_DHSCAN_LH_YLOC (RASTER_BASE+0x78)
++#define RASTER_SWLOCK (RASTER_BASE+0x7C)
++#define GS_LUT (RASTER_BASE+0x80)
++#define RASTER_TCR (RASTER_BASE+0x100)
++#define RASTER_TISRA (RASTER_BASE+0x104)
++#define RASTER_TISRB (RASTER_BASE+0x108)
++#define CURSOR_TISR (RASTER_BASE+0x10C)
++#define RASTER_TOCRA (RASTER_BASE+0x110)
++#define RASTER_TOCRB (RASTER_BASE+0x114)
++#define FIFO_TOCRA (RASTER_BASE+0x118)
++#define FIFO_TOCRB (RASTER_BASE+0x11C)
++#define BLINK_TISR (RASTER_BASE+0x120)
++#define DAC_TISRA (RASTER_BASE+0x124)
++#define DAC_TISRB (RASTER_BASE+0x128)
++#define SHIFT_TISR (RASTER_BASE+0x12C)
++#define DACMUX_TOCRA (RASTER_BASE+0x130)
++#define DACMUX_TOCRB (RASTER_BASE+0x134)
++#define PELMUX_TOCR (RASTER_BASE+0x138)
++#define VIDEO_TOCRA (RASTER_BASE+0x13C)
++#define VIDEO_TOCRB (RASTER_BASE+0x140)
++#define YCRCB_TOCR (RASTER_BASE+0x144)
++#define CURSOR_TOCR (RASTER_BASE+0x148)
++#define VIDEO_TOCRC (RASTER_BASE+0x14C)
++#define SHIFT_TOCR (RASTER_BASE+0x150)
++#define BLINK_TOCR (RASTER_BASE+0x154)
++#define RASTER_TCER (RASTER_BASE+0x180)
++#define SIGVAL (RASTER_BASE+0x200)
++#define SIGCTL (RASTER_BASE+0x204)
++#define VSIGSTRTSTOP (RASTER_BASE+0x208)
++#define HSIGSTRTSTOP (RASTER_BASE+0x20C)
++#define SIGCLR (RASTER_BASE+0x210)
++#define ACRATE (RASTER_BASE+0x214)
++#define LUTCONT (RASTER_BASE+0x218)
++#define VBLANKSTRTSTOP (RASTER_BASE+0x228)
++#define HBLANKSTRTSTOP (RASTER_BASE+0x22C)
++#define LUT (RASTER_BASE+0x400)
++#define CURSORBLINK1 (RASTER_BASE+0x21C)
++#define CURSORBLINK2 (RASTER_BASE+0x220)
++#define CURSORBLINK (RASTER_BASE+0x224)
++#define EOLOFFSET (RASTER_BASE+0x230)
++#define FIFOLEVEL (RASTER_BASE+0x234)
++#define GS_LUT2 (RASTER_BASE+0x280)
++#define GS_LUT3 (RASTER_BASE+0x300)
++#define COLOR_LUT (RASTER_BASE+0x400)
++
++/* 8004_0000 - 8004_ffff: Graphics */
++#define GRAPHICS_OFFSET 0x040000
++#define GRAPHICS_BASE (EP93XX_AHB_VIRT_BASE|GRAPHICS_OFFSET)
++#define SRCPIXELSTRT (GRAPHICS_BASE+0x00)
++#define DESTPIXELSTRT (GRAPHICS_BASE+0x04)
++#define BLKSRCSTRT (GRAPHICS_BASE+0x08)
++#define BLKDSTSTRT (GRAPHICS_BASE+0x0C)
++#define BLKSRCWIDTH (GRAPHICS_BASE+0x10)
++#define SRCLINELENGTH (GRAPHICS_BASE+0x14)
++#define BLKDESTWIDTH (GRAPHICS_BASE+0x18)
++#define BLKDESTHEIGHT (GRAPHICS_BASE+0x1C)
++#define DESTLINELENGTH (GRAPHICS_BASE+0x20)
++#define BLOCKCTRL (GRAPHICS_BASE+0x24)
++#define TRANSPATTRN (GRAPHICS_BASE+0x28)
++#define BLOCKMASK (GRAPHICS_BASE+0x2C)
++#define BACKGROUND (GRAPHICS_BASE+0x30)
++#define LINEINC (GRAPHICS_BASE+0x34)
++#define LINEINIT (GRAPHICS_BASE+0x38)
++#define LINEPATTRN (GRAPHICS_BASE+0x3C)
++
++#define EP93XX_RASTER_BASE (EP93XX_AHB_VIRT_BASE + 0x00030000)
++#define EP93XX_RASTER_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00030000)
++
++#define EP93XX_GRAPHICS_ACCEL_BASE (EP93XX_AHB_VIRT_BASE + 0x00040000)
++#define EP93XX_GRAPHICS_ACCEL_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00040000)
++
++#ifndef __ASSEMBLY__
++
++#define SysconSetLocked(registername,value) \
++ { \
++ local_irq_disable(); \
++ outl( 0xAA, EP93XX_SYSCON_SWLOCK); \
++ outl( value, registername); \
++ local_irq_enable(); \
++ }
++
++#endif /* Not __ASSEMBLY__ */
++
+ #endif
diff --git a/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch b/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch
new file mode 100644
index 0000000000..7480b28117
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch
@@ -0,0 +1,4342 @@
+Index: linux-2.6.30.9/arch/arm/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/Kconfig 2009-10-05 17:38:08.000000000 +0200
++++ linux-2.6.30.9/arch/arm/Kconfig 2009-11-24 02:01:42.000000000 +0100
+@@ -1407,6 +1407,8 @@
+
+ source "drivers/accessibility/Kconfig"
+
++source "drivers/lcd-linux/Kconfig"
++
+ source "drivers/leds/Kconfig"
+
+ source "drivers/rtc/Kconfig"
+Index: linux-2.6.30.9/drivers/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/drivers/Makefile 2009-10-05 17:38:08.000000000 +0200
++++ linux-2.6.30.9/drivers/Makefile 2009-11-24 02:01:42.000000000 +0100
+@@ -106,4 +106,5 @@
+ obj-$(CONFIG_SSB) += ssb/
+ obj-$(CONFIG_VIRTIO) += virtio/
+ obj-$(CONFIG_STAGING) += staging/
++obj-$(CONFIG_LCD_LINUX) += lcd-linux/
+ obj-y += platform/
+Index: linux-2.6.30.9/drivers/lcd-linux/Config.in
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Config.in 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,8 @@
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++ mainmenu_option next_comment
++ comment 'LCD support'
++
++ tristate 'LCD-Linux layer' CONFIG_LCD_LINUX
++ dep_tristate ' HD44780 controller' CONFIG_LCD_HD44780 $CONFIG_LCD_LINUX
++ endmenu
++fi
+Index: linux-2.6.30.9/drivers/lcd-linux/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Kconfig 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,33 @@
++menu "LCD-Linux support"
++ depends on EXPERIMENTAL
++
++config LCD_LINUX
++ tristate "LCD-Linux layer"
++ default m
++ help
++ LCD-Linux provides an easy way to drive LCD displays under
++ Linux by creating a character which can be read or written.
++ It features complete VT102 emulation and recognizes
++ many escape sequences. If you want to use it you must also
++ choose an appropriate driver, otherwise it will not be
++ very useful. For more information see
++ http://lcd-linux.sourceforge.net/
++
++ To compile LCD-Linux as a module, choose M here:
++ the module will be called lcd-linux.
++
++config LCD_HD44780
++ tristate "HD44780 controller"
++ depends on LCD_LINUX && MACH_SIM_ONE
++ default m
++ help
++ This is a LCD-Linux driver for LCD displays based on the
++ Hitachi HD44780 (and compatible) controllers connected
++ to the SimOne LCD port.
++
++ To compile this driver as a module, choose M here:
++ the module will be called hd44780.
++
++ If unsure, say N.
++
++endmenu
+Index: linux-2.6.30.9/drivers/lcd-linux/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Makefile 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,8 @@
++#
++#
++# Standard Makefile to statically compile LCD-Linux into the kernel
++# Linux 2.6
++
++obj-$(CONFIG_LCD_LINUX) += lcd-linux.o
++obj-$(CONFIG_LCD_HD44780) += hd44780.o
++
+Index: linux-2.6.30.9/drivers/lcd-linux/cgram/default.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/cgram/default.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,37 @@
++/* default.h
++ *
++ *
++ *
++ * Default user defined characters for lcdmod.
++ *
++ * Copyright (C) by Michael McLellan (mikey@cs.auckland.ac.nz)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *
++ */
++
++static void init_charmap(void)
++{
++}
++
++static unsigned char cg0[] = { 0x1f, 0x1f, 0x11, 0x0f, 0x11, 0x1e, 0x01, 0x1f };
++static unsigned char cg1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f };
++static unsigned char cg2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f };
++static unsigned char cg3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f };
++static unsigned char cg4[] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg5[] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg6[] = { 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg7[] = { 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+Index: linux-2.6.30.9/drivers/lcd-linux/charmap.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/charmap.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,79 @@
++/* charmap.h
++ *
++ *
++ *
++ * Character mapping for HD44780 devices by Mark Haemmerling <mail@markh.de>.
++ *
++ * Translates ISO 8859-1 to HD44780 charset.
++ * HD44780 charset reference: http://markh.de/hd44780-charset.png
++ *
++ * Initial table taken from lcd.o Linux kernel driver by
++ * Nils Faerber <nilsf@users.sourceforge.net>. Thanks!
++ *
++ * This file is released under the GNU General Public License. Refer to the
++ * COPYING file distributed with this package.
++ *
++ * Following translations are being performed:
++ * - maps umlaut accent characters to the corresponding umlaut characters
++ * - maps other accent characters to the characters without accents
++ * - maps beta (=ringel-S), micro and Yen
++ *
++ * Alternative mappings:
++ * - #112 ("p") -> #240 (large "p"), orig. mapped -> #112
++ * - #113 ("q") -> #241 (large "q"), orig. mapped -> #113
++ *
++ * HD44780 misses backslash
++ *
++ */
++
++static unsigned char charmap[] = {
++
++/* 0 - 31 */
++ 0, 1, 2, 3, 4, 5, 6, 7,
++ 8, 9, 10, 11, 12, 13, 14, 15,
++ 16, 17, 18, 19, 20, 21, 22, 23,
++ 24, 25, 26, 27, 28, 29, 30, 31,
++
++/* 32 - 63 */
++ 32, 33, 34, 35, 36, 37, 38, 39,
++ 40, 41, 42, 43, 44, 45, 46, 47,
++ 48, 49, 50, 51, 52, 53, 54, 55,
++ 56, 57, 58, 59, 60, 61, 62, 63,
++
++/* 64 - 95 */
++ 64, 65, 66, 67, 68, 69, 70, 71,
++ 72, 73, 74, 75, 76, 77, 78, 79,
++ 80, 81, 82, 83, 84, 85, 86, 87,
++ 88, 89, 90, 91, 47, 93, 94, 95,
++
++/* 96 - 127 */
++ 96, 97, 98, 99, 100, 101, 102, 103,
++104, 105, 106, 107, 108, 109, 110, 111,
++112, 113, 114, 115, 116, 117, 118, 119,
++120, 121, 122, 123, 124, 125, 126, 127,
++
++/* 128 - 159 */
++128, 129, 130, 131, 132, 133, 134, 135,
++136, 137, 138, 139, 140, 141, 142, 143,
++144, 145, 146, 147, 148, 149, 150, 151,
++152, 153, 154, 155, 156, 157, 158, 159,
++
++/* 160 - 191 */
++160, 33, 236, 237, 164, 92, 124, 167,
++ 34, 169, 170, 171, 172, 173, 174, 175,
++223, 177, 178, 179, 39, 249, 247, 165,
++ 44, 185, 186, 187, 188, 189, 190, 63,
++
++/* 192 - 223 */
++ 65, 65, 65, 65, 225, 65, 65, 67,
++ 69, 69, 69, 69, 73, 73, 73, 73,
++ 68, 78, 79, 79, 79, 79, 239, 120,
++ 48, 85, 85, 85, 245, 89, 240, 226,
++
++/* 224 - 255 */
++ 97, 97, 97, 97, 225, 97, 97, 99,
++101, 101, 101, 101, 105, 105, 105, 105,
++111, 110, 111, 111, 111, 111, 239, 253,
++ 48, 117, 117, 117, 245, 121, 240, 255
++
++};
+Index: linux-2.6.30.9/drivers/lcd-linux/commands.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/commands.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,77 @@
++/* commands.h
++ *
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * HD44780 commands.
++ *
++ * Copyright (C) 2004 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef HD44780_COMMANDS_H
++#define HD44780_COMMANDS_H
++
++/*** HD44780 Command Set ***/
++
++/* Clear Display*/
++#define CLR_DISP 0x01 /* Clear entire display; cursor at row 0, column 0 */
++
++/* Return Home */
++#define RET_HOME 0x02 /* Cursor at row 0, column 0; display content doesn't change */
++
++/* Entry Mode Set */
++#define ENTRY_MODE_SET 0x04
++#define DISP_SHIFT_ON (ENTRY_MODE_SET | 0x01) /* Shift display, not cursor after data write */
++#define DISP_SHIFT_OFF (ENTRY_MODE_SET | 0x00) /* Shift cursor, not display after data write */
++#define CURS_INC (ENTRY_MODE_SET | 0x02) /* Shift on the right after data read/write */
++#define CURS_DEC (ENTRY_MODE_SET | 0x00) /* Shift on the left after data read/write */
++
++/* Display on/off Control */
++#define DISP_ONOFF_CNTR 0x08
++#define BLINK_ON (DISP_ONOFF_CNTR | 0x01) /* Cursor blinking on */
++#define BLINK_OFF (DISP_ONOFF_CNTR | 0x00) /* Cursor blinking off */
++#define CURS_ON (DISP_ONOFF_CNTR | 0x02) /* Display Cursor */
++#define CURS_OFF (DISP_ONOFF_CNTR | 0x00) /* Hide Cursor */
++#define DISP_ON (DISP_ONOFF_CNTR | 0x04) /* Turn on display updating */
++#define DISP_OFF (DISP_ONOFF_CNTR | 0x00) /* Freeze display content */
++
++/* Cursor or Display Shift */
++#define CURS_DISP_SHIFT 0x10
++#define SHIFT_RIGHT (CURS_DISP_SHIFT | 0x04) /* Shift on the right */
++#define SHIFT_LEFT (CURS_DISP_SHIFT | 0x00) /* Shift on the left */
++#define SHIFT_DISP (CURS_DISP_SHIFT | 0x08) /* Shift display */
++#define SHIFT_CURS (CURS_DISP_SHIFT | 0x00) /* Shift cursor */
++
++/* Function Set */
++#define FUNC_SET 0x20
++#define FONT_5X10 (FUNC_SET | 0x04) /* Select 5x10 dots font */
++#define FONT_5X8 (FUNC_SET | 0x00) /* Select 5x8 dots font */
++#define DISP_2_LINES (FUNC_SET | 0x08) /* Select 2 lines display (only 5x8 font allowed) */
++#define DISP_1_LINE (FUNC_SET | 0x00) /* Select 1 line display */
++#define BUS_8_BITS (FUNC_SET | 0x10) /* Set 8 data bits */
++#define BUS_4_BITS (FUNC_SET | 0x00) /* Set 4 data bits */
++
++/* Set CGRAM Address */
++#define CGRAM_IO 0x40 /* Base CGRAM address */
++
++/* Set DDRAM Address */
++#define DDRAM_IO 0x80 /* Base DDRAM address */
++
++#endif /* commands included */
+Index: linux-2.6.30.9/drivers/lcd-linux/config.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/config.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,73 @@
++/* config.h
++ *
++ *
++ *
++ * Configure file for LCD-Linux. Here you must specify your hardware setup and
++ * timings constants. The default values will probably be right for you.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *
++ */
++
++/* Setup the default user defined characters in CGRAM */
++#include "cgram/default.h"
++
++/* Don't modify the default timing constants
++ * unless you know what you are doing.
++ */
++
++/* Execution times (in microseconds) */
++#define T_READ 60 /* Read execution time (min 43 us) */
++#define T_WRITE 60 /* Write execution time (min 43 us) */
++#define T_BF 2 /* Busy flag polling time (min 1 us) */
++
++/* Timings in nanoseconds */
++#define T_AS 200 /* Address set-up time (min 140 ns) */
++#define T_EH 500 /* Enable high time (min 450 ns) */
++#define T_EL 600 /* Enable low time (min 500 ns) */
++
++/* Various constants */
++#define DFLT_NUM_CNTR 1 /* Default number of controllers the display has */
++#define DFLT_CNTR_ROWS 2 /* Default number of rows per controller */
++#define DFLT_CNTR_COLS 16 /* Default number of columns the display has */
++#define DFLT_VS_ROWS 25 /* Default number of rows for the virtual screen */
++#define DFLT_VS_COLS 80 /* Default number of columns for the virtual screen */
++#define DFLT_TABSTOP 3 /* Default length of tabs */
++#define DFLT_FLAGS (HD44780_CHECK_BF | HD44780_4BITS_BUS ) /* Default flags */
++
++#define MAX_CNTR_ROWS 4 /* The HD44780 supports up to 4 lines as a fake 2 lines mode */
++#define MAX_CNTR_COLS 80 /* The HD44780 supports up to 80 characters (1*80; 2*40; etc) */
++
++#define SETUP 4
++#define HIGH_NIBBLE_WRITE(x) (((x) >> (4-SETUP)) & (0x0f << SETUP))
++#define LOW_NIBBLE_WRITE(x) (((x) << SETUP) & (0x0f << SETUP))
++#define HIGH_NIBBLE_READ(x) (((x) & (0x0f << SETUP)) << (4-SETUP))
++#define LOW_NIBBLE_READ(x) (((x) & (0x0f << SETUP)) >> SETUP)
++
++
++#define SIMONE_LCD_RS EP93XX_GPIO_LINE_A(2) /* OUT */
++#define SIMONE_LCD_RD EP93XX_GPIO_LINE_A(3) /* OUT */
++#define SIMONE_LCD_EN EP93XX_GPIO_LINE_B(4) /* EGPIO12 OUT */
++#define SIMONE_LCD_BCKLIGHT EP93XX_GPIO_LINE_B(5) /* EGPIO13 OUT */
++
++#define SIMONE_LCD_DATA0 EP93XX_GPIO_LINE_A(4)
++#define SIMONE_LCD_DATA1 EP93XX_GPIO_LINE_A(5)
++#define SIMONE_LCD_DATA2 EP93XX_GPIO_LINE_A(6)
++#define SIMONE_LCD_DATA3 EP93XX_GPIO_LINE_A(7)
++
++
+Index: linux-2.6.30.9/drivers/lcd-linux/hd44780.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/hd44780.c 2009-11-24 02:05:29.000000000 +0100
+@@ -0,0 +1,860 @@
++/* hd44780.c
++ *
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ * Adapted to Sim.One Hardware by Nuccio Raciti (raciti.nuccio@gmail.com)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++
++#include <linux/autoconf.h>
++
++
++#ifdef CONFIG_PROC_FS
++#define USE_PROC
++#else
++#undef USE_PROC
++#endif
++
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/delay.h>
++#include <linux/fs.h>
++
++#include <asm/uaccess.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <linux/ioport.h>
++#include <asm/gpio.h>
++
++#ifdef USE_PROC
++#include <linux/proc_fs.h>
++#endif
++
++#define LCD_LINUX_MAIN
++#include <linux/hd44780.h>
++
++#include "charmap.h"
++#include "commands.h"
++#include "config.h"
++
++/** Function prototypes **/
++static void read_display(unsigned char *byte, unsigned char bitmask);
++static void write_display(unsigned char byte, unsigned char bitmask);
++
++/* Initialization */
++static int hd44780_validate_driver(void);
++static int hd44780_init_port(void);
++static int hd44780_cleanup_port(void);
++static int hd44780_init_display(void);
++static int hd44780_cleanup_display(void);
++
++/* Write */
++static void hd44780_address_mode(int);
++static void hd44780_clear_display(void);
++static void hd44780_write_char(unsigned int, unsigned short);
++static void hd44780_write_cgram_char(unsigned char, unsigned char *);
++
++/* Read */
++static void check_bf(unsigned char);
++static void hd44780_read_char(unsigned int, unsigned short *);
++static void hd44780_read_cgram_char(unsigned char, unsigned char *);
++
++/* Input handling */
++static int hd44780_handle_custom_char(unsigned int);
++static int hd44780_handle_custom_ioctl(unsigned int,unsigned long , unsigned int);
++
++/* Proc operations */
++#ifdef USE_PROC
++static void create_proc_entries(void);
++static void remove_proc_entries(void);
++#endif
++
++/* hd44780 access */
++#define ACCESS_TO_READ 0
++#define ACCESS_TO_WRITE 1
++#define ACCESS_TO_DATA 2
++
++/* hd44780_flags */
++#define _CHECK_BF 0 /* Do busy-flag checking */
++#define _4BITS_BUS 1 /* The bus is 4 bits long */
++#define _5X10_FONT 2 /* Use 5x10 font */
++#define CURSOR_BLINK 3 /* Make the cursor blinking */
++#define SHOW_CURSOR 4 /* Make the cursor visible */
++#define DISPLAY_ON 5 /* Display status: on or off */
++#define INC_ADDR 6 /* Increment address after data read/write */
++#define BACKLIGHT 7 /* Display backlight: on or off */
++#define CGRAM_STATE 9 /* Controller status bitmask (bits 9->15): DDRAM or CGRAM access */
++#define ESC_MASK 0x00ff0000
++#define PROC_MASK 0x0f000000
++
++#define SET_STATE(state, mask) (hd44780_flags = (hd44780_flags & ~(mask)) | ((state) & (mask)))
++#define SET_ESC_STATE(state) SET_STATE((state) << 16, ESC_MASK)
++#define SET_PROC_LEVEL(level) SET_STATE((level) << 24, PROC_MASK)
++#define ESC_STATE ((hd44780_flags & ESC_MASK) >> 16)
++#define PROC_LEVEL ((hd44780_flags & PROC_MASK) >> 24)
++
++/* globals */
++static unsigned int disp_size; /* Display size (rows*columns) */
++static unsigned int disp_offset[1]; /* Physical cursor position on the display */
++static unsigned long hd44780_flags; /* Driver flags for internal use only */
++
++static struct lcd_parameters par = {
++ .name = HD44780_STRING,
++ .minor = HD44780_MINOR,
++ .flags = DFLT_FLAGS,
++ .tabstop = DFLT_TABSTOP,
++ .num_cntr = 1,
++ .cntr_rows = DFLT_CNTR_ROWS,
++ .cntr_cols = DFLT_CNTR_COLS,
++ .vs_rows = DFLT_VS_ROWS,
++ .vs_cols = DFLT_VS_COLS,
++ .cgram_chars = 8,
++ .cgram_bytes = 8,
++ .cgram_char0 = 0,
++};
++/* End of globals */
++
++#ifdef MODULE
++#include <linux/device.h>
++MODULE_ALIAS_CHARDEV(LCD_MAJOR, HD44780_MINOR);
++#include <linux/kmod.h>
++
++static unsigned short flags = DFLT_FLAGS;
++static unsigned short tabstop = DFLT_TABSTOP;
++static unsigned short cntr_rows = DFLT_CNTR_ROWS;
++static unsigned short cntr_cols = DFLT_CNTR_COLS;
++static unsigned short vs_rows = DFLT_VS_ROWS;
++static unsigned short vs_cols = DFLT_VS_COLS;
++static unsigned short minor = HD44780_MINOR;
++
++MODULE_DESCRIPTION("LCD SimOne driver for HD44780 compatible controllers.");
++MODULE_AUTHOR("Nuccio Raciti (raciti.nuccio@gmail.com)");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++module_param(flags, ushort, 0444);
++module_param(cntr_rows, ushort, 0444);
++module_param(cntr_cols, ushort, 0444);
++module_param(vs_rows, ushort, 0444);
++module_param(vs_cols, ushort, 0444);
++module_param(tabstop, ushort, 0444);
++module_param(minor, ushort, 0444);
++
++MODULE_PARM_DESC(flags, "Various flags (see Documentation)");
++MODULE_PARM_DESC(cntr_rows, "Number of rows per controller on the LCD: 1, 2, 4 (default: " string(DFLT_CNTR_ROWS) ")");
++MODULE_PARM_DESC(cntr_cols, "Number of columns on the LCD (default: " string(DFLT_CNTR_COLS) ", max: " string(MAX_CNTR_COLS) ")");
++MODULE_PARM_DESC(vs_rows, "Number of rows of the virtual screen (default: " string(DFLT_VS_ROWS) ")");
++MODULE_PARM_DESC(vs_cols, "Number of columns of the virtual screen (default: " string(DFLT_VS_COLS) ")");
++MODULE_PARM_DESC(tabstop, "Tab character length (default: " string(DFLT_TABSTOP) ")");
++MODULE_PARM_DESC(minor, "Assigned minor number (default: " string(HD44780_MINOR) ")");
++#else
++
++/*
++ * Parse boot command line
++ *
++ * hd44780=cntr_rows,cntr_cols,vs_rows,vs_cols,flags,minor,tabstop
++ */
++static int __init hd44780_boot_init(char *cmdline)
++{
++ char *str = cmdline;
++ int idx = 0;
++ unsigned short *args[] = {
++ &par.cntr_rows,
++ &par.cntr_cols,
++ &par.vs_rows,
++ &par.vs_cols,
++ &par.flags,
++ &par.num_cntr,
++ &par.minor,
++ &par.tabstop,
++ };
++
++ while (*cmdline && idx < (sizeof(args)/sizeof(unsigned short *))) {
++ switch (*str) {
++ case ',':
++ *str++ = 0;
++ case 0:
++ if (strlen(cmdline))
++ *args[idx] = simple_strtoul(cmdline, NULL, 0);
++ ++idx;
++ cmdline = str;
++ break;
++ default:
++ ++str;
++ break;
++ }
++ }
++
++ return (1);
++}
++
++__setup("hd44780=", hd44780_boot_init);
++#endif /* MODULE */
++
++/* Macros for iterator handling */
++static inline unsigned int iterator_inc_(unsigned int iterator, const unsigned int module)
++{
++ return ((++iterator)%module);
++}
++
++static inline unsigned int iterator_dec_(unsigned int iterator, const unsigned int module)
++{
++ return (iterator ? --iterator : module-1);
++}
++
++#define iterator_inc(iterator, module) (iterator = iterator_inc_(iterator, module))
++#define iterator_dec(iterator, module) (iterator = iterator_dec_(iterator, module))
++
++static inline void set_lines(unsigned char bitmask)
++{
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ if(bitmask & ACCESS_TO_WRITE ) {
++ gpio_direction_output (SIMONE_LCD_DATA0 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA1 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA2 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA3 , 0);
++ gpio_set_value(SIMONE_LCD_RD, 0); /* Write */
++ } else {
++
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++ gpio_set_value(SIMONE_LCD_RD, 1); /* Read */
++ }
++
++ if(bitmask & ACCESS_TO_DATA )
++ gpio_set_value(SIMONE_LCD_RS, 1); /* Data */
++ else
++ gpio_set_value(SIMONE_LCD_RS, 0); /* Cmds*/
++
++ if(test_bit(BACKLIGHT, &hd44780_flags))
++ gpio_set_value(SIMONE_LCD_BCKLIGHT, 1);
++ else
++ gpio_set_value(SIMONE_LCD_BCKLIGHT, 0);
++}
++
++
++/* Low level read from the display */
++static inline unsigned char __read_display(unsigned char bitmask)
++{
++ unsigned char byte;
++
++ set_lines (bitmask);
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1); /* Enable */
++ ndelay(T_EH);/* Enable high time */
++
++ byte = (gpio_get_value(SIMONE_LCD_DATA0) << 4);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA1) << 5);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA2) << 6);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA3) << 7);
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ ndelay(T_EL); /* Enable low time */
++
++
++ return (byte);
++
++}
++
++/* Low level write to the display */
++static inline void __write_display(unsigned char data, unsigned char bitmask)
++{
++ set_lines(bitmask);
++
++ if(data & 0x10)
++ gpio_set_value(SIMONE_LCD_DATA0, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA0, 0);
++
++ if(data & 0x20)
++ gpio_set_value(SIMONE_LCD_DATA1, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA1, 0);
++
++ if(data & 0x40)
++ gpio_set_value(SIMONE_LCD_DATA2, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA2, 0);
++
++ if(data & 0x80)
++ gpio_set_value(SIMONE_LCD_DATA3, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA3, 0);
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1);
++ ndelay(T_EH); /* Enable high time */
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++ ndelay(T_EL); /* Enable low time */
++}
++
++
++/* Read data from the display (4 bit bus, check busy flag) */
++static void read_display (unsigned char *byte,unsigned char bitmask)
++{
++ if(bitmask) check_bf(bitmask);
++ *byte = HIGH_NIBBLE_READ(__read_display(bitmask));
++ if(bitmask) check_bf(bitmask);
++ *byte |= LOW_NIBBLE_READ(__read_display(bitmask));
++}
++
++
++/* Output commands or data to the display (4 bit bus, check busy flag) */
++static void write_display(unsigned char byte,unsigned char bitmask)
++{
++ check_bf(bitmask);
++ __write_display(HIGH_NIBBLE_WRITE(byte),bitmask);
++ check_bf(bitmask);
++ __write_display(LOW_NIBBLE_WRITE(byte),bitmask);
++}
++
++
++/* Read Address Counter AC from the display */
++static unsigned char read_ac(unsigned char bitmask)
++{
++ unsigned char byte;
++
++ read_display(&byte, bitmask);
++
++ return (byte);
++}
++
++
++/* Do busy-flag check */
++static void check_bf(unsigned char bitmask)
++{
++ unsigned int timeout = 20;
++ static unsigned char do_check_bf = 5;
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++
++ gpio_set_value(SIMONE_LCD_RD, 1); /* Read */
++ gpio_set_value(SIMONE_LCD_RS, 0); /* Instru */
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1); /* Enable */
++ ndelay(T_EH);/* Enable high time */
++
++ do{
++ udelay(T_BF);
++ } while (gpio_get_value(SIMONE_LCD_DATA3) && --timeout);
++
++ if (! timeout) {
++ if (! --do_check_bf) {
++ printk(KERN_NOTICE "hd44780 error: is the LCD connected?\n");
++ }
++ }
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ ndelay(T_EL); /* Enable low time */
++}
++
++/* Send commands to the display */
++static void write_command(unsigned char command)
++{
++ write_display(command, ACCESS_TO_WRITE);
++
++ if (command <= 0x03)
++ mdelay(2);
++}
++
++static inline void set_cursor(unsigned int offset)
++{
++ unsigned int disp_number = offset/disp_size;
++ unsigned int local_offset = offset%disp_size;
++
++ if (disp_offset[disp_number] != local_offset || test_bit(CGRAM_STATE+disp_number, &hd44780_flags)) {
++ unsigned int disp_row = local_offset/par.cntr_cols;
++ unsigned int disp_column = local_offset%par.cntr_cols;
++
++ write_command(DDRAM_IO | ((disp_row%2)*0x40) | (((disp_row >= 2)*par.cntr_cols)+disp_column));
++ clear_bit(CGRAM_STATE+disp_number, &hd44780_flags);
++ disp_offset[disp_number] = local_offset;
++ }
++}
++
++/* HD44780 DDRAM addresses are consecutive only when
++ * the cursor moves on the same row of the display.
++ * Every time the row of the cursor changes we invalidate
++ * the cursor position to force hardware cursor repositioning.
++ */
++static inline void mov_cursor(unsigned int disp_number)
++{
++ if (test_bit(INC_ADDR, &hd44780_flags)) {
++ iterator_inc(disp_offset[disp_number], disp_size);
++ if (disp_offset[disp_number]%par.cntr_cols == 0)
++ disp_offset[disp_number] = disp_size;
++ } else {
++ iterator_dec(disp_offset[disp_number], disp_size);
++ if (disp_offset[disp_number]%par.cntr_cols == par.cntr_cols-1)
++ disp_offset[disp_number] = disp_size;
++ }
++}
++
++static struct lcd_driver hd44780 = {
++ .read_char = hd44780_read_char,
++ .read_cgram_char = hd44780_read_cgram_char,
++ .write_char = hd44780_write_char,
++ .write_cgram_char = hd44780_write_cgram_char,
++ .address_mode = hd44780_address_mode,
++ .clear_display = hd44780_clear_display,
++ .validate_driver = hd44780_validate_driver,
++ .init_display = hd44780_init_display,
++ .cleanup_display = hd44780_cleanup_display,
++ .init_port = hd44780_init_port,
++ .cleanup_port = hd44780_cleanup_port,
++ .handle_custom_char = hd44780_handle_custom_char,
++ .handle_custom_ioctl = hd44780_handle_custom_ioctl,
++
++ .charmap = charmap,
++};
++
++static void hd44780_read_char(unsigned int offset, unsigned short *data)
++{
++ unsigned int disp_number = offset/disp_size;
++ unsigned char tmp;
++
++ set_cursor(offset);
++ read_display(&tmp, ACCESS_TO_DATA);
++ *data = tmp;
++ mov_cursor(disp_number);
++}
++
++static void hd44780_read_cgram_char(unsigned char index, unsigned char *pixels)
++{
++ unsigned int i;
++
++ write_command(CGRAM_IO | (index << 3));
++ set_bit(CGRAM_STATE+0, &hd44780_flags);
++
++ for (i = 0; i < 8; ++i) {
++ read_display(pixels+i, ACCESS_TO_DATA );
++ pixels[i] &= 0x1f;
++ }
++}
++
++static void hd44780_write_char(unsigned int offset, unsigned short data)
++{
++ unsigned int disp_number = offset/disp_size;
++
++ set_cursor(offset);
++ write_display(data & 0xff, ACCESS_TO_WRITE | ACCESS_TO_DATA );
++ mov_cursor(disp_number);
++}
++
++static void hd44780_write_cgram_char(unsigned char index, unsigned char *pixels)
++{
++ unsigned int i;
++
++ /* Move address pointer to index in CGRAM */
++ write_command(CGRAM_IO | (index << 3));
++
++ set_bit(CGRAM_STATE+0, &hd44780_flags);
++
++ for (i = 0; i < 8; ++i) {
++ pixels[i] &= 0x1f;
++ write_display(pixels[i], ACCESS_TO_WRITE | ACCESS_TO_DATA );
++ }
++}
++
++/* Increment/decrement address mode after a data read/write */
++static void hd44780_address_mode(int mode)
++{
++ if (mode > 0 && ! test_bit(INC_ADDR, &hd44780_flags)) {
++ write_command(CURS_INC | DISP_SHIFT_OFF);
++ set_bit(INC_ADDR, &hd44780_flags);
++ } else if (mode < 0 && test_bit(INC_ADDR, &hd44780_flags)) {
++ write_command(CURS_DEC | DISP_SHIFT_OFF);
++ clear_bit(INC_ADDR, &hd44780_flags);
++ }
++}
++
++static void hd44780_clear_display(void)
++{
++ write_command(CLR_DISP);
++ if (! test_bit(INC_ADDR, &hd44780_flags))
++ write_command(CURS_DEC | DISP_SHIFT_OFF);
++ memset(disp_offset, 0, sizeof(disp_offset));
++}
++
++static int hd44780_validate_driver(void)
++{
++ if (par.cntr_rows != 1 && par.cntr_rows != 2 && par.cntr_rows != 4)
++ par.cntr_rows = DFLT_CNTR_ROWS;
++
++ if (par.cntr_rows != 1)
++ par.flags &= ~HD44780_5X10_FONT;
++
++
++ if (! par.cntr_cols || par.cntr_cols > MAX_CNTR_COLS/par.cntr_rows)
++ par.cntr_cols = MAX_CNTR_COLS/par.cntr_rows;
++
++ disp_size = par.cntr_rows*par.cntr_cols;
++
++ /* These parameters depend on the hardware and cannot be changed */
++ par.cgram_chars = 8;
++ par.cgram_bytes = 8;
++ par.cgram_char0 = 0;
++
++ return (0);
++}
++
++/* Send init commands to the display */
++static void write_init_command(void)
++{
++ unsigned char command = 0x30;
++
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ mdelay(20); /* Wait more than 4.1 ms */
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ udelay(200); /* Wait more than 100 us */
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ udelay(200); /* Wait more than 100 us */
++ command = BUS_4_BITS;
++ command |= ((par.cntr_rows == 1) ? DISP_1_LINE : DISP_2_LINES);
++ command |= (test_bit(_5X10_FONT, &hd44780_flags) ? FONT_5X10 : FONT_5X8);
++ write_command(command);
++ /* set_bit(BACKLIGHT, &hd44780_flags); */
++}
++
++static int hd44780_init_display(void)
++{
++ if (par.flags & HD44780_CHECK_BF)
++ set_bit(_CHECK_BF, &hd44780_flags);
++ else
++ clear_bit(_CHECK_BF, &hd44780_flags);
++
++ if (par.flags & HD44780_4BITS_BUS)
++ set_bit(_4BITS_BUS, &hd44780_flags);
++ else
++ clear_bit(_4BITS_BUS, &hd44780_flags);
++
++ if (par.flags & HD44780_5X10_FONT)
++ set_bit(_5X10_FONT, &hd44780_flags);
++ else
++ clear_bit(_5X10_FONT, &hd44780_flags);
++
++ write_init_command();
++
++ hd44780_clear_display();
++ hd44780_address_mode(1);
++ write_command(DISP_ON | CURS_OFF | BLINK_OFF);
++ set_bit(DISPLAY_ON, &hd44780_flags);
++ clear_bit(SHOW_CURSOR, &hd44780_flags);
++ clear_bit(CURSOR_BLINK, &hd44780_flags);
++
++ /* Set the CGRAM to default values */
++ hd44780_write_cgram_char(0, cg0);
++ hd44780_write_cgram_char(1, cg1);
++ hd44780_write_cgram_char(2, cg2);
++ hd44780_write_cgram_char(3, cg3);
++ hd44780_write_cgram_char(4, cg4);
++ hd44780_write_cgram_char(5, cg5);
++ hd44780_write_cgram_char(6, cg6);
++ hd44780_write_cgram_char(7, cg7);
++ init_charmap();
++
++ return (0);
++}
++
++static int hd44780_cleanup_display(void)
++{
++ hd44780_clear_display();
++
++ return (0);
++}
++
++static int hd44780_init_port(void)
++{
++ gpio_direction_output (SIMONE_LCD_BCKLIGHT, 0);
++ gpio_direction_output (SIMONE_LCD_RD , 0);
++ gpio_direction_output (SIMONE_LCD_RS , 0);
++ gpio_direction_output (SIMONE_LCD_EN , 0);
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ return (0);
++}
++
++static int hd44780_cleanup_port(void)
++{
++
++ gpio_direction_input (SIMONE_LCD_BCKLIGHT);
++ gpio_direction_input (SIMONE_LCD_RD);
++ gpio_direction_input (SIMONE_LCD_RS);
++ gpio_direction_input (SIMONE_LCD_EN);
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++ return (0);
++}
++
++static void display_attr(unsigned char input)
++{
++ unsigned char command;
++
++ switch (ESC_STATE) {
++ case 'a': /* Turn on/off the display cursor */
++ if (input == '1')
++ set_bit(SHOW_CURSOR, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(SHOW_CURSOR, &hd44780_flags);
++ break;
++ case 'b': /* Turn on/off the display cursor blinking */
++ if (input == '1')
++ set_bit(CURSOR_BLINK, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(CURSOR_BLINK, &hd44780_flags);
++ break;
++ case 'h': /* Turn on/off the display */
++ if (input == '1')
++ set_bit(DISPLAY_ON, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(DISPLAY_ON, &hd44780_flags);
++ break;
++ }
++
++ command = (test_bit(DISPLAY_ON, &hd44780_flags) ? DISP_ON : DISP_OFF);
++ command |= (test_bit(SHOW_CURSOR, &hd44780_flags) ? CURS_ON : CURS_OFF);
++ command |= (test_bit(CURSOR_BLINK, &hd44780_flags) ? BLINK_ON : BLINK_OFF);
++
++ if (ESC_STATE == 'h')
++ write_command(command);
++}
++
++static int hd44780_handle_custom_char(unsigned int _input)
++{
++ unsigned char input = _input & 0xff;
++
++ if (_input & (~0xff)) {
++ switch (ESC_STATE) {
++ case 'a': /* Turn on/off the display cursor */
++ case 'b': /* Turn on/off the display cursor blinking */
++ case 'h': /* Turn on/off the the display */
++ display_attr(input);
++ return (0);
++ case 'l': /* Turn on/off the backlight */
++ if (input == '1')
++ set_bit(BACKLIGHT, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(BACKLIGHT, &hd44780_flags);
++ read_ac(ACCESS_TO_READ);
++ return (0);
++ }
++ }
++
++ switch (input) {
++ case 'a': /* Turn on/off the display cursor */
++ case 'b': /* Turn on/off the display cursor blinking */
++ case 'h': /* Turn on/off the display */
++ case 'l': /* Turn on/off the backlight */
++ SET_ESC_STATE(input);
++ return (1);
++ case 'd': /* Shift display cursor Right */
++ write_command(SHIFT_CURS | SHIFT_RIGHT);
++ return (0);
++ case 'e': /* Shift display cursor Left */
++ write_command(SHIFT_CURS | SHIFT_LEFT);
++ return (0);
++ case 'f': /* Shift display Right */
++ write_command(SHIFT_DISP | SHIFT_RIGHT);
++ return (0);
++ case 'g': /* Shift display Left */
++ write_command(SHIFT_DISP | SHIFT_LEFT);
++ return (0);
++ }
++
++ return (-1);
++}
++
++static int hd44780_handle_custom_ioctl(unsigned int num, unsigned long arg, unsigned int user_space)
++{
++ unsigned char *buffer = (unsigned char *)arg;
++
++ if (num != HD44780_READ_AC)
++ return (-ENOIOCTLCMD);
++
++ if (user_space)
++ put_user(read_ac(ACCESS_TO_READ), buffer);
++ else
++ buffer[0] = read_ac(ACCESS_TO_READ);
++
++ return (0);
++}
++
++#ifdef USE_PROC
++static int hd44780_proc_status(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++
++ /* Print display configuration */
++ temp += sprintf(temp,
++ "Interface:\t%u bits\n"
++ "Display rows:\t%d\n"
++ "Display cols:\t%d\n"
++ "Screen rows:\t%d\n"
++ "Screen cols:\t%d\n"
++ "Read:\t\t%sabled\n"
++ "Busy flag chk:\t%sabled\n"
++ "Assigned minor:\t%u\n",
++ (test_bit(_4BITS_BUS, &hd44780_flags) ? 4 : 8),
++ par.cntr_rows, par.cntr_cols,
++ par.vs_rows, par.vs_cols,
++ (hd44780.read_char ? "En" : "Dis"),
++ (test_bit(_CHECK_BF, &hd44780_flags) ? "En" : "Dis"),
++ par.minor);
++
++ return (temp-buffer);
++}
++
++static int hd44780_proc_cgram(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ unsigned int i;
++
++ temp += sprintf(temp, "static void init_charmap(void)\n{\n"
++ "\t/*\n"
++ "\t * charmap[char mapped to cg0] = 0;\n"
++ "\t * charmap[char mapped to cg1] = 1;\n"
++ "\t * charmap[char mapped to cg2] = 2;\n"
++ "\t * charmap[char mapped to cg3] = 3;\n"
++ "\t * charmap[char mapped to cg4] = 4;\n"
++ "\t * charmap[char mapped to cg5] = 5;\n"
++ "\t * charmap[char mapped to cg6] = 6;\n"
++ "\t * charmap[char mapped to cg7] = 7;\n"
++ "\t */\n"
++ "}\n\n");
++
++ for (i = 0; i < 8; ++i) {
++ unsigned char cgram_buffer[8];
++ unsigned int j;
++
++ temp += sprintf(temp, "static unsigned char cg%u[] = { ", i);
++ hd44780_read_cgram_char(i, cgram_buffer);
++ for (j = 0; j < 8; ++j)
++ temp += sprintf(temp, "0x%.2x%s", cgram_buffer[j], (j == 7 ? " };\n" : ", "));
++ }
++
++ return (temp-buffer);
++}
++
++static void create_proc_entries(void)
++{
++ int count = 0;
++
++ SET_PROC_LEVEL(count);
++ if (create_proc_read_entry("status", 0, hd44780.driver_proc_root, hd44780_proc_status, NULL) == NULL) {
++ printk(KERN_ERR "hd44780: cannot create /proc/lcd/%s/status\n", par.name);
++ return;
++ }
++ SET_PROC_LEVEL(++count);
++ if (hd44780.read_cgram_char) {
++ if (create_proc_read_entry("cgram.h", 0, hd44780.driver_proc_root, hd44780_proc_cgram, NULL) == NULL) {
++ printk(KERN_ERR "hd44780: cannot create /proc/lcd/%s/cgram.h\n", par.name);
++ return;
++ }
++ SET_PROC_LEVEL(++count);
++ }
++}
++
++static void remove_proc_entries(void)
++{
++ switch (PROC_LEVEL) {
++ case 2:
++ remove_proc_entry("cgram.h", hd44780.driver_proc_root);
++ case 1:
++ remove_proc_entry("status", hd44780.driver_proc_root);
++ }
++ SET_PROC_LEVEL(0);
++}
++#endif
++
++/* Initialization */
++static int __init hd44780_init_module(void)
++{
++ int ret;
++
++#ifdef MODULE
++#ifdef NOT_DEF
++ if ((ret = request_module("lcd-linux"))) {
++ if (ret != -ENOSYS) {
++ printk(KERN_ERR "hd44780: failure while loading module lcd-linux\n");
++ return (ret);
++ }
++ printk(KERN_ERR "hd44780: your kernel does not have kmod or kerneld support;\n");
++ printk(KERN_ERR "hd44780: remember to load the lcd-linux module before\n");
++ printk(KERN_ERR "hd44780: loading the hd44780 module\n");
++ }
++#endif
++ if (flags != DFLT_FLAGS) par.flags = flags;
++ if (tabstop != DFLT_TABSTOP) par.tabstop = tabstop;
++ if (cntr_rows != DFLT_CNTR_ROWS) par.cntr_rows = cntr_rows;
++ if (cntr_cols != DFLT_CNTR_COLS) par.cntr_cols = cntr_cols;
++ if (vs_rows != DFLT_VS_ROWS) par.vs_rows = vs_rows;
++ if (vs_cols != DFLT_VS_COLS) par.vs_cols = vs_cols;
++ if (minor != HD44780_MINOR) par.minor = minor;
++#endif
++
++ lcd_driver_setup(&hd44780);
++ if ((ret = lcd_register_driver(&hd44780, &par)))
++ return (ret);
++
++#ifdef USE_PROC
++ if (hd44780.driver_proc_root)
++ create_proc_entries();
++#endif
++
++ printk(KERN_INFO "HD44780 driver (LCD-Linux" HD44780_VERSION ")\n");
++ printk(KERN_INFO "Nuccio Raciti <raciti.nuccio@gmail.com>\n");
++
++
++ return (0);
++}
++
++/* Cleanup */
++
++static void __exit hd44780_cleanup_module(void)
++{
++#ifdef USE_PROC
++ if (hd44780.driver_proc_root)
++ remove_proc_entries();
++#endif
++
++ lcd_unregister_driver(&hd44780, &par);
++}
++
++module_init(hd44780_init_module)
++module_exit(hd44780_cleanup_module)
+Index: linux-2.6.30.9/drivers/lcd-linux/lcd-linux.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/lcd-linux.c 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,2892 @@
++/* lcd-linux.c
++ *
++ *
++ *
++ * Software layer to drive LCD displays under Linux.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/version.h>
++
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
++#endif
++
++#ifndef LINUX_VERSION_CODE
++#error - LINUX_VERSION_CODE undefined in 'linux/version.h'
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/autoconf.h>
++#else
++#include <linux/config.h>
++#endif
++#ifdef CONFIG_PROC_FS
++#define USE_PROC
++#else
++#undef USE_PROC
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++#include <linux/semaphore.h>
++#else
++#include <asm/semaphore.h>
++#endif
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++
++#include <linux/fs.h>
++
++#include <asm/uaccess.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/device.h>
++#endif
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/selection.h>
++#include <linux/vmalloc.h>
++
++#ifdef USE_PROC
++#include <linux/proc_fs.h>
++#endif
++
++#define LCD_LINUX_MAIN
++#include <linux/lcd-linux.h>
++
++/*** struct_flags ***/
++#define NEED_WRAP 0 /* Next char will trigger a newline */
++#define DECIM 1 /* Insert mode */
++#define DECAWM 2 /* Autowrap */
++#define DECSCNM 3 /* Inverted screen */
++#define CRLF 4 /* Follow lf, vt, ff, with a cr */
++#define INC_CURS_POS 5 /* Increment cursor position after data read/write */
++#define QUES 6 /* CSI Esc sequence contains a question mark */
++#define USER_SPACE 7 /* If set, the buffer pointed by arg in do_lcd_ioctl() is
++ * assumed to be in user space otherwise it is in kernel space */
++#define NULL_CHARMAP 8 /* The driver doesn't provide a charmap so the
++ * lcd-linux layer provides one*/
++#define CAN_DO_COLOR 9 /* The display is color capable */
++#define WITH_ATTR 10 /* If set, the void * buffer in do_lcd_read/write() contains
++ * attributes and therefore is an unsigned short * otherwise it
++ * is an unsigned char *
++ */
++
++/*** attributes ***/
++#define I_MASK 0x03 /* Intensity (0 = low, 1 = normal, 2 = bright) */
++#define ULINE 0x04 /* Underlined text */
++#define REVERSE 0x08 /* Reversed video text */
++#define BLINK 0x80 /* Blinking text */
++
++/*** Color attributes ***/
++#define FG_MASK 0x0f /* Foreground color */
++#define BG_MASK 0xf0 /* Background color */
++
++/* input states */
++#define NORMAL 0x00001000 /* Normal mode */
++#define RAW 0x00002000 /* Raw mode (console emulation disabled) */
++#define SYN 0x00003000 /* Synchronous Idle mode */
++#define ESC 0x00004000 /* Escape mode */
++#define CSI 0x00005000 /* CSI escape mode */
++#define ESC_G0 0x00006000 /* G0 character set */
++#define ESC_G1 0x00007000 /* G1 character set */
++#define ESC_HASH 0x00008000 /* ESC # escape sequence */
++#define ESC_PERCENT 0x00009000 /* ESC % escape sequence */
++#define ARG 0x0000a000 /* Waiting for arguments for the lcd-linux layer */
++#define ARG_DRIVER 0x0000b000 /* Waiting for arguments for the display driver */
++#define INPUT_MASK 0x0000f000
++#define ESC_MASK 0x00ff0000
++#define INIT_MASK 0x0f000000
++#define PROC_MASK 0xf0000000
++
++#define SET_STATE(p, state, mask) ((p)->struct_flags = ((p)->struct_flags & ~(mask)) | ((state) & (mask)))
++#define SET_INPUT_STATE(p, state) SET_STATE(p, state, INPUT_MASK)
++#define SET_ESC_STATE(p, state) SET_STATE(p, (state) << 16, ESC_MASK)
++#define SET_INIT_LEVEL(p, level) SET_STATE(p, (level) << 24, INIT_MASK)
++#define SET_PROC_LEVEL(p, level) SET_STATE(p, (level) << 28, PROC_MASK)
++#define INPUT_STATE(p) ((p)->struct_flags & INPUT_MASK)
++#define ESC_STATE(p) (((p)->struct_flags & ESC_MASK) >> 16)
++#define INIT_LEVEL(p) (((p)->struct_flags & INIT_MASK) >> 24)
++#define PROC_LEVEL(p) (((p)->struct_flags & PROC_MASK) >> 28)
++
++#define NPAR 16 /* Max number of parameters in CSI escape sequence */
++#define FLIP_BUF_SIZE (1 << 6) /* Flip buffer size (64 bytes) */
++
++struct lcd_struct {
++ struct list_head lcd_list; /* Doubly linked list */
++ struct semaphore lcd_sem; /* Locks this structure */
++ struct lcd_driver *driver; /* The driver associated to this struct */
++ struct lcd_parameters *par; /* The parameters associated to this struct */
++ unsigned long struct_flags; /* Flags for internal use only */
++ unsigned int refcount; /* Number of references to this struct */
++
++ unsigned short *display; /* The display buffer */
++
++ unsigned short *fb; /* The virtual screen framebuffer */
++ unsigned int fb_size; /* Size of the framebuffer */
++ unsigned int frame_base; /* Offset of row 0, column 0 of a frame in fb */
++ unsigned int frame_size; /* Size of the frame */
++
++ unsigned int row; /* Current row in virtual screen */
++ unsigned int col; /* Current column in virtual screen */
++ unsigned int s_offset; /* Saved cursor position in virtual screen */
++
++ unsigned int top; /* Top scroll row in virtual screen */
++ unsigned int bot; /* Bottom scroll row in virtual screen */
++
++ int esc_args; /* Number of arguments for a normal escape sequence */
++ unsigned int csi_args[NPAR]; /* CSI parameters */
++ unsigned int index; /* Index in csi_args and counter for cgram characters generation */
++ unsigned char cgram_index; /* Index of the cgram character to be created */
++ unsigned char *cgram_buffer; /* Buffer for cgram operations in this driver */
++
++ unsigned short erase_char; /* Character to be used when erasing */
++ unsigned char attr; /* Current attributes */
++ unsigned char color; /* Color for normal intensity mode */
++ unsigned char s_color; /* Saved color for normal intensity mode */
++ unsigned char defcolor; /* Default color for normal intensity mode */
++ unsigned char ulcolor; /* Color for underline mode */
++ unsigned char halfcolor; /* Color for low intensity mode */
++ unsigned char attributes; /* Packed attributes */
++ unsigned char s_attributes; /* Saved packed attributes */
++
++ unsigned char *s_charmap; /* Saved character map for this driver */
++ unsigned char *flip_buf; /* High speed flip buffer */
++};
++
++/** Function prototypes **/
++
++/* Init/Cleanup the driver */
++static int init_driver(struct lcd_struct *);
++static int cleanup_driver(struct lcd_struct *);
++
++/* Read from/Write to the driver */
++static void read_data(struct lcd_struct *, unsigned short *);
++static void read_cgram(struct lcd_struct *, unsigned char, unsigned char *);
++static void write_data(struct lcd_struct *, unsigned short);
++static void write_cgram(struct lcd_struct *, unsigned char, unsigned char *);
++
++/* Input handlers */
++static void cr(struct lcd_struct *);
++static void lf(struct lcd_struct *);
++static void control_char(struct lcd_struct *, unsigned char);
++static void handle_csi(struct lcd_struct *, unsigned char);
++static int handle_custom_esc(struct lcd_struct *, unsigned int);
++static int handle_esc(struct lcd_struct *, unsigned char);
++static void handle_input(struct lcd_struct *, unsigned short);
++
++/* Low level file operations */
++static ssize_t do_lcd_read(struct lcd_struct *, void *, size_t);
++static ssize_t do_lcd_write(struct lcd_struct *, const void *, size_t);
++static int do_lcd_open(struct lcd_struct *);
++static int do_lcd_release(struct lcd_struct *);
++static int do_lcd_ioctl(struct lcd_struct *, unsigned int, unsigned long);
++
++/* Proc functions */
++#ifdef USE_PROC
++static void create_driver_proc_entries(struct lcd_struct *);
++static void remove_driver_proc_entries(struct lcd_struct *);
++#endif
++
++/* globals */
++static unsigned int major = LCD_MAJOR; /* Major number for LCD-Linux device */
++static unsigned short minors = LCD_MINORS; /* Minor numbers allocated for LCD-Linux */
++static LIST_HEAD(lcd_drivers); /* Registered lcd drivers */
++static struct semaphore drivers_sem; /* Locks the lcd_drivers list */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++static struct class *lcd_linux_class;
++#endif
++#ifdef USE_PROC
++static struct proc_dir_entry *lcd_proc_root;
++#endif
++/* End of globals */
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/device.h>
++MODULE_ALIAS_CHARDEV_MAJOR(LCD_MAJOR);
++#endif
++MODULE_DESCRIPTION("Software layer to drive LCD displays under Linux.");
++MODULE_AUTHOR("Mattia Jona-Lasinio <mjona@users.sourceforge.net>");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++module_param(minors, ushort, 0444);
++#else
++MODULE_PARM(minors, "h");
++#endif
++MODULE_PARM_DESC(minors, "Minor numbers allocated for LCD-Linux (default: " string(LCD_MINORS) ")");
++#else
++
++/*
++ * Parse boot command line
++ *
++ * lcd=minors
++ */
++static int __init lcd_linux_boot_init(char *cmdline)
++{
++ unsigned short args;
++
++ if ((args = simple_strtoul(cmdline, NULL, 0)))
++ minors = args;
++
++ return (1);
++}
++
++__setup("lcd=", lcd_linux_boot_init);
++#endif /* MODULE */
++
++/* Macros for iterator handling */
++static inline unsigned int iterator_inc_(unsigned int iterator, const unsigned int module)
++{
++ return ((++iterator)%module);
++}
++
++static inline unsigned int iterator_dec_(unsigned int iterator, const unsigned int module)
++{
++ return (iterator ? --iterator : module-1);
++}
++
++#define iterator_inc(iterator, module) (iterator = iterator_inc_(iterator, module))
++#define iterator_dec(iterator, module) (iterator = iterator_dec_(iterator, module))
++
++/* Uncomment the following two lines
++ * for non-atomic set_bit and clear_bit
++#define set_bit __set_bit
++#define clear_bit __clear_bit
++*/
++
++/************************************
++ * Low level routines and utilities *
++ ************************************/
++/*
++ * Set whether the address counter should be incremented
++ * or decremented after a Read/Write
++ */
++static void address_mode(struct lcd_struct *p, int mode)
++{
++ struct lcd_driver *driver = p->driver;
++
++ if (mode > 0 && ! test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (driver->address_mode)
++ driver->address_mode(mode);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++ } else if (mode < 0 && test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (driver->address_mode)
++ driver->address_mode(mode);
++ clear_bit(INC_CURS_POS, &p->struct_flags);
++ }
++}
++
++/* WARNING!! This function returns an int because if iterator is not
++ * within the visible area of the frame it returns -1
++ */
++static inline int vs_to_frame_(struct lcd_struct *p, unsigned int iterator)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row = iterator/vs_cols;
++ unsigned int col = iterator%vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ if (row < frame_base_row || row >= frame_base_row+frame_rows)
++ return (-1);
++ if (col < frame_base_col || col >= frame_base_col+frame_cols)
++ return (-1);
++
++ return ((row-frame_base_row)*frame_cols+(col-frame_base_col));
++}
++
++/* Given 'iterator' in vs, returns the offset in vs corresponding to the nearest
++ * visible offset in vs, or returns 'iterator' if it is already visible.
++ */
++static unsigned int round_vs_(struct lcd_struct *p, unsigned int iterator)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row = iterator/vs_cols;
++ unsigned int col = iterator%vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ if (row < frame_base_row)
++ row = frame_base_row;
++ else if (row >= frame_base_row+frame_rows)
++ row = frame_base_row+(frame_rows-1);
++
++ if (col < frame_base_col)
++ col = frame_base_col;
++ else if (col >= frame_base_col+frame_cols)
++ col = frame_base_col+(frame_cols-1);
++
++ return ((row*vs_cols)+col);
++}
++
++#define round_vs(p, iterator) (iterator = round_vs_(p, iterator))
++
++/*
++ * Sync the frame area starting at offset s, ending at offset e with fb content.
++ */
++static void redraw_screen(struct lcd_struct *p, unsigned int s, unsigned int e)
++{
++ unsigned int len;
++ unsigned int row = p->row, col = p->col;
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++ unsigned int frame_cols = p->par->cntr_cols;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned long flags;
++
++ if (s >= p->fb_size || e >= p->fb_size || e < s || e < p->frame_base)
++ return;
++
++ round_vs(p, s);
++ round_vs(p, e);
++
++ len = 1+e-s;
++
++ if (! inc_set)
++ s = e;
++
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++
++ flags = p->struct_flags;
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ clear_bit(DECIM, &p->struct_flags);
++ set_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, RAW);
++ if (inc_set)
++ while (len--)
++ if (vs_to_frame_(p, (p->row*vs_cols)+p->col) < 0) {
++ s += vs_cols-frame_cols;
++ len -= vs_cols-frame_cols-1;
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++ } else {
++ write_data(p, p->fb[s++]);
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++ }
++ else
++ while (len--)
++ if (vs_to_frame_(p, (p->row*vs_cols)+p->col) < 0) {
++ s -= vs_cols-frame_cols;
++ len -= vs_cols-frame_cols-1;
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++ } else {
++ write_data(p, p->fb[s--]);
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++ }
++ p->struct_flags = flags;
++
++ p->row = row; p->col = col;
++}
++
++static int make_cursor_visible(struct lcd_struct *p)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int frame_base, frame_base_row, frame_base_col;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++ unsigned int tmp = frame_cols/2;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ /* cursor always on the lowest row of the display */
++ frame_base_row = 0;
++ frame_base_col = 0;
++ if (p->row >= frame_rows)
++ frame_base_row = p->row-(frame_rows-1);
++ if (p->col >= frame_cols) {
++ frame_base_col = p->col-(frame_cols-1);
++ if (tmp) {
++ tmp = (tmp-(frame_base_col%tmp))%tmp;
++ if (frame_base_col+tmp <= vs_cols-frame_cols)
++ frame_base_col += tmp;
++ }
++ }
++ } else {
++ /* cursor always on the uppermost row of the display */
++ frame_base_row = vs_rows-frame_rows;
++ frame_base_col = vs_cols-frame_cols;
++ if (p->row < vs_rows-frame_rows)
++ frame_base_row = p->row;
++ if (p->col < vs_cols-frame_cols) {
++ frame_base_col = p->col;
++ if (tmp) {
++ tmp = frame_base_col%tmp;
++ if (frame_base_col >= tmp)
++ frame_base_col -= tmp;
++ }
++ }
++ }
++
++ frame_base = p->frame_base;
++ p->frame_base = (frame_base_row*vs_cols)+frame_base_col;
++
++ return (frame_base != p->frame_base);
++}
++
++/*
++ * Move the visible screen area at user's wish
++ */
++static void browse_screen(struct lcd_struct *p, unsigned char dir)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ switch (dir) {
++ case '1': /* Up */
++ if (! frame_base_row)
++ return;
++ --frame_base_row;
++ break;
++ case '2': /* Down */
++ if (frame_base_row >= vs_rows-frame_rows)
++ return;
++ ++frame_base_row;
++ break;
++ case '3': /* Left */
++ if (! frame_base_col)
++ return;
++ --frame_base_col;
++ break;
++ case '4': /* Right */
++ if (frame_base_col >= vs_cols-frame_cols)
++ return;
++ ++frame_base_col;
++ break;
++ default:
++ return;
++ }
++
++ p->frame_base = (frame_base_row*vs_cols)+frame_base_col;
++ redraw_screen(p, 0, p->fb_size-1);
++}
++
++static inline void __memset_short(unsigned short *buf, unsigned short c, unsigned int len)
++{
++ while (len--)
++ *buf++ = c;
++}
++
++/*
++ * A memset implementation writing to LCD instead of memory locations.
++ */
++static void lcd_memset(struct lcd_struct *p, unsigned int d, unsigned short c, unsigned int len)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! len || d >= p->fb_size)
++ return;
++
++ if (inc_set && d+len > p->fb_size)
++ len = p->fb_size-d;
++ else if (! inc_set && len > d+1)
++ len = d+1;
++
++ if (! inc_set)
++ d -= len-1;
++ __memset_short(p->fb+d, c, len);
++
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d, d+(len-1));
++}
++
++static inline void __memcpy_short(unsigned short *d, unsigned short *s, unsigned int len, int dir)
++{
++ if (dir > 0)
++ while (len--)
++ *d++ = *s++;
++ else
++ while (len--)
++ *d-- = *s--;
++}
++
++/*
++ * A memmove implementation writing to LCD instead of memory locations.
++ * Copy is done in a non destructive way. Display regions may overlap.
++ */
++static void lcd_memmove(struct lcd_struct *p, unsigned int d, unsigned int s, unsigned int len)
++{
++ if (! len || d == s || d >= p->fb_size || s >= p->fb_size)
++ return;
++
++ if (d < s) {
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (s+len > p->fb_size)
++ len = p->fb_size-s;
++ } else {
++ if (len > d+1)
++ len = d+1;
++ d -= len-1;
++ s -= len-1;
++ }
++ __memcpy_short(p->fb+d, p->fb+s, len, 1);
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d, d+(len-1));
++ } else {
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (d+len > p->fb_size)
++ len = p->fb_size-d;
++ d += len-1;
++ s += len-1;
++ } else {
++ if (len > s+1)
++ len = s+1;
++ }
++ __memcpy_short(p->fb+d, p->fb+s, len, -1);
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d-(len-1), d);
++ }
++}
++
++static void scrup(struct lcd_struct *p, unsigned int t, unsigned int b, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int d, s;
++
++ if (t+nr >= b)
++ nr = b-t-1;
++ if (b > vs_rows || t >= b || nr < 1)
++ return;
++ d = t*vs_cols;
++ s = (t+nr)*vs_cols;
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, d, s, (b-t-nr)*vs_cols);
++ lcd_memset(p, d+(b-t-nr)*vs_cols, p->erase_char, nr*vs_cols);
++ } else {
++ lcd_memmove(p, d+(b-t-nr)*vs_cols-1, s+(b-t-nr)*vs_cols-1, (b-t-nr)*vs_cols);
++ lcd_memset(p, d+(b-t)*vs_cols-1, p->erase_char, nr*vs_cols);
++ }
++}
++
++static void scrdown(struct lcd_struct *p, unsigned int t, unsigned int b, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int d, s;
++
++ if (t+nr >= b)
++ nr = b-t-1;
++ if (b > vs_rows || t >= b || nr < 1)
++ return;
++ s = t*vs_cols;
++ d = (t+nr)*vs_cols;
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, d, s, (b-t-nr)*vs_cols);
++ lcd_memset(p, s, p->erase_char, nr*vs_cols);
++ } else {
++ lcd_memmove(p, d+(b-t-nr)*vs_cols-1, s+(b-t-nr)*vs_cols-1, (b-t-nr)*vs_cols);
++ lcd_memset(p, s+nr*vs_cols-1, p->erase_char, nr*vs_cols);
++ }
++}
++
++static void lcd_insert_char(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags))
++ lcd_memmove(p, pos+nr, pos, vs_cols-p->col-nr);
++ else
++ lcd_memmove(p, pos-nr, pos, p->col-(nr-1));
++ lcd_memset(p, pos, p->erase_char, nr);
++}
++
++static void lcd_delete_char(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, pos, pos+nr, vs_cols-(p->col+nr));
++ lcd_memset(p, (p->row+1)*vs_cols-nr, p->erase_char, nr);
++ } else {
++ lcd_memmove(p, pos, pos-nr, p->col-(nr-1));
++ lcd_memset(p, (p->row*vs_cols)+(nr-1), p->erase_char, nr);
++ }
++}
++
++
++
++
++
++/******************************************************************************
++ ************************* VT 102 Emulation *************************
++ ******************************************************************************/
++
++/**********************
++ * Control characters *
++ **********************/
++static void bs(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->col)
++ --p->col;
++ } else {
++ if (p->col+1 < p->par->vs_cols)
++ ++p->col;
++ }
++}
++
++static void cr(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ p->col = (test_bit(INC_CURS_POS, &p->struct_flags) ? 0 : p->par->vs_cols-1);
++}
++
++static void lf(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->row+1 == p->bot && INPUT_STATE(p) != RAW) {
++ make_cursor_visible(p);
++ scrup(p, p->top, p->bot, 1);
++ } else if (p->row+1 < p->par->vs_rows)
++ ++p->row;
++ } else {
++ if (p->row == p->top && INPUT_STATE(p) != RAW) {
++ make_cursor_visible(p);
++ scrdown(p, p->top, p->bot, 1);
++ } else if (p->row)
++ --p->row;
++ }
++}
++
++static void ri(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->row == p->top) {
++ make_cursor_visible(p);
++ scrdown(p, p->top, p->bot, 1);
++ } else if (p->row)
++ --p->row;
++ } else {
++ if (p->row+1 == p->bot) {
++ make_cursor_visible(p);
++ scrup(p, p->top, p->bot, 1);
++ } else if (p->row+1 < p->par->vs_rows)
++ ++p->row;
++ }
++}
++
++static void ff(struct lcd_struct *p)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->driver->clear_display) {
++ p->driver->clear_display();
++ __memset_short(p->fb, p->erase_char, p->fb_size);
++ __memset_short(p->display, p->erase_char, p->frame_size);
++ p->frame_base = 0;
++ } else if (test_bit(INC_CURS_POS, &p->struct_flags))
++ lcd_memset(p, 0, p->erase_char, p->fb_size);
++ else
++ lcd_memset(p, p->fb_size-1, p->erase_char, p->fb_size);
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags))
++ p->row = p->col = 0;
++ else {
++ p->row = vs_rows-1;
++ p->col = vs_cols-1;
++ }
++}
++
++static void tab(struct lcd_struct *p)
++{
++ struct lcd_parameters *par = p->par;
++ unsigned int i, vs_cols = par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++
++ if (! par->tabstop)
++ return;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ i = par->tabstop-(p->col%par->tabstop);
++ if (p->col+i < vs_cols)
++ p->col += i;
++ } else {
++ i = p->col%par->tabstop;
++ i = (i == 0 ? par->tabstop : i);
++ if (p->col >= i)
++ p->col -= i;
++ }
++}
++
++/*
++ * Control character handler.
++ */
++static void control_char(struct lcd_struct *p, unsigned char val)
++{
++ switch (val) {
++ case 0x08: /* BS: Back Space (^H) */
++ case 0x7f: /* DEL: Delete */
++ bs(p);
++ return;
++
++ case 0x09: /* HT: Horizontal Tab (^I) */
++ tab(p);
++ return;
++
++ case 0x0c: /* FF: Form Feed (^L) */
++ ff(p);
++ return;
++
++ case 0x0a: /* LF: Line Feed (^J) */
++ case 0x0b: /* VT: Vertical Tab (^K) */
++ lf(p);
++ if (! test_bit(CRLF, &p->struct_flags))
++ return;
++
++ case 0x0d: /* CR: Carriage Return (^M) */
++ cr(p);
++ return;
++
++ case 0x16: /* SYN: Synchronous Idle (^V) */
++ SET_INPUT_STATE(p, SYN);
++ return;
++
++ case 0x1b: /* ESC: Start of escape sequence */
++ SET_INPUT_STATE(p, ESC);
++ return;
++
++ case 0x9b: /* CSI: Start of CSI escape sequence */
++ memset(p->csi_args, 0, sizeof(p->csi_args));
++ p->index = 0;
++ SET_INPUT_STATE(p, CSI);
++ return;
++ }
++}
++
++static void gotoxy(struct lcd_struct *p, int new_col, int new_row)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (new_row < 0)
++ p->row = 0;
++ else if (new_row >= vs_rows)
++ p->row = vs_rows-1;
++ else
++ p->row = new_row;
++
++ if (new_col < 0)
++ p->col = 0;
++ else if (new_col >= vs_cols)
++ p->col = vs_cols-1;
++ else
++ p->col = new_col;
++
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++}
++
++
++/******************************
++ * ECMA-48 CSI ESC- sequences *
++ ******************************/
++static void csi_at(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_insert_char(p, nr);
++}
++
++static void csi_J(struct lcd_struct *p, unsigned int action)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ switch (action) {
++ case 0: /* From cursor to end of display */
++ lcd_memset(p, pos, p->erase_char, p->fb_size-pos);
++ return;
++
++ case 1: /* From start of display to cursor */
++ lcd_memset(p, 0, p->erase_char, pos+1);
++ return;
++
++ case 2: /* Whole display */
++ lcd_memset(p, 0, p->erase_char, p->fb_size);
++ return;
++ }
++}
++
++static void csi_K(struct lcd_struct *p, unsigned int action)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row_start = p->row*vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ switch (action) {
++ case 0: /* From cursor to end of line */
++ lcd_memset(p, row_start+p->col, p->erase_char, vs_cols-p->col);
++ return;
++
++ case 1: /* From start of line to cursor */
++ lcd_memset(p, row_start, p->erase_char, p->col+1);
++ return;
++
++ case 2: /* Whole line */
++ lcd_memset(p, row_start, p->erase_char, vs_cols);
++ return;
++ }
++}
++
++static void csi_L(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->row+nr > vs_rows)
++ nr = vs_rows-p->row;
++ else if (! nr)
++ ++nr;;
++ lcd_memmove(p, (p->row+nr)*vs_cols, p->row*vs_cols, (vs_rows-p->row-nr)*vs_cols);
++ lcd_memset(p, p->row*vs_cols, p->erase_char, nr*vs_cols);
++}
++
++static void csi_M(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->row+nr > vs_rows)
++ nr = vs_rows-p->row;
++ else if (! nr)
++ ++nr;;
++ lcd_memmove(p, p->row*vs_cols, (p->row+nr)*vs_cols, (vs_rows-p->row-nr)*vs_cols);
++ lcd_memset(p, (vs_rows-nr)*vs_cols, p->erase_char, nr*vs_cols);
++}
++
++static void csi_P(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_delete_char(p, nr);
++}
++
++static void csi_X(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_memset(p, (p->row*vs_cols)+p->col, p->erase_char, nr);
++}
++
++static void csi_su(struct lcd_struct *p, unsigned char input)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (input == 'u') {
++ p->row = p->s_offset/vs_cols;
++ p->col = p->s_offset%vs_cols;
++ p->color = p->s_color;
++ p->attributes = p->s_attributes;
++ return;
++ }
++ p->s_offset = (p->row*vs_cols)+p->col;
++ p->s_color = p->color;
++ p->s_attributes = p->attributes;
++}
++
++static unsigned char build_attr(struct lcd_struct *p, unsigned char color, unsigned char intensity,
++ unsigned char blink, unsigned char underline, unsigned char reverse)
++{
++ unsigned char attr;
++
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags)) {
++ attr = color;
++ if (underline)
++ attr = (attr & BG_MASK) | p->ulcolor;
++ else if (intensity == 0)
++ attr = (attr & BG_MASK) | p->halfcolor;
++ if (reverse)
++ attr = (attr & 0x88) | ((attr & 0x70) >> 4) | ((attr & 0x07) << 4);
++ if (blink)
++ attr ^= 0x80;
++ if (intensity == 2)
++ attr ^= 0x08;
++ } else {
++ attr = intensity;
++ attr |= (underline ? ULINE : 0x00);
++ attr |= (reverse ? REVERSE : 0x00);
++ attr |= (blink ? BLINK : 0x00);
++ }
++
++ return (attr);
++}
++
++static void update_attr(struct lcd_struct *p)
++{
++ unsigned char intensity = p->attributes & 0x03;
++ unsigned char underline = (p->attributes >> 2) & 0x01;
++ unsigned char reverse = (p->attributes >> 3) & 0x01;
++ unsigned char blink = (p->attributes >> 7) & 0x01;
++ unsigned char decscnm = (p->struct_flags >> DECSCNM) & 0x01;
++
++ p->attr = build_attr(p, p->color, intensity, blink, underline, reverse^decscnm);
++ p->erase_char = (build_attr(p, p->color, 1, blink, 0, decscnm) << 8) | ' ';
++}
++
++static void default_attr(struct lcd_struct *p)
++{
++ p->attributes = 0x01;
++ p->color = p->defcolor;
++}
++
++static void lcd_invert_screen(struct lcd_struct *p, unsigned int offset, unsigned int len)
++{
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags))
++ while (len--) {
++ p->fb[offset] = (p->fb[offset] & 0x88ff) | ((p->fb[offset] & 0x7000) >> 4) | ((p->fb[offset] & 0x0700) << 4);
++ ++offset;
++ }
++ else
++ while (len--) {
++ p->fb[offset] ^= 0x0800;
++ ++offset;
++ }
++}
++
++static void csi_m(struct lcd_struct *p, unsigned int n)
++{
++ int i, arg;
++
++ for (i = 0; i <= n; ++i)
++ switch ((arg = p->csi_args[i]))
++ {
++ case 0:
++ default_attr(p);
++ break;
++
++ case 1:
++ p->attributes = (p->attributes & ~I_MASK) | 2;
++ break;
++
++ case 2:
++ p->attributes = (p->attributes & ~I_MASK) | 0;
++ break;
++
++ case 4:
++ p->attributes |= ULINE;
++ break;
++
++ case 5:
++ p->attributes |= BLINK;
++ break;
++
++ case 7:
++ p->attributes |= REVERSE;
++ break;
++
++ case 21: case 22:
++ p->attributes = (p->attributes & ~I_MASK) | 1;
++ break;
++
++ case 24:
++ p->attributes &= ~ULINE;
++ break;
++
++ case 25:
++ p->attributes &= ~BLINK;
++ break;
++
++ case 27:
++ p->attributes &= ~REVERSE;
++ break;
++
++ case 38:
++ p->attributes |= ULINE;
++ p->color = (p->color & BG_MASK) | (p->defcolor & FG_MASK);
++ break;
++
++ case 39:
++ p->attributes &= ~ULINE;
++ p->color = (p->color & BG_MASK) | (p->defcolor & FG_MASK);
++ break;
++
++ case 49:
++ p->color = (p->defcolor & BG_MASK) | (p->color & FG_MASK);
++ break;
++
++ default:
++ if (arg >= 30 && arg <= 37)
++ p->color = (p->color & BG_MASK) | color_table[arg-30];
++ else if (arg >= 40 && arg <= 47)
++ p->color = (p->color & FG_MASK) | (color_table[arg-40] << 4);
++ break;
++ }
++
++ update_attr(p);
++}
++
++static void csi_h(struct lcd_struct *p, unsigned char n)
++{
++ switch (n) {
++ case 4: /* Set insert mode */
++ set_bit(DECIM, &p->struct_flags);
++ return;
++
++ case 5: /* Inverted screen mode */
++ if (test_bit(QUES, &p->struct_flags) && ! test_bit(DECSCNM, &p->struct_flags)) {
++ set_bit(DECSCNM, &p->struct_flags);
++ lcd_invert_screen(p, 0, p->fb_size);
++ update_attr(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ }
++ return;
++
++ case 7: /* Set autowrap */
++ if (test_bit(QUES, &p->struct_flags))
++ set_bit(DECAWM, &p->struct_flags);
++ return;
++
++ case 20: /* Set cr lf */
++ set_bit(CRLF, &p->struct_flags);
++ return;
++ }
++}
++
++static void csi_l(struct lcd_struct *p, unsigned char n)
++{
++ switch (n) {
++ case 4: /* Reset insert mode */
++ clear_bit(DECIM, &p->struct_flags);
++ return;
++
++ case 5: /* Normal screen mode */
++ if (test_bit(QUES, &p->struct_flags) && test_bit(DECSCNM, &p->struct_flags)) {
++ clear_bit(DECSCNM, &p->struct_flags);
++ lcd_invert_screen(p, 0, p->fb_size);
++ update_attr(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ }
++ return;
++
++ case 7: /* Reset autowrap */
++ if (test_bit(QUES, &p->struct_flags))
++ clear_bit(DECAWM, &p->struct_flags);
++ return;
++
++ case 20: /* Reset cr lf */
++ clear_bit(CRLF, &p->struct_flags);
++ return;
++ }
++}
++
++static void csi_linux(struct lcd_struct *p)
++{
++ switch (p->csi_args[0]) {
++ case 1:
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags) && p->csi_args[1] < 16) {
++ p->ulcolor = color_table[p->csi_args[1]];
++ if (p->attributes & ULINE)
++ update_attr(p);
++ }
++ return;
++
++ case 2:
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags) && p->csi_args[1] < 16) {
++ p->halfcolor = color_table[p->csi_args[1]];
++ if ((p->attributes & I_MASK) == 0)
++ update_attr(p);
++ }
++ return;
++
++ case 8:
++ p->defcolor = p->color;
++ default_attr(p);
++ update_attr(p);
++ return;
++ }
++}
++
++static void csi_r(struct lcd_struct *p, unsigned int top, unsigned int bot)
++{
++ /* Minimum allowed region is 2 lines */
++ if (top < bot) {
++ p->top = top-1;
++ p->bot = bot;
++ gotoxy(p, 0, 0);
++ }
++}
++
++/*
++ * ECMA-48 CSI ESC- sequence handler.
++ */
++static void handle_csi(struct lcd_struct *p, unsigned char input)
++{
++ if (p->index >= NPAR) {
++ SET_INPUT_STATE(p, NORMAL);
++ printk(KERN_NOTICE "LCD: too many parameters in CSI escape sequence\n");
++ } else if (input == '?') {
++ set_bit(QUES, &p->struct_flags);
++ } else if (input == ';') {
++ ++p->index;
++ } else if (input >= '0' && input <= '9') {
++ p->csi_args[p->index] = (p->csi_args[p->index]*10)+(input-'0');
++ } else {
++ SET_INPUT_STATE(p, NORMAL);
++ if (! test_bit(INC_CURS_POS, &p->struct_flags))
++ return;
++ switch (input) {
++ case 'h': /* DECSET sequences and mode switches */
++ csi_h(p, p->csi_args[0]);
++ clear_bit(QUES, &p->struct_flags);
++ return;
++
++ case 'l': /* DECRST sequences and mode switches */
++ csi_l(p, p->csi_args[0]);
++ clear_bit(QUES, &p->struct_flags);
++ return;
++ }
++ clear_bit(QUES, &p->struct_flags);
++ switch (input) {
++ case '@': /* Insert # Blank character */
++ csi_at(p, p->csi_args[0]);
++ return;
++
++ case 'G': case '`': /* Cursor to indicated column in current row */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ gotoxy(p, p->csi_args[0], p->row);
++ return;
++
++ case 'A': /* Cursor # rows Up */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col, p->row-p->csi_args[0]);
++ return;
++
++ case 'B': case 'e': /* Cursor # rows Down */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col, p->row+p->csi_args[0]);
++ return;
++
++ case 'C': case 'a': /* Cursor # columns Right */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col+p->csi_args[0], p->row);
++ return;
++
++ case 'D': /* Cursor # columns Left */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col-p->csi_args[0], p->row);
++ return;
++
++ case 'E': /* Cursor # rows Down, column 1 */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, 0, p->row+p->csi_args[0]);
++ return;
++
++ case 'F': /* Cursor # rows Up, column 1 */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, 0, p->row-p->csi_args[0]);
++ return;
++
++ case 'd': /* Cursor to indicated row in current column */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ gotoxy(p, p->col, p->csi_args[0]);
++ return;
++
++ case 'H': case 'f': /* Cursor to indicated row, column (origin 1, 1) */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ if (p->csi_args[1])
++ --p->csi_args[1];
++ gotoxy(p, p->csi_args[1], p->csi_args[0]);
++ return;
++
++ case 'J': /* Erase display */
++ csi_J(p, p->csi_args[0]);
++ return;
++
++ case 'K': /* Erase line */
++ csi_K(p, p->csi_args[0]);
++ return;
++
++ case 'L': /* Insert # blank lines */
++ csi_L(p, p->csi_args[0]);
++ return;
++
++ case 'M': /* Delete # blank lines */
++ csi_M(p, p->csi_args[0]);
++ return;
++
++ case 'P': /* Delete # characters on the current line */
++ csi_P(p, p->csi_args[0]);
++ return;
++
++ case 'X': /* Erase # characters on the current line */
++ csi_X(p, p->csi_args[0]);
++ return;
++
++ case 'm': /* Set video attributes */
++ csi_m(p, p->index);
++ return;
++
++ case 's': /* Save cursor position */
++ case 'u': /* Restore cursor position */
++ csi_su(p, input);
++ return;
++
++ case ']': /* Linux private ESC [ ] sequence */
++ csi_linux(p);
++ return;
++
++ case 'r': /* Set the scrolling region */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ if (! p->csi_args[1] || p->csi_args[1] > p->par->vs_rows)
++ p->csi_args[1] = p->par->vs_rows;
++ csi_r(p, p->csi_args[0], p->csi_args[1]);
++ return;
++
++ /* Ignored escape sequences */
++ case 'c':
++ case 'g':
++ case 'n':
++ case 'q':
++ return;
++
++ default:
++ printk(KERN_NOTICE "LCD: unrecognized CSI escape sequence: ESC [ %u\n", input);
++ return;
++ }
++ }
++}
++
++/*
++ * Custom ESC- sequence handler.
++ */
++static int handle_custom_esc(struct lcd_struct *p, unsigned int _input)
++{
++ unsigned char input = _input & 0xff;
++ struct lcd_parameters *par = p->par;
++
++ if (_input & (~0xff)) {
++ switch (ESC_STATE(p)) {
++ case 's':
++ if (p->index++) {
++ unsigned char *buf = p->cgram_buffer+(p->cgram_index-par->cgram_char0)*par->cgram_bytes;
++
++ buf[p->index-2] = input;
++ if (p->index == par->cgram_bytes+1)
++ write_cgram(p, p->cgram_index, buf);
++ } else {
++ if (! p->driver->write_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to write to CGRAM\n", p->par->name);
++ return (-1);
++ }
++ if (input >= par->cgram_char0 && input < par->cgram_char0+par->cgram_chars)
++ p->cgram_index = input;
++ else {
++ printk(KERN_NOTICE "LCD: bad CGRAM index\n");
++ return (-1);
++ }
++ }
++ return (0);
++
++ case 'G':
++ if (input >= par->cgram_char0 && input < par->cgram_char0+par->cgram_chars)
++ write_data(p, (p->attr << 8) | p->driver->charmap[input]);
++ else {
++ SET_INPUT_STATE(p, NORMAL);
++ handle_input(p, (p->attr << 8) | input);
++ }
++ return (0);
++
++ case 'r':
++ if (input == '1')
++ address_mode(p, -1);
++ else if (input == '0')
++ address_mode(p, 1);
++ return (0);
++
++ case 'A':
++ scrup(p, p->top, p->bot, input);
++ return (0);
++
++ case 'B':
++ scrdown(p, p->top, p->bot, input);
++ return (0);
++
++ case 'C':
++ browse_screen(p, input);
++ return (0);
++ }
++ }
++
++ /* These are the custom ESC- sequences */
++ switch (input) {
++ case 's': /* CGRAM select */
++ if (p->cgram_buffer) {
++ SET_ESC_STATE(p, input);
++ p->index = 0;
++ return (par->cgram_bytes+1);
++ } else {
++ printk(KERN_NOTICE "LCD: driver %s does not support CGRAM chars\n", par->name);
++ return (0);
++ }
++
++ case 'A': /* Scroll up */
++ case 'B': /* Scroll down */
++ case 'C': /* Browse screen */
++ case 'G': /* Enter cgram mode */
++ case 'r': /* Decrement counter after data read/write */
++ SET_ESC_STATE(p, input);
++ return (1);
++ }
++
++ return (-1);
++}
++
++/*
++ * ESC- but not CSI sequence handler.
++ */
++static int handle_esc(struct lcd_struct *p, unsigned char input)
++{
++ int ret;
++
++ SET_INPUT_STATE(p, NORMAL);
++ switch (input) {
++ case 'c': /* Reset */
++ set_bit(DECAWM, &p->struct_flags);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++ ff(p);
++ return (0);
++
++ case 'D': /* Line Feed */
++ lf(p);
++ return (0);
++
++ case 'E': /* New Line */
++ cr(p);
++ lf(p);
++ return (0);
++
++ case 'M': /* Reverse Line Feed */
++ ri(p);
++ return (0);
++
++ case '7':
++ case '8':
++ csi_su(p, (input == '7' ? 's' : 'u'));
++ return (0);
++
++ /* CSI: Start of CSI escape sequence */
++ case '[':
++ memset(p->csi_args, 0, sizeof(p->csi_args));
++ p->index = 0;
++ SET_INPUT_STATE(p, CSI);
++ return (0);
++
++ /* Ignored escape sequences */
++ case '(':
++ SET_INPUT_STATE(p, ESC_G0);
++ return (1);
++
++ case ')':
++ SET_INPUT_STATE(p, ESC_G1);
++ return (1);
++
++ case '#':
++ SET_INPUT_STATE(p, ESC_HASH);
++ return (1);
++
++ case '%':
++ SET_INPUT_STATE(p, ESC_PERCENT);
++ return (1);
++
++ case 'H':
++ case 'Z':
++ case '>':
++ case '=':
++ case ']':
++ return (0);
++ }
++
++ /* These are the custom ESC- sequences */
++ if ((ret = handle_custom_esc(p, input)) > 0) {
++ SET_INPUT_STATE(p, ARG);
++ return (ret);
++ }
++
++ if (ret < 0 && p->driver->handle_custom_char)
++ if ((ret = p->driver->handle_custom_char(input)) > 0) {
++ SET_INPUT_STATE(p, ARG_DRIVER);
++ return (ret);
++ }
++
++ if (ret < 0)
++ printk(KERN_NOTICE "LCD: unrecognized escape sequence: ESC %u\n", input);
++
++ return (0);
++}
++
++/*
++ * Main input handler.
++ */
++static void handle_input(struct lcd_struct *p, unsigned short _input)
++{
++ unsigned char input = _input & 0xff;
++ struct lcd_driver *driver = p->driver;
++
++ switch (INPUT_STATE(p)) {
++ case NORMAL:
++ if (input < 0x20 || input == 0x9b)
++ control_char(p, input);
++ else
++ write_data(p, (_input & 0xff00) | driver->charmap[input]);
++ return;
++
++ case RAW:
++ write_data(p, (_input & 0xff00) | driver->charmap[input]);
++ return;
++
++ case SYN:
++ write_data(p, _input);
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case ESC:
++ p->esc_args = handle_esc(p, input);
++ return;
++
++ case ESC_G0:
++ case ESC_G1:
++ case ESC_HASH:
++ case ESC_PERCENT:
++ if (! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case CSI:
++ handle_csi(p, input);
++ return;
++
++ case ARG:
++ if (handle_custom_esc(p, 0x100 | input) || ! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case ARG_DRIVER:
++ if (driver->handle_custom_char(0x100 | input) || ! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++ }
++}
++
++
++
++
++
++/***************************************
++ * Read from/Write to display routines *
++ ***************************************/
++
++/*
++ * Write character data to the display.
++ */
++static void write_data(struct lcd_struct *p, unsigned short data)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos;
++ int frame_pos;
++
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++
++ if (test_bit(DECIM, &p->struct_flags))
++ lcd_insert_char(p, 1);
++
++ pos = (p->row*vs_cols)+p->col;
++ if ((frame_pos = vs_to_frame_(p, pos)) < 0) {
++ make_cursor_visible(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ frame_pos = vs_to_frame_(p, pos);
++ }
++
++ if (p->display[frame_pos] != data) {
++ p->driver->write_char(frame_pos, data);
++ p->display[frame_pos] = data;
++ }
++
++ p->fb[pos] = data;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->col+1 < vs_cols)
++ iterator_inc(p->col, vs_cols);
++ else if (test_bit(DECAWM, &p->struct_flags))
++ set_bit(NEED_WRAP, &p->struct_flags);
++ } else {
++ if (p->col)
++ iterator_dec(p->col, vs_cols);
++ else if (test_bit(DECAWM, &p->struct_flags))
++ set_bit(NEED_WRAP, &p->struct_flags);
++ }
++}
++
++/*
++ * Write an entire CGRAM character to the display.
++ */
++static void write_cgram(struct lcd_struct *p, unsigned char index, unsigned char *pixels)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! inc_set)
++ address_mode(p, 1);
++
++ p->driver->write_cgram_char(index, pixels);
++
++ if (! inc_set)
++ address_mode(p, -1);
++}
++
++/*
++ * Read character data from the display.
++ */
++static void read_data(struct lcd_struct *p, unsigned short *data)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++ int frame_pos;
++
++ if ((frame_pos = vs_to_frame_(p, pos)) < 0) {
++ make_cursor_visible(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ frame_pos = vs_to_frame_(p, pos);
++ }
++
++ p->driver->read_char(frame_pos, data);
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ iterator_inc(p->col, vs_cols);
++ if (! p->col) {
++ if (p->row+1 < vs_rows)
++ ++p->row;
++ }
++ } else {
++ iterator_dec(p->col, vs_cols);
++ if (p->col+1 == vs_cols) {
++ if (p->row)
++ --p->row;
++ }
++ }
++}
++
++/*
++ * Read an entire CGRAM character from the display.
++ */
++static void read_cgram(struct lcd_struct *p, unsigned char index, unsigned char *pixels)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! inc_set)
++ address_mode(p, 1);
++
++ p->driver->read_cgram_char(index, pixels);
++
++ if (! inc_set)
++ address_mode(p, -1);
++}
++
++
++
++
++
++/****************************
++ * Proc filesystem routines *
++ ****************************/
++#ifdef USE_PROC
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
++/* create_proc_read_entry is missing in 2.2.x kernels */
++static struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode,
++ struct proc_dir_entry *parent, read_proc_t *read_proc, void *data)
++{
++ struct proc_dir_entry *res = create_proc_entry(name, mode, parent);
++
++ if (res) {
++ res->read_proc = read_proc;
++ res->data = data;
++ }
++
++ return (res);
++}
++#endif
++
++static int proc_fb_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned int vs_cols;
++ static unsigned int nr, need_wrap;
++ static off_t _offset;
++
++ down(&p->lcd_sem);
++ if (! offset)
++ _offset = 0;
++ if ((*eof = (_offset >= p->fb_size))) {
++ up(&p->lcd_sem);
++ return (0);
++ }
++ vs_cols = p->par->vs_cols;
++ if (size && need_wrap) {
++ need_wrap = 0;
++ temp += sprintf(temp, "\n");
++ --size;
++ }
++ if (! nr)
++ nr = vs_cols;
++ *start = (char *)0;
++ while (size && nr) {
++ unsigned char c = (p->fb[_offset] & 0xff);
++
++ temp += sprintf(temp, "%c", (c < 0x20 ? '·' : c));
++ --size;
++ --nr;
++ ++*start;
++ ++_offset;
++ }
++ if (! nr) {
++ if (size) {
++ temp += sprintf(temp, "\n");
++ --size;
++ } else
++ need_wrap = 1;
++ }
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_display_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned int i, frame_cols;
++ int frame_pos;
++
++ down(&p->lcd_sem);
++ frame_cols = p->par->cntr_cols;
++ frame_pos = vs_to_frame_(p, (p->row*p->par->vs_cols)+p->col);
++ temp += sprintf(temp, " ");
++ for (i = 2; i <= frame_cols; i += 2)
++ temp += sprintf(temp, " %d", i%10);
++ temp += sprintf(temp, "\n");
++
++ temp += sprintf(temp, " +");
++ for (i = 0; i < frame_cols; ++i)
++ temp += sprintf(temp, "-");
++ temp += sprintf(temp, "+\n");
++
++ for (i = 0; i < p->frame_size; ++i) {
++ unsigned char c = (p->display[i] & 0xff);
++
++ if (! (i%frame_cols))
++ temp += sprintf(temp, "%2d |", 1+i/frame_cols);
++ if (frame_pos--)
++ temp += sprintf(temp, "%c", (c < 0x20 ? '·' : c));
++ else
++ temp += sprintf(temp, "_");
++ if (! ((i+1)%frame_cols))
++ temp += sprintf(temp, "|\n");
++ }
++
++ temp += sprintf(temp, " +");
++ for (i = 0; i < frame_cols; ++i)
++ temp += sprintf(temp, "-");
++ temp += sprintf(temp, "+\n");
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_charmap_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned char *charmap;
++ unsigned int i;
++
++ down(&p->lcd_sem);
++ charmap = p->driver->charmap;
++ temp += sprintf(temp, "static unsigned char charmap[] = {");
++ for (i = 0; i < 255; ++i) {
++ if (! (i & 7)) {
++ temp += sprintf(temp, "\n");
++ if (! (i & 31))
++ temp += sprintf(temp, "\n/* %d - %d */\n", i, i+31);
++ }
++ temp += sprintf(temp, "0x%.2x, ", *charmap++);
++ }
++ temp += sprintf(temp, "0x%.2x\n\n};\n", *charmap);
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_registered_drivers(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct list_head *entry;
++
++ down(&drivers_sem);
++ temp += sprintf(temp, "Registered drivers:\n");
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ down(&p->lcd_sem);
++ temp += sprintf(temp, "%3d %s\n", p->par->minor, p->par->name);
++ up(&p->lcd_sem);
++ }
++ up(&drivers_sem);
++
++ return (temp-buffer);
++}
++
++static void create_driver_proc_entries(struct lcd_struct *p)
++{
++ struct proc_dir_entry *driver_proc_root = p->driver->driver_proc_root;
++ int proc_level = 0;
++
++ SET_PROC_LEVEL(p, proc_level);
++ if (create_proc_read_entry("framebuffer", 0, driver_proc_root, proc_fb_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/framebuffer\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++ if (create_proc_read_entry("display", 0, driver_proc_root, proc_display_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/display\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++ if (create_proc_read_entry("charmap.h", 0, driver_proc_root, proc_charmap_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/charmap.h\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++}
++
++static void remove_driver_proc_entries(struct lcd_struct *p)
++{
++ struct proc_dir_entry *driver_proc_root = p->driver->driver_proc_root;
++
++ switch (PROC_LEVEL(p)) {
++ case 3:
++ remove_proc_entry("charmap.h", driver_proc_root);
++ case 2:
++ remove_proc_entry("display", driver_proc_root);
++ case 1:
++ remove_proc_entry("framebuffer", driver_proc_root);
++ }
++ SET_PROC_LEVEL(p, 0);
++}
++#endif
++
++
++
++
++
++/*****************************
++ * Low level file operations *
++ *****************************/
++static ssize_t do_lcd_read(struct lcd_struct *p, void *buffer, size_t length)
++{
++ unsigned int i;
++ unsigned short tmp;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ if (! p->driver->read_char) {
++ printk(KERN_NOTICE "LCD: driver %s doesn't support reading\n", p->par->name);
++ return (-ENOSYS);
++ }
++
++ if (test_bit(WITH_ATTR, &p->struct_flags))
++ for (i = 0; i < length; ++i) {
++ read_data(p, &tmp);
++ ((unsigned short *)buffer)[i] = tmp;
++ }
++ else
++ for (i = 0; i < length; ++i) {
++ read_data(p, &tmp);
++ ((unsigned char *)buffer)[i] = tmp & 0xff;
++ }
++
++ return (length);
++}
++
++static ssize_t do_lcd_write(struct lcd_struct *p, const void *buffer, size_t length)
++{
++ unsigned int i;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ if (test_bit(WITH_ATTR, &p->struct_flags))
++ for (i = 0; i < length; ++i)
++ handle_input(p, ((const unsigned short *)buffer)[i]);
++ else
++ for (i = 0; i < length; ++i)
++ handle_input(p, (p->attr << 8) | ((const unsigned char *)buffer)[i]);
++
++ return (length);
++}
++
++static int do_lcd_open(struct lcd_struct *p)
++{
++ if (! p->refcount) {
++ if (p->driver->driver_module) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ if (! try_module_get(p->driver->driver_module))
++ return (-EBUSY);
++#else
++ if (__MOD_IN_USE(p->driver->driver_module))
++ return (-EBUSY);
++
++ __MOD_INC_USE_COUNT(p->driver->driver_module);
++#endif
++ }
++ }
++
++ ++p->refcount;
++
++ return (0);
++}
++
++static int do_lcd_release(struct lcd_struct *p)
++{
++ if (! p->refcount)
++ return (0);
++
++ if (p->refcount == 1) {
++ if (p->driver->driver_module)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ module_put(p->driver->driver_module);
++#else
++ __MOD_DEC_USE_COUNT(p->driver->driver_module);
++#endif
++ }
++
++ --p->refcount;
++
++ return (0);
++}
++
++static int cgram_ioctl(struct lcd_struct *p, unsigned int cmd, unsigned char *argp)
++{
++ struct lcd_parameters *par = p->par;
++ unsigned int length = par->cgram_bytes;
++ unsigned char index = argp[0];
++ unsigned char *buffer = argp+1;
++ unsigned char *cgram_buffer = p->cgram_buffer+(index-par->cgram_char0)*length;
++
++ if (index < par->cgram_char0 || index >= par->cgram_char0+par->cgram_chars)
++ return (-EINVAL);
++
++ if (cmd == LCDL_SET_CGRAM_CHAR) {
++ if (! p->driver->write_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to write to CGRAM\n", p->par->name);
++ return (-ENOSYS);
++ }
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(cgram_buffer, buffer, length))
++ return (-EFAULT);
++ } else
++ memcpy(cgram_buffer, buffer, length);
++ write_cgram(p, index, cgram_buffer);
++ } else {
++ if (! p->driver->read_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to read from CGRAM or unable to read\n", p->par->name);
++ return (-ENOSYS);
++ }
++ read_cgram(p, index, cgram_buffer);
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(buffer, cgram_buffer, length))
++ return (-EFAULT);
++ } else
++ memcpy(buffer, cgram_buffer, length);
++ }
++
++ return (0);
++}
++
++static int do_lcd_ioctl(struct lcd_struct *p, unsigned int cmd, unsigned long arg)
++{
++ int i;
++ struct lcd_driver *driver = p->driver;
++ struct lcd_parameters *par = p->par;
++ unsigned char *argp = (unsigned char *)arg;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ switch (cmd) {
++ case LCDL_SET_PARAM:
++ if ((i = cleanup_driver(p)))
++ return (i);
++ i = par->minor;
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(par, argp, sizeof(struct lcd_parameters)))
++ return (-EFAULT);
++ } else
++ memcpy(par, argp, sizeof(struct lcd_parameters));
++ par->minor = i;
++ return (init_driver(p));
++
++ case LCDL_GET_PARAM:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(argp, par, sizeof(struct lcd_parameters)))
++ return (-EFAULT);
++ } else
++ memcpy(argp, par, sizeof(struct lcd_parameters));
++ return (0);
++
++ case LCDL_RESET_CHARMAP:
++ for (i = 0; i < 256; ++i)
++ driver->charmap[i] = i;
++ return (0);
++
++ case LCDL_CHARSUBST:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ get_user(i, argp);
++ get_user(driver->charmap[i], argp+1);
++ } else {
++ i = argp[0];
++ driver->charmap[i] = argp[1];
++ }
++ return (0);
++
++ case LCDL_SAVE_CHARMAP:
++ memcpy(p->s_charmap, driver->charmap, 256);
++ return (0);
++
++ case LCDL_RESTORE_CHARMAP:
++ memcpy(driver->charmap, p->s_charmap, 256);
++ return (0);
++
++ case LCDL_SWAP_CHARMAP:
++ {
++ unsigned char *tmp;
++
++ tmp = driver->charmap;
++ driver->charmap = p->s_charmap;
++ p->s_charmap = tmp;
++ }
++ return (0);
++
++ case LCDL_RAW_MODE:
++ if (arg) {
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ clear_bit(DECIM, &p->struct_flags);
++ clear_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, RAW);
++ } else {
++ set_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, NORMAL);
++ }
++ return (0);
++
++ case LCDL_CLEAR_DISP:
++ ff(p);
++ return (0);
++
++ case LCDL_SET_CGRAM_CHAR:
++ case LCDL_GET_CGRAM_CHAR:
++ if (p->cgram_buffer)
++ return (cgram_ioctl(p, cmd, argp));
++ else
++ printk(KERN_NOTICE "LCD: driver %s does not support CGRAM chars\n", par->name);
++ return (0);
++
++ case LCDL_SET_CHARMAP:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(driver->charmap, argp, 256))
++ return (-EFAULT);
++ } else
++ memcpy(driver->charmap, argp, 256);
++ return (0);
++
++ case LCDL_GET_CHARMAP:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(argp, driver->charmap, 256))
++ return (-EFAULT);
++ } else
++ memcpy(argp, driver->charmap, 256);
++ return (0);
++
++ case LCDL_MEMSET:
++ case LCDL_MEMMOVE:
++ {
++ int buf[3];
++
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(buf, argp, sizeof(buf)))
++ return (-EFAULT);
++ } else
++ memcpy(buf, argp, sizeof(buf));
++
++ if (cmd == LCDL_MEMSET)
++ lcd_memset(p, buf[0], buf[1], buf[2]);
++ else
++ lcd_memmove(p, buf[0], buf[1], buf[2]);
++
++ return (0);
++ }
++
++ default:
++ if (driver->handle_custom_ioctl)
++ return (driver->handle_custom_ioctl(cmd, arg, test_bit(USER_SPACE, &p->struct_flags)));
++ }
++
++ return (-ENOIOCTLCMD);
++}
++
++
++
++
++
++/**************************************************
++ * Kernel register/unregister lcd driver routines *
++ **************************************************/
++/*
++ * Find a driver in lcd_drivers linked list
++ */
++static struct lcd_struct *find_lcd_struct(unsigned short minor)
++{
++ struct list_head *entry;
++
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ if (p->par->minor == minor)
++ return (p);
++ }
++
++ return (NULL);
++}
++
++static void list_add_sorted(struct list_head *new)
++{
++ struct list_head *entry;
++ unsigned short new_minor = (list_entry(new, struct lcd_struct, lcd_list))->par->minor;
++
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ if (p->par->minor > new_minor)
++ break;
++ }
++ list_add_tail(new, entry);
++}
++
++/* Exported function */
++int lcd_register_driver(struct lcd_driver *driver, struct lcd_parameters *par)
++{
++ int ret;
++ struct lcd_struct *p;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ struct device *lcd_device;
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ struct class_device *lcd_class_device;
++#endif
++
++ if (! driver || ! par || par->minor >= minors)
++ return (-EINVAL);
++ if (! driver->write_char || ! driver->init_port || ! driver->cleanup_port) {
++ printk(KERN_ERR "LCD: missing functions\n");
++ return (-EINVAL);
++ }
++
++ down(&drivers_sem);
++
++ if (find_lcd_struct(par->minor)) {
++ up(&drivers_sem);
++ return (-EBUSY);
++ }
++
++ if ((p = (struct lcd_struct *)kmalloc(sizeof(struct lcd_struct), GFP_KERNEL)) == NULL) {
++ printk(KERN_ERR "LCD: memory allocation failed (kmalloc)\n");
++ up(&drivers_sem);
++ return (-ENOMEM);
++ }
++ memset(p, 0, sizeof(struct lcd_struct));
++
++ p->driver = driver;
++ p->par = par;
++ p->refcount = 0;
++ SET_INIT_LEVEL(p, 0);
++ SET_INPUT_STATE(p, NORMAL);
++ set_bit(DECAWM, &p->struct_flags);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ lcd_device = device_create(lcd_linux_class, NULL, MKDEV(major, par->minor), "%s", par->name);
++ if (IS_ERR(lcd_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_device));
++ }
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
++ lcd_class_device = class_device_create(lcd_linux_class, NULL, MKDEV(major, par->minor), NULL, "%s", par->name);
++ if (IS_ERR(lcd_class_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_class_device));
++ }
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ lcd_class_device = class_device_create(lcd_linux_class, MKDEV(major, par->minor), NULL, "%s", par->name);
++ if (IS_ERR(lcd_class_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_class_device));
++ }
++#endif
++
++#ifdef USE_PROC
++ if (lcd_proc_root && (driver->driver_proc_root = proc_mkdir(par->name, lcd_proc_root)) == NULL)
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/\n", par->name);
++#endif
++
++ if ((ret = init_driver(p))) {
++#ifdef USE_PROC
++ if (driver->driver_proc_root)
++ remove_proc_entry(p->par->name, lcd_proc_root);
++#endif
++ kfree(p);
++ up(&drivers_sem);
++ return (ret);
++ }
++
++ init_MUTEX(&p->lcd_sem);
++
++ list_add_sorted(&p->lcd_list);
++
++ up(&drivers_sem);
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ try_module_get(THIS_MODULE);
++#else
++ MOD_INC_USE_COUNT;
++#endif
++#endif
++
++ return (0);
++}
++EXPORT_SYMBOL(lcd_register_driver);
++
++/* Exported function */
++int lcd_unregister_driver(struct lcd_driver *driver, struct lcd_parameters *par)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ if (! driver || ! par || par->minor >= minors)
++ return (-EINVAL);
++
++ down(&drivers_sem);
++
++ if ((p = find_lcd_struct(par->minor)) == NULL || p->driver != driver) {
++ printk(KERN_ERR "LCD: driver not found; lcd_unregister_driver failed\n");
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++
++ if (p->refcount) {
++ printk(KERN_ERR "LCD: driver busy; lcd_unregister_driver failed\n");
++ up(&p->lcd_sem);
++ up(&drivers_sem);
++ return (-EBUSY);
++ }
++
++ if ((ret = cleanup_driver(p))) {
++ up(&p->lcd_sem);
++ up(&drivers_sem);
++ return (ret);
++ }
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ device_destroy(lcd_linux_class, MKDEV(major, par->minor));
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ class_device_destroy(lcd_linux_class, MKDEV(major, par->minor));
++#endif
++
++#ifdef USE_PROC
++ if (p->driver->driver_proc_root)
++ remove_proc_entry(p->par->name, lcd_proc_root);
++#endif
++
++ list_del(&p->lcd_list);
++ kfree(p);
++
++ up(&drivers_sem);
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ module_put(THIS_MODULE);
++#else
++ MOD_DEC_USE_COUNT;
++#endif
++#endif
++
++ return (0);
++}
++EXPORT_SYMBOL(lcd_unregister_driver);
++
++
++
++
++
++/************************
++ * Kernel I/O interface *
++ ************************/
++/* Exported function */
++int lcd_open(unsigned short minor, struct lcd_struct **pp)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ down(&drivers_sem);
++
++ if (minor >= minors || (*pp = p = find_lcd_struct(minor)) == NULL) {
++ printk(KERN_ERR "LCD: lcd_open failed. Device not found.\n");
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ up(&drivers_sem);
++
++ ret = do_lcd_open(p);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_open);
++
++/* Exported function */
++int lcd_close(struct lcd_struct **pp)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ if (! pp || ! (p = *pp)) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_close\n");
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++
++ if (! (ret = do_lcd_release(p)))
++ *pp = NULL;
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_close);
++
++static inline loff_t offset_to_row_col(struct lcd_struct *p, loff_t offset)
++{
++ unsigned long _offset = offset;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ gotoxy(p, _offset%vs_cols, _offset/vs_cols);
++
++ return ((p->row*vs_cols)+p->col);
++}
++
++/* Exported function */
++ssize_t lcd_read(struct lcd_struct *p, void *bufp, size_t length, loff_t offset, unsigned int with_attr)
++{
++ ssize_t ret = 0;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_read\n");
++ return (-ENODEV);
++ }
++ if (! bufp)
++ return (-EFAULT);
++ if (offset < 0 || offset >= p->fb_size)
++ return (-EINVAL);
++
++ if (length+offset > p->fb_size)
++ length = p->fb_size-offset;
++
++ if (with_attr)
++ set_bit(WITH_ATTR, &p->struct_flags);
++
++ offset_to_row_col(p, offset);
++ ret = do_lcd_read(p, bufp, length);
++
++ if (with_attr)
++ clear_bit(WITH_ATTR, &p->struct_flags);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_read);
++
++/* Exported function */
++ssize_t lcd_write(struct lcd_struct *p, const void *bufp, size_t length, loff_t offset, unsigned int with_attr)
++{
++ ssize_t ret;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_write\n");
++ return (-ENODEV);
++ }
++ if (! bufp)
++ return (-EFAULT);
++ if (offset < 0 || offset >= p->fb_size)
++ return (-EINVAL);
++
++ if (with_attr)
++ set_bit(WITH_ATTR, &p->struct_flags);
++
++ offset_to_row_col(p, offset);
++ ret = do_lcd_write(p, bufp, length);
++
++ if (with_attr)
++ clear_bit(WITH_ATTR, &p->struct_flags);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_write);
++
++/* Exported function */
++int lcd_ioctl(struct lcd_struct *p, unsigned int cmd, ...)
++{
++ int ret;
++ unsigned long arg;
++ va_list ap;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_ioctl\n");
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ va_start(ap, cmd);
++ arg = va_arg(ap, unsigned long);
++ ret = do_lcd_ioctl(p, cmd, arg);
++ va_end(ap);
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_ioctl);
++
++
++
++
++
++/*******************
++ * File operations *
++ *******************/
++static loff_t lcd_fops_llseek(struct file *filp, loff_t offset, int orig)
++{
++ struct lcd_struct *p;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ switch (orig) {
++ case 0:
++ filp->f_pos = offset;
++ break;
++
++ case 1:
++ filp->f_pos += offset;
++ break;
++
++ default:
++ up(&p->lcd_sem);
++ return (-EINVAL); /* SEEK_END not supported */
++ }
++
++ filp->f_pos = offset_to_row_col(p, filp->f_pos);
++
++ up(&p->lcd_sem);
++
++ return (filp->f_pos);
++}
++
++static ssize_t lcd_fops_read(struct file *filp, char *buffer, size_t length, loff_t *offp)
++{
++ ssize_t ret = 0;
++ char *bufp = buffer;
++ struct lcd_struct *p;
++
++ if (! bufp)
++ return (-EFAULT);
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (*offp < 0 || *offp >= p->fb_size) {
++ up(&p->lcd_sem);
++ return (-EINVAL);
++ }
++
++ if (length+(*offp) > p->fb_size)
++ length = p->fb_size-(*offp);
++
++ while (length) {
++ ret = (length > FLIP_BUF_SIZE ? FLIP_BUF_SIZE : length);
++ if ((ret = do_lcd_read(p, p->flip_buf, ret)) < 0)
++ break;
++ *offp = (p->row*p->par->vs_cols)+p->col;
++ if (copy_to_user(bufp, p->flip_buf, ret)) {
++ ret = -EFAULT;
++ break;
++ }
++ length -= ret;
++ bufp += ret;
++ ret = bufp-buffer;
++ if (length)
++ schedule();
++ }
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static ssize_t lcd_fops_write(struct file *filp, const char *buffer, size_t length, loff_t *offp)
++{
++ ssize_t ret = 0;
++ const char *bufp = buffer;
++ struct lcd_struct *p;
++
++ if (! bufp)
++ return (-EFAULT);
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (*offp < 0 || *offp >= p->fb_size) {
++ up(&p->lcd_sem);
++ return (-EINVAL);
++ }
++
++ while (length) {
++ ret = (length > FLIP_BUF_SIZE ? FLIP_BUF_SIZE : length);
++ if (copy_from_user(p->flip_buf, bufp, ret)) {
++ ret = -EFAULT;
++ break;
++ }
++ if ((ret = do_lcd_write(p, p->flip_buf, ret)) < 0)
++ break;
++ *offp = (p->row*p->par->vs_cols)+p->col;
++ length -= ret;
++ bufp += ret;
++ ret = bufp-buffer;
++ if (length)
++ schedule();
++ }
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_open(struct inode *inop, struct file *filp)
++{
++ unsigned short minor;
++ int ret;
++ struct lcd_struct *p;
++
++ down(&drivers_sem);
++
++ if ((minor = MINOR(inop->i_rdev)) >= minors || (filp->private_data = p = find_lcd_struct(minor)) == NULL) {
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ up(&drivers_sem);
++
++ ret = do_lcd_open(p);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_release(struct inode *inop, struct file *filp)
++{
++ struct lcd_struct *p;
++ int ret;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (! (ret = do_lcd_release(p)))
++ filp->private_data = NULL;
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_ioctl(struct inode *inop, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ struct lcd_struct *p;
++ int ret;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ set_bit(USER_SPACE, &p->struct_flags);
++ ret = do_lcd_ioctl(p, cmd, arg);
++ clear_bit(USER_SPACE, &p->struct_flags);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static struct file_operations lcd_linux_fops = {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
++ .owner = THIS_MODULE,
++#endif
++ .llseek = lcd_fops_llseek,
++ .read = lcd_fops_read,
++ .write = lcd_fops_write,
++ .open = lcd_fops_open,
++ .release = lcd_fops_release,
++ .ioctl = lcd_fops_ioctl,
++};
++
++
++
++
++
++/********************************
++ * Init/Cleanup driver routines *
++ ********************************/
++static int do_init_driver(struct lcd_struct *p)
++{
++ int ret, init_level;
++ struct lcd_driver *driver = p->driver;
++ struct lcd_parameters *par = p->par;
++ unsigned int frame_rows = par->cntr_rows*par->num_cntr;
++ unsigned int frame_cols = par->cntr_cols;
++
++ switch ((init_level = INIT_LEVEL(p))) {
++ case 0:
++ if (frame_rows == 0 || frame_cols == 0 || ! par->name) {
++ printk(KERN_ERR "LCD: wrong lcd parameters\n");
++ return (-EINVAL);
++ }
++ if (driver->validate_driver) {
++ if ((ret = driver->validate_driver()) < 0) {
++ printk(KERN_ERR "LCD: validate_driver failed\n");
++ return (-EINVAL);
++ } else if (ret > 0) {
++ set_bit(CAN_DO_COLOR, &p->struct_flags);
++ p->defcolor = 0x07;
++ p->ulcolor = 0x0f;
++ p->halfcolor = 0x08;
++ }
++ }
++ default_attr(p);
++ update_attr(p);
++ p->frame_size = frame_rows*frame_cols;
++ if (par->vs_rows < frame_rows)
++ par->vs_rows = frame_rows;
++ if (par->vs_cols < frame_cols)
++ par->vs_cols = frame_cols;
++ p->fb_size = par->vs_rows*par->vs_cols;
++
++ ret = sizeof(short)*p->fb_size;
++ ret += sizeof(short)*p->frame_size;
++ ret += FLIP_BUF_SIZE;
++ ret += (p->driver->charmap ? 256 : 512);
++ ret += par->cgram_chars*par->cgram_bytes;
++ if ((p->fb = (unsigned short *)vmalloc(ret)) == NULL) {
++ printk(KERN_ERR "LCD: memory allocation failed (vmalloc)\n");
++ return (-ENOMEM);
++ }
++ __memset_short(p->fb, p->erase_char, p->fb_size+p->frame_size);
++
++ p->display = p->fb+p->fb_size;
++ p->flip_buf = (unsigned char *)(p->display+p->frame_size);
++
++ if (! p->driver->charmap) {
++ set_bit(NULL_CHARMAP, &p->struct_flags);
++ p->driver->charmap = p->flip_buf+FLIP_BUF_SIZE;
++ for (ret = 0; ret < 256; ++ret)
++ p->driver->charmap[ret] = ret;
++ p->s_charmap = p->driver->charmap+256;
++ } else
++ p->s_charmap = p->flip_buf+FLIP_BUF_SIZE;
++ memset(p->s_charmap, 0, 256);
++
++ if (par->cgram_chars*par->cgram_bytes) {
++ p->cgram_buffer = p->s_charmap+256;
++ memset(p->cgram_buffer, 0, par->cgram_chars*par->cgram_bytes);
++ } else
++ p->cgram_buffer = NULL;
++
++ p->frame_base = 0;
++ p->row = p->col = 0;
++ p->top = 0;
++ p->bot = par->vs_rows;
++ SET_INIT_LEVEL(p, ++init_level);
++
++ case 1:
++ /* Initialize the communication port */
++ if ((ret = driver->init_port())) {
++ printk(KERN_ERR "LCD: failure while initializing the communication port\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, ++init_level);
++
++ case 2:
++ /* Initialize LCD display */
++ if (driver->init_display && (ret = driver->init_display())) {
++ printk(KERN_ERR "LCD: failure while initializing the display\n");
++ return (ret);
++ }
++
++#ifdef USE_PROC
++ /* Create entries in /proc/lcd/"driver" */
++ if (driver->driver_proc_root)
++ create_driver_proc_entries(p);
++#endif
++ SET_INIT_LEVEL(p, ++init_level);
++ }
++
++ return (0);
++}
++
++static int do_cleanup_driver(struct lcd_struct *p)
++{
++ int ret, init_level;
++ struct lcd_driver *driver = p->driver;
++
++ switch ((init_level = INIT_LEVEL(p))) {
++ case 3:
++#ifdef USE_PROC
++ if (driver->driver_proc_root)
++ remove_driver_proc_entries(p);
++#endif
++ if (driver->cleanup_display && (ret = driver->cleanup_display())) {
++ printk(KERN_ERR "LCD: failure while cleaning the display\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, --init_level);
++
++ case 2:
++ if ((ret = driver->cleanup_port())) {
++ printk(KERN_ERR "LCD: failure while cleaning the communication port\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, --init_level);
++
++ case 1:
++ if (test_bit(NULL_CHARMAP, &p->struct_flags)) {
++ p->driver->charmap = NULL;
++ clear_bit(NULL_CHARMAP, &p->struct_flags);
++ }
++ vfree(p->fb);
++ p->fb = NULL;
++ SET_INIT_LEVEL(p, --init_level);
++ }
++
++ return (0);
++}
++
++static int init_driver(struct lcd_struct *p)
++{
++ int ret;
++
++ if ((ret = do_init_driver(p))) {
++ do_cleanup_driver(p);
++ printk(KERN_ERR "LCD: init_driver failed\n");
++ }
++
++ return (ret);
++}
++
++static int cleanup_driver(struct lcd_struct *p)
++{
++ int ret;
++
++ if ((ret = do_cleanup_driver(p))) {
++ do_init_driver(p);
++ printk(KERN_ERR "LCD: cleanup_driver failed\n");
++ }
++
++ return (ret);
++}
++
++
++
++
++
++/********************************
++ * Init/Cleanup module routines *
++ ********************************/
++static int __init lcd_linux_init_module(void)
++{
++ int ret;
++
++ if (! minors || minors > 256)
++ minors = LCD_MINORS;
++
++ init_MUTEX(&drivers_sem);
++
++ if ((ret = register_chrdev(major, LCD_LINUX_STRING, &lcd_linux_fops)) < 0) {
++ printk(KERN_ERR "LCD: register_chrdev failed\n");
++ return (ret);
++ }
++ if (major == 0)
++ major = ret;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ if (IS_ERR((lcd_linux_class = class_create(THIS_MODULE, "lcd")))) {
++ ret = PTR_ERR(lcd_linux_class);
++ unregister_chrdev(major, LCD_LINUX_STRING);
++ return (ret);
++ }
++#endif
++
++#ifdef USE_PROC
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++ if ((lcd_proc_root = proc_mkdir("lcd", NULL)) == NULL)
++#else
++ if ((lcd_proc_root = proc_mkdir("lcd", &proc_root)) == NULL)
++#endif
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/\n");
++ else if (create_proc_read_entry("drivers", 0, lcd_proc_root, proc_registered_drivers, NULL) == NULL)
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/drivers\n");
++#endif
++
++ printk(KERN_INFO "LCD: --> LCD-Linux " LCD_LINUX_VERSION " <--\n");
++ printk(KERN_INFO "LCD: --> Mattia Jona-Lasinio <mjona@users.sourceforge.net> <--\n" );
++
++
++ return (0);
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
++static void __exit lcd_linux_cleanup_module(void)
++#else
++/* __exit is not defined in 2.2.x kernels */
++static void lcd_linux_cleanup_module(void)
++#endif
++{
++#ifdef USE_PROC
++ if (lcd_proc_root) {
++ remove_proc_entry("drivers", lcd_proc_root);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++ remove_proc_entry("lcd", NULL);
++#else
++ remove_proc_entry("lcd", &proc_root);
++#endif
++ }
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ class_destroy(lcd_linux_class);
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
++ unregister_chrdev(major, LCD_LINUX_STRING);
++#else
++ if (unregister_chrdev(major, LCD_LINUX_STRING))
++ printk(KERN_ERR "LCD: unregister_chrdev failed\n");
++#endif
++}
++
++module_init(lcd_linux_init_module)
++module_exit(lcd_linux_cleanup_module)
+Index: linux-2.6.30.9/include/linux/hd44780.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/include/linux/hd44780.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,46 @@
++/* hd44780.h
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * HD44780 header file.
++ *
++ * Copyright (C) 2004 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef HD44780_H
++#define HD44780_H
++
++#include <linux/lcd-linux.h>
++
++#define HD44780_VERSION LCD_LINUX_VERSION /* Version number */
++#define HD44780_STRING "hd44780"
++
++#define HD44780_MINOR 0 /* Minor number for the hd44780 driver */
++
++
++/* flags */
++#define HD44780_CHECK_BF 0x00000001 /* Do busy flag checking */
++#define HD44780_4BITS_BUS 0x00000002 /* Set the bus length to 4 bits */
++#define HD44780_5X10_FONT 0x00000004 /* Use 5x10 dots fonts */
++
++/* IOCTLs */
++#define HD44780_READ_AC _IOR(LCD_MAJOR, 0x00, unsigned char *)
++
++#endif /* HD44780 included */
+Index: linux-2.6.30.9/include/linux/lcd-linux.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/include/linux/lcd-linux.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,151 @@
++/* lcd-linux.h
++ *
++ *
++ * Software layer to drive LCD displays under Linux.
++ *
++ * External interface header file.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef LCD_LINUX_H
++#define LCD_LINUX_H
++
++#ifndef LCD_LINUX_MAIN
++#warning
++#warning LCD-Linux is still in development stage and
++#warning aims at speed and optimization. For these
++#warning reasons there is no guarantee of backward
++#warning compatibility between different LCD-Linux
++#warning versions. Be sure to use the lcd-linux.h
++#warning file of the same version as the module.
++#warning "http://lcd-linux.sourceforge.net/"
++#warning
++#endif
++
++#define LCD_LINUX_VERSION "0.13.6" /* Version number */
++#define LCD_LINUX_STRING "lcd"
++
++#define LCD_MAJOR 120 /* Major number for this device
++ * Set this to 0 for dynamic allocation
++ */
++#define LCD_MINORS 8 /* Minors allocated for LCD-Linux*/
++
++#include <linux/types.h>
++
++#define str(s) #s
++#define string(s) str(s)
++
++struct lcd_parameters {
++ const char *name; /* Driver's name */
++ unsigned long flags; /* Flags (see documentation) */
++ unsigned short minor; /* Minor number of the char device */
++ unsigned short tabstop; /* Tab character length */
++ unsigned short num_cntr; /* Controllers to drive */
++ unsigned short cntr_rows; /* Rows per controller */
++ unsigned short cntr_cols; /* Display columns */
++ unsigned short vs_rows; /* Virtual screen rows */
++ unsigned short vs_cols; /* Virtual screen columns */
++ unsigned short cgram_chars; /* Number of user definable characters */
++ unsigned short cgram_bytes; /* Number of bytes required to define a
++ * user definable character */
++ unsigned char cgram_char0; /* Ascii of first user definable character */
++};
++
++/* IOCTLs */
++#include <asm/ioctl.h>
++#define LCDL_SET_PARAM _IOW(LCD_MAJOR, 0x80, struct lcd_parameters *)
++#define LCDL_GET_PARAM _IOR(LCD_MAJOR, 0x81, struct lcd_parameters *)
++#define LCDL_CHARSUBST _IOW(LCD_MAJOR, 0x82, unsigned char *)
++#define LCDL_RAW_MODE _IOW(LCD_MAJOR, 0x83, unsigned int)
++#define LCDL_RESET_CHARMAP _IO(LCD_MAJOR, 0x84)
++#define LCDL_SAVE_CHARMAP _IO(LCD_MAJOR, 0x85)
++#define LCDL_RESTORE_CHARMAP _IO(LCD_MAJOR, 0x86)
++#define LCDL_SWAP_CHARMAP _IO(LCD_MAJOR, 0x87)
++#define LCDL_CLEAR_DISP _IO(LCD_MAJOR, 0x88)
++#define LCDL_SET_CGRAM_CHAR _IOW(LCD_MAJOR, 0x89, unsigned char *)
++#define LCDL_GET_CGRAM_CHAR _IOR(LCD_MAJOR, 0x8a, unsigned char *)
++#define LCDL_SET_CHARMAP _IOW(LCD_MAJOR, 0x8b, unsigned char *)
++#define LCDL_GET_CHARMAP _IOR(LCD_MAJOR, 0x8c, unsigned char *)
++#define LCDL_MEMSET _IOW(LCD_MAJOR, 0x8d, unsigned int *)
++#define LCDL_MEMMOVE _IOW(LCD_MAJOR, 0x8e, unsigned int *)
++
++
++
++#ifdef __KERNEL__ /* The rest is for kernel only */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++
++struct lcd_driver {
++ void (*read_char)(unsigned int offset, unsigned short *data);
++ void (*read_cgram_char)(unsigned char index, unsigned char *pixmap);
++ void (*write_char)(unsigned int offset, unsigned short data);
++ void (*write_cgram_char)(unsigned char index, unsigned char *pixmap);
++ void (*clear_display)(void);
++ void (*address_mode)(int mode);
++ int (*validate_driver)(void);
++ int (*init_display)(void);
++ int (*cleanup_display)(void);
++ int (*init_port)(void);
++ int (*cleanup_port)(void);
++ int (*handle_custom_char)(unsigned int data);
++ int (*handle_custom_ioctl)(unsigned int cmd, unsigned long arg, unsigned int arg_in_userspace);
++
++ /* The character map to be used */
++ unsigned char *charmap;
++
++ /* The root where the driver can create its own proc files.
++ * Will be filled by the lcd-linux layer.
++ */
++ struct proc_dir_entry *driver_proc_root;
++
++ /* Set this field to 'driver_module_init' or call lcd_driver_setup
++ * just before registering the driver with lcd_register_driver.
++ */
++ struct module *driver_module;
++};
++
++#ifdef MODULE
++#define driver_module_init THIS_MODULE
++#else
++#define driver_module_init NULL
++#endif
++
++/* Always call lcd_driver_setup just before registering the driver
++ * with lcd_register_driver.
++ */
++static inline void lcd_driver_setup(struct lcd_driver *p)
++{
++ p->driver_module = driver_module_init;
++}
++
++/* External interface */
++struct lcd_struct;
++int lcd_register_driver(struct lcd_driver *drv, struct lcd_parameters *par);
++int lcd_unregister_driver(struct lcd_driver *drv, struct lcd_parameters *par);
++int lcd_open(unsigned short minor, struct lcd_struct **lcd);
++int lcd_close(struct lcd_struct **lcd);
++int lcd_ioctl(struct lcd_struct *lcd, unsigned int cmd, ...);
++ssize_t lcd_write(struct lcd_struct *lcd, const void *buffer, size_t length, loff_t offset, unsigned int);
++ssize_t lcd_read(struct lcd_struct *lcd, void *buffer, size_t length, loff_t offset, unsigned int);
++
++#endif /* __KERNEL__ */
++
++#endif /* External interface included */
diff --git a/target/linux/ep93xx/patches-2.6.30/003-ep93xx-i2c.patch b/target/linux/ep93xx/patches-2.6.30/003-ep93xx-i2c.patch
new file mode 100644
index 0000000000..aaf4d3ca8e
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/003-ep93xx-i2c.patch
@@ -0,0 +1,225 @@
+Index: linux-2.6.30.9/drivers/i2c/busses/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/drivers/i2c/busses/Kconfig 2009-11-24 21:00:21.000000000 +0100
++++ linux-2.6.30.9/drivers/i2c/busses/Kconfig 2009-11-24 21:00:23.000000000 +0100
+@@ -326,6 +326,10 @@
+ devices such as DaVinci NIC.
+ For details please see http://www.ti.com/davinci
+
++config I2C_EP93XX
++ tristate "EP93XX I2C"
++ depends on I2C && ARCH_EP93XX
++
+ config I2C_GPIO
+ tristate "GPIO-based bitbanging I2C"
+ depends on GENERIC_GPIO
+Index: linux-2.6.30.9/drivers/i2c/busses/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/drivers/i2c/busses/Makefile 2009-11-24 21:00:21.000000000 +0100
++++ linux-2.6.30.9/drivers/i2c/busses/Makefile 2009-11-24 21:00:23.000000000 +0100
+@@ -30,6 +30,7 @@
+ obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
+ obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
+ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
++obj-$(CONFIG_I2C_EP93XX) += i2c-ep93xx.o
+ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
+ obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
+ obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
+Index: linux-2.6.30.9/drivers/i2c/busses/i2c-ep93xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/i2c/busses/i2c-ep93xx.c 2009-11-24 21:00:38.000000000 +0100
+@@ -0,0 +1,193 @@
++/* ------------------------------------------------------------------------ *
++ * i2c-ep933xx.c I2C bus glue for Cirrus EP93xx *
++ * ------------------------------------------------------------------------ *
++
++ Copyright (C) 2004 Michael Burian
++
++ Based on i2c-parport-light.c
++ Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ * ------------------------------------------------------------------------ */
++
++
++//#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++#include <asm/io.h>
++#include <mach/hardware.h>
++
++//1/(2*clockfrequency)
++#define EE_DELAY_USEC 50
++#define GPIOG_EECLK 1
++#define GPIOG_EEDAT 2
++
++/* ----- I2C algorithm call-back functions and structures ----------------- */
++
++// TODO: optimize
++static void ep93xx_setscl(void *data, int state)
++{
++ unsigned int uiPGDR, uiPGDDR;
++
++ uiPGDR = inl(GPIO_PGDR);
++ uiPGDDR = inl(GPIO_PGDDR);
++
++ /* Configure the clock line as output. */
++ uiPGDDR |= GPIOG_EECLK;
++ outl(uiPGDDR, GPIO_PGDDR);
++
++ /* Set clock line to state */
++ if(state)
++ uiPGDR |= GPIOG_EECLK;
++ else
++ uiPGDR &= ~GPIOG_EECLK;
++
++ outl(uiPGDR, GPIO_PGDR);
++}
++
++static void ep93xx_setsda(void *data, int state)
++{
++ unsigned int uiPGDR, uiPGDDR;
++
++ uiPGDR = inl(GPIO_PGDR);
++ uiPGDDR = inl(GPIO_PGDDR);
++
++ /* Configure the data line as output. */
++ uiPGDDR |= GPIOG_EEDAT;
++ outl(uiPGDDR, GPIO_PGDDR);
++
++ /* Set data line to state */
++ if(state)
++ uiPGDR |= GPIOG_EEDAT;
++ else
++ uiPGDR &= ~GPIOG_EEDAT;
++
++ outl(uiPGDR, GPIO_PGDR);
++}
++
++static int ep93xx_getscl(void *data)
++{
++ unsigned int uiPGDR, uiPGDDR;
++
++ uiPGDR = inl(GPIO_PGDR);
++ uiPGDDR = inl(GPIO_PGDDR);
++
++ /* Configure the clock line as input */
++ uiPGDDR &= ~GPIOG_EECLK;
++ outl(uiPGDDR, GPIO_PGDDR);
++
++ /* Return state of the clock line */
++ return (inl(GPIO_PGDR) & GPIOG_EECLK) ? 1 : 0;
++}
++
++static int ep93xx_getsda(void *data)
++{
++ unsigned int uiPGDR, uiPGDDR;
++ uiPGDR = inl(GPIO_PGDR);
++ uiPGDDR = inl(GPIO_PGDDR);
++
++ /* Configure the data line as input */
++ uiPGDDR &= ~GPIOG_EEDAT;
++ outl(uiPGDDR, GPIO_PGDDR);
++
++ /* Return state of the data line */
++ return (inl(GPIO_PGDR) & GPIOG_EEDAT) ? 1 : 0;
++}
++
++/* ------------------------------------------------------------------------
++ * Encapsulate the above functions in the correct operations structure.
++ * This is only done when more than one hardware adapter is supported.
++ */
++
++/* last line (us, ms, timeout)
++ * us dominates the bit rate: 10us means: 100Kbit/sec(25 means 40kbps)
++ * 10ms not known
++ * 100ms timeout
++ */
++static struct i2c_algo_bit_data ep93xx_data = {
++ .setsda = ep93xx_setsda,
++ .setscl = ep93xx_setscl,
++ .getsda = ep93xx_getsda,
++ .getscl = ep93xx_getscl,
++ .udelay = 10,
++ //.mdelay = 10,
++ .timeout = HZ,
++};
++
++/* ----- I2c structure ---------------------------------------------------- */
++static struct i2c_adapter ep93xx_adapter = {
++ .owner = THIS_MODULE,
++ .class = I2C_CLASS_HWMON,
++ .algo_data = &ep93xx_data,
++ .name = "EP93XX I2C bit-bang interface",
++};
++
++/* ----- Module loading, unloading and information ------------------------ */
++
++static int __init i2c_ep93xx_init(void)
++{
++ unsigned long uiPGDR, uiPGDDR;
++
++ /* Read the current value of the GPIO data and data direction registers. */
++ uiPGDR = inl(GPIO_PGDR);
++ uiPGDDR = inl(GPIO_PGDDR);
++
++ /* If the GPIO pins have not been configured since reset, the data
++ * and clock lines will be set as inputs and with data value of 0.
++ * External pullup resisters are pulling them high.
++ * Set them both high before configuring them as outputs. */
++ uiPGDR |= (GPIOG_EEDAT | GPIOG_EECLK);
++ outl(uiPGDR, GPIO_PGDR);
++
++ /* Delay to meet the EE Interface timing specification. */
++ udelay(EE_DELAY_USEC);
++
++
++ /* Configure the EE data and clock lines as outputs. */
++ uiPGDDR |= (GPIOG_EEDAT | GPIOG_EECLK);
++ outl(uiPGDDR, GPIO_PGDDR);
++
++ /* Delay to meet the EE Interface timing specification. */
++ udelay(EE_DELAY_USEC);
++
++ /* Reset hardware to a sane state (SCL and SDA high) */
++ ep93xx_setsda(NULL, 1);
++ ep93xx_setscl(NULL, 1);
++
++ if (i2c_bit_add_bus(&ep93xx_adapter) > 0) {
++ printk(KERN_ERR "i2c-ep93xx: Unable to register with I2C\n");
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit i2c_ep93xx_exit(void)
++{
++ //i2c_bit_del_bus(&ep93xx_adapter);
++ i2c_del_adapter(&ep93xx_adapter);
++}
++
++MODULE_AUTHOR("Michael Burian");
++MODULE_DESCRIPTION("I2C bus glue for Cirrus EP93xx processors");
++MODULE_LICENSE("GPL");
++
++module_init(i2c_ep93xx_init);
++module_exit(i2c_ep93xx_exit);
diff --git a/target/linux/ep93xx/patches-2.6.30/004-simone-rtc.patch b/target/linux/ep93xx/patches-2.6.30/004-simone-rtc.patch
new file mode 100644
index 0000000000..69f2c5a9bd
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/004-simone-rtc.patch
@@ -0,0 +1,78 @@
+--- a/drivers/rtc/rtc-ds1307.c
++++ b/drivers/rtc/rtc-ds1307.c
+@@ -661,6 +661,13 @@ static int __devinit ds1307_probe(struct
+ goto exit_free;
+ }
+
++#if (defined(CONFIG_MACH_SIM_ONE))
++ /* SIM.ONE board needs 32khz clock on SQW/INTB pin */
++ i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
++ ds1307->regs[0] & ~DS1337_BIT_INTCN);
++ i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
++ ds1307->regs[0] | (DS1337_BIT_RS1 | DS1337_BIT_RS2));
++#endif
+ /* oscillator off? turn it on, so clock can tick. */
+ if (ds1307->regs[0] & DS1337_BIT_nEOSC)
+ ds1307->regs[0] &= ~DS1337_BIT_nEOSC;
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -570,6 +570,14 @@ config RTC_DRV_EP93XX
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ep93xx.
+
++config RTC_DRV_EP93XX_DS1337
++ bool "Cirrus Logic EP93XX using DS1337 chip"
++ depends on RTC_DRV_EP93XX && I2C && MACH_SIM_ONE
++ help
++ If you say yes here, the EP93XX driver will use the
++ battery-backed-up DS1337 RTC chip on the SIM.ONE board.
++ You almost certainly want this.
++
+ config RTC_DRV_SA1100
+ tristate "SA11x0/PXA2xx"
+ depends on ARCH_SA1100 || ARCH_PXA
+--- a/drivers/rtc/rtc-ep93xx.c
++++ b/drivers/rtc/rtc-ep93xx.c
+@@ -13,6 +13,13 @@
+ #include <linux/rtc.h>
+ #include <linux/platform_device.h>
+ #include <mach/hardware.h>
++#include <asm/io.h>
++
++#if defined(CONFIG_RTC_DRV_EP93XX_DS1337)
++extern int ds1337_do_command(int id, int cmd, void *arg);
++#define DS1337_GET_DATE 0
++#define DS1337_SET_DATE 1
++#endif
+
+ #define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x))
+ #define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000)
+@@ -37,16 +44,28 @@ static int ep93xx_get_swcomp(struct devi
+
+ static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+ {
++#if defined(CONFIG_RTC_DRV_EP93XX_DS1337)
++ /* Reroute the internal device to the DS1337 */
++ return ds1337_do_command(0, DS1337_GET_DATE, (void *)tm);
++#else
+ unsigned long time = __raw_readl(EP93XX_RTC_DATA);
+
+ rtc_time_to_tm(time, tm);
+ return 0;
++#endif
+ }
+
+ static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+ {
++#if defined(CONFIG_RTC_DRV_EP93XX_DS1337)
++ struct rtc_time tm;
++
++ rtc_time_to_tm(secs, &tm);
++ return ds1337_do_command(0, DS1337_SET_DATE, (void *)&tm);
++#else
+ __raw_writel(secs + 1, EP93XX_RTC_LOAD);
+ return 0;
++#endif
+ }
+
+ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
diff --git a/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch b/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch
new file mode 100644
index 0000000000..3664132021
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch
@@ -0,0 +1,3622 @@
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/dma_ep93xx.c
+@@ -0,0 +1,2940 @@
++/******************************************************************************
++ * arch/arm/mach-ep9312/dma_ep93xx.c
++ *
++ * Support functions for the ep93xx internal DMA channels.
++ * (see also Documentation/arm/ep93xx/dma.txt)
++ *
++ * Copyright (C) 2003 Cirrus Logic
++ *
++ * A large portion of this file is based on the dma api implemented by
++ * Nicolas Pitre, dma-sa1100.c, copyrighted 2000.
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ ****************************************************************************/
++#include <linux/autoconf.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/mach/dma.h>
++#include "dma_ep93xx.h"
++
++/*****************************************************************************
++ *
++ * Debugging macros
++ *
++ ****************************************************************************/
++#undef DEBUG
++//#define DEBUG 1
++#ifdef DEBUG
++#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )
++#else
++#define DPRINTK( fmt, arg... )
++#endif
++
++/*****************************************************************************
++ *
++ * static global variables
++ *
++ ****************************************************************************/
++ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];
++
++/*
++ * lock used to protect the list of dma channels while searching for a free
++ * channel during dma_request.
++ */
++//static spinlock_t dma_list_lock;
++static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;
++
++/*****************************************************************************
++ *
++ * Internal DMA processing functions.
++ *
++ ****************************************************************************/
++/*****************************************************************************
++ *
++ * get_dma_channel_from_handle()
++ *
++ * If Handle is valid, returns the DMA channel # (0 to 9 for channels 1-10)
++ * If Handle is not valid, returns -1.
++ *
++ ****************************************************************************/
++static int
++dma_get_channel_from_handle(int handle)
++{
++ int channel;
++
++ /*
++ * Get the DMA channel # from the handle.
++ */
++ channel = ((int)handle & DMA_HANDLE_SPECIFIER_MASK) >> 28;
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (dma_chan[channel].last_valid_handle != (int)handle) {
++ DPRINTK("DMA ERROR - invalid handle 0x%x \n", handle);
++ return(-1);
++ }
++
++ /*
++ * See if this instance is still open
++ */
++ if (!dma_chan[channel].ref_count )
++ return(-1);
++
++ return(channel);
++}
++
++static void dma_m2m_transfer_done(ep93xx_dma_t *dma)
++{
++ unsigned int uiCONTROL;
++ unsigned int M2M_reg_base = dma->reg_base;
++ unsigned int read_back;
++
++ DPRINTK("1 ");
++
++ outl( 0, M2M_reg_base+M2M_OFFSET_INTERRUPT );
++
++ if (dma->total_buffers) {
++ /*
++ * The current_buffer has already been tranfered, so add the
++ * byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current_buffer
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * check if there's a new buffer to transfer.
++ */
++ if (dma->new_buffers && dma->xfer_enable) {
++ /*
++ * We have a new buffer to transfer so program in the
++ * buffer values. Since a STALL interrupt was
++ * triggered, we program the buffer descriptor 0
++ *
++ * Set the SAR_BASE/DAR_BASE/BCR registers with values
++ * from the next buffer in the queue.
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2M_reg_base + M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].dest,
++ M2M_reg_base + M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2M_reg_base + M2M_OFFSET_BCR0 );
++
++ DPRINTK("SAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].source);
++ DPRINTK("DAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].dest);
++ DPRINTK("BCR0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].size);
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ /*
++ * If there's a second new buffer, we program the
++ * second buffer descriptor.
++ */
++ if (dma->new_buffers) {
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ dma->new_buffers--;
++ }
++ } else {
++ DPRINTK("2 \n");
++ /*
++ * There's a chance we setup both buffer descriptors,
++ * but didn't service the NFB quickly enough, causing
++ * the channel to transfer both buffers, then enter the
++ * stall state. So, we need to be able to process the
++ * second buffer.
++ */
++ if ((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
++ {
++ DPRINTK("3 ");
++
++ /*
++ * The current_buffer has already been
++ * tranferred, so add the byte count to the
++ * total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current buffer pointer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ }
++
++ /*
++ * No new buffers to transfer, so disable the channel.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++ } else {
++ /*
++ * No buffers to transfer, or old buffers to mark as used,
++ * so disable the channel
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Must read the control register back after a write.
++ */
++ read_back = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++}
++
++static void dma_m2m_next_frame_buffer(ep93xx_dma_t *dma)
++{
++ int loop;
++ unsigned int uiCONTROL;
++ unsigned int M2M_reg_base = dma->reg_base;
++
++ DPRINTK("5 ");
++
++ if (dma->total_buffers) {
++ DPRINTK("6 ");
++ /*
++ * The iCurrentBuffer has already been transfered. so add the
++ * byte count from the current buffer to the total byte count.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the Current Buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ if ((dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
++ (dma->new_buffers == 0) || (dma->xfer_enable == FALSE)) {
++ DPRINTK("7 ");
++
++ /*
++ * This is the last Buffer in this transaction, so
++ * disable the NFB interrupt. We shouldn't get an NFB
++ * int when the FSM moves to the ON state where it
++ * would typically get the NFB int indicating a new
++ * buffer can be programmed. Instead, once in the ON
++ * state, the DMA will just proceed to complete the
++ * transfer of the current buffer, move the FSB
++ * directly to the STALL state where a STALL interrupt
++ * will be generated.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_NFBINTEN ;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * The current buffer has been transferred, so
++ * increment the current buffer counter to reflect
++ * this.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ DPRINTK("End of NFB handling. \n");
++ DPRINTK("CONTROL - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
++ DPRINTK("STATUS - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_STATUS) );
++ DPRINTK("SAR_BASE0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_SAR_BASE0) );
++ DPRINTK("SAR_CUR0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_SAR_CURRENT0) );
++ DPRINTK("DAR_BASE0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_DAR_BASE0) );
++ DPRINTK("DAR_CUR0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_DAR_CURRENT0) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id,
++ dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last,
++ dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source,
++ dma->pause_buf.size, dma->pause_buf.last,
++ dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++
++ } else if (dma->new_buffers) {
++ DPRINTK("8 ");
++ /*
++ * We have a new buffer, so increment the current
++ * buffer to point to the next buffer, which is already
++ * programmed into the DMA. Next time around, it'll be
++ * pointing to the current buffer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * We know we have a new buffer to program as the next
++ * buffer, so check which set of SAR_BASE/DAR_BASE/BCR
++ * registers to program.
++ */
++ if ( inl(M2M_reg_base+M2M_OFFSET_STATUS) & STATUS_M2M_NB ) {
++ /*
++ * Set the SAR_BASE1/DAR_BASE1/BCR1 registers
++ * with values from the next buffer in the
++ * queue.
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++ } else {
++ /*
++ * Set the SAR_BASE0/DAR_BASE0/BCR0 registers
++ * with values from the next buffer in the
++ * queue.
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR0 );
++ }
++
++ /*
++ * Decrement the new buffers counter
++ */
++ dma->new_buffers--;
++ }
++ } else {
++ /*
++ * Total number of buffers is 0 - really we should never get
++ * here, but just in case.
++ */
++ DPRINTK("9 \n");
++
++ /*
++ * No new buffers to transfer, so Disable the channel
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Indicate that the channel is paused by starvation.
++ */
++ dma->pause = 1;
++ }
++}
++
++/*****************************************************************************
++ *
++ * dma_m2m_irq_handler
++ *
++ ****************************************************************************/
++static irqreturn_t
++dma_m2m_irq_handler(int irq, void *dev_id)
++{
++ ep93xx_dma_t *dma = (ep93xx_dma_t *)dev_id;
++ unsigned int M2M_reg_base = dma->reg_base;
++ ep93xx_dma_dev_t dma_int = UNDEF_INT;
++ int status;
++
++// printk("+m2m irq=%d\n", irq);
++
++ /*
++ * Determine what kind of dma interrupt this is.
++ */
++ status = inl(M2M_reg_base + M2M_OFFSET_INTERRUPT);
++ if ( status & INTERRUPT_M2M_DONEINT )
++ dma_int = DONE; // we're done with a requested dma
++ else if ( status & INTERRUPT_M2M_NFBINT )
++ dma_int = NFB; // we're done with one dma buffer
++
++ DPRINTK("IRQ: b=%#x st=%#x\n", (int)dma->current_buffer, dma_int);
++
++ switch (dma_int) {
++ /*
++ * Next Frame Buffer Interrupt. If there's a new buffer program it
++ * Check if this is the last buffer in the transfer,
++ * and if it is, disable the NFB int to prevent being
++ * interrupted for another buffer when we know there won't be
++ * another.
++ */
++ case NFB:
++ dma_m2m_next_frame_buffer(dma);
++ break;
++ /*
++ * Done interrupt generated, indicating that the transfer is complete.
++ */
++ case DONE:
++ dma_m2m_transfer_done(dma);
++ break;
++
++ default:
++ break;
++ }
++
++ if ((dma_int != UNDEF_INT) && dma->callback)
++ dma->callback(dma_int, dma->device, dma->user_data);
++
++ return IRQ_HANDLED;
++}
++
++/*****************************************************************************
++ *
++ * dma_m2p_irq_handler
++ *
++ *
++ *
++ ****************************************************************************/
++static irqreturn_t
++dma_m2p_irq_handler(int irq, void *dev_id)
++{
++ ep93xx_dma_t *dma = (ep93xx_dma_t *) dev_id;
++ unsigned int M2P_reg_base = dma->reg_base;
++ unsigned int read_back;
++ ep93xx_dma_dev_t dma_int = UNDEF_INT;
++ unsigned int loop, uiCONTROL, uiINTERRUPT;
++
++ /*
++ * Determine what kind of dma interrupt this is.
++ */
++ if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_STALLINT )
++ dma_int = STALL;
++ else if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_NFBINT )
++ dma_int = NFB;
++ else if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_CHERRORINT )
++ dma_int = CHERROR;
++
++ /*
++ * Stall Interrupt: The Channel is stalled, meaning nothing is
++ * programmed to transfer right now. So, we're back to the
++ * beginnning. If there's a buffer to transfer, program it into
++ * max and base 0 registers.
++ */
++ if (dma_int == STALL) {
++ DPRINTK("1 ");
++
++ if (dma->total_buffers) {
++ /*
++ * The current_buffer has already been tranfered, so
++ * add the byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current_buffer
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * check if there's a new buffer to transfer.
++ */
++ if (dma->new_buffers && dma->xfer_enable) {
++ /*
++ * We have a new buffer to transfer so program
++ * in the buffer values. Since a STALL
++ * interrupt was triggered, we program the
++ * base0 and maxcnt0
++ *
++ * Set the MAXCNT0 register with the buffer
++ * size
++ */
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT0 );
++
++ /*
++ * Set the BASE0 register with the buffer base
++ * address
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2P_reg_base+M2P_OFFSET_BASE0 );
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ if (dma->new_buffers) {
++ DPRINTK("A ");
++ /*
++ * Set the MAXCNT1 register with the
++ * buffer size
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT1 );
++
++ /*
++ * Set the BASE1 register with the
++ * buffer base address
++ */
++ outl( dma->buffer_queue[dma->current_buffer + 1 %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE1 );
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ /*
++ * Enable the NFB Interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ }
++ } else {
++ /*
++ * No new buffers.
++ */
++ DPRINTK("2 \n");
++
++ /*
++ * There's a chance we setup both buffer descriptors, but
++ * didn't service the NFB quickly enough, causing the channel
++ * to transfer both buffers, then enter the stall state.
++ * So, we need to be able to process the second buffer.
++ */
++ if ((dma->used_buffers + dma->new_buffers) < dma->total_buffers) {
++ DPRINTK("3 ");
++
++ /*
++ * The current_buffer has already been tranfered, so add the
++ * byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current buffer pointer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ }
++
++ /*
++ * No new buffers to transfer, so disable the channel.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Indicate that this channel is in the pause by starvation
++ * state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++ }
++ } else {
++ /*
++ * No buffers to transfer, or old buffers to mark as used,
++ * so Disable the channel
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Must read the control register back after a write.
++ */
++ read_back = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++ }
++
++ /*
++ * Next Frame Buffer Interrupt. If there's a new buffer program it
++ * Check if this is the last buffer in the transfer,
++ * and if it is, disable the NFB int to prevent being
++ * interrupted for another buffer when we know there won't be
++ * another.
++ */
++ if (dma_int == NFB) {
++ DPRINTK("5 ");
++
++ if (dma->total_buffers) {
++ DPRINTK("6 ");
++ /*
++ * The iCurrentBuffer has already been transfered. so add the
++ * byte count from the current buffer to the total byte count.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the Current Buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ if ((dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
++ (dma->new_buffers == 0) || (dma->xfer_enable == FALSE)) {
++ DPRINTK("7 ");
++
++ /*
++ * This is the last Buffer in this transaction, so disable
++ * the NFB interrupt. We shouldn't get an NFB int when the
++ * FSM moves to the ON state where it would typically get the
++ * NFB int indicating a new buffer can be programmed.
++ * Instead, once in the ON state, the DMA will just proceed
++ * to complet the transfer of the current buffer, move the
++ * FSB directly to the STALL state where a STALL interrupt
++ * will be generated.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * The current buffer has been transferred, so increment
++ * the current buffer counter to reflect this.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ DPRINTK("End of NFB handling. \n");
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++
++ } else if (dma->new_buffers) {
++ DPRINTK("8 ");
++ /*
++ * we have a new buffer, so increment the current buffer to
++ * point to the next buffer, which is already programmed into
++ * the DMA. Next time around, it'll be pointing to the
++ * current buffer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * we know we have a new buffer to program as the next
++ * buffer, so check which set of MAXCNT and BASE registers
++ * to program.
++ */
++ if ( inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER ) {
++ /*
++ * Set the MAXCNT1 register with the buffer size
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT1 );
++
++ /*
++ * Set the BASE1 register with the buffer base address
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE1 );
++ } else {
++ /*
++ * Set the MAXCNT0 register with the buffer size
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT0 );
++
++ /*
++ * Set the BASE0 register with the buffer base address
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE0 );
++ }
++
++ /*
++ * Decrement the new buffers counter
++ */
++ dma->new_buffers--;
++ }
++ } else {
++ /*
++ * Total number of buffers is 0 - really we should never get here,
++ * but just in case.
++ */
++ DPRINTK("9 \n");
++
++ /*
++ * No new buffers to transfer, so Disable the channel
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ }
++ }
++
++ /*
++ * Channel Error Interrupt, or perhipheral interrupt, specific to the
++ * memory to/from peripheral channels.
++ */
++ if (dma_int == CHERROR) {
++ /*
++ * just clear the interrupt, it's really up to the peripheral
++ * driver to determine if any further action is necessary.
++ */
++ uiINTERRUPT = inl(M2P_reg_base+M2P_OFFSET_INTERRUPT);
++ uiINTERRUPT &= ~INTERRUPT_M2P_CHERRORINT;
++ outl( uiINTERRUPT, M2P_reg_base+M2P_OFFSET_INTERRUPT );
++ }
++
++ /*
++ * Make sure the interrupt was valid, and if it was, then check
++ * if a callback function was installed for this DMA channel. If a
++ * callback was installed call it.
++ */
++ if ((dma_int != UNDEF_INT) && dma->callback)
++ dma->callback(dma_int, dma->device, dma->user_data);
++
++ return IRQ_HANDLED;
++}
++
++/*****************************************************************************
++ *
++ * ep9312_dma_open_m2p(int device)
++ *
++ * Description: This function will attempt to open a M2P/P2M DMA channel.
++ * If the open is successful, the channel number is returned,
++ * otherwise a negative number is returned.
++ *
++ * Parameters:
++ * device: device for which the dma channel is requested.
++ *
++ ****************************************************************************/
++static int
++dma_open_m2p(int device)
++{
++ int channel = -1;
++ unsigned int loop;
++ unsigned int M2P_reg_base;
++ unsigned int uiPWRCNT;
++ /*unsigned long flags;*/
++
++ DPRINTK("DMA Open M2P with hw dev %d\n", device);
++
++ /*
++ * Lock the dma channel list.
++ */
++ //spin_lock_irqsave(&dma_list_lock, flags);
++ spin_lock(&dma_list_lock);
++
++ /*
++ * Verify that the device requesting DMA isn't already using a DMA channel
++ */
++ if (device >= 10)
++ loop = 1; // Rx transfer requested
++ else
++ loop = 0; // Tx transfer requested
++
++ for (; loop < 10; loop = loop + 2)
++ /*
++ * Before checking for a matching device, check that the
++ * channel is in use, otherwise the device field is
++ * invalid.
++ */
++ if (dma_chan[loop].ref_count)
++ if (device == dma_chan[loop].device) {
++ DPRINTK("DMA Open M2P - Error\n");
++ return(-1);
++ }
++
++ /*
++ * Get a DMA channel instance for the given hardware device.
++ * If this is a TX look for even numbered channels, else look for
++ * odd numbered channels
++ */
++ if (device >= 10)
++ loop = 1; /* Rx transfer requested */
++ else
++ loop = 0; /* Tx transfer requested */
++
++ for (; loop < 10; loop = loop + 2)
++ if (!dma_chan[loop].ref_count) {
++ /*
++ * Capture the channel and increment the reference count.
++ */
++ channel = loop;
++ dma_chan[channel].ref_count++;
++ break;
++ }
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++ spin_unlock(&dma_list_lock);
++ /*
++ * See if we got a valid channel.
++ */
++ if (channel < 0)
++ return(-1);
++
++ /*
++ * Point regs to the correct dma channel register base.
++ */
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Turn on the clock for the specified DMA channel
++ * TODO: need to use the correct register name for the
++ * power control register.
++ */
++ uiPWRCNT = inl(/*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++ switch (channel) {
++ case 0:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH0;
++ break;
++
++ case 1:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH1;
++ break;
++
++ case 2:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH2;
++ break;
++
++ case 3:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH3;
++ break;
++
++ case 4:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH4;
++ break;
++
++ case 5:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH5;
++ break;
++
++ case 6:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH6;
++ break;
++
++ case 7:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH7;
++ break;
++
++ case 8:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH8;
++ break;
++
++ case 9:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH9;
++ break;
++
++ default:
++ return(-1);
++ }
++ outl( uiPWRCNT, /*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL );
++
++ /*
++ * Clear out the control register before any further setup.
++ */
++ outl( 0, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Setup the peripheral port value in the DMA channel registers.
++ */
++ if (device < 10)
++ outl( (unsigned int)device, M2P_reg_base+M2P_OFFSET_PPALLOC );
++ else
++ outl( (unsigned int)(device - 10), M2P_reg_base+M2P_OFFSET_PPALLOC );
++
++ /*
++ * Let's hold on to the value of the Hw device for comparison later.
++ */
++ dma_chan[channel].device = device;
++
++ /*
++ * Success.
++ */
++ return(channel);
++}
++
++/*****************************************************************************
++ *
++ * dma_open_m2m(int device)
++ *
++ * Description: This function will attempt to open a M2M DMA channel.
++ * If the open is successful, the channel number is returned,
++ * otherwise a negative number is returned.
++ *
++ * Parameters:
++ * device: device for which the dma channel is requested.
++ *
++ ****************************************************************************/
++static int
++dma_open_m2m(int device)
++{
++ int channel = -1;
++ unsigned int loop;
++ unsigned int M2M_reg_base;
++ unsigned int uiPWRCNT, uiCONTROL;
++ /*unsigned long flags;*/
++
++ DPRINTK("DMA Open M2M with hw dev %d\n", device);
++
++ /*
++ * Lock the dma channel list.
++ */
++ //spin_lock_irqsave(&dma_list_lock, flags);
++ spin_lock(&dma_list_lock);
++
++
++ /*
++ * Check if this device is already allocated a channel.
++ * TODO: can one M2M device be allocated multiple channels?
++ */
++ for (loop = 10; loop < 12; loop++)
++ /*
++ * Before checking for a matching device, check that the
++ * channel is in use, otherwise the device field is
++ * invalid.
++ */
++ if (dma_chan[loop].ref_count)
++ if (device == dma_chan[loop].device) {
++ DPRINTK("Error - dma_open_m2m - already allocated channel\n");
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++ spin_unlock(&dma_list_lock);
++ /*
++ * Fail.
++ */
++ return(-1);
++ }
++
++ /*
++ * Get a DMA channel instance for the given hardware device.
++ */
++ for (loop = 10; loop < 12; loop++)
++ if (!dma_chan[loop].ref_count) {
++ /*
++ * Capture the channel and increment the reference count.
++ */
++ channel = loop;
++ dma_chan[channel].ref_count++;
++ break;
++ }
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock(dma_list_lock);
++ spin_unlock(&dma_list_lock);
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++
++ /*
++ * See if we got a valid channel.
++ */
++ if (channel < 0)
++ return(-1);
++
++ /*
++ * Point regs to the correct dma channel register base.
++ */
++ M2M_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Turn on the clock for the specified DMA channel
++ * TODO: need to use the correct register name for the
++ * power control register.
++ */
++ uiPWRCNT = inl(/*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++ switch (channel) {
++ case 10:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH0;
++ break;
++
++ case 11:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH1;
++ break;
++
++ default:
++ return(-1);
++ }
++ outl( uiPWRCNT, /*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++
++ DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) );
++
++ /*
++ * Clear out the control register before any further setup.
++ */
++ outl( 0, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Setup the transfer mode and the request source selection within
++ * the DMA M2M channel registers.
++ */
++ switch (device) {
++ case DMA_MEMORY:
++ /*
++ * Clear TM field, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_TM_MASK | CONTROL_M2M_RSS_MASK);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMA_IDE:
++ /*
++ * Set RSS field to 3, Set NO_HDSK, Set PW field to 1
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_PW_MASK);
++ uiCONTROL |= (3<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (2<<CONTROL_M2M_PW_SHIFT);
++
++ uiCONTROL &= ~(CONTROL_M2M_ETDP_MASK);
++ uiCONTROL &= ~(CONTROL_M2M_DACKP);
++ uiCONTROL &= ~(CONTROL_M2M_DREQP_MASK);
++
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ break;
++
++ case DMARx_SSP:
++ /*
++ * Set RSS field to 1, Set NO_HDSK, Set TM field to 2
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= (1<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (2<<CONTROL_M2M_TM_SHIFT);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMATx_SSP:
++ /*
++ * Set RSS field to 2, Set NO_HDSK, Set TM field to 1
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= (2<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (1<<CONTROL_M2M_TM_SHIFT);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMATx_EXT_DREQ:
++ /*
++ * Set TM field to 2, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= 1<<CONTROL_M2M_TM_SHIFT;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMARx_EXT_DREQ:
++ /*
++ * Set TM field to 2, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= 2<<CONTROL_M2M_TM_SHIFT;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ default:
++ return -1;
++ }
++
++ /*
++ * Let's hold on to the value of the Hw device for comparison later.
++ */
++ dma_chan[channel].device = device;
++
++ /*
++ * Success.
++ */
++ return(channel);
++}
++
++/*****************************************************************************
++ *
++ * int dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
++ * dma_callback callback, unsigned int user_data)
++ *
++ * Description: Configure the DMA channel and install a callback function.
++ * This function will have to be called for every transfer
++ *
++ * dma: Pointer to the dma instance data for the M2M channel to
++ * configure.
++ * flags_m2m Flags used to configure an M2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call.
++ * callback function pointer which is called near the end of the
++ * dma channel's irq handler.
++ * user_data defined by the calling driver.
++ *
++ ****************************************************************************/
++static int
++dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
++ dma_callback callback, unsigned int user_data)
++{
++ unsigned long flags;
++ unsigned int M2M_reg_base, uiCONTROL;
++
++ /*
++ * Make sure the channel is disabled before configuring the channel.
++ *
++ * TODO: Is this correct?? Making a big change here...
++ */
++ /* if (!dma->pause || (!dma->pause && dma->xfer_enable)) */
++ if (dma->xfer_enable) {
++ /*
++ * DMA channel is not paused, so we can't configure it.
++ */
++ DPRINTK("DMA channel not paused, so can't configure! \n");
++ return(-1);
++ }
++
++ /*
++ * Mask interrupts.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Setup a pointer into the dma channel's register set.
++ */
++ M2M_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++ outl(0, M2M_reg_base + M2M_OFFSET_CONTROL);
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++ outl(uiCONTROL, M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * By default we disable the stall interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_STALLINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * By default we disable the done interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_DONEINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Set up the transfer control fields based on values passed in
++ * the flags_m2m field.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++
++ if ( flags_m2m & DESTINATION_HOLD )
++ uiCONTROL |= CONTROL_M2M_DAH;
++ else
++ uiCONTROL &= ~CONTROL_M2M_DAH;
++
++ if ( flags_m2m & SOURCE_HOLD )
++ uiCONTROL |= CONTROL_M2M_SAH;
++ else
++ uiCONTROL &= ~CONTROL_M2M_SAH;
++
++ uiCONTROL &= ~CONTROL_M2M_TM_MASK;
++ uiCONTROL |= (((flags_m2m & TRANSFER_MODE_MASK) >> TRANSFER_MODE_SHIFT) <<
++ CONTROL_M2M_TM_SHIFT) & CONTROL_M2M_TM_MASK;
++
++ uiCONTROL &= ~CONTROL_M2M_PWSC_MASK;
++ uiCONTROL |= (((flags_m2m & WAIT_STATES_MASK) >> WAIT_STATES_SHIFT) <<
++ CONTROL_M2M_PWSC_SHIFT) & CONTROL_M2M_PWSC_MASK;
++
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * Save the callback function in the dma instance for this channel.
++ */
++ dma->callback = callback;
++
++ /*
++ * Save the user data in the the dma instance for this channel.
++ */
++ dma->user_data = user_data;
++
++ /*
++ * Put the dma instance into the pause state by setting the
++ * pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int dma_start(int handle, unsigned int channels, unsigned int * handles)
++ *
++ * Description: Initiate a transfer on up to 3 channels.
++ *
++ * handle: handle for the channel to initiate transfer on.
++ * channels: number of channels to initiate transfers on.
++ * handles: pointer to an array of handles, one for each channel which
++ * is to be started.
++ *
++ ****************************************************************************/
++static int
++dma_start_m2m(int channel, ep93xx_dma_t * dma)
++{
++ unsigned long flags;
++ unsigned int M2M_reg_base = dma->reg_base;
++ unsigned int uiCONTROL;
++
++ /*
++ * Mask interrupts while we get this started.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Make sure the channel has at least one buffer in the queue.
++ */
++ if (dma->new_buffers < 1) {
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ DPRINTK("DMA Start: Channel starved.\n");
++
++ /*
++ * This channel does not have enough buffers queued up,
++ * so enter the pause by starvation state.
++ */
++ dma->xfer_enable = TRUE;
++ dma->pause = TRUE;
++
++ /*
++ * Success.
++ */
++ return(0);
++ }
++
++ /*
++ * Clear any pending interrupts.
++ */
++ outl(0x0, M2M_reg_base+M2M_OFFSET_INTERRUPT);
++
++ /*
++ * Set up one or both buffer descriptors with values from the next one or
++ * two buffers in the queue. By default disable the next frame buffer
++ * interrupt on the channel.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * enable the done interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_DONEINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Update the dma channel instance transfer state.
++ */
++ dma->xfer_enable = TRUE;
++ dma->pause = FALSE;
++
++ /*
++ * Program up the first buffer descriptor with a source and destination
++ * and a byte count.
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2M_reg_base+M2M_OFFSET_BCR0 );
++
++ /*
++ * Decrement the new buffers counter.
++ */
++ dma->new_buffers--;
++
++ /*
++ * Set up the second buffer descriptor with a second buffer if we have
++ * a second buffer.
++ */
++ if (dma->new_buffers) {
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ dma->new_buffers--;
++ }
++
++ /*
++ * Now we enable the channel. This initiates the transfer.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * If this is a memory to memory transfer, we need to s/w trigger the
++ * transfer by setting the start bit within the control register.
++ */
++ if (dma->device == DMA_MEMORY) {
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_START;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ DPRINTK("DMA - It's been started!!");
++ DPRINTK("CONTROL - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
++ DPRINTK("STATUS - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_STATUS) );
++ DPRINTK("BCR0 - 0x%x \n", dma->buffer_queue[dma->current_buffer].size);
++ DPRINTK("SAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_BASE0) );
++ DPRINTK("SAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_CURRENT0) );
++ DPRINTK("DAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_BASE0) );
++ DPRINTK("DAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_CURRENT0) );
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * DMA interface functions
++ *
++ ****************************************************************************/
++
++/*****************************************************************************
++ *
++ * int dma_init(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
++ * dma_callback callback, unsigned int user_data)
++ *
++ * Description: Configure the DMA channel and install a callback function.
++ *
++ * handle: Handle unique the each instance of the dma interface, used
++ * to verify this call.
++ * flags_m2p Flags used to configure an M2P/P2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call. This field should be NULL if handle represents
++ * an M2M channel.
++ * flags_m2m Flags used to configure an M2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call. This field should be NULL if handle represents
++ * an M2P/P2M channel.
++ * callback function pointer which is called near the end of the
++ * dma channel's irq handler.
++ * user_data defined by the calling driver.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_config(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
++ dma_callback callback, unsigned int user_data)
++{
++ int channel;
++ ep93xx_dma_t * dma;
++ unsigned long flags;
++ unsigned int M2P_reg_base, uiCONTROL;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Config: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA Config \n");
++
++ dma = &dma_chan[channel];
++
++ local_irq_save(flags);
++
++ /*
++ * Check if the channel is currently transferring.
++ */
++ if (dma->xfer_enable) {
++ local_irq_restore(flags);
++ return(-EINVAL);
++ }
++
++ /*
++ * Check if this is an m2m function.
++ */
++ if (channel >= 10) {
++ local_irq_restore(flags);
++
++ /*
++ * Call another function to handle m2m config.
++ */
++ return(dma_config_m2m(dma, flags_m2m, callback, user_data));
++ }
++
++ /*
++ * Setup a pointer into the dma channel's register set.
++ */
++ M2P_reg_base = dma->reg_base;
++
++ /*
++ * By default we enable the stall interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_STALLINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Configure the channel for an error from the peripheral.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && CHANNEL_ERROR_INT_ENABLE )
++ uiCONTROL |= CONTROL_M2P_CHERRORINTEN;
++ else
++ uiCONTROL &= ~CONTROL_M2P_CHERRORINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && CHANNEL_ABORT )
++ uiCONTROL |= CONTROL_M2P_ABRT;
++ else
++ uiCONTROL &= ~CONTROL_M2P_ABRT;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && IGNORE_CHANNEL_ERROR )
++ uiCONTROL |= CONTROL_M2P_ICE;
++ else
++ uiCONTROL &= ~CONTROL_M2P_ICE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Save the callback function in the dma instance for this channel.
++ */
++ dma->callback = callback;
++
++ /*
++ * Save the user data in the the dma instance for this channel.
++ */
++ dma->user_data = user_data;
++
++ /*
++ * Put the dma instance into the pause state by setting the
++ * pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int dma_start(int handle, unsigned int channels, unsigned int * handles)
++ *
++ * Description: Initiate a transfer on up to 3 channels.
++ *
++ * handle: handle for the channel to initiate transfer on.
++ * channels: number of channels to initiate transfers on.
++ * handles: pointer to an array of handles, one for each channel which
++ * is to be started.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_start(int handle, unsigned int channels, unsigned int * handles)
++{
++ ep93xx_dma_t * dma_pointers[3];
++ unsigned int M2P_reg_bases[3];
++ unsigned int loop, uiCONTROL;
++ unsigned long flags;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ if (channels < 1) {
++ printk(KERN_ERR "DMA Start: Invalid parameter.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA Start \n");
++
++ /*
++ * Mask off registers.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Check if this is a start multiple.
++ */
++ if (channels > 1) {
++ DPRINTK("DMA ERROR: Start, multiple start not supported yet \n");
++ return(-1);
++ } else {
++ /*
++ * Check if this channel is already transferring.
++ */
++ if (dma_chan[channel].xfer_enable && !dma_chan[channel].pause) {
++ printk(KERN_ERR
++ "DMA Start: Invalid command for channel %d.\n", channel);
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * This channel is already transferring, so return an error.
++ */
++ return(-EINVAL);
++ }
++
++ /*
++ * If this is an M2M channel, call a different function.
++ */
++ if (channel >= 10) {
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Call the m2m start function. Only start one channel.
++ */
++ return(dma_start_m2m(channel, &dma_chan[channel]));
++ }
++
++ /*
++ * Make sure the channel has at least one buffer in the queue.
++ */
++ if (dma_chan[channel].new_buffers < 1) {
++ DPRINTK("DMA Start: Channel starved.\n");
++
++ /*
++ * This channel does not have enough buffers queued up,
++ * so enter the pause by starvation state.
++ */
++ dma_chan[channel].xfer_enable = TRUE;
++ dma_chan[channel].pause = TRUE;
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++ }
++
++ /*
++ * Set up a dma instance pointer for this dma channel.
++ */
++ dma_pointers[0] = &dma_chan[channel];
++
++ /*
++ * Set up a pointer to the register set for this channel.
++ */
++ M2P_reg_bases[0] = dma_pointers[0]->reg_base;
++ }
++
++ /*
++ * Setup both MAXCNT registers with values from the next two buffers
++ * in the queue, and enable the next frame buffer interrupt on the channel.
++ */
++ for (loop = 0; loop < channels; loop++) {
++ /*
++ * Check if we need to restore a paused transfer.
++ */
++ if (dma_pointers[loop]->pause_buf.buf_id != -1)
++ outl( dma_pointers[loop]->pause_buf.size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
++ else
++ outl( dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
++ }
++
++ for (loop = 0; loop < channels; loop++) {
++ /*
++ * Enable the specified dma channels.
++ */
++ uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
++
++ /*
++ * Update the dma channel instance transfer state.
++ */
++ dma_pointers[loop]->xfer_enable = TRUE;
++ dma_pointers[loop]->pause = FALSE;
++ }
++
++ /*
++ * Program up the BASE0 registers for all specified channels, this
++ * will initiate transfers on all specified channels.
++ */
++ for (loop = 0; loop < channels; loop++)
++ /*
++ * Check if we need to restore a paused transfer.
++ */
++ if (dma_pointers[loop]->pause_buf.buf_id != -1) {
++ outl( dma_pointers[loop]->pause_buf.source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
++
++ /*
++ * Set the pause buffer to NULL
++ */
++ dma_pointers[loop]->pause_buf.buf_id = -1;
++ dma_pointers[loop]->pause_buf.size = 0;
++ } else if(dma_pointers[loop]->new_buffers){
++ outl( dma_pointers[loop]->buffer_queue[
++ dma_pointers[loop]->current_buffer].source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
++ dma_pointers[loop]->new_buffers--;
++
++ }
++
++ /*
++ * Before restoring irqs setup the second MAXCNT/BASE
++ * register with a second buffer.
++ */
++ for (loop = 0; loop < channels; loop++)
++ if (dma_pointers[loop]->new_buffers) {
++ /*
++ * By default we enable the next frame buffer interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
++
++ outl( dma_pointers[loop]->buffer_queue[
++ (dma_pointers[loop]->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT1 );
++
++ outl( dma_pointers[loop]->buffer_queue[
++ (dma_pointers[loop]->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE1 );
++ dma_pointers[loop]->new_buffers--;
++ }
++
++ /*
++ DPRINTK("DMA - It's been started!!");
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
++ DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
++ DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
++ DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
++ DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
++ DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
++ DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
++ */
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_add_buffer(int handle, unsigned int * address,
++ * unsigned int size, unsigned int last)
++ *
++ * Description: Add a buffer entry to the DMA buffer queue.
++ *
++ * handle: handle for the channel to add this buffer to.
++ * address: Pointer to an integer which is the start address of the
++ * buffer which is to be added to the queue.
++ * size: size of the buffer in bytes.
++ * last: 1 if this is the last buffer in this stream, 0 otherwise.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_add_buffer(int handle, unsigned int source, unsigned int dest,
++ unsigned int size, unsigned int last,
++ unsigned int buf_id)
++{
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++#if 0
++ static int peak_total_buffers=0;
++#endif
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Add Buffer: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the dma instance.
++ */
++ dma = &dma_chan[channel];
++
++#if 0
++ if( dma->total_buffers > peak_total_buffers )
++ {
++ peak_total_buffers=dma->total_buffers;
++ printk("peak_total_buffers=%d\n", peak_total_buffers );
++ }
++#endif
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * If the buffer queue is full, last_buffer is the same as current_buffer and
++ * we're not tranfering, or last_buffer is pointing to a used buffer, then exit.
++ * TODO: do I need to do any more checks?
++ */
++ if (dma->total_buffers >= MAX_EP93XX_DMA_BUFFERS)
++ {
++ DPRINTK("too many dma buffers: MAX_EP93XX_DMA_BUFFERS set to low ?\n");
++ /*
++ * Restore the state of the irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Fail.
++ */
++ return(-1);
++ }
++
++ /*
++ * Add this buffer to the queue
++ */
++ dma->buffer_queue[dma->last_buffer].source = source;
++ dma->buffer_queue[dma->last_buffer].dest = dest;
++ dma->buffer_queue[dma->last_buffer].size = size;
++ dma->buffer_queue[dma->last_buffer].last = last;
++ dma->buffer_queue[dma->last_buffer].buf_id = buf_id;
++
++ /*
++ * Reset the used field of the buffer structure.
++ */
++ dma->buffer_queue[dma->last_buffer].used = FALSE;
++
++ /*
++ * Increment the End Item Pointer.
++ */
++ dma->last_buffer = (dma->last_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * Increment the new buffers counter and the total buffers counter
++ */
++ dma->new_buffers++;
++ dma->total_buffers++;
++
++ /*
++ * restore the interrupt state.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Check if the channel was starved into a stopped state.
++ */
++ if (dma->pause && dma->xfer_enable) {
++ if (dma->new_buffers >= 1) {
++ DPRINTK("DMA - calling start from add after starve. \n");
++
++ /*
++ * The channel was starved into a stopped state, and we've got
++ * 2 new buffers, so start tranferring again.
++ */
++ ep93xx_dma_start(handle, 1, 0);
++ }
++ }
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_remove_buffer(int handle, unsigned int * address,
++ * unsigned int * size)
++ *
++ * Description: Remove a buffer entry from the DMA buffer queue. If
++ * buffer was removed successfully, return 0, otherwise
++ * return -1.
++ *
++ * handle: handle for the channel to remove a buffer from.
++ * address: Pointer to an integer which is filled in with the start
++ * address of the removed buffer.
++ * size: Pointer to an integer which is filled in with the size in
++ * bytes of the removed buffer.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id)
++{
++ unsigned int test;
++ unsigned int loop;
++ int return_val = -1;
++ unsigned long flags;
++ ep93xx_dma_t *dma;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Remove Buffer: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ dma = &dma_chan[channel];
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Make sure there are used buffers to be returned.
++ */
++ if (dma->used_buffers) {
++ test = dma->last_buffer;
++
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++) {
++ if (dma->buffer_queue[test].used && (dma->buffer_queue[test].buf_id != -1)) {
++ /*DPRINTK("buffer %d used \n", test); */
++
++ /*
++ * This is a used buffer, fill in the buf_id pointer
++ * with the buf_id for this buffer.
++ */
++ *buf_id = dma->buffer_queue[test].buf_id;
++
++ /*
++ * Reset this buffer structure
++ */
++ dma->buffer_queue[test].buf_id = -1;
++
++ /*
++ * Decrement the used buffer counter, and the total buffer counter.
++ */
++ dma->used_buffers--;
++ dma->total_buffers--;
++
++ /*
++ * Successful removal of a buffer, so set the return
++ * value to 0, then exit this loop.
++ */
++ return_val = 0;
++ break;
++ }
++
++ /*
++ * This buffer isn't used, let's see if the next one is.
++ */
++ test = (test + 1) % MAX_EP93XX_DMA_BUFFERS;
++ }
++ }
++
++ /*
++ * Restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(return_val);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_pause(int handle, unsigned int channels,
++ * unsigned int * handles)
++ *
++ * Description: Disable any ongoing transfer for the given channel, retaining
++ * the state of the current buffer transaction so that upon
++ * resume, the dma will continue where it left off.
++ *
++ * handle: Handle for the channel to be paused. If this is a pause for
++ * for multiple channels, handle is a valid handle for one of
++ * the channels to be paused.
++ * channels: number of channel to pause transfers on.
++ * handles: Pointer to an array of handles, one for each channel which
++ * to be paused. If this pause is intended only for one
++ * channel, this field should be set to NULL.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_pause(int handle, unsigned int channels, unsigned int * handles)
++{
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++
++ DPRINTK("ep93xx_dma_pause \n");
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ printk(KERN_ERR
++ "DMA Pause: Invalid dma handle.\n");
++
++ /*
++ * Fail.
++ */
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: pause \n", channel);
++
++ /*
++ * Set up a pointer to the dma instance data.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Check if we're already paused.
++ */
++ if (dma->pause) {
++ /*
++ * We're paused, but are we stopped?
++ */
++ if (dma->xfer_enable)
++ /*
++ * Put the channel in the stopped state.
++ */
++ dma->xfer_enable = FALSE;
++
++ DPRINTK("DMA Pause - already paused.");
++ } else {
++ /*
++ * Put the channel into the stopped state.
++ */
++ dma->xfer_enable = FALSE;
++ dma->pause = TRUE;
++ }
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Already paused, so exit.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * void ep93xx_dma_flush(int handle)
++ *
++ * Description: Flushes all queued buffers and transfers in progress
++ * for the given channel. Return the buffer entries
++ * to the calling function.
++ *
++ * handle: handle for the channel for which the flush is intended.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_flush(int handle)
++{
++ unsigned int loop;
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++ unsigned int M2P_reg_base,uiCONTROL;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Flush: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: flush \n", channel);
++
++ /*
++ * Set up a pointer to the dma instance data for this channel
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Disable the dma channel
++ */
++ if (channel < 10) {
++ /*
++ * M2P channel
++ */
++ uiCONTROL = inl(dma->reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, dma->reg_base+M2P_OFFSET_CONTROL );
++ } else {
++ /*
++ * M2M channel
++ */
++ uiCONTROL = inl(dma->reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, dma->reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
++ {
++ dma->buffer_queue[loop].buf_id = -1;
++ dma->buffer_queue[loop].last = 0;
++ }
++
++ /*
++ * Set the Current and Last item to zero.
++ */
++ dma->current_buffer = 0;
++ dma->last_buffer = 0;
++
++ /*
++ * Reset the Buffer counters
++ */
++ dma->used_buffers = 0;
++ dma->new_buffers = 0;
++ dma->total_buffers = 0;
++
++ /*
++ * reset the Total bytes counter.
++ */
++ dma->total_bytes = 0;
++
++ /*
++ * Reset the paused buffer.
++ */
++ dma->pause_buf.last = 0;
++ dma->pause_buf.buf_id = -1;
++
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_queue_full(int handle)
++ *
++ * Description: Query to determine if the DMA queue of buffers for
++ * a given channel is full.
++ * 0 = queue is full
++ * 1 = queue is not full
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_queue_full(int handle)
++{
++ int list_full = 0;
++ unsigned long flags;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Queue Full: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: queue full \n", channel);
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * If the last item is equal to the used item then
++ * the queue is full.
++ */
++ if (dma_chan[channel].total_buffers < MAX_EP93XX_DMA_BUFFERS)
++ list_full = FALSE;
++ else
++ list_full = TRUE;
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ return(list_full);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_get_position()
++ *
++ * Description: Takes two integer pointers and fills them with the start
++ * and current address of the buffer currently transferring
++ * on the specified DMA channel.
++ *
++ * handle handle for the channel to query.
++ * *buf_id buffer id for the current buffer transferring on the
++ * dma channel.
++ * *total total bytes transferred on the channel. Only counts
++ * whole buffers transferred.
++ * *current_frac number of bytes transferred so far in the current buffer.
++ ****************************************************************************/
++int
++ep93xx_dma_get_position(int handle, unsigned int * buf_id,
++ unsigned int * total, unsigned int * current_frac )
++{
++ int channel;
++ ep93xx_dma_t * dma;
++ unsigned int buf_id1, total1, current_frac1, buf_id2, total2;
++ unsigned int Status, NextBuffer, StateIsBufNext, M2P_reg_base=0;
++ unsigned int pause1, pause2;
++
++ /*
++ * Get the DMA hw channel # from the handle. See if this is a
++ * valid handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Get Position: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ dma = &dma_chan[channel];
++
++ /*
++ * If DMA moves to a new buffer in the middle of us grabbing the
++ * buffer info, then do it over again.
++ */
++ do{
++ buf_id1 = dma->buffer_queue[dma->current_buffer].buf_id;
++ total1 = dma->total_bytes;
++ pause1 = dma->pause;
++
++ if (channel < 10) {
++ // M2P
++ M2P_reg_base = dma->reg_base;
++
++ Status = inl(M2P_reg_base+M2P_OFFSET_STATUS);
++
++ NextBuffer = ((Status & STATUS_M2P_NEXTBUFFER) != 0);
++
++ StateIsBufNext = ((Status & STATUS_M2P_CURRENT_MASK) ==
++ STATUS_M2P_DMA_BUF_NEXT);
++
++ if( NextBuffer ^ StateIsBufNext )
++ current_frac1 = inl(M2P_reg_base+M2P_OFFSET_CURRENT1) -
++ inl(M2P_reg_base+M2P_OFFSET_BASE1);
++ else
++ current_frac1 = inl(M2P_reg_base+M2P_OFFSET_CURRENT0) -
++ inl(M2P_reg_base+M2P_OFFSET_BASE0);
++
++ } else {
++ // M2M - TODO implement this for M2M
++ current_frac1 = 0;
++ }
++
++ buf_id2 = dma->buffer_queue[dma->current_buffer].buf_id;
++ total2 = dma->total_bytes;
++ pause2 = dma->pause;
++
++ } while ( (buf_id1 != buf_id2) || (total1 != total2) || (pause1 != pause2) );
++
++ if (pause1)
++ current_frac1 = 0;
++
++ if (buf_id)
++ *buf_id = buf_id1;
++
++ if (total)
++ *total = total1;
++
++ if (current_frac)
++ *current_frac = current_frac1;
++
++// DPRINTK("DMA buf_id %d, total %d, frac %d\n", buf_id1, total1, current_frac1);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_get_total(int handle)
++ *
++ * Description: Returns the total number of bytes transferred on the
++ * specified channel since the channel was requested.
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_get_total(int handle)
++{
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Get Total: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: total: %d \n", channel, dma_chan[channel].total_bytes);
++
++ /*
++ * Return the total number of bytes transferred on this channel since
++ * it was requested.
++ */
++ return(dma_chan[channel].total_bytes);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_is_done(int handle)
++ *
++ * Description: Determines if the specified channel is done
++ * transferring the requested data.
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_is_done(int handle)
++{
++ ep93xx_dma_t *dma;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "ep93xx_dma_is_done: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the DMA channel state structure.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * See if there are any buffers remaining to be provided to the HW.
++ */
++ if (dma->new_buffers)
++ return 0;
++
++ /*
++ * See if this is a M2P or M2M channel.
++ */
++ if (channel < 10) {
++ /*
++ * If the bytes remaining register of the HW is not zero, then
++ * there is more work to be done.
++ */
++ if (inl(dma->reg_base + M2P_OFFSET_REMAIN) != 0)
++ return 0;
++ } else {
++ /*
++ * If either byte count register in the HW is not zero, then there
++ * is more work to be done.
++ */
++ if ((inl(dma->reg_base + M2M_OFFSET_BCR0) != 0) ||
++ (inl(dma->reg_base + M2M_OFFSET_BCR1) != 0))
++ return 0;
++ }
++
++ /*
++ * The DMA is complete.
++ */
++ return 1;
++}
++
++/*****************************************************************************
++ * ep93xx_dma_request
++ *
++ * Description: This function will allocate a DMA channel for a particular
++ * hardware peripheral. Before initiating a transfer on the allocated
++ * channel, the channel must be set up and buffers have to queued up.
++ *
++ * handle: pointer to an integer which is filled in with a unique
++ * handle for this instance of the dma interface.
++ * device_id string with the device name, primarily used by /proc.
++ * device hardware device ID for which the requested dma channel will
++ * transfer data.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_request(int * handle, const char *device_id,
++ ep93xx_dma_dev_t device)
++{
++ ep93xx_dma_t *dma = NULL;
++ int channel;
++ unsigned int error = 0;
++ unsigned int loop;
++ unsigned int M2P_reg_base;
++
++ /*
++ * Check if the device requesting a DMA channel is a valid device.
++ */
++ if ((device >= UNDEF_DMA) || (device < 0))
++ return(-ENODEV);
++
++ /*
++ * We've got a valid hardware device requesting a DMA channel.
++ * Now check if the device should open an M2P or M2M channel
++ */
++ if (device < 20)
++ channel = dma_open_m2p(device);
++ else
++ channel = dma_open_m2m(device);
++
++ /*
++ * Check if we successfully opened a DMA channel
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "%s: Could not open dma channel for this device.\n",
++ device_id);
++ return(-EBUSY);
++ }
++
++ dma = &dma_chan[channel];
++
++ if(dma->terminated==1) {
++ free_irq(dma->irq, (void *) dma);
++ dma->terminated=0;
++ }
++
++ /*
++ * Request the appropriate IRQ for the specified channel
++ */
++ if (channel < 10)
++ error = request_irq(dma->irq, dma_m2p_irq_handler,
++ IRQF_DISABLED, device_id, (void *) dma);
++ else
++ error = request_irq(dma->irq, &dma_m2m_irq_handler,
++ IRQF_DISABLED, device_id, (void *) dma);
++
++ /*
++ * Check for any errors during the irq request
++ */
++ if (error) {
++ printk(KERN_ERR "%s: unable to request IRQ %d for DMA channel\n",
++ device_id, dma->irq);
++ return(error);
++ }
++
++ /*
++ * Generate a valid handle and exit.
++ *
++ * Increment the last valid handle.
++ * Check for wraparound (unlikely, but we like to be complete).
++ */
++ dma->last_valid_handle++;
++
++ if ( (dma->last_valid_handle & DMA_HANDLE_SPECIFIER_MASK) !=
++ (channel << 28) )
++ dma->last_valid_handle = (channel << 28) + 1;
++
++ /*
++ * Fill in the handle pointer with a valid handle for
++ * this dma channel instance.
++ */
++ *handle = dma->last_valid_handle;
++
++ DPRINTK("Handle for channel %d: 0x%x\n", channel, *handle);
++
++ /*
++ * Save the device ID and device name.
++ */
++ dma->device = device;
++ dma->device_id = device_id;
++
++ /*
++ * Init all fields within the dma instance.
++ */
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
++ dma->buffer_queue[loop].buf_id = -1;
++
++ /*
++ * Initialize all buffer queue variables.
++ */
++ dma->current_buffer = 0;
++ dma->last_buffer = 0;
++
++ dma->new_buffers = 0;
++ dma->used_buffers = 0;
++ dma->total_buffers = 0;
++
++ /*
++ * Initialize the total bytes variable
++ */
++ dma->total_bytes = 0;
++
++ /*
++ * Initialize the transfer and pause state variables to 0.
++ */
++ dma->xfer_enable = 0;
++
++ dma->pause = 0;
++
++ /*
++ * Initialize the pause buffer structure.
++ */
++ dma->pause_buf.buf_id = -1;
++
++ /*
++ * Initialize the callback function and user data fields.
++ */
++ dma->callback = NULL;
++
++ /*
++ * User data used as a parameter for the Callback function. The user
++ * sets up the data and sends it with the callback function.
++ */
++ dma->user_data = 0;
++
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Debugging message.
++ */
++ DPRINTK("Successfully requested dma channel %d\n", channel);
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer source size last used \n");
++ for (loop = 0; loop < 5; loop ++)
++ DPRINTK("%d 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x %d %d \n",
++ dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++ DPRINTK("VIC0IRQSTATUS - 0x%x, VIC0INTENABLE - 0x%x \n",
++ *(unsigned int *)(VIC0IRQSTATUS),
++ *(unsigned int *)(VIC0INTENABLE));
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * ep93xx_dma_free
++ *
++ * Description: This function will free the dma channel for future requests.
++ *
++ * handle: handle for the channel to be freed.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_free(int handle)
++{
++ ep93xx_dma_t *dma;
++ unsigned int M2M_reg_base, M2P_reg_base, uiCONTROL;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Free: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the dma instance.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Disable the dma channel
++ */
++ if (channel < 10) {
++ /*
++ * M2P channel
++ */
++ M2P_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ } else {
++ /*
++ * M2M channel
++ */
++ M2M_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ /*
++ * Free the interrupt servicing this dma channel
++ */
++ //free_irq(dma->irq, (void *) dma);
++ dma->terminated=1;
++
++ /*
++ * Decrement the reference count for this instance of the dma interface
++ */
++ dma->ref_count--;
++
++ /*
++ * Set the transfer and pause state variables to 0
++ * (unititialized state).
++ */
++ dma->xfer_enable = 0;
++ dma->pause = 0;
++
++ /*
++ * Debugging message.
++ */
++ DPRINTK("Successfully freed dma channel %d\n", channel);
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * ep93xx_dma_init(void)
++ *
++ * Description: This function is called during system initialization to
++ * setup the interrupt number and register set base address for each DMA
++ * channel.
++ *
++ ****************************************************************************/
++static int __init
++ep93xx_dma_init(void)
++{
++ int channel;
++
++ /*
++ * Init some values in each dma instance.
++ */
++ for (channel = 0; channel < MAX_EP93XX_DMA_CHANNELS; channel++) {
++ /*
++ * IRQ for the specified dma channel.
++ */
++ dma_chan[channel].irq = IRQ_EP93XX_DMAM2P0 + channel;
++
++ dma_chan[channel].terminated = 0;
++
++ /*
++ * Initial value of the dma channel handle.
++ */
++ dma_chan[channel].last_valid_handle = channel << 28;
++
++ /*
++ * Give the instance a pointer to the dma channel register
++ * base.
++ */
++ if (channel < 10)
++ dma_chan[channel].reg_base = DMAM2PChannelBase[channel];
++ else
++ dma_chan[channel].reg_base = DMAM2MChannelBase[channel - 10];
++
++ /*
++ * Initialize the reference count for this channel.
++ */
++ dma_chan[channel].ref_count = 0;
++ }
++
++ DPRINTK("DMA Interface intitialization complete\n");
++
++ /*
++ * Success
++ */
++ return 0;
++}
++
++arch_initcall(ep93xx_dma_init);
++
++EXPORT_SYMBOL(ep93xx_dma_free);
++EXPORT_SYMBOL(ep93xx_dma_request);
++EXPORT_SYMBOL(ep93xx_dma_flush);
++EXPORT_SYMBOL(ep93xx_dma_pause);
++EXPORT_SYMBOL(ep93xx_dma_remove_buffer);
++EXPORT_SYMBOL(ep93xx_dma_add_buffer);
++EXPORT_SYMBOL(ep93xx_dma_start);
++EXPORT_SYMBOL(ep93xx_dma_config);
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/dma_ep93xx.h
+@@ -0,0 +1,676 @@
++/*****************************************************************************
++ *
++ * arch/arm/mach-ep93xx/dma_ep93xx.h
++ *
++ * DESCRIPTION: 93XX DMA controller API private defintions.
++ *
++ * Copyright Cirrus Logic Corporation, 2003. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ ****************************************************************************/
++#ifndef _EP93XX_DMA_H_
++#define _EP93XX_DMA_H_
++
++// as it turns out the ide dma is the biggest dma buffer hog so far
++// in case the HDD is "thinking" (seek/buffer flush)
++// the continueing r/w DMAs to the HDD will be queued up to up to PRD_ENTRIES entries...
++#include <linux/ide.h>
++#define MAX_EP93XX_DMA_BUFFERS PRD_ENTRIES
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#define EP93XX_DMA_BASE (EP93XX_AHB_VIRT_BASE + 0x00000000)
++
++/*****************************************************************************
++ * 0x8000.0000 -> 0x8000.003C M2P Channel 0 Registers (Tx)
++ * 0x8000.0040 -> 0x8000.007C M2P Channel 1 Registers (Rx)
++ * 0x8000.0080 -> 0x8000.00BC M2P Channel 2 Registers (Tx)
++ * 0x8000.00C0 -> 0x8000.00FC M2P Channel 3 Registers (Rx)
++ * 0x8000.0100 -> 0x8000.013C M2M Channel 0 Registers
++ * 0x8000.0140 -> 0x8000.017C M2M Channel 1 Registers
++ * 0x8000.0180 -> 0x8000.01BC Not Used
++ * 0x8000.01C0 -> 0x8000.01FC Not Used
++ * 0x8000.0200 -> 0x8000.023C M2P Channel 5 Registers (Rx)
++ * 0x8000.0240 -> 0x8000.027C M2P Channel 4 Registers (Tx)
++ * 0x8000.0280 -> 0x8000.02BC M2P Channel 7 Registers (Rx)
++ * 0x8000.02C0 -> 0x8000.02FC M2P Channel 6 Registers (Tx)
++ * 0x8000.0300 -> 0x8000.033C M2P Channel 9 Registers (Rx)
++ * 0x8000.0340 -> 0x8000.037C M2P Channel 8 Registers (Tx)
++ * 0x8000.0380 DMA Channel Arbitration register
++ * 0x8000.03C0 DMA Global Interrupt register
++ * 0x8000.03C4 -> 0x8000.03FC Not Used
++ *
++ *
++ * Internal M2P/P2M Channel Register Map
++ *
++ * Offset Name Access Bits Reset Value
++ * 0x00 CONTROL R/W 6 0
++ * 0x04 INTERRUPT R/W TC* 3 0
++ * 0x08 PPALLOC R/W 4 channel dependant
++ * (see reg description)
++ * 0x0C STATUS RO 8 0
++ * 0x10 reserved
++ * 0x14 REMAIN RO 16 0
++ * 0X18 Reserved
++ * 0X1C Reserved
++ * 0x20 MAXCNT0 R/W 16 0
++ * 0x24 BASE0 R/W 32 0
++ * 0x28 CURRENT0 RO 32 0
++ * 0x2C Reserved
++ * 0x30 MAXCNT1 R/W 16 0
++ * 0x34 BASE1 R/W 32 0
++ * 0X38 CURRENT1 RO 32 0
++ * 0X3C Reserved
++ *
++ * M2M Channel Register Map
++ * Offset Name Access Bits Reset Value
++ *
++ * 0x00 CONTROL R/W 22 0
++ * 0x04 INTERRUPT R/W TC* 3 0
++ * 0x08 Reserved
++ * 0x0C STATUS R/W TC* 14 0
++ * 0x10 BCR0 R/W 16 0
++ * 0x14 BCR1 R/W 16 0
++ * 0x18 SAR_BASE0 R/W 32 0
++ * 0x1C SAR_BASE1 R/W 32 0
++ * 0x20 Reserved
++ * 0x24 SAR_CURRENT0 RO 32 0
++ * 0x28 SAR_CURRENT1 RO 32 0
++ * 0x2C DAR_BASE0 R/W 32 0
++ * 0x30 DAR_BASE1 R/W 32 0
++ * 0x34 DAR_CURRENT0 RO 32 0
++ * 0X38 Reserved
++ * 0X3C DAR_CURRENT1 RO 32 0
++ * * Write this location once to clear the bit (see
++ * Interrupt/Status register description for which bits
++ * this rule applies to).
++ *
++ ****************************************************************************/
++
++
++/*----------------------------------------------------------------------------------*/
++/* M2P Registers */
++/*----------------------------------------------------------------------------------*/
++/*
++ * M2P CONTROL register bit defines
++ */
++#define CONTROL_M2P_STALLINTEN 0x00000001 /* Enables the STALL interrupt */
++#define CONTROL_M2P_NFBINTEN 0x00000002 /* Enables the NFB interrupt */
++#define CONTROL_M2P_CHERRORINTEN 0x00000008 /* Enables the ChError interrupt*/
++#define CONTROL_M2P_ENABLE 0x00000010 /* Enables the channel */
++#define CONTROL_M2P_ABRT 0x00000020 /* Determines how DMA behaves in*/
++ /* NEXT state with peripheral */
++ /* error */
++ /* 0: NEXT -> ON, ignore error */
++ /* 1: NEXT -> STALL, disable ch.*/
++#define CONTROL_M2P_ICE 0x00000040 /* Ignore Channel Error */
++
++/*
++ * M2P INTERRUPT register bit defines
++ */
++#define INTERRUPT_M2P_STALLINT 0x00000001 /* Indicates channel stalled. */
++#define INTERRUPT_M2P_NFBINT 0x00000002 /* Indicates channel is hungry. */
++#define INTERRUPT_M2P_CHERRORINT 0x00000008 /* Peripheral detects error */
++
++
++/*
++ * STATUS register bit defines
++ */
++#define STATUS_M2P_STALL 0x00000001 /* A '1' indicates channel is */
++ /* stalled */
++#define STATUS_M2P_NFB 0x00000002 /* A '1' indicates channel has moved*/
++ /* from NEXT state to ON state, but */
++ /* waiting for next buffer to be */
++ /* programmed. */
++#define STATUS_M2P_CHERROR 0x00000008 /* Enables the ChError interrupt */
++#define STATUS_M2P_CURRENT_MASK 0x00000030 /* Current state of the FSM */
++#define STATUS_M2P_CURRENT_SHIFT 4
++#define STATUS_M2P_NEXTBUFFER 0x00000040 /* Informs the int handler after an */
++ /* NFB int which pair of maxcnt and */
++ /* base regs to update. */
++#define STATUS_M2P_BYTES_MASK 0x0000f800 /* number of valid DMA data */
++#define STATUS_M2P_BYTES_SHIFT 7 /* currently in */
++ /* packer/unpacker */
++
++#define STATUS_M2P_DMA_NO_BUF 0x00000000
++#define STATUS_M2P_DMA_BUF_ON 0x00000010
++#define STATUS_M2P_DMA_BUF_NEXT 0x00000020
++
++/*
++ * Register masks to mask off reserved bits after reading register.
++ */
++#define M2P_MASK_PPALLOC 0x0000000f
++#define M2P_MASK_REMAIN 0x0000ffff
++#define M2P_MASK_MAXCNT0 0x0000ffff
++#define M2P_MASK_BASE0 0xffffffff
++#define M2P_MASK_CURRENT0 0xffffffff
++#define M2P_MASK_MAXCNT1 0x0000ffff
++#define M2P_MASK_BASE1 0xffffffff
++#define M2P_MASK_CURRENT1 0xffffffff
++
++
++/*----------------------------------------------------------------------------------*/
++/* M2M Registers */
++/*----------------------------------------------------------------------------------*/
++
++#define CONTROL_M2M_STALLINTEN 0x00000001 /* Enables the STALL interrupt */
++#define CONTROL_M2M_SCT 0x00000002 /* Source Copy Transfer. Setup a */
++ /* block transfer from 1 memory source */
++ /* location. */
++#define CONTROL_M2M_DONEINTEN 0x00000004 /* Enables the DONE interrupt which */
++ /* indicates if the xfer completed */
++ /* successfully */
++#define CONTROL_M2M_ENABLE 0x00000008 /* Enables the channel */
++#define CONTROL_M2M_START 0x00000010 /* Initiates the xfer. 'software trigger' */
++#define CONTROL_M2M_BWC_MASK 0x000001e0 /* Bandwidth control. Indicate number of */
++#define CONTROL_M2M_BWC_SHIFT 5 /* bytes in a transfer. */
++#define CONTROL_M2M_PW_MASK 0x00000600 /* Peripheral width. Used for xfers */
++#define CONTROL_M2M_PW_SHIFT 9 /* between memory and external peripheral. */
++ /* 00: byte, 01: halfword, 10: word. */
++#define CONTROL_M2M_DAH 0x00000800 /* Destination Address Hold */
++#define CONTROL_M2M_SAH 0x00001000 /* Source Address Hold */
++#define CONTROL_M2M_TM_MASK 0x00006000 /* Transfer Mode. 00: sw triggered, */
++#define CONTROL_M2M_TM_SHIFT 13 /* 01: hw initiated M2P, 01: hw initiated P2M */
++#define CONTROL_M2M_ETDP_MASK 0x00018000 /* End-of-Transfer/Terminal Count pin */
++#define CONTROL_M2M_ETDP_SHIFT 15 /* direction and polarity. */
++#define CONTROL_M2M_DACKP 0x00020000 /* DMA acknowledge pin polarity */
++
++#define CONTROL_M2M_DREQP_MASK 0x00180000 /* DMA request pin polarity. must be set */
++#define CONTROL_M2M_DREQP_SHIFT 19 /* before enable bit. */
++#define CONTROL_M2M_NFBINTEN 0x00200000 /* Enables generation of the NFB interrupt. */
++#define CONTROL_M2M_RSS_MASK 0x00c00000 /* Request source selection: */
++#define CONTROL_M2M_RSS_SHIFT 22 /* 000 - External DReq[0] */
++ /* 001 - External DReq[1] */
++ /* 01X - Internal SSPRx */
++ /* 10X - Internal SSPTx */
++ /* 11X - Internal IDE */
++#define CONTROL_M2M_NO_HDSK 0x01000000 /* No handshake. When set the peripheral doesn't */
++ /* require the regular handshake protocal. Must */
++ /* be set for SSP and IDE operations, optional */
++ /* for external peripherals. */
++#define CONTROL_M2M_PWSC_MASK 0xfe000000 /* Peripheral wait states count. Gives the latency */
++#define CONTROL_M2M_PWSC_SHIFT 25 /* (in PCLK cycles) needed by the peripheral to */
++ /* deassert its' request once the M2M xfer w/ DMA */
++ /* is complete. */
++
++/*
++ * M2M INTERRUPT register bit defines
++ */
++#define INTERRUPT_M2M_STALLINT 0x00000001 /* Stall interrupt indicates channel stalled. */
++#define INTERRUPT_M2M_DONEINT 0x00000002 /* Transaction done. */
++#define INTERRUPT_M2M_NFBINT 0x00000004 /* Next frame buffer interrupt indicates */
++ /* channel requires a new buffer */
++
++
++
++/*
++ * M2M STATUS register bit defines
++ */
++#define STATUS_M2M_STALL 0x00000001 /* A '1' indicates channel is stalled */
++#define STATUS_M2M_CURRENTSTATE_MASK 0x0000003e /* Indicates state of M2M Channel control */
++#define STATUS_M2M_CURRENTSTATE_SHIFT 1 /* FSM (0-2): */
++ /* 000 - IDLE, 001 - STALL, 010 - MEM_RD, */
++ /* 011 - MEM_WR, 100 - BWC_WAIT */
++ /* and M2M buffer FSM (3-2): */
++ /* 00 - NO_BUF, 01 - BUF_ON, 10 - BUF_NEXT */
++#define STATUS_M2M_DONE 0x00000040 /* Transfer completed successfully if 1. */
++#define STATUS_M2M_TCS_MASK 0x00000180 /* Terminal Count status. Indicates whether or */
++#define STATUS_M2M_TCS_SHIFT 7 /* or not the actual byte count reached */
++ /* programmed limit for buffer descriptor */
++#define STATUS_M2M_EOTS_MASK 0x00000600 /* End-of-Transfer status for buffer */
++#define STATUS_M2M_EOTS_SHIFT 9
++#define STATUS_M2M_NFB 0x00000800 /* A '1' indicates channel has moved */
++ /* from NEXT state to ON state, but the next */
++ /* byte count reg for next buffer has not been */
++ /* programmed yet. */
++#define STATUS_M2M_NB 0x00001000 /* NextBuffer status. Informs NFB service */
++ /* routine, after NFB int, which pair of buffer */
++ /* descriptor registers is free to update. */
++#define STATUS_M2M_DREQS 0x00002000 /* DREQ status. Reflects the status of the */
++ /* synchronized external peripherals DMA */
++ /* request signal. */
++
++/*
++ * Register masks to mask off reserved bits after reading register.
++ */
++#define M2M_MASK_BCR0 0x0000ffff
++#define M2M_MASK_BCR1 0x0000ffff
++#define M2M_MASK_SAR_BASE0 0xffffffff
++#define M2M_MASK_SAR_BASE1 0xffffffff
++#define M2M_MASK_SAR_CURRENT0 0xffffffff
++#define M2M_MASK_SAR_CURRENT1 0xffffffff
++#define M2M_MASK_DAR_BASE0 0xffffffff
++#define M2M_MASK_DAR_BASE1 0xffffffff
++#define M2M_MASK_DAR_CURRENT0 0xffffffff
++#define M2M_MASK_DAR_CURRENT1 0xffffffff
++
++
++//
++/* 8000_0000 - 8000_ffff: DMA */
++#define DMA_OFFSET 0x000000
++#define DMA_BASE (EP93XX_DMA_BASE)
++#define DMAMP_TX_0_CONTROL (DMA_BASE+0x0000)
++#define DMAMP_TX_0_INTERRUPT (DMA_BASE+0x0004)
++#define DMAMP_TX_0_PPALLOC (DMA_BASE+0x0008)
++#define DMAMP_TX_0_STATUS (DMA_BASE+0x000C)
++#define DMAMP_TX_0_REMAIN (DMA_BASE+0x0014)
++#define DMAMP_TX_0_MAXCNT0 (DMA_BASE+0x0020)
++#define DMAMP_TX_0_BASE0 (DMA_BASE+0x0024)
++#define DMAMP_TX_0_CURRENT0 (DMA_BASE+0x0028)
++#define DMAMP_TX_0_MAXCNT1 (DMA_BASE+0x0030)
++#define DMAMP_TX_0_BASE1 (DMA_BASE+0x0034)
++#define DMAMP_TX_0_CURRENT1 (DMA_BASE+0x0038)
++
++#define DMAMP_RX_1_CONTROL (DMA_BASE+0x0040)
++#define DMAMP_RX_1_INTERRUPT (DMA_BASE+0x0044)
++#define DMAMP_RX_1_PPALLOC (DMA_BASE+0x0048)
++#define DMAMP_RX_1_STATUS (DMA_BASE+0x004C)
++#define DMAMP_RX_1_REMAIN (DMA_BASE+0x0054)
++#define DMAMP_RX_1_MAXCNT0 (DMA_BASE+0x0060)
++#define DMAMP_RX_1_BASE0 (DMA_BASE+0x0064)
++#define DMAMP_RX_1_CURRENT0 (DMA_BASE+0x0068)
++#define DMAMP_RX_1_MAXCNT1 (DMA_BASE+0x0070)
++#define DMAMP_RX_1_BASE1 (DMA_BASE+0x0074)
++#define DMAMP_RX_1_CURRENT1 (DMA_BASE+0x0078)
++
++#define DMAMP_TX_2_CONTROL (DMA_BASE+0x0080)
++#define DMAMP_TX_2_INTERRUPT (DMA_BASE+0x0084)
++#define DMAMP_TX_2_PPALLOC (DMA_BASE+0x0088)
++#define DMAMP_TX_2_STATUS (DMA_BASE+0x008C)
++#define DMAMP_TX_2_REMAIN (DMA_BASE+0x0094)
++#define DMAMP_TX_2_MAXCNT0 (DMA_BASE+0x00A0)
++#define DMAMP_TX_2_BASE0 (DMA_BASE+0x00A4)
++#define DMAMP_TX_2_CURRENT0 (DMA_BASE+0x00A8)
++#define DMAMP_TX_2_MAXCNT1 (DMA_BASE+0x00B0)
++#define DMAMP_TX_2_BASE1 (DMA_BASE+0x00B4)
++#define DMAMP_TX_2_CURRENT1 (DMA_BASE+0x00B8)
++
++#define DMAMP_RX_3_CONTROL (DMA_BASE+0x00C0)
++#define DMAMP_RX_3_INTERRUPT (DMA_BASE+0x00C4)
++#define DMAMP_RX_3_PPALLOC (DMA_BASE+0x00C8)
++#define DMAMP_RX_3_STATUS (DMA_BASE+0x00CC)
++#define DMAMP_RX_3_REMAIN (DMA_BASE+0x00D4)
++#define DMAMP_RX_3_MAXCNT0 (DMA_BASE+0x00E0)
++#define DMAMP_RX_3_BASE0 (DMA_BASE+0x00E4)
++#define DMAMP_RX_3_CURRENT0 (DMA_BASE+0x00E8)
++#define DMAMP_RX_3_MAXCNT1 (DMA_BASE+0x00F0)
++#define DMAMP_RX_3_BASE1 (DMA_BASE+0x00F4)
++#define DMAMP_RX_3_CURRENT1 (DMA_BASE+0x00F8)
++
++#define DMAMM_0_CONTROL (DMA_BASE+0x0100)
++#define DMAMM_0_INTERRUPT (DMA_BASE+0x0104)
++#define DMAMM_0_STATUS (DMA_BASE+0x010C)
++#define DMAMM_0_BCR0 (DMA_BASE+0x0110)
++#define DMAMM_0_BCR1 (DMA_BASE+0x0114)
++#define DMAMM_0_SAR_BASE0 (DMA_BASE+0x0118)
++#define DMAMM_0_SAR_BASE1 (DMA_BASE+0x011C)
++#define DMAMM_0_SAR_CURRENT0 (DMA_BASE+0x0124)
++#define DMAMM_0_SAR_CURRENT1 (DMA_BASE+0x0128)
++#define DMAMM_0_DAR_BASE0 (DMA_BASE+0x012C)
++#define DMAMM_0_DAR_BASE1 (DMA_BASE+0x0130)
++#define DMAMM_0_DAR_CURRENT0 (DMA_BASE+0x0134)
++#define DMAMM_0_DAR_CURRENT1 (DMA_BASE+0x013C)
++
++#define DMAMM_1_CONTROL (DMA_BASE+0x0140)
++#define DMAMM_1_INTERRUPT (DMA_BASE+0x0144)
++#define DMAMM_1_STATUS (DMA_BASE+0x014C)
++#define DMAMM_1_BCR0 (DMA_BASE+0x0150)
++#define DMAMM_1_BCR1 (DMA_BASE+0x0154)
++#define DMAMM_1_SAR_BASE0 (DMA_BASE+0x0158)
++#define DMAMM_1_SAR_BASE1 (DMA_BASE+0x015C)
++#define DMAMM_1_SAR_CURRENT0 (DMA_BASE+0x0164)
++#define DMAMM_1_SAR_CURRENT1 (DMA_BASE+0x0168)
++#define DMAMM_1_DAR_BASE0 (DMA_BASE+0x016C)
++#define DMAMM_1_DAR_BASE1 (DMA_BASE+0x0170)
++#define DMAMM_1_DAR_CURRENT0 (DMA_BASE+0x0174)
++#define DMAMM_1_DAR_CURRENT1 (DMA_BASE+0x017C)
++
++#define DMAMP_RX_5_CONTROL (DMA_BASE+0x0200)
++#define DMAMP_RX_5_INTERRUPT (DMA_BASE+0x0204)
++#define DMAMP_RX_5_PPALLOC (DMA_BASE+0x0208)
++#define DMAMP_RX_5_STATUS (DMA_BASE+0x020C)
++#define DMAMP_RX_5_REMAIN (DMA_BASE+0x0214)
++#define DMAMP_RX_5_MAXCNT0 (DMA_BASE+0x0220)
++#define DMAMP_RX_5_BASE0 (DMA_BASE+0x0224)
++#define DMAMP_RX_5_CURRENT0 (DMA_BASE+0x0228)
++#define DMAMP_RX_5_MAXCNT1 (DMA_BASE+0x0230)
++#define DMAMP_RX_5_BASE1 (DMA_BASE+0x0234)
++#define DMAMP_RX_5_CURRENT1 (DMA_BASE+0x0238)
++
++#define DMAMP_TX_4_CONTROL (DMA_BASE+0x0240)
++#define DMAMP_TX_4_INTERRUPT (DMA_BASE+0x0244)
++#define DMAMP_TX_4_PPALLOC (DMA_BASE+0x0248)
++#define DMAMP_TX_4_STATUS (DMA_BASE+0x024C)
++#define DMAMP_TX_4_REMAIN (DMA_BASE+0x0254)
++#define DMAMP_TX_4_MAXCNT0 (DMA_BASE+0x0260)
++#define DMAMP_TX_4_BASE0 (DMA_BASE+0x0264)
++#define DMAMP_TX_4_CURRENT0 (DMA_BASE+0x0268)
++#define DMAMP_TX_4_MAXCNT1 (DMA_BASE+0x0270)
++#define DMAMP_TX_4_BASE1 (DMA_BASE+0x0274)
++#define DMAMP_TX_4_CURRENT1 (DMA_BASE+0x0278)
++
++#define DMAMP_RX_7_CONTROL (DMA_BASE+0x0280)
++#define DMAMP_RX_7_INTERRUPT (DMA_BASE+0x0284)
++#define DMAMP_RX_7_PPALLOC (DMA_BASE+0x0288)
++#define DMAMP_RX_7_STATUS (DMA_BASE+0x028C)
++#define DMAMP_RX_7_REMAIN (DMA_BASE+0x0294)
++#define DMAMP_RX_7_MAXCNT0 (DMA_BASE+0x02A0)
++#define DMAMP_RX_7_BASE0 (DMA_BASE+0x02A4)
++#define DMAMP_RX_7_CURRENT0 (DMA_BASE+0x02A8)
++#define DMAMP_RX_7_MAXCNT1 (DMA_BASE+0x02B0)
++#define DMAMP_RX_7_BASE1 (DMA_BASE+0x02B4)
++#define DMAMP_RX_7_CURRENT1 (DMA_BASE+0x02B8)
++
++#define DMAMP_TX_6_CONTROL (DMA_BASE+0x02C0)
++#define DMAMP_TX_6_INTERRUPT (DMA_BASE+0x02C4)
++#define DMAMP_TX_6_PPALLOC (DMA_BASE+0x02C8)
++#define DMAMP_TX_6_STATUS (DMA_BASE+0x02CC)
++#define DMAMP_TX_6_REMAIN (DMA_BASE+0x02D4)
++#define DMAMP_TX_6_MAXCNT0 (DMA_BASE+0x02E0)
++#define DMAMP_TX_6_BASE0 (DMA_BASE+0x02E4)
++#define DMAMP_TX_6_CURRENT0 (DMA_BASE+0x02E8)
++#define DMAMP_TX_6_MAXCNT1 (DMA_BASE+0x02F0)
++#define DMAMP_TX_6_BASE1 (DMA_BASE+0x02F4)
++#define DMAMP_TX_6_CURRENT1 (DMA_BASE+0x02F8)
++
++#define DMAMP_RX_9_CONTROL (DMA_BASE+0x0300)
++#define DMAMP_RX_9_INTERRUPT (DMA_BASE+0x0304)
++#define DMAMP_RX_9_PPALLOC (DMA_BASE+0x0308)
++#define DMAMP_RX_9_STATUS (DMA_BASE+0x030C)
++#define DMAMP_RX_9_REMAIN (DMA_BASE+0x0314)
++#define DMAMP_RX_9_MAXCNT0 (DMA_BASE+0x0320)
++#define DMAMP_RX_9_BASE0 (DMA_BASE+0x0324)
++#define DMAMP_RX_9_CURRENT0 (DMA_BASE+0x0328)
++#define DMAMP_RX_9_MAXCNT1 (DMA_BASE+0x0330)
++#define DMAMP_RX_9_BASE1 (DMA_BASE+0x0334)
++#define DMAMP_RX_9_CURRENT1 (DMA_BASE+0x0338)
++
++#define DMAMP_TX_8_CONTROL (DMA_BASE+0x0340)
++#define DMAMP_TX_8_INTERRUPT (DMA_BASE+0x0344)
++#define DMAMP_TX_8_PPALLOC (DMA_BASE+0x0348)
++#define DMAMP_TX_8_STATUS (DMA_BASE+0x034C)
++#define DMAMP_TX_8_REMAIN (DMA_BASE+0x0354)
++#define DMAMP_TX_8_MAXCNT0 (DMA_BASE+0x0360)
++#define DMAMP_TX_8_BASE0 (DMA_BASE+0x0364)
++#define DMAMP_TX_8_CURRENT0 (DMA_BASE+0x0368)
++#define DMAMP_TX_8_MAXCNT1 (DMA_BASE+0x0370)
++#define DMAMP_TX_8_BASE1 (DMA_BASE+0x0374)
++#define DMAMP_TX_8_CURRENT1 (DMA_BASE+0x0378)
++
++#define DMA_ARBITRATION (DMA_BASE+0x0380)
++#define DMA_INTERRUPT (DMA_BASE+0x03C0)
++
++
++/*
++ * DMA Register Base addresses and Offsets
++ */
++#define DMA_M2P_TX_0_BASE DMAMP_TX_0_CONTROL
++#define DMA_M2P_RX_1_BASE DMAMP_RX_1_CONTROL
++#define DMA_M2P_TX_2_BASE DMAMP_TX_2_CONTROL
++#define DMA_M2P_RX_3_BASE DMAMP_RX_3_CONTROL
++#define DMA_M2M_0_BASE DMAMM_0_CONTROL
++#define DMA_M2M_1_BASE DMAMM_1_CONTROL
++#define DMA_M2P_RX_5_BASE DMAMP_RX_5_CONTROL
++#define DMA_M2P_TX_4_BASE DMAMP_TX_4_CONTROL
++#define DMA_M2P_RX_7_BASE DMAMP_RX_7_CONTROL
++#define DMA_M2P_TX_6_BASE DMAMP_TX_6_CONTROL
++#define DMA_M2P_RX_9_BASE DMAMP_RX_9_CONTROL
++#define DMA_M2P_TX_8_BASE DMAMP_TX_8_CONTROL
++
++#define M2P_OFFSET_CONTROL 0x0000
++#define M2P_OFFSET_INTERRUPT 0x0004
++#define M2P_OFFSET_PPALLOC 0x0008
++#define M2P_OFFSET_STATUS 0x000C
++#define M2P_OFFSET_REMAIN 0x0014
++#define M2P_OFFSET_MAXCNT0 0x0020
++#define M2P_OFFSET_BASE0 0x0024
++#define M2P_OFFSET_CURRENT0 0x0028
++#define M2P_OFFSET_MAXCNT1 0x0030
++#define M2P_OFFSET_BASE1 0x0034
++#define M2P_OFFSET_CURRENT1 0x0038
++
++#define M2M_OFFSET_CONTROL 0x0000
++#define M2M_OFFSET_INTERRUPT 0x0004
++#define M2M_OFFSET_STATUS 0x000C
++#define M2M_OFFSET_BCR0 0x0010
++#define M2M_OFFSET_BCR1 0x0014
++#define M2M_OFFSET_SAR_BASE0 0x0018
++#define M2M_OFFSET_SAR_BASE1 0x001C
++#define M2M_OFFSET_SAR_CURRENT0 0x0024
++#define M2M_OFFSET_SAR_CURRENT1 0x0028
++#define M2M_OFFSET_DAR_BASE0 0x002C
++#define M2M_OFFSET_DAR_BASE1 0x0030
++#define M2M_OFFSET_DAR_CURRENT0 0x0034
++#define M2M_OFFSET_DAR_CURRENT1 0x003C
++
++
++
++//-----------------------------------------------------------------------------
++// PWRCNT Register Defines
++//-----------------------------------------------------------------------------
++#define SYSCON_PWRCNT_FIREN 0x80000000
++#define SYSCON_PWRCNT_UARTBAUD 0x20000000
++#define SYSCON_PWRCNT_USHEN 0x10000000
++#define SYSCON_PWRCNT_DMA_M2MCH1 0x08000000
++#define SYSCON_PWRCNT_DMA_M2MCH0 0x04000000
++#define SYSCON_PWRCNT_DMA_M2PCH8 0x02000000
++#define SYSCON_PWRCNT_DMA_M2PCH9 0x01000000
++#define SYSCON_PWRCNT_DMA_M2PCH6 0x00800000
++#define SYSCON_PWRCNT_DMA_M2PCH7 0x00400000
++#define SYSCON_PWRCNT_DMA_M2PCH4 0x00200000
++#define SYSCON_PWRCNT_DMA_M2PCH5 0x00100000
++#define SYSCON_PWRCNT_DMA_M2PCH2 0x00080000
++#define SYSCON_PWRCNT_DMA_M2PCH3 0x00040000
++#define SYSCON_PWRCNT_DMA_M2PCH0 0x00020000
++#define SYSCON_PWRCNT_DMA_M2PCH1 0x00010000
++
++#ifndef __ASSEMBLY__
++/*
++ * DMA Register Base addresses
++ */
++static unsigned int const DMAM2PChannelBase[10] =
++{
++ DMA_M2P_TX_0_BASE,
++ DMA_M2P_RX_1_BASE,
++ DMA_M2P_TX_2_BASE,
++ DMA_M2P_RX_3_BASE,
++ DMA_M2P_TX_4_BASE,
++ DMA_M2P_RX_5_BASE,
++ DMA_M2P_TX_6_BASE,
++ DMA_M2P_RX_7_BASE,
++ DMA_M2P_TX_8_BASE,
++ DMA_M2P_RX_9_BASE
++};
++
++static unsigned int const DMAM2MChannelBase[2] =
++{
++ DMA_M2M_0_BASE,
++ DMA_M2M_1_BASE
++};
++
++#endif /* __ASSEMBLY__ */
++
++/*****************************************************************************
++ *
++ * DMA buffer structure type.
++ *
++ ****************************************************************************/
++typedef struct ep93xx_dma_buffer_s
++{
++ unsigned int source; /* buffer physical source address. */
++ unsigned int dest; /* buffer physical destination address, */
++ /* only used with the 2 M2M channels. */
++ unsigned int size; /* buffer size in bytes */
++ unsigned int last; /* 1 if this is the last buffer */
++ /* in this transaction. If 1, */
++ /* disable the NFBint so we aren't */
++ /* interrupted for another buffer */
++ /* when we know there won't be another. */
++ unsigned int used; /* This field is set to 1 by the DMA */
++ /* interface after the buffer is transferred*/
++ int buf_id; /* unique identifyer specified by the */
++ /* the driver which requested the dma */
++} ep93xx_dma_buffer_t;
++
++typedef ep93xx_dma_buffer_t * ep93xx_dma_buffer_p;
++
++/*****************************************************************************
++ *
++ * Instance definition for the DMA interface.
++ *
++ ****************************************************************************/
++typedef struct ep9312_dma_s
++{
++ /*
++ * This 1 when the instance is in use, and 0 when it's not.
++ */
++ unsigned int ref_count;
++
++ /*
++ * This is the last valid handle for this instance. When giving out a
++ * new handle this will be incremented and given out.
++ */
++ int last_valid_handle;
++
++ /*
++ * device specifies one of the 20 DMA hardware ports this
++ * DMA channel will service.
++ */
++ ep93xx_dma_dev_t device;
++
++ /*
++ * DMABufferQueue is the queue of buffer structure pointers which the
++ * dma channel will use to setup transfers.
++ */
++ ep93xx_dma_buffer_t buffer_queue[MAX_EP93XX_DMA_BUFFERS];
++
++ /*
++ * currnt_buffer : This is the buffer currently being transfered on
++ * this channel.
++ * last_buffer : This is the last buffer for this transfer.
++ * Note: current_buffer + 1 is already programmed into the dma
++ * channel as the next buffer to transfer. Don't write
++ * over either entry.
++ */
++ int current_buffer;
++ int last_buffer;
++
++ /*
++ * The following 3 fields are buffer counters.
++ *
++ * iNewBuffers: Buffers in the queue which have not been transfered.
++ * iUsedBuffers: Buffers in the queue which have have been tranferred,
++ * and are waiting to be returned.
++ * iTotalBuffers: Total number of buffers in the queue.
++ */
++ int new_buffers;
++ int used_buffers;
++ int total_buffers;
++
++ /*
++ * uiTotalBytes has the total bytes transfered on the channel since the
++ * last flush. This value does not include the bytes tranfered in the
++ * current buffer. A byte count is only added after a complete buffer
++ * is tranfered.
++ */
++ unsigned int total_bytes;
++
++ /*
++ * Interrupt number for this channel
++ */
++ unsigned int irq;
++
++ /*
++ * Indicates whether or not the channel is currently enabled to transfer
++ * data.
++ */
++ unsigned int xfer_enable;
++
++ /*
++ * pause indicates if the dma channel was paused by calling the pause
++ * ioctl.
++ */
++ unsigned int pause;
++
++ /*
++ * buffer structure used during a pause to capture the current
++ * address and remaining bytes for the buffer actively being transferred
++ * on the channel. This buffer will be used to reprogram the dma
++ * channel upon a resume.
++ */
++ ep93xx_dma_buffer_t pause_buf;
++
++ /*
++ * DMACallback is a function pointer which the calling application can
++ * use install a function to. this fuction can be used to notify the
++ * calling application of an interrupt.
++ */
++ dma_callback callback;
++
++ /*
++ * User data used as a parameter for the Callback function. The user
++ * sets up the data and sends it with the callback function.
++ */
++ unsigned int user_data;
++
++ /*
++ * A string representation of the device attached to the channel.
++ */
++ const char * device_id;
++
++ /*
++ * The register base address for this dma channel.
++ */
++ unsigned int reg_base;
++
++ /*
++ * terminated indicates
++ */
++ unsigned int terminated;
++
++
++} ep93xx_dma_t;
++
++/*****************************************************************************
++ *
++ * DMA macros
++ *
++ ****************************************************************************/
++#define DMA_HANDLE_SPECIFIER_MASK 0xF0000000
++#define DMA_CH0_HANDLE_SPECIFIER 0x00000000
++#define DMA_CH1_HANDLE_SPECIFIER 0x10000000
++#define DMA_CH2_HANDLE_SPECIFIER 0x20000000
++#define DMA_CH3_HANDLE_SPECIFIER 0x30000000
++#define DMA_CH4_HANDLE_SPECIFIER 0x40000000
++#define DMA_CH5_HANDLE_SPECIFIER 0x50000000
++#define DMA_CH6_HANDLE_SPECIFIER 0x60000000
++#define DMA_CH7_HANDLE_SPECIFIER 0x70000000
++#define DMA_CH8_HANDLE_SPECIFIER 0x80000000
++#define DMA_CH9_HANDLE_SPECIFIER 0x90000000
++#define DMA_CH10_HANDLE_SPECIFIER 0xA0000000
++#define DMA_CH11_HANDLE_SPECIFIER 0xB0000000
++
++#endif // _DMADRV_H_
diff --git a/target/linux/ep93xx/patches-2.6.30/006-ep93xx-touchscreen.patch b/target/linux/ep93xx/patches-2.6.30/006-ep93xx-touchscreen.patch
new file mode 100644
index 0000000000..6a0d5dbfd6
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/006-ep93xx-touchscreen.patch
@@ -0,0 +1,1319 @@
+Index: linux-2.6.30.9/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/drivers/input/touchscreen/Kconfig 2009-11-24 21:00:18.000000000 +0100
++++ linux-2.6.30.9/drivers/input/touchscreen/Kconfig 2009-11-24 21:00:45.000000000 +0100
+@@ -161,6 +161,10 @@
+ module will be called wacom_w8001.
+
+
++config TOUCHSCREEN_EP93XX
++ tristate "EP93xx Touchscreen"
++ depends on ARM && INPUT && ARCH_EP93XX
++
+ config TOUCHSCREEN_MTOUCH
+ tristate "MicroTouch serial touchscreens"
+ select SERIO
+Index: linux-2.6.30.9/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/drivers/input/touchscreen/Makefile 2009-11-24 21:00:18.000000000 +0100
++++ linux-2.6.30.9/drivers/input/touchscreen/Makefile 2009-11-24 21:00:45.000000000 +0100
+@@ -14,6 +14,7 @@
+ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
++obj-$(CONFIG_TOUCHSCREEN_EP93XX) += ep93xx_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+ obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
+Index: linux-2.6.30.9/drivers/input/touchscreen/ep93xx_ts.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/input/touchscreen/ep93xx_ts.c 2009-11-24 21:09:48.000000000 +0100
+@@ -0,0 +1,1117 @@
++/*
++ * linux/drivers/input/touchscreen/ep93xx_ts.c
++ *
++ * Copyright (C) 2003-2004 Cirrus Corp.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/wait.h>
++#include <linux/fs.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/compiler.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/syscalls.h>
++#include <linux/input.h>
++#include <asm/irq.h>
++#include <mach/hardware.h>
++#include <asm/io.h>
++
++#define TOUCH_OFFSET 0x100000
++#define TOUCH_BASE (EP93XX_APB_VIRT_BASE|TOUCH_OFFSET)
++#define TSSetup (TOUCH_BASE+0x00) /* R/W touchscreen controller setup control register. */
++#define TSXYMaxMin (TOUCH_BASE+0x04) /* R/W touchscreen controller max/min register. */
++#define TSXYResult (TOUCH_BASE+0x08) /* R touchscreen controller result register. */
++#define TSDischarge (TOUCH_BASE+0x0C) /* LOCKED R/W touchscreen Switch Matrix control register. */
++#define TSXSample (TOUCH_BASE+0x10) /* LOCKED R/W touchscreen Switch Matrix control register. */
++#define TSYSample (TOUCH_BASE+0x14) /* LOCKED R/W touchscreen Switch Matrix control register. */
++#define TSDirect (TOUCH_BASE+0x18) /* LOCKED R/W touchscreen Switch Matrix control register. */
++#define TSDetect (TOUCH_BASE+0x1C) /* LOCKED R/W touchscreen Switch Matrix control register. */
++#define TSSWLock (TOUCH_BASE+0x20) /* NA R/W touchscreen software lock register. */
++#define TSSetup2 (TOUCH_BASE+0x24) /* R/W touchscreen setup control register #2. */
++
++//
++// To customize for a new touchscreen, there are various macros that
++// have to be set. If you allow UART_HACK_DEBUG to be defined, you
++// will get real time ts data scrolling up your serial terminal
++// screen that will help you empirically determine good values for these.
++//
++
++//
++// These are used as trigger levels to know when we have pen up/down
++//
++// The rules:
++// 1. TS_HEAVY_INV_PRESSURE < TS_LIGHT_INV_PRESSURE because these
++// are Inverse pressure.
++// 2. Any touch lighter than TS_LIGHT_INV_PRESSURE is a pen up.
++// 3. Any touch heavier than TS_HEAVY_INV_PRESSURE is a pen down.
++//
++#define TS_HEAVY_INV_PRESSURE 0xFE0 //C00
++#define TS_LIGHT_INV_PRESSURE 0xFFF //e00
++
++//
++// If the x, y, or inverse pressure changes more than these values
++// between two succeeding points, the point is not reported.
++//
++#define TS_MAX_VALID_XY_CHANGE 0x300
++#define TS_MAX_VALID_PRESSURE_CHANGE 0x100
++
++//
++// This is the minimum Z1 Value that is valid.
++//
++#define MIN_Z1_VALUE 0x50
++
++//
++// Settling delay for taking each ADC measurement. Increase this
++// if ts is jittery.
++//
++#define EP93XX_TS_ADC_DELAY_USEC 2000
++
++//
++// Delay between TS points.
++//
++#define EP93XX_TS_PER_POINT_DELAY_USEC 10000
++
++//-----------------------------------------------------------------------------
++// Debug messaging thru the UARTs
++//-----------------------------------------------------------------------------
++/*
++ * Hello there! Are you trying to get this driver to work with a new
++ * touschscreen? Turn this on and you will get useful info coming
++ * out of your serial port.
++ */
++/* #define PRINT_CALIBRATION_FACTORS */
++#ifdef PRINT_CALIBRATION_FACTORS
++#define UART_HACK_DEBUG 1
++int iMaxX=0, iMaxY=0, iMinX = 0xfff, iMinY = 0xfff;
++#endif
++
++/*
++ * For debugging, let's spew messages out serial port 1 or 3 at 57,600 baud.
++ */
++/* #define UART_HACK_DEBUG 1 */
++#if 0
++#ifdef UART_HACK_DEBUG
++static char szBuf[256];
++void UARTWriteString(char * msg);
++#define DPRINTK( x... ) \
++ sprintf( szBuf, ##x ); \
++ UARTWriteString( szBuf );
++#else
++static char szBuf[256];
++#define DPRINTK( x... ) \
++ sprintf( szBuf, ##x ); \
++ printk( szBuf );
++#endif
++#endif // 0
++#define DPRINTK( x... )
++
++//-----------------------------------------------------------------------------
++// A few more macros...
++//-----------------------------------------------------------------------------
++#define TSSETUP_DEFAULT ( TSSETUP_NSMP_32 | TSSETUP_DEV_64 | \
++ ((128<<TSSETUP_SDLY_SHIFT) & TSSETUP_SDLY_MASK) | \
++ ((128<<TSSETUP_DLY_SHIFT) & TSSETUP_DLY_MASK) )
++
++#define TSSETUP2_DEFAULT (TSSETUP2_NSIGND)
++
++//
++// For now, we use one of the minor numbers from the local/experimental
++// range.
++//
++#define EP93XX_TS_MINOR 240
++
++//-----------------------------------------------------------------------------
++// Static Declarations
++//-----------------------------------------------------------------------------
++static unsigned int guiLastX, guiLastY;
++static unsigned int guiLastInvPressure;
++
++struct TouchScreenSample
++{
++ int currentX;
++ int currentY;
++ int currentButton;
++ int currentPressure;
++ struct timeval currentTime;
++};
++
++//
++// This must match the structure in tslib.
++//
++struct ts_sample {
++ int x;
++ int y;
++ unsigned int pressure;
++ struct timeval tv;
++};
++
++
++static struct TouchScreenSample gSample;
++
++// static int currentX, currentY, currentButton;
++// static int gPressure;
++// static struct timeval gtime;
++
++static int bFreshTouchData;
++static int bCurrentPenDown;
++
++
++
++static DECLARE_WAIT_QUEUE_HEAD(queue);
++static DECLARE_MUTEX(open_sem);
++static spinlock_t event_buffer_lock = SPIN_LOCK_UNLOCKED;
++static struct fasync_struct *ep93xx_fasync;
++
++//-----------------------------------------------------------------------------
++// Typedef Declarations
++//-----------------------------------------------------------------------------
++typedef enum {
++ TS_MODE_UN_INITIALIZED,
++ TS_MODE_HARDWARE_SCAN,
++ TS_MODE_SOFT_SCAN
++} ts_mode_t;
++
++static ts_mode_t gScanningMode;
++
++typedef enum{
++ TS_STATE_STOPPED = 0,
++ TS_STATE_Z1,
++ TS_STATE_Z2,
++ TS_STATE_Y,
++ TS_STATE_X,
++ TS_STATE_DONE
++} ts_states_t;
++
++typedef struct
++{
++ unsigned int uiX;
++ unsigned int uiY;
++ unsigned int uiZ1;
++ unsigned int uiZ2;
++ ts_states_t state;
++} ts_struct_t;
++
++static ts_struct_t sTouch;
++
++/*
++ * From the spec, here's how to set up the touch screen's switch registers.
++ */
++typedef struct
++{
++ unsigned int uiDetect;
++ unsigned int uiDischarge;
++ unsigned int uiXSample;
++ unsigned int uiYSample;
++ unsigned int uiSwitchZ1;
++ unsigned int uiSwitchZ2;
++}SwitchStructType;
++
++//
++// Here's the switch settings for a 4-wire touchscreen. See the spec
++// for how to handle a 4, 7, or 8-wire.
++//
++const static SwitchStructType sSwitchSettings =
++/* s28en=0
++ * TSDetect TSDischarge TSXSample TSYSample SwitchZ1 SwitchZ2
++ */
++ {0x00403604, 0x0007fe04, 0x00081604, 0x00104601, 0x00101601, 0x00101608};
++
++
++//-----------------------------------------------------------------------------
++// Function Declarations
++//-----------------------------------------------------------------------------
++static void ep93xx_ts_set_direct( unsigned int uiADCSwitch );
++static irqreturn_t ep93xx_ts_isr(int irq, void *dev_id);
++static irqreturn_t ep93xx_timer2_isr(int irq, void *dev_id);
++static void ee93xx_ts_evt_add( int button, int dX, int dY, int Pressure );
++static ssize_t ep93xx_ts_read(struct file *filp, char *buf,
++ size_t count, loff_t *l);
++static unsigned int ep93xx_ts_poll(struct file *filp, poll_table *wait);
++static int ep93xx_ts_open(struct inode *inode, struct file *filp);
++static int ep93xx_ts_fasync(int fd, struct file *filp, int on);
++static int ep93xx_ts_release(struct inode *inode, struct file *filp);
++static ssize_t ep93xx_ts_write(struct file *file, const char *buffer,
++ size_t count, loff_t *ppos);
++static void ep93xx_ts_setup(void);
++static void ep93xx_ts_shutdown(void);
++int __init ep93xx_ts_init(void);
++void __exit ep93xx_ts_exit(void);
++static unsigned int CalculateInvPressure( void );
++static unsigned int ADCGetData( unsigned int uiSamples, unsigned int uiMaxDiff);
++static void TS_Soft_Scan_Mode(void);
++static void TS_Hardware_Scan_Mode(void);
++static void ProcessPointData(void);
++static void Set_Timer2_uSec( unsigned int Delay_mSec );
++static void Stop_Timer2(void);
++
++
++
++//-----------------------------------------------------------------------------
++// Debug stuff...
++//-----------------------------------------------------------------------------
++
++#ifdef UART_HACK_DEBUG
++
++// This "array" is a cheap-n-easy way of getting access to the UART registers.
++static unsigned int * const pDebugUART=(unsigned int *)IO_ADDRESS(UART1_BASE);
++//static unsigned int * const pDebugUART=(unsigned int *)IO_ADDRESS(UART3_BASE);
++static int bUartInitialized = 0;
++
++void SendChar(char value)
++{
++ // wait for Tx fifo full flag to clear.
++ while (pDebugUART[0x18>>2] & 0x20);
++
++ // send a char to the uart
++ pDebugUART[0] = value;
++}
++
++void UARTWriteString(char * msg)
++{
++ int index = 0;
++ unsigned int uiTemp;
++
++ //if((pDebugUART[0x14>>2] & 0x1) == 0)
++ if( bUartInitialized == 0 )
++ {
++ uiTemp = inl(EP93XX_SYSCON_DEVICE_CONFIG);
++ uiTemp |= EP93XX_SYSCON_DEVCFG_U1E;
++ //uiTemp |= EP93XX_SYSCON_DEVCFG_U3E;
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, uiTemp);
++ pDebugUART[0x10>>2] = 0xf;
++ pDebugUART[0xc>>2] = 0;
++ pDebugUART[0x8>>2] = 0x70;
++ pDebugUART[0x14>>2] = 0x1;
++ bUartInitialized = 1;
++ }
++ while (msg[index] != 0)
++ {
++ if (msg[index] == '\n')
++ {
++ SendChar('\r');
++ SendChar('\n');
++ }
++ else
++ {
++ SendChar(msg[index]);
++ }
++ index++;
++ }
++}
++#endif // UART_HACK_DEBUG
++
++/*
++ * ep93xx_ts_isr
++ */
++static irqreturn_t ep93xx_ts_isr(int irq, void *dev_id)
++{
++ DPRINTK("isr\n");
++
++ //
++ // Note that we don't clear the interrupt here. The interrupt
++ // gets cleared in TS_Soft_Scan_Mode when the TS ENABLE
++ // bit is cleared.
++ //
++
++ //
++ // Set the ts to manual polling mode and schedule a callback.
++ // That way we can return from the isr in a reasonable amount of
++ // time and process the touch in the callback after a brief delay.
++ //
++ TS_Soft_Scan_Mode();
++
++ return(IRQ_HANDLED);
++}
++
++/*
++ * Save the current ts 'event' in an atomic fashion.
++ */
++static void ee93xx_ts_evt_add( int buttons, int iX, int iY, int iPressure )
++{
++#ifdef PRINT_CALIBRATION_FACTORS
++ if( iX > iMaxX ) iMaxX = iX;
++ if( iX < iMinX ) iMinX = iX;
++ if( iY > iMaxY ) iMaxY = iY;
++ if( iY < iMinY ) iMinY = iY;
++#endif
++
++
++ // printk("ee93xx_ts_evt_add\n");
++ //DPRINTK("cb\n");
++ /*
++ * Note the event, but use spinlocks to keep it from getting
++ * halfway read if we get interrupted.
++ */
++
++ spin_lock(&event_buffer_lock);
++ gSample.currentX = iX;
++ gSample.currentY = iY;
++ gSample.currentButton = buttons;
++ gSample.currentPressure = iPressure;
++ bFreshTouchData = 1;
++ do_gettimeofday(&gSample.currentTime);
++
++
++ spin_unlock(&event_buffer_lock);
++
++ kill_fasync(&ep93xx_fasync, SIGIO, POLL_IN);
++ wake_up_interruptible(&queue);
++
++}
++
++
++static ssize_t ep93xx_ts_read(struct file *filp, char *buf, size_t count, loff_t *l)
++{
++
++ unsigned short data[3];
++ struct ts_sample ts_data;
++ int iReturn = -EFAULT;
++ // printk("ep93xx_ts_read\n");
++
++#ifdef PRINT_CALIBRATION_FACTORS
++ static int lala=0;
++ if( bFreshTouchData && (lala++ > 9) )
++ {
++ DPRINTK("%4d, %4d - range [%4d to %4d],[%4d to %4d]\n",
++ f, currentY, iMinX, iMaxX, iMinY, iMaxY );
++ lala = 0;
++ }
++#endif
++ if( !bFreshTouchData)
++ {
++ iReturn = 0;
++ }
++ else if( (count == sizeof(data)) )
++ {
++ spin_lock_irq(&event_buffer_lock);
++ bFreshTouchData = 0;
++ data[0] = gSample.currentX;
++ data[1] = gSample.currentY;
++ data[2] = gSample.currentButton;
++
++ spin_unlock_irq(&event_buffer_lock);
++
++ if (copy_to_user(buf, data, sizeof data))
++ return -EFAULT;
++
++ count -= sizeof(data);
++
++ /* return the # of bytes that got read */
++ iReturn = sizeof(data) ;
++ }
++ else if (count == sizeof(struct ts_sample) )
++ {
++ spin_lock_irq(&event_buffer_lock);
++ bFreshTouchData = 0;
++ ts_data.x = gSample.currentX;
++ ts_data.y = gSample.currentY;
++ ts_data.pressure = gSample.currentPressure;
++ ts_data.tv = gSample.currentTime;
++ spin_unlock_irq(&event_buffer_lock);
++
++ if (copy_to_user(buf, &ts_data, sizeof(struct ts_sample)))
++ {
++ iReturn = -EFAULT;
++ }
++ else
++ {
++ count -= sizeof(ts_data);
++ iReturn = sizeof(ts_data);
++ }
++
++ }
++
++ return iReturn;
++}
++
++static unsigned int ep93xx_ts_poll(struct file *filp, poll_table *wait)
++{
++ // printk("ep93xx_ts_poll\n");
++ poll_wait(filp, &queue, wait);
++
++ if( bFreshTouchData )
++ {
++ return POLLIN | POLLRDNORM;
++ }
++
++ return 0;
++}
++
++static int ep93xx_ts_open(struct inode *inode, struct file *filp)
++{
++ printk("ep93xx_ts_open");
++
++ if( down_trylock(&open_sem) )
++ {
++ return -EBUSY;
++ }
++
++ ep93xx_ts_setup();
++
++ return 0;
++}
++
++/*
++ * Asynchronous I/O support.
++ */
++static int ep93xx_ts_fasync(int fd, struct file *filp, int on)
++{
++ int retval;
++
++ retval = fasync_helper(fd, filp, on, &ep93xx_fasync);
++ if (retval < 0)
++ {
++ return retval;
++ }
++
++ return 0;
++}
++
++static int ep93xx_ts_release(struct inode *inode, struct file *filp)
++{
++ Stop_Timer2();
++
++ /*
++ * Call our async I/O support to request that this file
++ * cease to be used for async I/O.
++ */
++ ep93xx_ts_fasync(-1, filp, 0);
++
++ ep93xx_ts_shutdown();
++
++ up(&open_sem);
++
++ return 0;
++}
++
++static ssize_t ep93xx_ts_write(struct file *file, const char *buffer, size_t count,
++ loff_t *ppos)
++{
++ return -EINVAL;
++}
++
++
++static int ep93xx_ts_ioctl(struct inode *inode, struct file *file, uint command, ulong u)
++{
++ static const int version = EV_VERSION;
++ static const u_int32_t bit =(1 << EV_ABS);
++ static const u_int32_t absbit = (1 << ABS_X) | (1 << ABS_Y) | (1 << ABS_PRESSURE);
++ int iReturn ;
++ int i = 0;
++
++ switch(command)
++ {
++ case EVIOCGVERSION:
++ DPRINTK("ep93xx_ts_ioctl command = EVIOCGVERSION\r\n");
++ i = copy_to_user((void __user *)u, (void *)version, sizeof(version));
++ iReturn = i ? -EFAULT : 0;
++ break;
++
++ case EVIOCGBIT(0,sizeof(u_int32_t) * 8) :
++ DPRINTK("ep93xx_ts_ioctl command = EVIOCGBIT(0,sizeof(uint32) * 8)\r\n");
++ i = copy_to_user((void __user *)u, (void *)bit, sizeof(bit));
++ iReturn = i ? -EFAULT : 0;
++ break;
++
++ case EVIOCGBIT(EV_ABS, sizeof(absbit) * 8):
++ DPRINTK("ep93xx_ts_ioctl command = EVIOCGBIT(0,sizeof(uint32) * 8)\r\n");
++ copy_to_user((void __user *)u, (void *)absbit, sizeof(absbit));
++ iReturn = i ? -EFAULT : 0;
++ break;
++ default:
++ DPRINTK(" ep93xx_ts_ioctl unknown command = %d\n",u);
++ iReturn = -1;
++ break;
++ }
++
++ return iReturn;
++}
++
++static struct file_operations ep93xx_ts_fops = {
++ owner: THIS_MODULE,
++ read: ep93xx_ts_read,
++ write: ep93xx_ts_write,
++ poll: ep93xx_ts_poll,
++ open: ep93xx_ts_open,
++ ioctl: ep93xx_ts_ioctl,
++ release: ep93xx_ts_release,
++ fasync: ep93xx_ts_fasync,
++};
++
++static struct miscdevice ep93xx_ts_miscdev =
++{
++ EP93XX_TS_MINOR,
++ "ep93xx_ts",
++ &ep93xx_ts_fops
++};
++
++void ep93xx_ts_setup(void)
++{
++ unsigned int uiKTDIV, uiTSXYMaxMin;
++ // printk("ep93xx_hw_setup\n");
++
++ /*
++ * Set the TSEN bit in KTDIV so that we are enabling the clock
++ * for the touchscreen.
++ */
++ uiKTDIV = inl(SYSCON_KTDIV);
++ uiKTDIV |= SYSCON_KTDIV_TSEN;
++ SysconSetLocked( SYSCON_KTDIV, uiKTDIV );
++
++ //
++ // Program the TSSetup and TSSetup2 registers.
++ //
++ outl( TSSETUP_DEFAULT, TSSetup );
++ outl( TSSETUP2_DEFAULT, TSSetup2 );
++
++ //
++ // Set the the touch settings.
++ //
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiDischarge, TSDirect );
++
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiDischarge, TSDischarge );
++
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiSwitchZ1, TSXSample );
++
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiSwitchZ2, TSYSample );
++
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiDetect, TSDetect );
++
++ //
++ // X,YMin set to 0x40 = have to drag that many pixels for a new irq.
++ // X,YMax set to 0x40 = 1024 pixels is the maximum movement within the
++ // time scan limit.
++ //
++ uiTSXYMaxMin = (50 << TSMAXMIN_XMIN_SHIFT) & TSMAXMIN_XMIN_MASK;
++ uiTSXYMaxMin |= (50 << TSMAXMIN_YMIN_SHIFT) & TSMAXMIN_YMIN_MASK;
++ uiTSXYMaxMin |= (0xff << TSMAXMIN_XMAX_SHIFT) & TSMAXMIN_XMAX_MASK;
++ uiTSXYMaxMin |= (0xff << TSMAXMIN_YMAX_SHIFT) & TSMAXMIN_YMAX_MASK;
++ outl( uiTSXYMaxMin, TSXYMaxMin );
++
++ bCurrentPenDown = 0;
++ bFreshTouchData = 0;
++ guiLastX = 0;
++ guiLastY = 0;
++ guiLastInvPressure = 0xffffff;
++
++ //
++ // Enable the touch screen scanning engine.
++ //
++ TS_Hardware_Scan_Mode();
++}
++
++/*
++ * ep93xx_ts_shutdown
++ *
++ */
++static void
++ep93xx_ts_shutdown(void)
++{
++ unsigned int uiKTDIV;
++
++ DPRINTK("ep93xx_ts_shutdown\n");
++
++ sTouch.state = TS_STATE_STOPPED;
++ Stop_Timer2();
++
++ /*
++ * Disable the scanning engine.
++ */
++ outl( 0, TSSetup );
++ outl( 0, TSSetup2 );
++
++ /*
++ * Clear the TSEN bit in KTDIV so that we are disabling the clock
++ * for the touchscreen.
++ */
++ uiKTDIV = inl(SYSCON_KTDIV);
++ uiKTDIV &= ~SYSCON_KTDIV_TSEN;
++ SysconSetLocked( SYSCON_KTDIV, uiKTDIV );
++
++} /* ep93xx_ts_shutdown */
++
++static irqreturn_t ep93xx_timer2_isr(int irq, void *dev_id)
++{
++ DPRINTK("%d", (int)sTouch.state );
++
++ switch( sTouch.state )
++ {
++ case TS_STATE_STOPPED:
++ TS_Hardware_Scan_Mode();
++ break;
++
++ //
++ // Get the Z1 value for pressure measurement and set up
++ // the switch register for getting the Z2 measurement.
++ //
++ case TS_STATE_Z1:
++ Set_Timer2_uSec( EP93XX_TS_ADC_DELAY_USEC );
++ sTouch.uiZ1 = ADCGetData( 2, 200 );
++ ep93xx_ts_set_direct( sSwitchSettings.uiSwitchZ2 );
++ sTouch.state = TS_STATE_Z2;
++ break;
++
++ //
++ // Get the Z2 value for pressure measurement and set up
++ // the switch register for getting the Y measurement.
++ //
++ case TS_STATE_Z2:
++ sTouch.uiZ2 = ADCGetData( 2, 200 );
++ ep93xx_ts_set_direct( sSwitchSettings.uiYSample );
++ sTouch.state = TS_STATE_Y;
++ break;
++
++ //
++ // Get the Y value and set up the switch register for
++ // getting the X measurement.
++ //
++ case TS_STATE_Y:
++ sTouch.uiY = ADCGetData( 4, 20 );
++ ep93xx_ts_set_direct( sSwitchSettings.uiXSample );
++ sTouch.state = TS_STATE_X;
++ break;
++
++ //
++ // Read the X value. This is the last of the 4 adc values
++ // we need so we continue on to process the data.
++ //
++ case TS_STATE_X:
++ Stop_Timer2();
++
++ sTouch.uiX = ADCGetData( 4, 20 );
++
++ outl( 0xaa, TSSWLock );
++ outl( sSwitchSettings.uiDischarge, TSDirect );
++
++ sTouch.state = TS_STATE_DONE;
++
++ /*
++ * Process this set of ADC readings.
++ */
++ ProcessPointData();
++
++ break;
++
++
++ //
++ // Shouldn't get here. But if we do, we can recover...
++ //
++ case TS_STATE_DONE:
++ TS_Hardware_Scan_Mode();
++ break;
++ }
++
++ //
++ // Clear the timer2 interrupt.
++ //
++ outl( 1, TIMER2CLEAR );
++ return(IRQ_HANDLED);
++}
++
++/*---------------------------------------------------------------------
++ * ProcessPointData
++ *
++ * This routine processes the ADC data into usable point data and then
++ * puts the driver into hw or sw scanning mode before returning.
++ *
++ * We calculate inverse pressure (lower number = more pressure) then
++ * do a hystheresis with the two pressure values 'light' and 'heavy'.
++ *
++ * If we are above the light, we have pen up.
++ * If we are below the heavy we have pen down.
++ * As long as the pressure stays below the light, pen stays down.
++ * When we get above the light again, pen goes back up.
++ *
++ */
++static void ProcessPointData(void)
++{
++ int bValidPoint = 0;
++ unsigned int uiXDiff, uiYDiff, uiInvPressureDiff;
++ unsigned int uiInvPressure;
++
++ //
++ // Calculate the current pressure.
++ //
++ uiInvPressure = CalculateInvPressure();
++
++ DPRINTK(" X=0x%x, Y=0x%x, Z1=0x%x, Z2=0x%x, InvPressure=0x%x",
++ sTouch.uiX, sTouch.uiY, sTouch.uiZ1, sTouch.uiZ2, uiInvPressure );
++
++ //
++ // If pen pressure is so light that it is greater than the 'max' setting
++ // then we consider this to be a pen up.
++ //
++ if( uiInvPressure >= TS_LIGHT_INV_PRESSURE )
++ {
++ DPRINTK(" -- up \n");
++ bCurrentPenDown = 0;
++ ee93xx_ts_evt_add( 0, guiLastX, guiLastY, 0 );
++ TS_Hardware_Scan_Mode();
++ return;
++ }
++
++ //
++ // Hystheresis:
++ // If the pen pressure is hard enough to be less than the 'min' OR
++ // the pen is already down and is still less than the 'max'...
++ //
++ if( (uiInvPressure < TS_HEAVY_INV_PRESSURE) ||
++ ( bCurrentPenDown && (uiInvPressure < TS_LIGHT_INV_PRESSURE) ) )
++ {
++ if( bCurrentPenDown )
++ {
++ //
++ // If pen was previously down, check the difference between
++ // the last sample and this one... if the difference between
++ // samples is too great, ignore the sample.
++ //
++ uiXDiff = abs(guiLastX - sTouch.uiX);
++ uiYDiff = abs(guiLastY - sTouch.uiY);
++ uiInvPressureDiff = abs(guiLastInvPressure - uiInvPressure);
++
++ if( (uiXDiff < TS_MAX_VALID_XY_CHANGE) && (uiYDiff < TS_MAX_VALID_XY_CHANGE) &&
++ (uiInvPressureDiff < TS_MAX_VALID_PRESSURE_CHANGE) )
++ {
++ DPRINTK(" -- valid(two) \n");
++ bValidPoint = 1;
++ }
++ else
++ {
++ DPRINTK(" -- INvalid(two) \n");
++ }
++ }
++ else
++ {
++ DPRINTK(" -- valid \n");
++ bValidPoint = 1;
++ }
++
++ /*
++ * If either the pen was put down or dragged make a note of it.
++ */
++ if( bValidPoint )
++ {
++ guiLastX = sTouch.uiX;
++ guiLastY = sTouch.uiY;
++ guiLastInvPressure = uiInvPressure;
++ bCurrentPenDown = 1;
++ ee93xx_ts_evt_add( 1, sTouch.uiX, sTouch.uiY, (0x7000000 /uiInvPressure) );
++ }
++
++ TS_Soft_Scan_Mode();
++ return;
++ }
++
++ DPRINTK(" -- fallout \n");
++ TS_Hardware_Scan_Mode();
++}
++
++static void ep93xx_ts_set_direct( unsigned int uiADCSwitch )
++{
++ unsigned int uiResult;
++
++ //
++ // Set the switch settings in the direct register.
++ //
++ outl( 0xaa, TSSWLock );
++ outl( uiADCSwitch, TSDirect );
++
++ //
++ // Read and throw away the first sample.
++ //
++ do {
++ uiResult = inl(TSXYResult);
++ } while( !(uiResult & TSXYRESULT_SDR) );
++
++}
++
++static unsigned int ADCGetData
++(
++ unsigned int uiSamples,
++ unsigned int uiMaxDiff
++)
++{
++ unsigned int uiResult, uiValue, uiCount, uiLowest, uiHighest, uiSum, uiAve;
++
++ do
++ {
++ //
++ //Initialize our values.
++ //
++ uiLowest = 0xfffffff;
++ uiHighest = 0;
++ uiSum = 0;
++
++ for( uiCount = 0 ; uiCount < uiSamples ; uiCount++ )
++ {
++ //
++ // Read the touch screen four more times and average.
++ //
++ do {
++ uiResult = inl(TSXYResult);
++ } while( !(uiResult & TSXYRESULT_SDR) );
++
++ uiValue = (uiResult & TSXYRESULT_AD_MASK) >> TSXYRESULT_AD_SHIFT;
++ uiValue = ((uiValue >> 4) + ((1 + TSXYRESULT_X_MASK)>>1)) & TSXYRESULT_X_MASK;
++
++ //
++ // Add up the values.
++ //
++ uiSum += uiValue;
++
++ //
++ // Get the lowest and highest values.
++ //
++ if( uiValue < uiLowest )
++ {
++ uiLowest = uiValue;
++ }
++ if( uiValue > uiHighest )
++ {
++ uiHighest = uiValue;
++ }
++ }
++
++ } while( (uiHighest - uiLowest) > uiMaxDiff );
++
++ //
++ // Calculate the Average value.
++ //
++ uiAve = uiSum / uiSamples;
++
++ return uiAve;
++}
++
++//****************************************************************************
++// CalculateInvPressure
++//****************************************************************************
++// Is the Touch Valid. Touch is not valid if the X or Y value is not
++// in range and the pressure is not enough.
++//
++// Touch resistance can be measured by the following formula:
++//
++// Rx * X * Z2
++// Rtouch = --------- * (-- - 1)
++// 4096 Z1
++//
++// This is simplified in the ration of Rtouch to Rx. The lower the value, the
++// higher the pressure.
++//
++// Z2
++// InvPressure = X * (-- - 1)
++// Z1
++//
++static unsigned int CalculateInvPressure(void)
++{
++ unsigned int uiInvPressure;
++
++ //
++ // Check to see if the point is valid.
++ //
++ if( sTouch.uiZ1 < MIN_Z1_VALUE )
++ {
++ uiInvPressure = 0x10000;
++ }
++
++ //
++ // Can omit the pressure calculation if you need to get rid of the division.
++ //
++ else
++ {
++ uiInvPressure = ((sTouch.uiX * sTouch.uiZ2) / sTouch.uiZ1) - sTouch.uiX;
++ }
++
++ return uiInvPressure;
++}
++
++
++
++//****************************************************************************
++// TS_Hardware_Scan_Mode
++//****************************************************************************
++// Enables the ep93xx ts scanning engine so that when the pen goes down
++// we will get an interrupt.
++//
++//
++static void TS_Hardware_Scan_Mode(void)
++{
++ unsigned int uiDevCfg;
++
++ DPRINTK("S\n");
++
++ //
++ // Disable the soft scanning engine.
++ //
++ sTouch.state = TS_STATE_STOPPED;
++ Stop_Timer2();
++
++ //
++ // Clear the TIN (Touchscreen INactive) bit so we can go to
++ // automatic scanning mode.
++ //
++ uiDevCfg = inl( EP93XX_SYSCON_DEVICE_CONFIG );
++ SysconSetLocked( EP93XX_SYSCON_DEVICE_CONFIG, (uiDevCfg & ~EP93XX_SYSCON_DEVCFG_TIN) );
++
++ //
++ // Enable the touch screen scanning state machine by setting
++ // the ENABLE bit.
++ //
++ outl( (TSSETUP_DEFAULT | TSSETUP_ENABLE), TSSetup );
++
++ //
++ // Set the flag to show that we are in interrupt mode.
++ //
++ gScanningMode = TS_MODE_HARDWARE_SCAN;
++
++ //
++ // Initialize TSSetup2 register.
++ //
++ outl( TSSETUP2_DEFAULT, TSSetup2 );
++
++}
++
++//****************************************************************************
++// TS_Soft_Scan_Mode
++//****************************************************************************
++// Sets the touch screen to manual polling mode.
++//
++//
++static void TS_Soft_Scan_Mode(void)
++{
++ unsigned int uiDevCfg;
++
++ DPRINTK("M\n");
++
++ if( gScanningMode != TS_MODE_SOFT_SCAN )
++ {
++ //
++ // Disable the touch screen scanning state machine by clearing
++ // the ENABLE bit.
++ //
++ outl( TSSETUP_DEFAULT, TSSetup );
++
++ //
++ // Set the TIN bit so we can do manual touchscreen polling.
++ //
++ uiDevCfg = inl(EP93XX_SYSCON_DEVICE_CONFIG );
++ SysconSetLocked( EP93XX_SYSCON_DEVICE_CONFIG, (uiDevCfg | EP93XX_SYSCON_DEVCFG_TIN) );
++ }
++
++ //
++ // Set the switch register up for the first ADC reading
++ //
++ ep93xx_ts_set_direct( sSwitchSettings.uiSwitchZ1 );
++
++ //
++ // Initialize our software state machine to know which ADC
++ // reading to take
++ //
++ sTouch.state = TS_STATE_Z1;
++
++ //
++ // Set the timer so after a mSec or two settling delay it will
++ // take the first ADC reading.
++ //
++ Set_Timer2_uSec( EP93XX_TS_PER_POINT_DELAY_USEC );
++
++ //
++ // Note that we are in sw scanning mode not hw scanning mode.
++ //
++ gScanningMode = TS_MODE_SOFT_SCAN;
++
++}
++
++static void Set_Timer2_uSec( unsigned int uiDelay_uSec )
++{
++ unsigned int uiClockTicks;
++
++ /*
++ * Stop timer 2
++ */
++ outl( 0, TIMER2CONTROL );
++
++ uiClockTicks = ((uiDelay_uSec * 508) + 999) / 1000;
++ outl( uiClockTicks, TIMER2LOAD );
++ outl( uiClockTicks, TIMER2VALUE );
++
++ /*
++ * Set up Timer 2 for 508 kHz clock and periodic mode.
++ */
++ outl( 0xC8, TIMER2CONTROL );
++
++}
++
++static void Stop_Timer2(void)
++{
++ outl( 0, TIMER2CONTROL );
++}
++
++/*
++ * Initialization and exit routines
++ */
++int __init ep93xx_ts_init(void)
++{
++ int retval;
++
++ //printk("ep93xx_ts_init\n");
++
++ // printk("request Touchscreen interrupt.\n");
++ retval = request_irq( IRQ_EP93XX_TOUCH, ep93xx_ts_isr, IRQF_DISABLED, "ep93xx_ts", 0);
++ if( retval )
++ {
++ printk(KERN_WARNING "ep93xx_ts: failed to get touchscreen IRQ\n");
++ return retval;
++ }
++
++ // printk("Request Timer interrupt.\n");
++ retval = request_irq( IRQ_EP93XX_TIMER2, ep93xx_timer2_isr,
++ IRQF_DISABLED, "ep93xx_timer2", 0);
++ if( retval )
++ {
++ printk(KERN_WARNING "ep93xx_ts: failed to get timer2 IRQ\n");
++ return retval;
++ }
++
++ // printk("Register Touchscreen Driver\n");
++ misc_register(&ep93xx_ts_miscdev);
++
++ sTouch.state = TS_STATE_STOPPED;
++ gScanningMode = TS_MODE_UN_INITIALIZED;
++
++ printk(KERN_NOTICE "ep93xx touchscreen driver configured for 4-wire operation\n");
++
++ return 0;
++}
++void __exit
++ep93xx_ts_exit(void)
++{
++ DPRINTK("ep93xx_ts_exit\n");
++
++ Stop_Timer2();
++
++ free_irq(IRQ_EP93XX_TOUCH, 0);
++ free_irq(IRQ_EP93XX_TIMER2, 0);
++
++ misc_deregister(&ep93xx_ts_miscdev);
++}
++
++module_init(ep93xx_ts_init);
++module_exit(ep93xx_ts_exit);
++
++MODULE_DESCRIPTION("Cirrus EP93xx touchscreen driver");
++MODULE_SUPPORTED_DEVICE("touchscreen/ep93xx");
+Index: linux-2.6.30.9/drivers/input/touchscreen/ep93xx_ts.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/input/touchscreen/ep93xx_ts.h 2009-11-24 21:00:45.000000000 +0100
+@@ -0,0 +1,53 @@
++/*
++ * ep93xx_ts.h
++ *
++ * The contents of this file are subject to the Mozilla Public License
++ * Version 1.1 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License
++ * at http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS"
++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
++ * the License for the specific language governing rights and
++ * limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ * Alternatively, the contents of this file may be used under the
++ * terms of the GNU General Public License version 2 (the "GPL"), in
++ * which case the provisions of the GPL are applicable instead of the
++ * above. If you wish to allow the use of your version of this file
++ * only under the terms of the GPL and not to allow others to use
++ * your version of this file under the MPL, indicate your decision by
++ * deleting the provisions above and replace them with the notice and
++ * other provisions required by the GPL. If you do not delete the
++ * provisions above, a recipient may use your version of this file
++ * under either the MPL or the GPL.
++ */
++
++#ifndef _LINUX_EP93XX_TS_H
++#define _LINUX_EP93XX_TS_H
++
++/*touchscreen register defines*/
++#define SYSCON_KTDIV EP93XX_SYSCON_KEY_TOUCH_CLOCK_DIV
++#define SYSCON_SWLOCK EP93XX_SYSCON_SWLOCK
++#define TSSetup EP93XX_TOUCHSCREEN_TSSetup
++#define TSXYMaxMin EP93XX_TOUCHSCREEN_TSXYMaxMin
++#define TSXYResult EP93XX_TOUCHSCREEN_TSDischarge
++#define TSDischarge EP93XX_TOUCHSCREEN_TSDischarge
++#define TSXSample EP93XX_TOUCHSCREEN_TSXSample
++#define TSYSample EP93XX_TOUCHSCREEN_TSYSample
++#define TSDirect EP93XX_TOUCHSCREEN_TSDirect
++#define TSDetect EP93XX_TOUCHSCREEN_TSDetect
++#define TSSWLock EP93XX_TOUCHSCREEN_TSSWLock
++#define TSSetup2 EP93XX_TOUCHSCREEN_TSSetup2
++
++
++//#define SYSCON_DEVCFG EP93XX_SYSCON_DEVICE_CONFIG
++#define TIMER2CONTROL EP93XX_TIMER2_CONTROL
++#define TIMER2LOAD EP93XX_TIMER2_LOAD
++#define TIMER2VALUE EP93XX_TIMER2_VALUE
++#define TIMER2CLEAR EP93XX_TIMER2_CLEAR
++#endif
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/hardware.h
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/mach-ep93xx/include/mach/hardware.h 2009-11-24 21:00:18.000000000 +0100
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/hardware.h 2009-11-24 21:09:21.000000000 +0100
+@@ -7,6 +7,7 @@
+ #include "ep93xx-regs.h"
+
+ #define pcibios_assign_all_busses() 0
++#include "regs_touch.h"
+
+ #include "platform.h"
+
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/regs_touch.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/regs_touch.h 2009-11-24 21:00:45.000000000 +0100
+@@ -0,0 +1,95 @@
++/*=============================================================================
++ *
++ * FILE: regs_touch.h
++ *
++ * DESCRIPTION: Analog Touchscreen Register Definition
++ *
++ * Copyright Cirrus Logic, 2001-2003
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *=============================================================================
++ */
++#ifndef _REGS_TOUCH_H_
++#define _REGS_TOUCH_H_
++
++/*
++ *-----------------------------------------------------------------------------
++ * Individual bit #defines
++ *-----------------------------------------------------------------------------
++ */
++#define TSSETUP_SDLY_MASK 0x000003FF
++#define TSSETUP_SDLY_SHIFT 0
++#define TSSETUP_NSMP_4 0x00000000
++#define TSSETUP_NSMP_8 0x00000400
++#define TSSETUP_NSMP_16 0x00000800
++#define TSSETUP_NSMP_32 0x00000C00
++#define TSSETUP_NSMP_MASK 0x00000C00
++#define TSSETUP_DEV_4 0x00000000
++#define TSSETUP_DEV_8 0x00001000
++#define TSSETUP_DEV_12 0x00002000
++#define TSSETUP_DEV_16 0x00003000
++#define TSSETUP_DEV_24 0x00004000
++#define TSSETUP_DEV_32 0x00005000
++#define TSSETUP_DEV_64 0x00006000
++#define TSSETUP_DEV_128 0x00007000
++#define TSSETUP_ENABLE 0x00008000
++#define TSSETUP_DLY_MASK 0x03FF0000
++#define TSSETUP_DLY_SHIFT 16
++#define TSSETUP_TDTCT 0x80000000
++
++#define TSMAXMIN_XMIN_MASK 0x000000FF
++#define TSMAXMIN_XMIN_SHIFT 0
++#define TSMAXMIN_YMIN_MASK 0x0000FF00
++#define TSMAXMIN_YMIN_SHIFT 8
++#define TSMAXMIN_XMAX_MASK 0x00FF0000
++#define TSMAXMIN_XMAX_SHIFT 16
++#define TSMAXMIN_YMAX_MASK 0xFF000000
++#define TSMAXMIN_YMAX_SHIFT 24
++
++#define TSXYRESULT_X_MASK 0x00000FFF
++#define TSXYRESULT_X_SHIFT 0
++#define TSXYRESULT_AD_MASK 0x0000FFFF
++#define TSXYRESULT_AD_SHIFT 0
++#define TSXYRESULT_Y_MASK 0x0FFF0000
++#define TSXYRESULT_Y_SHIFT 16
++#define TSXYRESULT_SDR 0x80000000
++
++#define TSX_SAMPLE_MASK 0x00003FFF
++#define TSX_SAMPLE_SHIFT 0x00
++#define TSY_SAMPLE_MASK 0x3FFF0000
++#define TSY_SAMPLE_SHIFT 0x10
++
++#define TSSETUP2_TINT 0x00000001
++#define TSSETUP2_NICOR 0x00000002
++#define TSSETUP2_PINT 0x00000004
++#define TSSETUP2_PENSTS 0x00000008
++#define TSSETUP2_PINTEN 0x00000010
++#define TSSETUP2_DEVINT 0x00000020
++#define TSSETUP2_DINTEN 0x00000040
++#define TSSETUP2_DTMEN 0x00000080
++#define TSSETUP2_DISDEV 0x00000100
++#define TSSETUP2_NSIGND 0x00000200
++#define TSSETUP2_S28EN 0x00000400
++#define TSSETUP2_RINTEN 0x00000800
++
++#define TSXYRESULT_SDR 0x80000000
++
++/*
++ *-----------------------------------------------------------------------------
++ *-----------------------------------------------------------------------------
++ */
++
++
++#endif /* _REGS_TOUCH_H_ */
diff --git a/target/linux/ep93xx/patches-2.6.30/007-ep93xx-eth.patch b/target/linux/ep93xx/patches-2.6.30/007-ep93xx-eth.patch
new file mode 100644
index 0000000000..cd715bfaf1
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/007-ep93xx-eth.patch
@@ -0,0 +1,405 @@
+--- /dev/null
++++ b/drivers/net/arm/ep93xx_eth.h
+@@ -0,0 +1,402 @@
++/*------------------------------------------------------------------------
++ * ep93xx_eth.h
++ * : header file of Ethernet Device Driver for Cirrus Logic EP93xx.
++ *
++ * Copyright (C) 2003 by Cirrus Logic www.cirrus.com
++ * This software may be used and distributed according to the terms
++ * of the GNU Public License.
++ *
++ * This file contains device related information like register info
++ * and register access method macros for the Ethernet device
++ * embedded within Cirrus Logic's EP93xx SOC chip.
++ *
++ * Information contained in this file was obtained from
++ * the EP9312 Manual Revision 0.12 and 0.14 from Cirrus Logic.
++ *
++ * History
++ * 05/18/01 Sungwook Kim Initial release
++ * 03/25/2003 Melody Modified for EP92xx
++ *--------------------------------------------------------------------------*/
++
++
++#ifndef _EP9213_ETH_H_
++#define _EP9213_ETH_H_
++
++
++/*---------------------------------------------------------------
++ * Definition of H/W Defects and Their Workarounds
++ *-------------------------------------------------------------*/
++
++
++
++/*---------------------------------------------------------------
++ * Data types used in this driver
++ *-------------------------------------------------------------*/
++typedef unsigned char U8;
++typedef unsigned short U16;
++typedef unsigned long U32;
++typedef unsigned int UINT;
++
++
++
++/*---------------------------------------------------------------
++ * Definition of the registers.
++ * For details, refer to the datasheet .
++ *
++ * Basically, most registers are 32 bits width register.
++ * But some are 16 bits and some are 6 or 8 bytes long.
++ *-------------------------------------------------------------*/
++
++#define REG_RxCTL 0x0000 /*offset to Receiver Control Reg*/
++#define RxCTL_PauseA (1<<20)
++#define RxCTL_RxFCE1 (1<<19)
++#define RxCTL_RxFCE0 (1<<18)
++#define RxCTL_BCRC (1<<17)
++#define RxCTL_SRxON (1<<16)
++#define RxCTL_RCRCA (1<<13)
++#define RxCTL_RA (1<<12)
++#define RxCTL_PA (1<<11)
++#define RxCTL_BA (1<<10)
++#define RxCTL_MA (1<<9)
++#define RxCTL_IAHA (1<<8)
++#define RxCTL_IA3 (1<<3)
++#define RxCTL_IA2 (1<<2)
++#define RxCTL_IA1 (1<<1)
++#define RxCTL_IA0 (1<<0)
++
++#define REG_TxCTL 0x0004 /*offset to Transmit Control Reg*/
++#define TxCTL_DefDis (1<<7)
++#define TxCTL_MBE (1<<6)
++#define TxCTL_ICRC (1<<5)
++#define TxCTL_TxPD (1<<5)
++#define TxCTL_OColl (1<<3)
++#define TxCTL_SP (1<<2)
++#define TxCTL_PB (1<<1)
++#define TxCTL_STxON (1<<0)
++
++#define REG_TestCTL 0x0008 /*Test Control Reg, R/W*/
++#define TestCTL_MACF (1<<7)
++#define TestCTL_MFDX (1<<6)
++#define TestCTL_DB (1<<5)
++#define TestCTL_MIIF (1<<4)
++
++#define REG_MIICmd 0x0010 /*offset to MII Command Reg, R/W*/
++#define MIICmd_OP (0x03<<14)
++#define MIICmd_OP_RD (2<<14)
++#define MIICmd_OP_WR (1<<14)
++#define MIICmd_PHYAD (0x1f<<5)
++#define MIICmd_REGAD (0x1f<<0)
++
++#define REG_MIIData 0x0014 /*offset to MII Data Reg, R/W*/
++#define MIIData_MIIData (0xffff<<0)
++
++#define REG_MIISts 0x0018 /*offset to MII Status Reg, R*/
++#define MIISts_Busy (1<<0)
++
++#define REG_SelfCTL 0x0020 /*offset to Self Control Reg*/
++#define SelfCTL_RWP (1<<7) /*Remote Wake Pin*/
++#define SelfCTL_GPO0 (1<<5)
++#define SelfCTL_PUWE (1<<4)
++#define SelfCTL_PDWE (1<<3)
++#define SelfCTL_MIIL (1<<2)
++#define SelfCTL_RESET (1<<0)
++
++#define REG_IntEn 0x0024 /*Interrupt Enable Reg, R/W*/
++#define IntEn_RWIE (1<<30)
++#define IntEn_RxMIE (1<<29)
++#define IntEn_RxBIE (1<<28)
++#define IntEn_RxSQIE (1<<27)
++#define IntEn_TxLEIE (1<<26)
++#define IntEn_ECIE (1<<25)
++#define IntEn_TxUHIE (1<<24)
++#define IntEn_MOIE (1<<18)
++#define IntEn_TxCOIE (1<<17)
++#define IntEn_RxROIE (1<<16)
++#define IntEn_MIIIE (1<<12)
++#define IntEn_PHYSIE (1<<11)
++#define IntEn_TIE (1<<10)
++#define IntEn_SWIE (1<<8)
++#define IntEn_TxSQIE (1<<3)
++#define IntEn_RxEOFIE (1<<2)
++#define IntEn_RxEOBIE (1<<1)
++#define IntEn_RxHDRIE (1<<0)
++
++#define REG_IntStsP 0x0028 /*offset to Interrupt Status Preserve Reg, R/W*/
++#define REG_IntStsC 0x002c /*offset to Interrupt Status Clear Reg, R*/
++#define IntSts_RWI (1<<30)
++#define IntSts_RxMI (1<<29)
++#define IntSts_RxBI (1<<28)
++#define IntSts_RxSQI (1<<27)
++#define IntSts_TxLEI (1<<26)
++#define IntSts_ECI (1<<25)
++#define IntSts_TxUHI (1<<24)
++#define IntSts_MOI (1<<18)
++#define IntSts_TxCOI (1<<17)
++#define IntSts_RxROI (1<<16)
++#define IntSts_MIII (1<<12)
++#define IntSts_PHYSI (1<<11)
++#define IntSts_TI (1<<10)
++#define IntSts_AHBE (1<<9)
++#define IntSts_SWI (1<<8)
++#define IntSts_OTHER (1<<4)
++#define IntSts_TxSQ (1<<3)
++#define IntSts_RxSQ (1<<2)
++
++#define REG_GT 0x0040 /*offset to General Timer Reg*/
++#define GT_GTC (0xffff<<16)
++#define GT_GTP (0xffff<<0)
++
++#define REG_FCT 0x0044 /*offset to Flow Control Timer Reg*/
++#define FCT_FCT (0x00ffffff<<0)
++
++#define REG_FCF 0x0048 /*offset to Flow Control Format Reg*/
++#define FCF_MACCT (0xffff<<16)
++#define FCF_TPT (0xffff<<0)
++
++#define REG_AFP 0x004c /*offset to Address Filter Pointer Reg*/
++#define AFP_AFP (0x07<<0) /*Address Filter Pointer (bank control for REG_IndAD)*/
++#define AFP_AFP_IA0 0 /*Primary Individual Address (MAC Addr)*/
++#define AFP_AFP_IA1 1 /*Individual Address 1*/
++#define AFP_AFP_IA2 2 /*Individual Address 2*/
++#define AFP_AFP_IA3 3 /*Individual Address 3*/
++#define AFP_AFP_DTxP 6 /*Destination Address of Tx Pause Frame*/
++#define AFP_AFP_HASH 7 /*Hash Table*/
++
++#define REG_IndAD 0x0050 /*offset to Individual Address Reg, n bytes, R/W*/
++
++#define REG_GIntSts 0x0060 /*offset to Global Interrupt Status Reg (writing 1 will clear)*/
++#define REG_GIntROS 0x0068 /*offset to Global Interrupt Status Read Only Reg*/
++#define GIntSts_INT (1<<15) /*Global Interrupt Request Status*/
++
++#define REG_GIntMsk 0x0064 /*offset to Global Interrupt Mask Reg*/
++#define GIntMsk_IntEn (1<<15) /*Global Interrupt Enable*/
++
++#define REG_GIntFrc 0x006c /*offset to Global Interrupt Force Reg*/
++#define GIntFrc_INT (1<<15) /*Force to set GIntSts*/
++
++#define REG_TxCollCnt 0x0070 /*Transmit Collision Count Reg, R*/
++#define REG_RxMissCnt 0x0074 /*Receive Miss Count Reg, R*/
++#define REG_RxRntCnt 0x0078 /*Receive Runt Count Reg, R*/
++
++#define REG_BMCtl 0x0080 /*offset to Bus Master Control Reg, R/W*/
++#define BMCtl_MT (1<<13)
++#define BMCtl_TT (1<<12)
++#define BMCtl_UnH (1<<11)
++#define BMCtl_TxChR (1<<10)
++#define BMCtl_TxDis (1<<9)
++#define BMCtl_TxEn (1<<8)
++#define BMCtl_EH2 (1<<6)
++#define BMCtl_EH1 (1<<5)
++#define BMCtl_EEOB (1<<4)
++#define BMCtl_RxChR (1<<2)
++#define BMCtl_RxDis (1<<1)
++#define BMCtl_RxEn (1<<0)
++
++#define REG_BMSts 0x0084 /*offset to Bus Master Status Reg, R*/
++#define BMSts_TxAct (1<<7)
++#define BMSts_TP (1<<4)
++#define BMSts_RxAct (1<<3)
++#define BMSts_QID (0x07<<0)
++#define BMSts_QID_RxDt (0<<0)
++#define BMSts_QID_TxDt (1<<0)
++#define BMSts_QID_RxSts (2<<0)
++#define BMSts_QID_TxSts (3<<0)
++#define BMSts_QID_RxDesc (4<<0)
++#define BMSts_QID_TxDesc (5<<0)
++
++#define REG_RBCA 0x0088 /*offset to Receive Buffer Current Address Reg, R*/
++#define REG_TBCA 0x008c /*offset to Transmit Buffer Current Address Reg, R*/
++
++#define REG_RxDBA 0x0090 /*offset to Receive Descriptor Queue Base Address Reg, R/W*/
++#define REG_RxDBL 0x0094 /*offset to Receive Descriptor Queue Base Length Reg, R/W, 16bits*/
++#define REG_RxDCL 0x0096 /*offset to Receive Descriptor Queue Current Length Reg, R/W, 16bits*/
++#define REG_RxDCA 0x0098 /*offset to Receive Descriptor Queue Current Address Reg, R/W*/
++
++#define REG_RxDEQ 0x009c /*offset to Receive Descriptor Enqueue Reg, R/W*/
++#define RxDEQ_RDV (0xffff<<16) /*R 16bit; Receive Descriptor Value*/
++#define RxDEQ_RDI (0xff<<0) /*W 8bit; Receive Descriptor Increment*/
++
++#define REG_RxSBA 0x00a0 /*offset to Receive Status Queue Base Address Reg, R/W*/
++#define REG_RxSBL 0x00a4 /*offset to Receive Status Queue Base Length Reg, R/W, 16bits*/
++#define REG_RxSCL 0x00a6 /*offset to Receive Status Queue Current Length Reg, R/W, 16bits*/
++#define REG_RxSCA 0x00a8 /*offset to Receive Status Queue Current Address Reg, R/W*/
++
++#define REG_RxSEQ 0x00ac /*offset to Receive Status Queue Current Address Reg, R/W*/
++#define RxSEQ_RSV (0xffff<<16)
++#define RxSEQ_RSI (0xff<<0)
++
++#define REG_TxDBA 0x00b0 /*offset to Transmit Descriptor Queue Base Address Reg, R/W*/
++#define REG_TxDBL 0x00b4 /*offset to Transmit Descriptor Queue Base Length Reg, R/W, 16bits*/
++#define REG_TxDCL 0x00b6 /*offset to Transmit Descriptor Queue Current Length Reg, R/W, 16bits*/
++#define REG_TxDCA 0x00b8 /*offset to Transmit Descriptor Queue Current Address Reg, R/W*/
++
++#define REG_TxDEQ 0x00bc /*offset to Transmit Descriptor Queue Current Address Reg, R/W*/
++#define TxDEQ_TDV (0xffff<<16)
++#define TxDEQ_TDI (0xff<<0)
++
++#define REG_TxSBA 0x00c0 /*offset to Transmit Status Queue Base Address Reg, R/W*/
++#define REG_TxSBL 0x00c4 /*offset to Transmit Status Queue Base Length Reg, R/W, 16bits*/
++#define REG_TxSCL 0x00c6 /*offset to Transmit Status Queue Current Length Reg, R/W, 16bits*/
++#define REG_TxSCA 0x00c8 /*offset to Transmit Status Queue Current Address Reg, R/W*/
++
++#define REG_RxBTH 0x00d0 /*offset to Receive Buffer Threshold Reg, R/W*/
++#define RxBTH_RDHT (0x03ff<<16)
++#define RxBTH_RDST (0x03ff<<0)
++
++#define REG_TxBTH 0x00d4 /*offset to Transmit Buffer Threshold Reg, R/W*/
++#define TxBTH_TDHT (0x03ff<<16)
++#define TxBTH_TDST (0x03ff<<0)
++
++#define REG_RxSTH 0x00d8 /*offset to Receive Status Threshold Reg, R/W*/
++#define RxSTH_RSHT (0x003f<<16)
++#define RxSTH_RSST (0x003f<<0)
++
++#define REG_TxSTH 0x00dc /*offset to Transmit Status Threshold Reg, R/W*/
++#define TxSTH_TSHT (0x003f<<16)
++#define TxSTH_TSST (0x003f<<0)
++
++#define REG_RxDTH 0x00e0 /*offset to Receive Descriptor Threshold Reg, R/W*/
++#define RxDTH_RDHT (0x003f<<16)
++#define RxDTH_RDST (0x003f<<0)
++
++#define REG_TxDTH 0x00e4 /*offset to Transmit Descriptor Threshold Reg, R/W*/
++#define TxDTH_TDHT (0x003f<<16)
++#define TxDTH_TDST (0x003f<<0)
++
++#define REG_MaxFL 0x00e8 /*offset to Max Frame Length Reg, R/W*/
++#define MaxFL_MFL (0x07ff<<16)
++#define MaxFL_TST (0x07ff<<0)
++
++#define REG_RxHL 0x00ec /*offset to Receive Header Length Reg, R/W*/
++#define RxHL_RHL2 (0x07ff<<16)
++#define RxHL_RHL1 (0x03ff<<0)
++
++#define REG_MACCFG0 0x0100 /*offset to Test Reg #0, R/W*/
++#define MACCFG0_DbgSel (1<<7)
++#define MACCFG0_LCKEN (1<<6)
++#define MACCFG0_LRATE (1<<5)
++#define MACCFG0_RXERR (1<<4)
++#define MACCFG0_BIT33 (1<<2)
++#define MACCFG0_PMEEN (1<<1)
++#define MACCFG0_PMEST (1<<0)
++
++#define REG_MACCFG1 0x0104 /*offset to Test Reg #1, R/W*/
++#define REG_MACCFG2 0x0108 /*offset to Test Reg #2, R*/
++#define REG_MACCFG3 0x010c /*offset to Test Reg #3, R*/
++
++
++
++/*---------------------------------------------------------------
++ * Definition of Descriptor/Status Queue Entry
++ *-------------------------------------------------------------*/
++
++typedef union receiveDescriptor { /*data structure of Receive Descriptor Queue Entry*/
++ struct { /*whole value*/
++ U32 e0, /*1st dword entry*/
++ e1; /*2nd dword entry*/
++ } w;
++ struct { /*bit field definitions*/
++ U32 ba:32, /*Buffer Address (keep in mind this is physical address)*/
++ bl:16, /*b15-0; Buffer Length*/
++ bi:15, /*b30-16; Buffer Index*/
++ nsof:1; /*b31; Not Start Of Frame*/
++ } f;
++} receiveDescriptor;
++
++
++typedef union receiveStatus { /*data structure of Receive Status Queue Entry*/
++ struct { /*whole word*/
++ U32 e0, /*1st dword entry*/
++ e1; /*2nd dword entry*/
++ } w;
++ struct { /*bit field*/
++ U32 rsrv1:8, /*b7-0: reserved*/
++ hti:6, /*b13-8: Hash Table Index*/
++ rsrv2:1, /*b14: reserved*/
++ crci:1, /*b15: CRC Included*/
++ crce:1, /*b16: CRC Error*/
++ edata:1, /*b17: Extra Data*/
++ runt:1, /*b18: Runt Frame*/
++ fe:1, /*b19: Framing Error*/
++ oe:1, /*b20: Overrun Error*/
++ rxerr:1, /*b21: Rx Error*/
++ am:2, /*b23-22: Address Match*/
++ rsrv3:4, /*b27-24: reserved*/
++ eob:1, /*b28: End Of Buffer*/
++ eof:1, /*b29: End Of Frame*/
++ rwe:1, /*b30: Received Without Error*/
++ rfp:1, /*b31: Receive Frame Processed*/
++ fl:16, /*b15-0: frame length*/
++ bi:15, /*b30-16: Buffer Index*/
++ rfp2:1; /*b31: Receive Frame Processed at 2nd word*/
++ } f;
++} receiveStatus;
++
++
++typedef union transmitDescriptor { /*data structure of Transmit Descriptor Queue Entry*/
++ struct { /*whole value*/
++ U32 e0, /*1st dword entry*/
++ e1; /*2nd dword entry*/
++ } w;
++ struct { /*bit field*/
++ U32 ba:32, /*b31-0: Buffer Address (keep in mind this is physical address)*/
++ bl:12, /*b11-0: Buffer Length*/
++ rsrv1:3, /*b14-12: reserved*/
++ af:1, /*b15: Abort Frame*/
++ bi:15, /*b30-16: Buffer Index*/
++ eof:1; /*b31: End Of Frame*/
++
++ } f;
++} transmitDescriptor;
++
++
++typedef union transmitStatus { /*data structure of Transmit Status Queue Entry*/
++ struct { /*whole word*/
++ U32 e0; /*1st dword entry*/
++ } w;
++ struct { /*bit field*/
++ U32 bi:15, /*b14-0: Buffer Index*/
++ rsrv3:1, /*b15: reserved*/
++ ncoll:5, /*b20-16: Number of Collisions*/
++ rsrv2:3, /*b23-21: reserved*/
++ ecoll:1, /*b24: Excess Collisions*/
++ txu:1, /*b25: Tx Underrun*/
++ ow:1, /*b26: Out of Window*/
++ rsrv1:1, /*b27: reserved*/
++ lcrs:1, /*b28: Loss of CRS*/
++ fa:1, /*b29: Frame Abort*/
++ txwe:1, /*b30: Transmitted Without Error*/
++ txfp:1; /*b31: Transmit Frame Processed*/
++ } f;
++} transmitStatus;
++
++
++
++/*---------------------------------------------------------------
++ * Size of device registers occupied in memory/IO address map
++ *-------------------------------------------------------------*/
++#define DEV_REG_SPACE 0x00010000
++
++/*
++#define U8 unsigned char
++#define U16 unsigned short
++#define U32 unsigned long
++*/
++
++/*---------------------------------------------------------------
++ * A definition of register access macros
++ *-------------------------------------------------------------*/
++#define _RegRd(type,ofs) (*(volatile type*)(ofs))
++#define _RegWr(type,ofs,dt) *(volatile type*)(ofs)=((type)(dt))
++
++#define RegRd8(ofs) _RegRd(U8,(char*)pD->base_addr+(ofs))
++#define RegRd16(ofs) _RegRd(U16,(char*)pD->base_addr+(ofs))
++#define RegRd32(ofs) _RegRd(U32,(char*)pD->base_addr+(ofs))
++#define RegWr8(ofs,dt) _RegWr(U8,(char*)pD->base_addr+(ofs),(dt))
++#define RegWr16(ofs,dt) _RegWr(U16,(char*)pD->base_addr+(ofs),(dt))
++#define RegWr32(ofs,dt) _RegWr(U32,(char*)pD->base_addr+(ofs),(dt))
++
++
++
++#endif /* _EP9213_ETH_H_ */
++
diff --git a/target/linux/ep93xx/patches-2.6.30/008-ep93xx-spi.patch b/target/linux/ep93xx/patches-2.6.30/008-ep93xx-spi.patch
new file mode 100644
index 0000000000..8292bbcf16
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/008-ep93xx-spi.patch
@@ -0,0 +1,714 @@
+Index: linux-2.6.30.9/drivers/spi/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/drivers/spi/Kconfig 2009-11-24 21:09:23.000000000 +0100
++++ linux-2.6.30.9/drivers/spi/Kconfig 2009-11-24 21:09:53.000000000 +0100
+@@ -125,6 +125,12 @@
+
+ If unsure, say N.
+
++config SPI_EP93XX
++ tristate "EP93xx SSP SPI master"
++ depends on SPI_MASTER && ARCH_EP93XX && EXPERIMENTAL
++ help
++ This enables the EP93xx SPI master controller.
++
+ config SPI_IMX
+ tristate "Freescale iMX SPI controller"
+ depends on ARCH_IMX && EXPERIMENTAL
+Index: linux-2.6.30.9/drivers/spi/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/drivers/spi/Makefile 2009-11-24 21:09:23.000000000 +0100
++++ linux-2.6.30.9/drivers/spi/Makefile 2009-11-24 21:09:53.000000000 +0100
+@@ -16,6 +16,7 @@
+ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
+ obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
+ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
++obj-$(CONFIG_SPI_EP93XX) += spi_ep93xx.o
+ obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
+ obj-$(CONFIG_SPI_GPIO_OLD) += spi_gpio_old.o
+ obj-$(CONFIG_SPI_IMX) += spi_imx.o
+Index: linux-2.6.30.9/drivers/spi/spi_ep93xx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/spi/spi_ep93xx.c 2009-11-24 21:21:25.000000000 +0100
+@@ -0,0 +1,680 @@
++/*
++ * linux/drivers/spi/spi_ep93xx.c
++ *
++ * Copyright (C) 2007 Manfred Gruber <m.gruber@tirol.com>
++ * Small changes by Peter Ivanov <ivanovp@gmail.com> to support MMC over SPI, 2008
++ * SIM.ONE changes by Nuccio Raciti Simplemachine <nuccio.raciti@gmail.com>
++ *
++ * Based on pxa2xx_spi.c/spi_imx.c and bitbang.c driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/blkdev.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/workqueue.h>
++
++#include <linux/spi/spi.h>
++
++#include <mach/hardware.h>
++#include <mach/ep93xx-regs.h>
++#include <asm/gpio.h>
++
++/* #define SPI_EP93XX_DEBUG */
++
++#define DEFINE_SSP_REG(reg, off) \
++ static inline u32 read_##reg(void *p) \
++ { return __raw_readl(p + (off)); } \
++ static inline void write_##reg(u32 v, void *p) \
++ { __raw_writel(v, p + (off)); }
++
++DEFINE_SSP_REG(SSPCR0, 0x00)
++DEFINE_SSP_REG(SSPCR1, 0x04)
++DEFINE_SSP_REG(SSPDR, 0x08)
++DEFINE_SSP_REG(SSPSR, 0x0c)
++DEFINE_SSP_REG(SSPCPSR, 0x10)
++DEFINE_SSP_REG(SSPIIR, 0x14)
++DEFINE_SSP_REG(SSPICR, 0x14)
++
++/* Bits in SSPCR0 */
++#define SSPCR0_DSS_MASK 0x0000000f
++#define SSPCR0_FRF_MASK 0x00000030
++#define SSPCR0_FRF_SHIFT 4
++#define SSPCR0_FRF_MOTOROLA (0 << SSPCR0_FRF_SHIFT)
++#define SSPCR0_FRF_TI (1 << SSPCR0_FRF_SHIFT)
++#define SSPCR0_FRF_NI (2 << SSPCR0_FRF_SHIFT)
++#define SSPCR0_SPO 0x00000040
++#define SSPCR0_SPH 0x00000080
++#define SSPCR0_SCR_MASK 0x0000ff00
++#define SSPCR0_SCR_SHIFT 8
++
++/* Bits in SSPCR1 */
++#define SSPC1_RIE 0x00000001
++#define SSPC1_TIE 0x00000002
++#define SSPC1_RORIE 0x00000004
++#define SSPC1_LBM 0x00000008
++#define SSPC1_SSE 0x00000010
++#define SSPC1_MS 0x00000020
++#define SSPC1_SOD 0x00000040
++
++/* Bits in SSPSR */
++#define SSPSR_TFE 0x00000001 /* TX FIFO is empty */
++#define SSPSR_TNF 0x00000002 /* TX FIFO is not full */
++#define SSPSR_RNE 0x00000004 /* RX FIFO is not empty */
++#define SSPSR_RFF 0x00000008 /* RX FIFO is full */
++#define SSPSR_BSY 0x00000010 /* SSP is busy */
++#define SSPSR_MASK 0x0000001F /* SSP is busy */
++
++/* Bits in SSPCPSR */
++#define SSPCPSR_SCR_MASK 0x000000ff
++
++/* Bits in SSPIIR */
++#define SSPIIR_RIS 0x00000001 /* RX FIFO IRQ status */
++#define SSPIIR_TIS 0x00000002 /* TX FIFO is not full */
++#define SSPIIR_RORIS 0x00000004 /* RX FIFO is full */
++
++#define SPI_SSPCLK 7.4e6
++#define SPI_SSPCLK_REV_E2 14.8e6 /* only for chip Rev E2 */
++#define SPI_MAX_SPEED 3.7e6
++#define SPI_MAX_SPEED_REV_E2 7.4e6 /* only for chip Rev E2 */
++#define SPI_CPSDVR_DIV_MIN 2
++#define SPI_CPSDVR_DIV_MAX 254
++#define SPI_SCR_DIV_MIN 0
++#define SPI_SCR_DIV_MAX 255
++#define SPI_DATARATE_OK 0
++#define SPI_DATARATE_NOK -1
++
++struct driver_data {
++ /* Driver model hookup */
++ struct platform_device *pdev;
++
++ /* SPI framework hookup */
++ struct spi_master *master;
++
++ /* SSP register addresses */
++ void *ioaddr;
++
++ /* SSP irq */
++ int irq;
++
++ struct list_head queue;
++
++ /* SSP spinlock */
++ spinlock_t lock;
++
++ struct workqueue_struct *workqueue;
++ struct work_struct work;
++
++ u8 busy;
++ u8 use_dma;
++};
++
++static unsigned ep93xx_txrx_8(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct driver_data *drv_data;
++ const u8 *tx = t->tx_buf;
++ u8 *rx = t->rx_buf;
++ unsigned count = t->len;
++ u8 byte;
++ int busy;
++
++ drv_data = spi_master_get_devdata(spi->master);
++
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_8: t->len %u \n", t->len);
++#endif
++
++ while (likely(count > 0)) {
++ byte = 0;
++ if (tx) {
++ byte = *tx++;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_8: write 0x%x \n", byte);
++#endif
++ }
++
++ write_SSPDR(byte, drv_data->ioaddr);
++ busy = read_SSPSR(drv_data->ioaddr);
++ while (busy & SSPSR_BSY) {
++ cpu_relax();
++ busy = read_SSPSR(drv_data->ioaddr);
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_8: delay. SSPSR: 0x%X\n", busy);
++#endif
++ }
++ byte = read_SSPDR(drv_data->ioaddr);
++
++ if (rx) {
++ *rx++ = byte;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_8: read 0x%x \n", byte);
++#endif
++ }
++ count -= 1;
++ }
++ return t->len - count;
++}
++
++
++static unsigned ep93xx_txrx_16(struct spi_device *spi, struct spi_transfer *t)
++{
++
++ struct driver_data *drv_data;
++ const u16 *tx = t->tx_buf;
++ u16 *rx = t->rx_buf;
++ unsigned count = t->len;
++ u16 word;
++ int busy;
++
++ drv_data = spi_master_get_devdata(spi->master);
++
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_16: t->len %u \n", t->len);
++#endif
++ while (likely(count > 0)) {
++ word = 0;
++ if (tx) {
++ word = *tx++;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_16: write 0x%x \n", word);
++#endif
++ }
++
++ write_SSPDR(word, drv_data->ioaddr);
++ busy = read_SSPSR(drv_data->ioaddr);
++ while (busy & SSPSR_BSY) {
++ cpu_relax();
++ busy = read_SSPSR(drv_data->ioaddr);
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_8: delay.\n");
++#endif
++ }
++
++ word = read_SSPDR(drv_data->ioaddr);
++
++ if (rx) {
++ *rx++ = word;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "ep93xx_txrx_16: read 0x%x \n", word);
++#endif
++ }
++ count -= 2;
++ }
++ return t->len - count;
++}
++
++static u32 spi_data_rate(u32 speed_hz, u32 *div_cpsdvr, u32 *div_scr,
++ struct driver_data *drv_data, struct spi_device *spi)
++{
++ unsigned int spi_sspclk = SPI_SSPCLK;
++ unsigned int bus_speed_max = SPI_MAX_SPEED;
++ unsigned int bus_hz_tmp = 0;
++ u32 div_cpsdvr_tmp;
++ u32 div_scr_tmp;
++ u32 rv = SPI_DATARATE_NOK;
++ int chip_rev;
++
++ /* Checking CHIP_ID */
++ chip_rev = (__raw_readl (EP93XX_SYSCON_CHIP_ID) >> 28) & 0xF;
++ if (chip_rev == 7)
++ {
++ /* Chip version: Rev E2 */
++ /* This device has double speed SSP clock */
++ spi_sspclk = SPI_SSPCLK_REV_E2;
++ bus_speed_max = SPI_MAX_SPEED_REV_E2;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "Chip Rev E2 detected! This device has double speed SSP clock.\n");
++#endif
++ }
++
++ *div_cpsdvr = SPI_CPSDVR_DIV_MAX;
++ *div_scr = SPI_SCR_DIV_MAX;
++
++ for (div_cpsdvr_tmp = SPI_CPSDVR_DIV_MIN;
++ div_cpsdvr_tmp <= SPI_CPSDVR_DIV_MAX && rv; div_cpsdvr_tmp++) {
++ for (div_scr_tmp = SPI_SCR_DIV_MIN;
++ div_scr_tmp <= SPI_SCR_DIV_MAX && rv; div_scr_tmp++) {
++ bus_hz_tmp = spi_sspclk / (div_cpsdvr_tmp * (1 + div_scr_tmp));
++ if (bus_hz_tmp <= speed_hz && bus_hz_tmp <= bus_speed_max) {
++ *div_cpsdvr = div_cpsdvr_tmp;
++ *div_scr = div_scr_tmp;
++ rv = SPI_DATARATE_OK;
++ }
++ }
++ }
++#ifdef SPI_EP93XX_DEBUG
++ dev_info(&spi->dev,
++ "Needed SPI bus frequency: %i Hz\n", speed_hz);
++ dev_info(&spi->dev,
++ "Actual SPI bus frequency: %i Hz\n", bus_hz_tmp);
++#endif
++ return rv;
++}
++
++/* Supported modes (returns -EINVAL if not supported mode requested) */
++#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
++
++static int ep93xx_spi_setup(struct spi_device *spi)
++{
++ struct driver_data *drv_data;
++ u16 val;
++ u32 div_scr;
++ u32 div_cpsdvr;
++ unsigned int bits = spi->bits_per_word;
++ unsigned long speed_hz = spi->max_speed_hz;
++
++ drv_data = spi_master_get_devdata(spi->master);
++
++ /* enable SSP */
++ write_SSPCR1(SSPC1_SSE, drv_data->ioaddr);
++ /* Enable SSP and loopback mode (only for testing!) */
++ /* write_SSPCR1(SSPC1_SSE | SSPC1_LBM, drv_data->ioaddr); */
++
++ if (bits == 0)
++ bits = 8;
++ if (bits < 4 || bits > 16) {
++ dev_err(&spi->dev,
++ "setup invalid bits_per_word %u (4 to 16)\n", bits);
++ return -EINVAL;
++ } else {
++ val = read_SSPCR0(drv_data->ioaddr);
++ val = val & ~SSPCR0_DSS_MASK ;
++ val = val | (bits-1);
++ write_SSPCR0(val, drv_data->ioaddr);
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "Bits per word: %i\n", bits);
++#endif
++ }
++
++ if (spi->mode & ~MODEBITS) {
++ dev_err(&spi->dev, "unsupported mode bits: %x\n",
++ spi->mode & ~MODEBITS);
++ return -EINVAL;
++ } else {
++ val = read_SSPCR0(drv_data->ioaddr);
++ val = val & ~SSPCR0_SPO;
++ val = val & ~SSPCR0_SPH;
++ if (spi->mode & SPI_CPOL)
++ {
++ val = val | SSPCR0_SPO;
++ }
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "Clock polarity (CPOL): %s\n", (spi->mode & SPI_CPHA) ? "1" : "0");
++#endif
++ if (spi->mode & SPI_CPHA)
++ {
++ val = val | SSPCR0_SPH;
++ }
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "Clock phase (CPHA): %s\n", (spi->mode & SPI_CPHA) ? "1" : "0");
++#endif
++ write_SSPCR0(val, drv_data->ioaddr);
++ }
++
++ if (SPI_DATARATE_OK == (spi_data_rate(speed_hz, &div_cpsdvr,
++ &div_scr, drv_data, spi))) {
++
++ val = read_SSPCPSR(drv_data->ioaddr);
++ val = val & ~SSPCPSR_SCR_MASK;
++ val = val | div_cpsdvr;
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "SSPCPSR: 0x%X\n", val);
++#endif
++ write_SSPCPSR(val, drv_data->ioaddr);
++
++ val = read_SSPCR0(drv_data->ioaddr);
++ val = val & ~SSPCR0_SCR_MASK;
++ val = val | (div_scr << SSPCR0_SCR_SHIFT);
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "SSPCR0: 0x%X (div_scr: 0x%X)\n", val, div_scr);
++#endif
++ write_SSPCR0(val, drv_data->ioaddr);
++ } else
++ return -EINVAL;
++
++ /* reenable */
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val & ~SSPC1_SSE;
++ write_SSPCR1(val, drv_data->ioaddr);
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val | SSPC1_SSE;
++ write_SSPCR1(val, drv_data->ioaddr);
++#ifdef SPI_EP93XX_DEBUG
++ dev_info (&spi->dev, "Loopback mode: %s\n", (val & SSPC1_LBM) ? "On" : "Off");
++#endif
++
++ return 0;
++}
++
++static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
++{
++ struct driver_data *drv_data;
++ unsigned long flags;
++ int status = 0;
++
++ m->actual_length = 0;
++ m->status = -EINPROGRESS;
++
++ drv_data = spi_master_get_devdata(spi->master);
++
++ spin_lock_irqsave(&drv_data->lock, flags);
++ if (!spi->max_speed_hz)
++ status = -ENETDOWN;
++ else {
++ list_add_tail(&m->queue, &drv_data->queue);
++ queue_work(drv_data->workqueue, &drv_data->work);
++ }
++ spin_unlock_irqrestore(&drv_data->lock, flags);
++ return status;
++}
++
++static void ep93xx_work(struct work_struct *work)
++{
++ struct driver_data *drv_data =
++ container_of(work, struct driver_data, work);
++ unsigned long flags;
++
++ spin_lock_irqsave(&drv_data->lock, flags);
++ drv_data->busy = 1;
++
++ while (!list_empty(&drv_data->queue)) {
++ struct spi_message *m;
++ struct spi_device *spi;
++ struct spi_transfer *t = NULL;
++ int status;
++
++ m = container_of(drv_data->queue.next, struct spi_message,
++ queue);
++ list_del_init(&m->queue);
++ spin_unlock_irqrestore(&drv_data->lock, flags);
++
++ spi = m->spi;
++ status = 0;
++
++ list_for_each_entry(t, &m->transfers, transfer_list) {
++
++ if (!t->tx_buf && !t->rx_buf && t->len) {
++ status = -EINVAL;
++ break;
++ }
++
++ if (t->len) {
++ if (!m->is_dma_mapped) {
++ t->rx_dma = 0;
++ t->tx_dma = 0;
++ }
++ if (t->bits_per_word <= 8)
++ status = ep93xx_txrx_8(spi, t);
++ else
++ status = ep93xx_txrx_16(spi, t);
++ }
++
++ if (status != t->len) {
++ if (status > 0)
++ status = -EMSGSIZE;
++ break;
++ }
++ m->actual_length += status;
++ status = 0;
++
++ /* protocol tweaks before next transfer */
++ if (t->delay_usecs)
++ udelay(t->delay_usecs);
++
++ if (t->transfer_list.next == &m->transfers)
++ break;
++ }
++
++ m->status = status;
++ m->complete(m->context);
++
++ spin_lock_irqsave(&drv_data->lock, flags);
++ }
++ drv_data->busy = 0;
++ spin_unlock_irqrestore(&drv_data->lock, flags);
++}
++
++static irqreturn_t ssp_int(int irq, void *dev_id)
++{
++ struct driver_data *drv_data = dev_id;
++ u8 status;
++ status = read_SSPIIR(drv_data->ioaddr);
++
++ if (status & SSPIIR_RORIS) {
++ dev_err(&drv_data->pdev->dev, "SPI rx overrun.\n");
++
++ /* We clear the overrun here ! */
++ write_SSPICR(0, drv_data->ioaddr);
++ }
++
++ /* RX interrupt */
++ if (status & SSPIIR_RIS)
++ dev_info(&drv_data->pdev->dev, "SPI RX interrupt\n");
++
++ /* TX interrupt */
++ if (status & SSPIIR_TIS)
++ dev_info(&drv_data->pdev->dev, "SPI TX interrupt\n");
++
++ write_SSPICR(0, drv_data->ioaddr);
++ return IRQ_HANDLED;
++}
++
++static int __init ep93xx_spi_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct spi_master *master;
++ struct driver_data *drv_data = 0;
++ struct resource *memory_resource;
++ int status = 0;
++ u16 val;
++
++ /* Allocate master with space for drv_data and null dma buffer */
++ master = spi_alloc_master(dev, sizeof(struct driver_data));
++ if (!master) {
++ dev_err(&pdev->dev, "cannot alloc spi_master\n");
++ return -ENOMEM;
++ }
++ drv_data = spi_master_get_devdata(master);
++ drv_data->master = master;
++ drv_data->pdev = pdev;
++
++ master->num_chipselect = EP93XX_GPIO_LINE_H(7) + 1;
++ master->bus_num = pdev->id;
++ master->setup = ep93xx_spi_setup;
++ master->transfer = ep93xx_spi_transfer;
++
++ spin_lock_init(&drv_data->lock);
++ INIT_LIST_HEAD(&drv_data->queue);
++
++ /* Setup register addresses */
++ memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!memory_resource) {
++ dev_err(&pdev->dev, "memory resources not defined\n");
++ status = -EIO;
++ goto out_error_master_alloc;
++ } else {
++ drv_data->ioaddr = ioremap(memory_resource->start,
++ memory_resource->end - memory_resource->start);
++ if (drv_data->ioaddr == NULL) {
++ dev_err(&pdev->dev, "ioremap failed\n");
++ status = -EIO;
++ goto out_error_master_alloc;
++ }
++ }
++
++ /* Attach to IRQ */
++ drv_data->irq = platform_get_irq(pdev, 0);
++ if (drv_data->irq < 0)
++ return drv_data->irq;
++
++ if (drv_data->irq <= 0) {
++ dev_err(&pdev->dev, "IRQ resource not defined\n");
++ status = -ENODEV;
++ goto out_error_master_alloc;
++ }
++
++ status = request_irq(drv_data->irq, ssp_int, 0, "ep93xx-spi", drv_data);
++ if (status < 0) {
++ dev_err(&pdev->dev, "cannot get SPI IRQ 0\n");
++ goto out_error_master_alloc;
++ }
++
++ /* SSP default configuration, enable */
++ write_SSPCR1(SSPC1_SSE, drv_data->ioaddr);
++
++ /* run as master */
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val & ~SSPC1_MS;
++ write_SSPCR1(val, drv_data->ioaddr);
++
++ /* frame format to Motorola SPI Format */
++ val = read_SSPCR0(drv_data->ioaddr);
++ val = val & ~SSPCR0_FRF_MASK ;
++ val = val | SSPCR0_FRF_MOTOROLA;
++ write_SSPCR0(val, drv_data->ioaddr);
++
++ /* enable interrupts */
++ val = read_SSPCR1(drv_data->ioaddr);
++ /* for now only overrun is handled */
++ /* val = val | SSPC1_RIE | SSPC1_TIE | SSPC1_RORIE; */
++ val = val | SSPC1_RORIE;
++ write_SSPCR1(val, drv_data->ioaddr);
++
++ /* SSP default configuration, re enable */
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val & ~SSPC1_SSE;
++ write_SSPCR1(val, drv_data->ioaddr);
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val | SSPC1_SSE;
++ write_SSPCR1(val, drv_data->ioaddr);
++
++ /* Register with the SPI framework */
++ platform_set_drvdata(pdev, drv_data);
++ status = spi_register_master(master);
++ if (status != 0) {
++ dev_err(&pdev->dev, "cannot register SPI master\n");
++ goto out_error_master_alloc;
++ } else
++ dev_info(&pdev->dev, "SPI Controller initalized\n");
++
++ INIT_WORK(&drv_data->work, ep93xx_work);
++ spin_lock_init(&drv_data->lock);
++ INIT_LIST_HEAD(&drv_data->queue);
++
++ /* this task is the only thing to touch the SPI bits */
++ drv_data->busy = 0;
++ drv_data->workqueue = create_singlethread_workqueue(
++ dev_name(drv_data->master->dev.parent));
++/* drv_data->master->cdev.dev->bus_id); */
++ if (drv_data->workqueue == NULL) {
++ status = -EBUSY;
++ goto out_error_free_irq;
++ }
++
++ return status;
++
++out_error_free_irq:
++ free_irq(drv_data->irq, master);
++out_error_master_alloc:
++ if (drv_data->ioaddr != NULL)
++ iounmap(drv_data->ioaddr);
++ spi_master_put(master);
++ return status;
++}
++
++static int __exit ep93xx_spi_remove(struct platform_device *pdev)
++{
++ struct driver_data *drv_data = platform_get_drvdata(pdev);
++ u8 val;
++
++ WARN_ON(!list_empty(&drv_data->queue));
++
++ destroy_workqueue(drv_data->workqueue);
++
++ /* switch off SSP*/
++ val = read_SSPCR1(drv_data->ioaddr);
++ val = val & ~SSPC1_SSE;
++ write_SSPCR1(val, drv_data->ioaddr);
++
++ /* release irqs */
++ if (drv_data->irq > 0)
++ free_irq(drv_data->irq, drv_data);
++
++ /* Disconnect from the SPI framework */
++ spi_unregister_master(drv_data->master);
++ spi_master_put(drv_data->master);
++
++ if (drv_data->ioaddr != NULL)
++ iounmap(drv_data->ioaddr);
++
++ /* Prevent double remove */
++ platform_set_drvdata(pdev, NULL);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int ep93xx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
++{
++ return 0;
++}
++
++static int ep93xx_spi_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++
++#else
++#define ep93xx_spi_suspend NULL
++#define ep93xx_spi_resume NULL
++#endif
++
++struct platform_driver ep93xx_spi_device = {
++ .remove = __exit_p(ep93xx_spi_remove),
++#ifdef CONFIG_PM
++ .suspend = ep93xx_spi_suspend,
++ .resume = ep93xx_spi_resume,
++#endif
++ .driver = {
++ .name = "ep93xx-spi",
++ .bus = &spi_bus_type,
++ .owner = THIS_MODULE,
++ },
++};
++
++int __init ep93xx_spi_init(void)
++{
++ return platform_driver_probe(&ep93xx_spi_device, ep93xx_spi_probe);
++}
++
++void __exit ep93xx_spi_exit(void)
++{
++ platform_driver_unregister(&ep93xx_spi_device);
++}
++
++module_init(ep93xx_spi_init);
++module_exit(ep93xx_spi_exit);
++
++MODULE_DESCRIPTION("EP93XX SPI Driver");
++MODULE_AUTHOR("Manfred Gruber, <m.gruber@tirol.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/ep93xx/patches-2.6.30/009-ep93xx-fb.patch b/target/linux/ep93xx/patches-2.6.30/009-ep93xx-fb.patch
new file mode 100644
index 0000000000..d7816bd722
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/009-ep93xx-fb.patch
@@ -0,0 +1,3565 @@
+--- a/drivers/video/Kconfig
++++ b/drivers/video/Kconfig
+@@ -255,6 +255,25 @@ config FB_CIRRUS
+ Say N unless you have such a graphics board or plan to get one
+ before you next recompile the kernel.
+
++config FB_EP93XX
++ tristate "EP93xx frame buffer support"
++ depends on FB
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ This is the frame buffer device driver for the internal raster engine
++ on certain members of the EP93xx family. For VGA and LCD output.
++
++config FB_EP93XX_MONO
++ tristate "EP93xx Mono frame buffer support"
++ depends on FB
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ help
++ This is the frame buffer device driver for the internal raster engine
++ on certain members of the EP93xx family. For LCD output.
++
+ config FB_PM2
+ tristate "Permedia2 support"
+ depends on FB && ((AMIGA && BROKEN) || PCI)
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -95,6 +95,8 @@ obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.
+ obj-$(CONFIG_FB_68328) += 68328fb.o
+ obj-$(CONFIG_FB_GBE) += gbefb.o
+ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
++obj-$(CONFIG_FB_EP93XX) += ep93xxfb.o
++obj-$(CONFIG_FB_EP93XX_MONO) += ep93xxfb_mono.o
+ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
+ obj-$(CONFIG_FB_PXA) += pxafb.o
+ obj-$(CONFIG_FB_W100) += w100fb.o
+--- /dev/null
++++ b/drivers/video/ep93xxfb.c
+@@ -0,0 +1,1628 @@
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/reboot.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include "ep93xxfb.h"
++#include <mach/hardware.h>
++#include <linux/platform_device.h>
++
++#include "console/fbcon.h"
++
++
++#if defined(CONFIG_MACH_EDB9312) || defined(CONFIG_MACH_EDB9315) || defined(CONFIG_MACH_EDB9307) || defined(CONFIG_MACH_EDB9301) || defined(CONFIG_MACH_EDB9302)
++#define CONFIG_EP93XX_SDCS3
++#else
++#define CONFIG_EP93XX_SDCS0
++#endif
++
++//#define DEBUG 1
++#ifdef DEBUG
++#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )
++#else
++#define DPRINTK( fmt, arg... )
++#endif
++
++#define FBDEV_NAME "ep93xxfb"
++
++#define ep93xxfb_outl(value, reg) \
++{ \
++ outl(RASTER_SWLOCK_VALUE, RASTER_SWLOCK); \
++ outl(value, reg); \
++}
++
++#define DEFAULT_OUT CRT_OUT
++#define DEFAULT_MODE 7
++#define DEFAULT_BPP 24
++
++static DECLARE_WAIT_QUEUE_HEAD(ep93xxfb_wait_in);
++
++static unsigned int pseudo_palette[256];
++static unsigned long *cursor_data = NULL;
++static struct ep93xxfb_info epinfo;
++
++static int vout = DEFAULT_OUT;
++static int vmode = DEFAULT_MODE;
++static int depth = DEFAULT_BPP;
++
++
++static int ep93xxfb_setcol(struct fb_info *info, int bpp);
++
++
++static struct ep93xxfb_videomodes ep93xxfb_vmods[] = {
++ {
++ "Philips-LB064V02-640x480x60",
++ 640, 24, 96, 40, 480, 10, 2, 33, 60,
++ CLK_INTERNAL, EDGE_FALLING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 1
++ "CRT-640x480-60",
++ 640, 24, 96, 40, 480, 11, 2, 32, 60,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 2
++ "CRT-640x480-72",
++ 640, 40, 40, 144, 480, 8, 3, 30, 72,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 3
++ "CRT-640x480-75",
++ 640, 16, 76, 120, 480, 1, 3, 16, 75,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 4
++ "CRT-640x480-85",
++ 640, 56, 56, 80, 480, 1, 3, 25, 85,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 5
++ "CTR-640x480-100",
++ 640, 32, 96, 96, 480, 8, 6, 36, 100,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 6
++ "CRT-800x600-56",
++ 800, 24, 72, 128, 600, 1, 2, 22, 56,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 7
++ "CRT-800x600-60",
++ 800, 40, 128, 88, 600, 1, 4, 23, 60,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_HIGH, POL_HIGH,
++ },
++ { // 8
++ "CRT-800x600-72",
++ 800, 56, 120, 64, 600, 37, 6, 23, 72,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 9
++ "CRT-800x600-85",
++ 800, 64, 64, 160, 600, 16, 5, 36, 85,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 10
++ "CRT-800x600-100",
++ 800, 64, 64, 160, 600, 4, 6, 30, 100,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 11
++ "CRT-1024x768-60",
++ 1024, 8, 144, 168, 768, 3, 6, 29, 60,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 12
++ "CRT-1024x768-70",
++ 1024, 24, 136, 144, 768, 3, 6, 29, 70,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_LOW, POL_LOW,
++ },
++ { // 13
++ "CRT-1024x768-75",
++ 1024, 16, 96, 176, 768, 1, 3, 28, 75,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_HIGH, POL_HIGH,
++ },
++ { // 14
++ "CRT-1024x768-85",
++ 1024, 48, 96, 208, 768, 1, 3, 36, 85,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_HIGH, POL_HIGH,
++ },
++ { // 15
++ "CRT-1280x720-60",
++ 1280, 48, 112, 248, 720, 1, 3, 38, 60,
++ CLK_INTERNAL, EDGE_RISING, POL_LOW, POL_HIGH, POL_HIGH,
++ }
++};
++
++static void philips_lb064v02_on(unsigned char value)
++{
++ DPRINTK("philips_lb064v02_on \n");
++ outl(inl(GPIO_PADDR) | 2, GPIO_PADDR);
++ outl(inl(GPIO_PADR) | 2, GPIO_PADR);
++}
++
++static void philips_lb064v02_off(unsigned char value)
++{
++ DPRINTK("philips_lb064v02_off \n");
++ outl(inl(GPIO_PADR) & ~2, GPIO_PADR);
++}
++
++static irqreturn_t ep93xxfb_irq_handler(int i, void *blah)
++{
++ outl(0x00000000, BLOCKCTRL);
++ wake_up(&ep93xxfb_wait_in);
++ return IRQ_HANDLED;
++}
++
++static void ep93xxfb_wait(void)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ add_wait_queue(&ep93xxfb_wait_in, &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++
++ while (inl(BLOCKCTRL) & 0x00000001){
++ if(/*(pls_proba==1)&&*/(!in_atomic()))
++ schedule();
++ }
++
++ remove_wait_queue(&ep93xxfb_wait_in, &wait);
++ set_current_state(TASK_RUNNING);
++
++}
++
++void ep93xxfb_fillrect(struct fb_info *p, const struct fb_fillrect *fill)
++{
++ unsigned long blkdestwidth,tmp;
++
++ if (!fill->width || !fill->height ||
++ (fill->dx >= p->var.xres) ||
++ (fill->dy >= p->var.yres) ||
++ ((fill->dx + fill->width - 1) >= p->var.xres) ||
++ ((fill->dy + fill->height - 1) >= p->var.yres))
++ return;
++
++ tmp = (( fill->dx + fill->width ) * epinfo.bpp );
++ blkdestwidth = tmp / 32;
++ if(blkdestwidth > 0 && (tmp % 32 == 0))
++ blkdestwidth--;
++ blkdestwidth = blkdestwidth - (fill->dx * epinfo.bpp) / 32;
++
++ outl(fill->color, BLOCKMASK);
++ outl( ((fill->dx * epinfo.bpp) & 0x1F) |
++ ((((fill->dx + fill->width - 1) * epinfo.bpp ) & 0x1F) << 16),
++ DESTPIXELSTRT);
++ outl( ((epinfo.xres * epinfo.bpp) / 32), DESTLINELENGTH);
++ outl( blkdestwidth, BLKDESTWIDTH );
++ outl(fill->height - 1, BLKDESTHEIGHT);
++ outl((epinfo.fb_phys + (fill->dy * epinfo.xres * epinfo.bpp ) / 8 +
++ (fill->dx * epinfo.bpp ) / 8 )
++ , BLKDSTSTRT);
++ outl( epinfo.pixformat | 0x0000000B, BLOCKCTRL);
++ ep93xxfb_wait();
++
++}
++
++void ep93xxfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
++{
++ unsigned long startsx,stopsx,startdx,stopdx,startsy,startdy;
++ unsigned long blksrcwidth,blkdestwidth,tmp;
++ unsigned long val = 0;
++
++ if( !area->width || !area->width ||
++ (area->sx >= p->var.xres) || (area->sy >= p->var.yres) ||
++ (area->dx >= p->var.xres) || (area->dy >= p->var.yres) ||
++ ((area->dx + area->width - 1) >= p->var.xres) ||
++ ((area->dy + area->height - 1) >= p->var.yres))
++ return;
++
++ if(area->sx == area->dx && area->sy == area->dy)
++ return;
++
++ if ((area->dy == area->sy) && (area->dx > area->sx) &&
++ (area->dx < (area->sx + area->width - 1))) {
++ startdx = area->dx + area->width - 1;
++ stopdx = area->dx;
++ startsx = area->sx + area->width - 1;
++ stopsx = area->sx;
++ val |= 0x000000A0;
++ }
++ else {
++ startdx = area->dx;
++ stopdx = area->dx + area->width - 1;
++ startsx = area->sx;
++ stopsx = area->sx + area->width - 1;
++ }
++
++ if (area->dy <= area->sy) {
++ startdy = area->dy;
++ startsy = area->sy;
++ }
++ else {
++ startdy = area->dy + area->height -1;
++ startsy = area->sy + area->height -1;
++ val |= 0x00000140;
++ }
++
++ tmp = (( area->sx + area->width ) * epinfo.bpp );
++ blksrcwidth = tmp / 32;
++ if(blksrcwidth > 0 && (tmp % 32 == 0))
++ blksrcwidth--;
++ blksrcwidth = blksrcwidth - (area->sx * epinfo.bpp) / 32;
++
++ tmp = (( area->dx + area->width ) * epinfo.bpp );
++ blkdestwidth = tmp / 32;
++ if(blkdestwidth > 0 && (tmp % 32 == 0))
++ blkdestwidth--;
++ blkdestwidth = blkdestwidth - (area->dx * epinfo.bpp) / 32;
++
++ outl( 0x00000000 , BLOCKCTRL);
++
++ /*** src ***/
++ outl((((startsx * epinfo.bpp) & 0x1F) |
++ (((stopsx * epinfo.bpp ) & 0x1F) << 16))
++ , SRCPIXELSTRT);
++ outl((epinfo.fb_phys + (startsy * epinfo.xres * epinfo.bpp ) / 8 +
++ (startsx * epinfo.bpp ) / 8 )
++ , BLKSRCSTRT);
++ outl(((epinfo.xres * epinfo.bpp) / 32), SRCLINELENGTH);
++ outl( blksrcwidth, BLKSRCWIDTH );
++
++ /*** dest ***/
++ outl((((startdx * epinfo.bpp) & 0x1F) |
++ (((stopdx * epinfo.bpp ) & 0x1F) << 16))
++ , DESTPIXELSTRT);
++ outl((epinfo.fb_phys + (startdy * epinfo.xres * epinfo.bpp ) / 8 +
++ (startdx * epinfo.bpp ) / 8 )
++ , BLKDSTSTRT);
++ outl( ((epinfo.xres * epinfo.bpp) / 32), DESTLINELENGTH);
++ outl( blkdestwidth, BLKDESTWIDTH);
++ outl( area->height - 1 , BLKDESTHEIGHT);
++ outl( epinfo.pixformat | val | 0x00000003, BLOCKCTRL);
++ ep93xxfb_wait();
++}
++
++void ep93xxfb_imageblit(struct fb_info *p, const struct fb_image *image)
++{
++// unsigned long blkdestwidth,tmp;
++// void * pucBlitBuf;
++ cfb_imageblit( p , image );
++ return;
++/*
++ if ((image->dx >= p->var.xres) ||
++ (image->dy >= p->var.yres) ||
++ ((image->dx + image->width - 1) >= p->var.xres) ||
++ ((image->dy + image->height - 1) >= p->var.yres))
++ return;
++ if (epinfo.bpp != image->depth )
++ return;
++
++ tmp = (( image->dx + image->width ) * epinfo.bpp );
++ blkdestwidth = tmp / 32;
++ if(blkdestwidth > 0 && (tmp % 32 == 0))
++ blkdestwidth--;
++ blkdestwidth = blkdestwidth - (image->dx * epinfo.bpp) / 32;
++
++ pucBlitBuf = kmalloc(1024*8,GFP_KERNEL);
++ copy_from_user(pucBlitBuf, image->data, 5000);
++
++ outl( 0x00000000 , BLOCKCTRL);
++
++ outl( 0x00000000, SRCPIXELSTRT);
++ outl( virt_to_phys(pucBlitBuf), BLKSRCSTRT);
++ outl( (image->width * epinfo.bpp) / 32 , SRCLINELENGTH);
++ outl(((image->width - 1) * epinfo.bpp) / 32, BLKSRCWIDTH );
++
++ outl(((image->dx * epinfo.bpp) & 0x1F) |
++ ((((image->dx + image->width - 1) * epinfo.bpp ) & 0x1F) << 16)
++ , DESTPIXELSTRT);
++ outl((epinfo.fb_phys + (image->dy * epinfo.xres * epinfo.bpp ) / 8 +
++ (image->dx * epinfo.bpp ) / 8 )
++ , BLKDSTSTRT);
++ outl( ((epinfo.xres * epinfo.bpp) / 32), DESTLINELENGTH );
++ outl( blkdestwidth, BLKDESTWIDTH );
++ outl( image->height - 1 , BLKDESTHEIGHT);
++ outl(image->fg_color, BLOCKMASK);
++ outl(image->bg_color, BACKGROUND);
++ outl( epinfo.pixformat | 0x00000003, BLOCKCTRL );
++ ep93xxfb_wait();
++*/
++}
++
++
++static unsigned long isqrt(unsigned long a)
++{
++ unsigned long rem = 0;
++ unsigned long root = 0;
++ int i;
++
++ for (i = 0; i < 16; i++) {
++ root <<= 1;
++ rem = ((rem << 2) + (a >> 30));
++ a <<= 2;
++ root++;
++ if (root <= rem) {
++ rem -= root;
++ root++;
++ }
++ else
++ root--;
++ }
++ return root >> 1;
++}
++
++int ep93xxfb_line(struct fb_info *info, struct ep93xx_line *line)
++{
++ unsigned long value = 0;
++ long x, y, dx, dy, count, xinc, yinc, xval, yval, incr;
++
++ if ((line->x1 > info->var.xres) ||
++ (line->x2 > info->var.xres) ||
++ (line->y1 > info->var.yres) ||
++ (line->y2 > info->var.yres))
++ return -EFAULT;
++ x = line->x1;
++ y = line->y1;
++ dx = line->x2 - line->x1;
++ dy = line->y2 - line->y1;
++
++ if ( !dx || !dy )
++ return -EFAULT;
++
++ if ( dx < 0 && dy < 0 ) {
++ x = line->x2;
++ y = line->y2;
++ dx *= -1;
++ dy *= -1;
++ }
++ else if ( dx < 0 && dy > 0 ){
++ dx *= -1;
++ value = 0x000000A0;
++ }
++ else if( dy < 0 && dx > 0 ){
++ dy *= -1;
++ value = 0x00000140;
++ }
++
++ if (line->flags & LINE_PRECISE) {
++ count = isqrt(((dy * dy) + (dx * dx)) * 4096);
++ xinc = (4095 * 64 * dx) / count;
++ yinc = (4095 * 64 * dy) / count;
++ xval = 2048;
++ yval = 2048;
++ count = 0;
++ while (dx || dy) {
++ incr = 0;
++ xval -= xinc;
++ if (xval <= 0) {
++ xval += 4096;
++ dx--;
++ incr = 1;
++ }
++ yval -= yinc;
++ if (yval <= 0) {
++ yval += 4096;
++ dy--;
++ incr = 1;
++ }
++ count += incr;
++ }
++ }
++ else {
++ if ( dx == dy ) {
++ xinc = 4095;
++ yinc = 4095;
++ count = dx;
++
++ }
++ else if ( dx < dy ) {
++ xinc = ( dx * 4095 ) / dy;
++ yinc = 4095;
++ count = dy;
++
++ }
++ else {
++ xinc = 4095;
++ yinc = ( dy * 4095 ) / dx;
++ count = dx;
++ }
++ }
++
++ outl(0x08000800, LINEINIT);
++ if (line->flags & LINE_PATTERN)
++ outl(line->pattern, LINEPATTRN);
++ else
++ outl(0x000fffff, LINEPATTRN);
++ outl(epinfo.fb_phys + ((y * epinfo.xres * epinfo.bpp) / 8) +
++ ((x * epinfo.bpp ) / 8 ), BLKDSTSTRT);
++
++ outl(((x * epinfo.bpp) & 0x1F) |
++ ((((x + dx - 1) * epinfo.bpp) & 0x1F ) << 16),
++ DESTPIXELSTRT);
++ outl((epinfo.xres * epinfo.bpp) / 32, DESTLINELENGTH);
++ outl(line->fgcolor, BLOCKMASK);
++ outl(line->bgcolor, BACKGROUND);
++ outl((yinc << 16) | xinc, LINEINC);
++ outl( count & 0xFFF, BLKDESTWIDTH);
++ outl( 0 , BLKDESTHEIGHT);
++ value |= (line->flags & LINE_BACKGROUND) ? 0x00004000 : 0;
++ outl(epinfo.pixformat | value | 0x00000013, BLOCKCTRL);
++ ep93xxfb_wait();
++ return 0;
++}
++
++int ioctl_cursor=0;
++
++int ep93xxfb_cursor(struct fb_info *info, struct ep93xx_cursor *cursor)
++{
++ unsigned long x,y,save;
++
++ if((cursor->width ==0) || (cursor->height ==0) )
++ {
++ struct fb_cursor *fbcon_cursor =(struct fb_cursor *)cursor;
++ struct fbcon_ops *ops = (struct fbcon_ops *)info->fbcon_par;
++ unsigned int scan_align = info->pixmap.scan_align - 1;
++ unsigned int buf_align = info->pixmap.buf_align - 1;
++ unsigned int i, size, dsize, s_pitch, d_pitch;
++ struct fb_image *image;
++ u8 *src, *dst;
++
++ if(ioctl_cursor==1 ){
++ DPRINTK("softcursor error return\n");
++ return 0;
++ }
++
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return 0;
++
++ s_pitch = (fbcon_cursor->image.width + 7) >> 3;
++ dsize = s_pitch * fbcon_cursor->image.height;
++
++ if (dsize + sizeof(struct fb_image) != ops->cursor_size) {
++ if (ops->cursor_src != NULL)
++ kfree(ops->cursor_src);
++ ops->cursor_size = dsize + sizeof(struct fb_image);
++
++ ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC);
++ if (!ops->cursor_src) {
++ ops->cursor_size = 0;
++ return -ENOMEM;
++ }
++ }
++ src = ops->cursor_src + sizeof(struct fb_image);
++ image = (struct fb_image *)ops->cursor_src;
++ *image = fbcon_cursor->image;
++ d_pitch = (s_pitch + scan_align) & ~scan_align;
++
++ size = d_pitch * image->height + buf_align;
++ size &= ~buf_align;
++ dst = fb_get_buffer_offset(info, &info->pixmap, size);
++
++ if (fbcon_cursor->enable) {
++ switch (fbcon_cursor->rop) {
++ case ROP_XOR:
++ for (i = 0; i < dsize; i++)
++ src[i] = image->data[i] ^ fbcon_cursor->mask[i];
++ break;
++ case ROP_COPY:
++ default:
++ for (i = 0; i < dsize; i++)
++ src[i] = image->data[i] & fbcon_cursor->mask[i];
++ break;
++ }
++ } else
++ memcpy(src, image->data, dsize);
++
++ fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, image->height);
++ image->data = dst;
++ info->fbops->fb_imageblit(info, image);
++ return 0;
++
++ }
++ else{
++ ioctl_cursor = 1;
++
++ /*if (cursor->width > 16 || cursor->height > 16){
++ DPRINTK("%s width %d or heright %d error\n",__FUNCTION__,cursor->width,cursor->height);
++ return -ENXIO;
++ }*/
++
++ if (cursor->flags & CURSOR_OFF)
++ outl(inl(CURSORXYLOC) & ~0x00008000, CURSORXYLOC);
++
++ if (cursor->flags & CURSOR_SETSHAPE) {
++ copy_from_user(cursor_data, cursor->data,
++ cursor->width * cursor->height / 4);
++ save = inl(CURSORXYLOC);
++ outl(save & ~0x00008000, CURSORXYLOC);
++
++ outl(virt_to_phys(cursor_data), CURSOR_ADR_START);
++ outl(virt_to_phys(cursor_data), CURSOR_ADR_RESET);
++ outl(((cursor->width - 1) & 0x30) << 4 | ((cursor->height - 1) << 2) |
++ ((cursor->width - 1) >> 4), CURSORSIZE);
++ outl(save, CURSORXYLOC);
++
++ }
++
++ if (cursor->flags & CURSOR_SETCOLOR) {
++ outl(cursor->color1, CURSORCOLOR1);
++ outl(cursor->color2, CURSORCOLOR2);
++ outl(cursor->blinkcolor1, CURSORBLINK1);
++ outl(cursor->blinkcolor2, CURSORBLINK2);
++ }
++
++ if (cursor->flags & CURSOR_BLINK) {
++ if (cursor->blinkrate)
++ outl(0x00000100 | cursor->blinkrate, CURSORBLINK);
++ else
++ outl(0x0000000FF, CURSORBLINK);
++ }
++
++ if (cursor->flags & CURSOR_MOVE) {
++ x = (inl(HACTIVESTRTSTOP) & 0x000007ff) - cursor->dx - 2;
++ y = (inl(VACTIVESTRTSTOP) & 0x000007ff) - cursor->dy;
++ outl((inl(CURSORXYLOC) & 0x8000) | (y << 16) | x, CURSORXYLOC);
++ }
++
++ if(cursor->flags & CURSOR_ON)
++ outl(inl(CURSORXYLOC) | 0x00008000, CURSORXYLOC);
++
++ return 0;
++ }
++}
++
++
++
++static inline void
++ep93xxfb_palette_write(u_int regno, u_int red, u_int green,
++ u_int blue, u_int trans)
++{
++ unsigned int cont, i, pal;
++ DPRINTK("ep93xxfb_palette_write - enter\n");
++ pal = ((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
++ pseudo_palette[regno] = pal;
++ outl( pal, ( COLOR_LUT + ( regno << 2 )));
++ cont = inl( LUTCONT );
++
++ if (( cont & LUTCONT_STAT && cont & LUTCONT_RAM1 ) ||
++ ( !( cont & LUTCONT_STAT ) && !( cont&LUTCONT_RAM1 ))) {
++ for ( i = 0; i < 256; i++ ) {
++ outl( pseudo_palette[i], ( COLOR_LUT + ( i << 2 )) );
++ }
++ outl( cont ^ LUTCONT_RAM1, LUTCONT );
++ }
++}
++
++int ep93xxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info)
++{
++
++#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
++
++ switch ( info->fix.visual )
++ {
++ case FB_VISUAL_PSEUDOCOLOR:
++ ep93xxfb_palette_write(regno, red, green, blue, transp);
++ break;
++ case FB_VISUAL_TRUECOLOR:
++ if (regno >= 16)
++ return 1;
++ red = CNVT_TOHW(red, info->var.red.length);
++ green = CNVT_TOHW(green, info->var.green.length);
++ blue = CNVT_TOHW(blue, info->var.blue.length);
++ transp = CNVT_TOHW(transp, info->var.transp.length);
++ ((u32 *)(info->pseudo_palette))[regno] =
++ (red << info->var.red.offset) |
++ (green << info->var.green.offset) |
++ (blue << info->var.blue.offset) |
++ (transp << info->var.transp.offset);
++ break;
++ case FB_VISUAL_DIRECTCOLOR:
++ red = CNVT_TOHW(red, 8);
++ green = CNVT_TOHW(green, 8);
++ blue = CNVT_TOHW(blue, 8);
++ transp = CNVT_TOHW(transp, 8);
++ break;
++ }
++#undef CNVT_TOHW
++ return 0;
++}
++
++static int ep93xx_pan_display(struct fb_var_screeninfo *var,
++ struct fb_info *info)
++{
++ DPRINTK("ep93xx_pan_display - enter\n");
++
++ if (var->yoffset < 0
++ || var->yoffset + var->yres > info->var.yres_virtual
++ || var->xoffset)
++ return -EINVAL;
++
++ outl(epinfo.fb_phys + info->fix.line_length * var->yoffset
++ , VIDSCRNPAGE);
++
++ info->var.xoffset = var->xoffset;
++ info->var.yoffset = var->yoffset;
++
++ DPRINTK("ep93xx_pan_display - exit\n");
++ return 0;
++}
++
++
++static int
++ep93xxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
++{
++ struct fb_var_screeninfo tmp_var;
++ unsigned long pclk;
++ DPRINTK("ep93xxfb_check_var - enter\n");
++
++ if( vout != 0) {
++ printk(" ep93xxfb_check_var - vout != 0\n");
++ return -EINVAL;
++ }
++
++ memcpy (&tmp_var, var, sizeof (tmp_var));
++
++ if( (tmp_var.vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED ) {
++ printk(" ep93xxfb_check_var - unsupported video mode\n");
++ return -EINVAL;
++ }
++
++ if( ((tmp_var.xres * tmp_var.yres * tmp_var.bits_per_pixel) / 8) >
++ MAX_FBMEM_SIZE ) {
++ printk(" ep93xxfb_check_var - memory error \n");
++ return -ENOMEM;
++ }
++
++ if( ((tmp_var.xres_virtual * tmp_var.yres_virtual * tmp_var.bits_per_pixel) / 8) >
++ MAX_FBMEM_SIZE ) {
++ printk(" ep93xxfb_check_var - memory error \n");
++ return -ENOMEM;
++ }
++
++ pclk = 1000 * (1000000000 / tmp_var.pixclock);
++
++ if( pclk > ep93xx_get_max_video_clk() ) {
++ printk(" ep93xxfb_check_var - pixel clock error %lu\n",pclk);
++ return -EINVAL;
++ }
++
++ if (var->xres_virtual != var->xres)
++ var->xres_virtual = var->xres;
++ if (var->yres_virtual < var->yres)
++ var->yres_virtual = var->yres;
++
++ if (var->xoffset < 0)
++ var->xoffset = 0;
++ if (var->yoffset < 0)
++ var->yoffset = 0;
++
++ switch (tmp_var.bits_per_pixel) {
++ case 8:
++ break;
++ case 16:
++ break;
++ case 24:
++ break;
++ case 32:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ DPRINTK("ep93xxfb_check_var - exit\n");
++ return 0;
++}
++
++
++static int ep93xxfb_set_par(struct fb_info *info)
++{
++ struct fb_var_screeninfo tmp_var;
++ unsigned long attribs;
++
++ DPRINTK("ep93xxfb_set_par - enter\n");
++
++ if( ep93xxfb_check_var(&info->var,info) < 0 )
++ return -EINVAL;
++
++ if( !ep93xxfb_setcol(info,info->var.bits_per_pixel) )
++ return -EINVAL;
++
++
++ info->fix.line_length = (info->var.xres * info->var.bits_per_pixel) / 8;
++
++ ep93xxfb_blank( 1 , info );
++
++ memcpy(&tmp_var,&info->var,sizeof(tmp_var));
++
++ epinfo.xres = tmp_var.xres;
++ epinfo.xsync = tmp_var.hsync_len;
++ epinfo.xfp = tmp_var.right_margin;
++ epinfo.xbp = tmp_var.left_margin;
++ epinfo.xtotal = epinfo.xres + epinfo.xsync +
++ epinfo.xfp + epinfo.xbp;
++
++ epinfo.yres = tmp_var.yres;
++ epinfo.ysync = tmp_var.vsync_len;
++ epinfo.yfp = tmp_var.lower_margin;
++ epinfo.ybp = tmp_var.upper_margin;
++ epinfo.ytotal = epinfo.yres + epinfo.ysync +
++ epinfo.yfp + epinfo.ybp;
++
++ epinfo.pixclock = tmp_var.pixclock ;
++ epinfo.refresh = 1000 * (1000000000 / tmp_var.pixclock) ;
++
++ if( epinfo.refresh > ep93xx_get_max_video_clk())
++ epinfo.refresh = ep93xx_get_max_video_clk();
++ epinfo.bpp = tmp_var.bits_per_pixel;
++
++ ep93xxfb_setclk();
++ ep93xxfb_timing_signal_generation();
++
++ // set video memory parameters
++ outl(epinfo.fb_phys, VIDSCRNPAGE);
++ outl(epinfo.yres , SCRNLINES);
++ outl(((epinfo.xres * epinfo.bpp) / 32) - 1, LINELENGTH);
++ outl((epinfo.xres * epinfo.bpp) / 32, VLINESTEP);
++
++ // set pixel mode
++ ep93xxfb_pixelmod(epinfo.bpp);
++
++ attribs = 0;
++#ifdef CONFIG_EP93XX_SDCS0
++ attribs |= 0 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ attribs |= 1 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ attribs |= 2 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ attribs |= 3 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++
++ attribs |= VIDEOATTRIBS_INVCLK;
++ if( tmp_var.sync & FB_SYNC_HOR_HIGH_ACT )
++ attribs |= VIDEOATTRIBS_HSPOL;
++ if( tmp_var.sync & FB_SYNC_VERT_HIGH_ACT )
++ attribs |= VIDEOATTRIBS_VCPOL;
++
++ ep93xxfb_outl(attribs, VIDEOATTRIBS);
++ ep93xxfb_blank( 0 , info );
++
++ DPRINTK("ep93xxfb_set_par - exit\n");
++
++ return 0;
++}
++
++
++static int ep93xxfb_blank(int blank_mode,struct fb_info *info)
++{
++ unsigned long attribs;
++ DPRINTK("ep93xxfb_blank - enter\n");
++ attribs = inl(VIDEOATTRIBS);
++
++#ifdef CONFIG_EP93XX_SDCS0
++ attribs |= 0 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ attribs |= 1 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ attribs |= 2 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ attribs |= 3 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++
++ if (blank_mode) {
++ if (epinfo.off)
++ (epinfo.off)( 0 );
++
++ ep93xxfb_outl(attribs & ~(VIDEOATTRIBS_DATAEN |
++ VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_PCLKEN |
++ VIDEOATTRIBS_EN), VIDEOATTRIBS);
++ }
++ else {
++ if (epinfo.clk_src == CLK_INTERNAL)
++ attribs |= VIDEOATTRIBS_PCLKEN;
++ else
++ attribs &= ~VIDEOATTRIBS_PCLKEN;
++
++ ep93xxfb_outl(attribs | VIDEOATTRIBS_DATAEN |
++ VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_EN,
++ VIDEOATTRIBS);
++
++ if (epinfo.configure)
++ (epinfo.configure)( epinfo.automods );
++ if (epinfo.on)
++ (epinfo.on)( 0 );
++ }
++ return 0;
++}
++
++static int ep93xxfb_mmap(struct fb_info *info,struct vm_area_struct *vma)
++{
++ unsigned long off, start, len;
++
++ DPRINTK("ep93xxfb_mmap - enter\n");
++
++ off = vma->vm_pgoff << PAGE_SHIFT;
++ start = info->fix.smem_start;
++ len = PAGE_ALIGN(start & ~PAGE_MASK) + info->fix.smem_len;
++ start &= PAGE_MASK;
++ if ((vma->vm_end - vma->vm_start + off) > len)
++ return -EINVAL;
++
++ off += start;
++ vma->vm_pgoff = off >> PAGE_SHIFT;
++
++ vma->vm_flags |= VM_IO;
++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
++ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
++ DPRINTK("ep93xxfb_mmap error\n");
++ return -EAGAIN;
++ }
++
++ DPRINTK("ep93xxfb_mmap - exit\n");
++ return 0;
++}
++
++static unsigned long ep93xx_get_pll_frequency(unsigned long pll)
++{
++ unsigned long fb1, fb2, ipd, ps, freq;
++
++ if (pll == 1)
++ pll = inl(EP93XX_SYSCON_CLOCK_SET1);
++ else if (pll == 2)
++ pll = inl(EP93XX_SYSCON_CLOCK_SET2);
++ else
++ return 0;
++
++ ps = (pll & SYSCON_CLKSET1_PLL1_PS_MASK) >> SYSCON_CLKSET1_PLL1_PS_SHIFT;
++ fb1 = ((pll & SYSCON_CLKSET1_PLL1_X1FBD1_MASK) >> SYSCON_CLKSET1_PLL1_X1FBD1_SHIFT);
++ fb2 = ((pll & SYSCON_CLKSET1_PLL1_X2FBD2_MASK) >> SYSCON_CLKSET1_PLL1_X2FBD2_SHIFT);
++ ipd = ((pll & SYSCON_CLKSET1_PLL1_X2IPD_MASK) >> SYSCON_CLKSET1_PLL1_X2IPD_SHIFT);
++
++ freq = (((0x00e10000 * (fb1+1)) / (ipd+1)) * (fb2+1)) >> ps;
++ return freq;
++}
++
++static int ep93xx_get_max_video_clk()
++{
++ unsigned long f,freq = 0;
++
++ freq = 14745600 / 4;
++ f = ep93xx_get_pll_frequency(1) / 4;
++ if ( f > freq )
++ freq = f;
++ f = ep93xx_get_pll_frequency(2) / 4;
++ if ( f > freq )
++ freq = f;
++
++ return freq;
++}
++
++static int ep93xx_set_video_div(unsigned long freq)
++{
++ unsigned long pdiv = 0, div = 0, psel = 0, esel = 0;
++ unsigned long err, f, i, j, k;
++
++ err = -1;
++
++ for (i = 0; i < 3; i++) {
++ if (i == 0)
++ f = 14745600 * 2;
++ else if (i == 1)
++ f = ep93xx_get_pll_frequency(1) * 2;
++ else
++ f = ep93xx_get_pll_frequency(2) * 2;
++
++ for (j = 4; j <= 6; j++) {
++ k = f / (freq * j);
++ if (k < 2)
++ continue;
++
++ if (abs(((f / (j * k))) - freq ) < err ) {
++ pdiv = j - 3;
++ div = k;
++ psel = (i == 2) ? 1 : 0;
++ esel = (i == 0) ? 0 : 1;
++ err = (f / (j * k)) - freq;
++ }
++ }
++ }
++
++ if (err == -1)
++ return -1;
++
++ SysconSetLocked(SYSCON_VIDDIV,SYSCON_VIDDIV_VENA | (esel ? SYSCON_VIDDIV_ESEL : 0) |
++ (psel ? SYSCON_VIDDIV_PSEL : 0) |
++ (pdiv << SYSCON_VIDDIV_PDIV_SHIFT) |
++ (div << SYSCON_VIDDIV_VDIV_SHIFT)
++ );
++
++ return freq + err;
++}
++
++static void ep93xxfb_pixelmod(int bpp)
++{
++ unsigned long tmpdata = 0;
++
++ DPRINTK("ep93xxfb_pixelmod %dbpp -enter\n",bpp);
++ switch(bpp) {
++ case 8:
++ tmpdata = PIXELMODE_P_8BPP |
++ PIXELMODE_S_1PPCMAPPED |
++ PIXELMODE_C_LUT;
++ epinfo.pixformat = PIXEL_FORMAT_8;
++ break;
++ case 16:
++ tmpdata = PIXELMODE_P_16BPP |
++ PIXELMODE_S_1PPCMAPPED |
++ PIXELMODE_C_565;
++ epinfo.pixformat = PIXEL_FORMAT_16;
++ break;
++ case 24:
++ tmpdata = PIXELMODE_P_24BPP |
++ PIXELMODE_S_1PPC |
++ PIXELMODE_C_888;
++ epinfo.pixformat = PIXEL_FORMAT_24;
++ break;
++ case 32:
++ tmpdata = PIXELMODE_P_32BPP |
++ PIXELMODE_S_1PPC |
++ PIXELMODE_C_888;
++ epinfo.pixformat = PIXEL_FORMAT_32;
++ break;
++ default:
++ break;
++ }
++ outl(tmpdata,PIXELMODE);
++}
++
++static void ep93xxfb_timing_signal_generation(void)
++{
++ unsigned long vlinestotal,vsyncstart,vsyncstop,
++ vactivestart,vactivestop,
++ vblankstart,vblankstop,
++ vclkstart,vclkstop;
++
++ unsigned long hclkstotal,hsyncstart,hsyncstop,
++ hactivestart,hactivestop,
++ hblankstart,hblankstop,
++ hclkstart,hclkstop;
++
++ DPRINTK("ep93xxfb_timing_signal_generation - enter\n");
++
++ vlinestotal = epinfo.ytotal - 1;
++ vsyncstart = vlinestotal;
++ vsyncstop = vlinestotal - epinfo.ysync;
++ vblankstart = vlinestotal - epinfo.ysync - epinfo.ybp;
++ vblankstop = epinfo.yfp - 1;
++ vactivestart = vblankstart;
++ vactivestop = vblankstop;
++ vclkstart = vlinestotal;
++ vclkstop = vlinestotal + 1;
++
++ hclkstotal = epinfo.xtotal - 1;
++ hsyncstart = hclkstotal;
++ hsyncstop = hclkstotal - epinfo.xsync;
++ hblankstart = hclkstotal - epinfo.xsync - epinfo.xbp;
++ hblankstop = epinfo.xfp - 1;
++ hactivestart = hblankstart;
++ hactivestop = hblankstop;
++ hclkstart = hclkstotal ;
++ hclkstop = hclkstotal ;
++
++ ep93xxfb_outl(0, VIDEOATTRIBS);
++
++ ep93xxfb_outl( vlinestotal , VLINESTOTAL );
++ ep93xxfb_outl( vsyncstart + (vsyncstop << 16), VSYNCSTRTSTOP );
++ ep93xxfb_outl( vactivestart + (vactivestop << 16), VACTIVESTRTSTOP );
++ ep93xxfb_outl( vblankstart + (vblankstop << 16), VBLANKSTRTSTOP );
++ ep93xxfb_outl( vclkstart + (vclkstop << 16), VCLKSTRTSTOP );
++
++ ep93xxfb_outl( hclkstotal , HCLKSTOTAL );
++ ep93xxfb_outl( hsyncstart + (hsyncstop << 16), HSYNCSTRTSTOP );
++ ep93xxfb_outl( hactivestart + (hactivestop << 16) , HACTIVESTRTSTOP );
++ ep93xxfb_outl( hblankstart + (hblankstop << 16) , HBLANKSTRTSTOP );
++ ep93xxfb_outl( hclkstart + (hclkstop << 16) , HCLKSTRTSTOP );
++
++ ep93xxfb_outl(0, LINECARRY);
++
++}
++
++static int ep93xxfb_setcol(struct fb_info *info, int bpp)
++{
++
++ DPRINTK("ep93xxfb_setcol %dbpp\n",bpp);
++ switch(bpp) {
++ case 8:
++ info->var.bits_per_pixel = 8;
++ info->var.red.length = 8;
++ info->var.green.length = 8;
++ info->var.blue.length = 8;
++ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ break;
++ case 16:
++ info->var.bits_per_pixel = 16;
++ info->var.red.offset = 11;
++ info->var.red.length = 5;
++ info->var.green.offset = 5;
++ info->var.green.length = 6;
++ info->var.blue.offset = 0;
++ info->var.blue.length = 5;
++ info->fix.visual = FB_VISUAL_TRUECOLOR;
++ break;
++ case 24:
++ info->var.bits_per_pixel = 24;
++ info->var.red.length = 8;
++ info->var.blue.length = 8;
++ info->var.green.length = 8;
++ info->var.red.offset = 16;
++ info->var.green.offset = 8;
++ info->var.blue.offset = 0;
++ info->fix.visual = FB_VISUAL_TRUECOLOR;
++ break;
++ case 32:
++ info->var.bits_per_pixel = 32;
++ info->var.red.length = 8;
++ info->var.blue.length = 8;
++ info->var.green.length = 8;
++ info->var.transp.length = 0;
++ info->var.red.offset = 16;
++ info->var.green.offset = 8;
++ info->var.blue.offset = 0;
++ info->var.transp.offset = 0;
++ info->fix.visual = FB_VISUAL_TRUECOLOR;
++ break;
++ default:
++ return 0;
++ }
++ return 1;
++}
++
++static int ep93xxfb_setclk()
++{
++ unsigned long calc_clk,act_clk;
++
++ if ( epinfo.clk_src == CLK_INTERNAL ) {
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG,inl(EP93XX_SYSCON_DEVICE_CONFIG) & ~EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
++ calc_clk = epinfo.refresh;
++ act_clk = ep93xx_set_video_div( calc_clk );
++ if ( act_clk == -1 )
++ return -ENODEV;
++
++ epinfo.refresh = act_clk;
++ epinfo.pixclock = 1000000000 / (act_clk / 1000);
++ }
++ else {
++ SysconSetLocked(SYSCON_VIDDIV,0);
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG,inl(EP93XX_SYSCON_DEVICE_CONFIG) | EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
++
++ }
++
++ return 0;
++}
++
++
++static void ep93xxfb_get_par(struct fb_info *info)
++{
++
++ DPRINTK("ep93xxfb_get_par - enter\n");
++ epinfo.configure = NULL;
++ epinfo.on = NULL;
++ epinfo.off = NULL;
++
++ switch( vout ) {
++ case LCD_OUT:
++ epinfo.on = philips_lb064v02_on;
++ epinfo.off = philips_lb064v02_off;
++
++ case CRT_OUT:
++ epinfo.xres = ep93xxfb_vmods[vmode].hres;
++ epinfo.xsync = ep93xxfb_vmods[vmode].hsync;
++ epinfo.xfp = ep93xxfb_vmods[vmode].hfp;
++ epinfo.xbp = ep93xxfb_vmods[vmode].hbp;
++ epinfo.xtotal = epinfo.xres + epinfo.xsync +
++ epinfo.xfp + epinfo.xbp;
++
++ epinfo.yres = ep93xxfb_vmods[vmode].vres;
++ epinfo.ysync = ep93xxfb_vmods[vmode].vsync;
++ epinfo.yfp = ep93xxfb_vmods[vmode].vfp;
++ epinfo.ybp = ep93xxfb_vmods[vmode].vbp;
++ epinfo.ytotal = epinfo.yres + epinfo.ysync +
++ epinfo.yfp + epinfo.ybp;
++
++ epinfo.refresh = ep93xxfb_vmods[vmode].refresh;
++ epinfo.refresh = epinfo.xtotal * epinfo.ytotal * epinfo.refresh;
++ epinfo.pixclock = 1000000000 / ( epinfo.refresh / 1000);
++ epinfo.bpp = depth;
++
++ epinfo.clk_src = ep93xxfb_vmods[vmode].clk_src;
++ epinfo.clk_edge = ep93xxfb_vmods[vmode].clk_edge;
++ epinfo.pol_blank = ep93xxfb_vmods[vmode].pol_blank;
++ epinfo.pol_xsync = ep93xxfb_vmods[vmode].pol_hsync;
++ epinfo.pol_ysync = ep93xxfb_vmods[vmode].pol_vsync;
++ break;
++
++ }
++}
++
++static int ep93xxfb_alloc_videomem(void)
++{
++ unsigned long adr,size,pgsize;
++ int order;
++
++ DPRINTK("ep93xxfb_alloc_videomem - enter \n");
++
++ epinfo.fb_log = NULL;
++ epinfo.fb_size = PAGE_ALIGN( MAX_FBMEM_SIZE/*ep93xxfb_vmods[vmode].hres * ep93xxfb_vmods[vmode].vres * (depth / 8)*/ );
++ order = get_order( epinfo.fb_size );
++ epinfo.fb_log = (void*) __get_free_pages( GFP_KERNEL, order );
++
++ if (epinfo.fb_log) {
++ epinfo.fb_phys = __virt_to_phys((int) epinfo.fb_log );
++ adr = (unsigned long)epinfo.fb_log;
++ size = epinfo.fb_size;
++ pgsize = 1 << order;
++ do {
++ adr += pgsize;
++ SetPageReserved(virt_to_page(adr));
++ } while(size -= pgsize);
++ }
++ else{
++ printk("%s memory fail \n",__FUNCTION__);
++ return -ENOMEM;
++ }
++
++ memset(epinfo.fb_log,0x00,epinfo.fb_size);
++ DPRINTK(" fb_log_addres = 0x%x\n",epinfo.fb_log);
++ DPRINTK(" fb_phys_address = 0x%x\n",epinfo.fb_phys);
++ DPRINTK(" fb_size = %lu\n",epinfo.fb_size);
++ DPRINTK(" fb_page_order = %d\n",order);
++ DPRINTK("ep93xxfb_alloc_videomem - exit \n");
++ return 0;
++}
++
++static void ep93xxfb_release_videomem(void)
++{
++ unsigned long adr,size,psize;
++ int order;
++
++ DPRINTK("ep93xxfb_release_videomem - enter \n");
++
++ if (epinfo.fb_log) {
++ order = get_order(epinfo.fb_size);
++ adr = (unsigned long)epinfo.fb_log;
++ size = epinfo.fb_size;
++ psize = 1 << order ;
++ do {
++ adr += psize;
++ ClearPageReserved(virt_to_page(adr));
++ } while(size -= psize);
++ free_pages((unsigned long)epinfo.fb_log, order );
++ }
++
++
++ DPRINTK("ep93xxfb_release_videomem - exit \n");
++}
++
++static void ep93xxfb_setinfo(struct fb_info *info)
++{
++
++ info->pseudo_palette = pseudo_palette;
++ info->var.xres = epinfo.xres;
++ info->var.yres = epinfo.yres;
++ info->var.xres_virtual = epinfo.xres;
++ info->var.yres_virtual = epinfo.yres;
++
++ ep93xxfb_setcol( info, depth );
++
++ info->var.activate = FB_ACTIVATE_NOW;
++ info->var.left_margin = epinfo.xbp;
++ info->var.right_margin = epinfo.xfp;
++ info->var.upper_margin = epinfo.ybp;
++ info->var.lower_margin = epinfo.yfp;
++ info->var.hsync_len = epinfo.xsync;
++ info->var.vsync_len = epinfo.ysync;
++
++ if( epinfo.pol_xsync == POL_HIGH )
++ info->var.sync |= FB_SYNC_HOR_HIGH_ACT;
++ if( epinfo.pol_ysync == POL_HIGH )
++ info->var.sync |= FB_SYNC_VERT_HIGH_ACT;
++
++ info->var.vmode = FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP;
++ info->fix.smem_start = epinfo.fb_phys;
++ info->fix.smem_len = epinfo.fb_size;
++ info->fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fix.line_length = epinfo.xres * (epinfo.bpp / 8);
++ info->screen_base = epinfo.fb_log;
++ info->var.pixclock = epinfo.pixclock;
++ info->fix.ypanstep = 1;
++ info->fix.ywrapstep = 1;
++
++}
++
++static int ep93xxfb_config(struct fb_info *info)
++{
++ unsigned long attribs;
++
++ DPRINTK("ep93xxfb_config - enter\n");
++
++ ep93xxfb_get_par( info );
++ if( ep93xxfb_alloc_videomem() != 0 ) {
++ printk("Unable to allocate video memory\n");
++ return -ENOMEM;
++ }
++
++ if( ep93xxfb_setclk() != 0 ) {
++ printk("Unable to set pixel clock\n");
++ ep93xxfb_release_videomem();
++ return -ENODEV;
++ }
++
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, inl(EP93XX_SYSCON_DEVICE_CONFIG) |EP93XX_SYSCON_DEVCFG_RasOnP3);
++ ep93xxfb_timing_signal_generation();
++
++ /* set video memory parameters */
++ outl(epinfo.fb_phys, VIDSCRNPAGE);
++ outl(epinfo.yres , SCRNLINES);
++ outl(((epinfo.xres * epinfo.bpp) / 32) - 1, LINELENGTH);
++ outl((epinfo.xres * epinfo.bpp) / 32, VLINESTEP);
++
++
++ /* set pixel mode */
++ ep93xxfb_pixelmod(depth);
++
++ attribs = 0;
++
++#ifdef CONFIG_EP93XX_SDCS0
++ attribs |= 0 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ attribs |= 1 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ attribs |= 2 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ attribs |= 3 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++
++ if(epinfo.clk_edge == EDGE_RISING)
++ attribs |= VIDEOATTRIBS_INVCLK;
++ if(epinfo.pol_blank == POL_HIGH)
++ attribs |= VIDEOATTRIBS_BLKPOL;
++ if(epinfo.pol_xsync == POL_HIGH)
++ attribs |= VIDEOATTRIBS_HSPOL;
++ if(epinfo.pol_ysync == POL_HIGH)
++ attribs |= VIDEOATTRIBS_VCPOL;
++
++ ep93xxfb_outl(attribs, VIDEOATTRIBS);
++ ep93xxfb_setinfo( info );
++
++ if(epinfo.configure)
++ (epinfo.configure)( epinfo.automods );
++
++ ep93xxfb_blank( 0 , info );
++
++ DPRINTK("ep93xxfb_config - exit\n");
++ return 0;
++}
++
++int ep93xxfb_ioctl(struct fb_info *info,unsigned int cmd, unsigned long arg)
++{
++ struct fb_fillrect fill;
++ struct fb_copyarea cparea;
++ struct fb_image img;
++ struct ep93xx_line line;
++ struct ep93xx_cursor cursor;
++
++ switch (cmd) {
++ case FBIO_EP93XX_CURSOR:
++ copy_from_user(&cursor, (void *)arg, sizeof(struct ep93xx_cursor));
++ ep93xxfb_cursor(info,&cursor);
++ break;
++ case FBIO_EP93XX_LINE:
++ copy_from_user(&line, (void *)arg, sizeof(struct ep93xx_line));
++ ep93xxfb_line(info,&line);
++ break;
++ case FBIO_EP93XX_FILL:
++ copy_from_user(&fill, (void *)arg, sizeof(struct fb_fillrect));
++ ep93xxfb_fillrect(info,&fill);
++ break;
++ case FBIO_EP93XX_BLIT:
++ copy_from_user(&img, (void *)arg, sizeof(struct fb_image));
++ ep93xxfb_imageblit(info, &img);
++ break;
++ case FBIO_EP93XX_COPY:
++ copy_from_user(&cparea, (void *)arg, sizeof(struct fb_copyarea));
++ ep93xxfb_copyarea(info,&cparea);
++ break;
++ default:
++ return -EFAULT;
++ }
++ return 0;
++}
++
++
++static struct fb_ops ep93xxfb_ops = {
++ .owner = THIS_MODULE,
++ .fb_setcolreg = ep93xxfb_setcolreg,
++ .fb_check_var = ep93xxfb_check_var,
++ .fb_set_par = ep93xxfb_set_par,
++ .fb_blank = ep93xxfb_blank,
++ .fb_pan_display = ep93xx_pan_display,
++ .fb_fillrect = ep93xxfb_fillrect,
++ .fb_copyarea = ep93xxfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++ .fb_cursor = ep93xxfb_cursor,
++ .fb_ioctl = ep93xxfb_ioctl,
++ .fb_mmap = ep93xxfb_mmap,
++};
++
++
++static struct resource ep93xxfb_raster_resources = {
++ .start = EP93XX_RASTER_PHYS_BASE,
++ .end = EP93XX_RASTER_PHYS_BASE + 0x1ffff,
++ .flags = IORESOURCE_MEM,
++};
++
++
++static int __init ep93xxfb_probe(struct platform_device *device)
++{
++ struct fb_info *info = NULL;
++ struct resource *res = NULL;
++ int ret = 0;
++ int arb = 0;
++
++ DPRINTK("ep93xxfb_probe - enter \n");
++
++
++ if(!device) {
++ printk("error : to_platform_device\n");
++ return -ENODEV;
++ }
++ res = platform_get_resource( device, IORESOURCE_MEM, 0);
++ if(!res) {
++ printk("error : platform_get_resource \n");
++ return -ENODEV;
++ }
++ cursor_data = kmalloc( 64 * 64 * 2, GFP_KERNEL );
++ memset( cursor_data, 0x00, 64 * 64 * 2 );
++ if(!cursor_data) {
++ printk("Unable to allocate memory for hw_cursor\n");
++ return -ENOMEM;
++ }
++ if (!request_mem_region(res->start,res->end - res->start + 1, FBDEV_NAME ))
++ return -EBUSY;
++
++ info = framebuffer_alloc(sizeof(u32) * 256, &device->dev);
++
++ if(!info) {
++ printk("Unable to allocate memory for frame buffer\n");
++ return -ENOMEM;
++ }
++
++ info->flags = FBINFO_DEFAULT;
++ strncpy(info->fix.id, FBDEV_NAME, sizeof(info->fix.id));
++ info->fix.mmio_start = res->start;
++ info->fix.mmio_len = res->end - res->start + 1;
++ info->fbops = &ep93xxfb_ops;
++ info->pseudo_palette = info->par;
++ info->state = FBINFO_STATE_RUNNING;
++
++ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
++ ret = -ENOMEM;
++ goto fbuff;
++ }
++
++ if ((ret = ep93xxfb_config(info)) < 0)
++ goto clmap;
++
++ if (register_framebuffer(info) < 0) {
++ printk(KERN_ERR "Unable to register ep93xxfb frame buffer\n");
++ ret = -EINVAL;
++ goto clmap;
++ }
++ platform_set_drvdata(device, info);
++ printk(KERN_INFO "fb%d: EP93xx frame buffer at %dx%dx%dbpp\n", info->node,
++ info->var.xres, info->var.yres, info->var.bits_per_pixel);
++
++ /*change the raster arb to the highest one--Bo*/
++ arb = inl(EP93XX_SYSCON_BMAR);
++ arb = (arb & 0x3f8) | 0x01;
++ outl(arb,EP93XX_SYSCON_BMAR);
++
++ DPRINTK("ep93xxfb_probe - exit \n");
++ return 0;
++
++clmap:
++ fb_dealloc_cmap(&info->cmap);
++
++fbuff:
++ framebuffer_release(info);
++ return ret;
++}
++
++static int ep93xxfb_remove(struct platform_device *device)
++{
++ struct resource *res;
++ struct fb_info *info;
++ struct ep93xx_cursor cursor;
++
++ DPRINTK("ep93xxfb_remove - enter \n");
++
++ info = platform_get_drvdata(device);
++
++ ep93xxfb_release_videomem();
++
++ res = platform_get_resource( device, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, res->end - res->start + 1);
++
++ platform_set_drvdata(device, NULL);
++ unregister_framebuffer(info);
++
++ fb_dealloc_cmap(&info->cmap);
++ framebuffer_release(info);
++
++ cursor.flags = CURSOR_OFF;
++ ep93xxfb_cursor(info,&cursor);
++ if(cursor_data!=NULL)
++ kfree(cursor_data);
++
++ ep93xxfb_blank( 1, info );
++
++ DPRINTK("ep93xxfb_remove - exit \n");
++ return 0;
++}
++
++static void ep93xxfb_platform_release(struct device *device)
++{
++ DPRINTK("ep93xxfb_platform_release - enter\n");
++}
++
++static int ep93xxfb_check_param(void)
++{
++
++ switch(vout) {
++ case CRT_OUT:
++ if( vmode >=(sizeof(ep93xxfb_vmods)/sizeof(ep93xxfb_vmods[0]))){
++ vmode = 1;
++ depth = DEFAULT_BPP;
++ return 0;
++ }
++ break;
++ case LCD_OUT:
++ if( vmode != 0 || depth != 16 ) {
++ vmode = 0;
++ depth = DEFAULT_BPP;
++ return 0;
++ }
++ break;
++ default:
++ vmode = DEFAULT_MODE;
++ depth = DEFAULT_BPP;
++ vout = DEFAULT_OUT;
++ return 0;
++ break;
++ }
++
++ if(!((depth == 8) || (depth == 16) || (depth == 24) || (depth == 32)))
++ depth = DEFAULT_BPP;
++
++ return 1;
++}
++
++int __init ep93xxfb_setup(char *options)
++{
++ char *opt;
++
++ DPRINTK("ep93xxfb_setup - %s\n",options);
++
++ if (!options || !*options)
++ return 0;
++
++ while ((opt = strsep(&options, ",")) != NULL) {
++ if (!strncmp(opt, "vout=", 5))
++ vout = simple_strtoul(opt + 5, NULL, 0);
++ else if (!strncmp(opt, "vmode=", 6))
++ vmode = simple_strtoul(opt + 6, NULL, 0);
++ else if (!strncmp(opt, "depth=", 6))
++ depth = simple_strtoul(opt + 6, NULL, 0);
++ }
++ ep93xxfb_check_param();
++ return 0;
++}
++
++
++static struct platform_driver ep93xxfb_driver = {
++ .probe = ep93xxfb_probe,
++ .remove = ep93xxfb_remove,
++ .driver = {
++ .name = FBDEV_NAME,
++ },
++};
++
++static struct platform_device ep93xxfb_device = {
++ .name = FBDEV_NAME,
++ .id = -1,
++ .dev = {
++ .release = ep93xxfb_platform_release,
++ },
++ .num_resources = 1,
++ .resource = &ep93xxfb_raster_resources,
++};
++
++int __init ep93xxfb_init(void)
++{
++ int ret = 0;
++ char *option = NULL;
++
++ DPRINTK("ep93xxfb_init - enter\n");
++
++ if (fb_get_options("ep93xxfb", &option))
++ return -ENODEV;
++ ep93xxfb_setup(option);
++
++
++ if( !ep93xxfb_check_param() ) {
++ printk("Unsupported format \n");
++ return -1;
++ }
++ /*Add the Hardware accel irq */
++ outl(0x00000000, BLOCKCTRL);
++ ret = request_irq(IRQ_EP93XX_GRAPHICS, ep93xxfb_irq_handler, IRQF_DISABLED,"graphics",NULL);
++
++ if (ret != 0) {
++ printk("%s: can't get irq %i, err %d\n",__FUNCTION__, IRQ_EP93XX_GRAPHICS, ret);
++ return -EBUSY;
++ }
++
++ /*-------------------------------*/
++ ret = platform_driver_register(&ep93xxfb_driver);
++
++ if (!ret) {
++ ret = platform_device_register(&ep93xxfb_device);
++ if (ret)
++ platform_driver_unregister(&ep93xxfb_driver);
++ }
++
++ DPRINTK("ep93xxfb_init - exit\n");
++ return ret;
++}
++
++
++
++static void __exit ep93xxfb_exit(void)
++{
++ DPRINTK("ep93xxfb_exit - enter\n");
++ platform_driver_unregister(&ep93xxfb_driver);
++ platform_device_unregister(&ep93xxfb_device);
++ DPRINTK("ep93xxfb_exit - exit\n");
++}
++
++module_init(ep93xxfb_init);
++module_exit(ep93xxfb_exit);
++
++
++module_param( vmode, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++MODULE_PARM_DESC(vmode, "Specify the video mode number that should be used");
++module_param( vout , int , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );
++MODULE_PARM_DESC(vout ,"Specify video output (0 = CRT ,1 = LCD )");
++module_param( depth , int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++MODULE_PARM_DESC(depth ,"Color depth (8,16,24,32)");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/video/ep93xxfb.h
+@@ -0,0 +1,236 @@
++#ifndef __EP93XXFB_H__
++#define __EP93XXFB_H__
++
++
++#define POL_HIGH 1
++#define POL_LOW 0
++#define EDGE_RISING 1
++#define EDGE_FALLING 0
++#define CLK_INTERNAL 1
++#define CLK_EXTERNAL 0
++
++#define CRT_OUT 0
++#define LCD_OUT 1
++#define TV_OUT 2
++
++#define MAX_XRES 1280
++#define MAX_YRES 1024
++#define MAX_BPP 16
++#define MAX_FBMEM_SIZE 3686400/*1920000*/
++
++#define MAX_XRES_CRT MAX_XRES
++#define MAX_YRES_CRT MAX_YRES
++#define MAX_XRES_SVIDEO 1024
++#define MAX_YRES_SVIDEO 768
++
++#define PIXEL_FORMAT_SHIFT 17
++#define PIXEL_FORMAT_4 ( 1 << PIXEL_FORMAT_SHIFT )
++#define PIXEL_FORMAT_8 ( 2 << PIXEL_FORMAT_SHIFT )
++#define PIXEL_FORMAT_16 ( 4 << PIXEL_FORMAT_SHIFT )
++#define PIXEL_FORMAT_24 ( 6 << PIXEL_FORMAT_SHIFT )
++#define PIXEL_FORMAT_32 ( 7 << PIXEL_FORMAT_SHIFT )
++
++
++struct ep93xxfb_videomodes
++{
++ const char *name;
++
++ unsigned long hres; // Horizontal Valid
++ unsigned long hfp; // Horizontal Front Porch
++ unsigned long hsync; // Horizontal Sync Width
++ unsigned long hbp; // Horizontal Back Porch
++
++ unsigned long vres; // Vertical Valid
++ unsigned long vfp; // Vertical Front Porch
++ unsigned long vsync; // Vertical Sync Width
++ unsigned long vbp; // Vertical Back Porch
++
++ unsigned long refresh; // Vertical Sync Frequency
++
++ unsigned long clk_src;
++ unsigned long clk_edge;
++ unsigned long pol_blank;
++ unsigned long pol_hsync;
++ unsigned long pol_vsync;
++};
++
++
++struct ep93xxfb_info
++{
++
++
++ dma_addr_t fb_phys;
++ void *fb_log;
++ unsigned long fb_size;
++ unsigned long fb_actsize;
++
++ unsigned long xtotal;
++ unsigned long ytotal;
++
++ unsigned int xres;
++ unsigned int xfp;
++ unsigned int xsync;
++ unsigned int xbp;
++
++ unsigned int yres;
++ unsigned int yfp;
++ unsigned int ysync;
++ unsigned int ybp;
++ unsigned int bpp;
++
++ unsigned long refresh;
++ unsigned long pixclock;
++ unsigned long pixformat;
++
++ unsigned int clk_src;
++ unsigned int clk_edge;
++ unsigned int pol_blank;
++ unsigned int pol_xsync;
++ unsigned int pol_ysync;
++
++ unsigned char automods;
++
++ void (*configure)(unsigned char value);
++ void (*on)(unsigned char value);
++ void (*off)(unsigned char value);
++};
++
++static int ep93xxfb_setclk(void);
++static int ep93xx_get_max_video_clk(void);
++static void ep93xxfb_pixelmod(int bpp);
++static void ep93xxfb_timing_signal_generation(void);
++static int ep93xxfb_blank(int blank_mode,struct fb_info *info);
++
++#define EE_DELAY_USEC 2
++#define EE_READ_TIMEOUT 100
++#define CX25871_DEV_ADDRESS 0x88
++#define GPIOG_EEDAT 2
++#define GPIOG_EECLK 1
++#define CXMODES_COUNT 24
++
++struct cx25871_vmodes
++{
++
++ const char *name;
++ unsigned char automode;
++ unsigned int hres;
++ unsigned int vres;
++ unsigned int hclktotal;
++ unsigned int vclktotal;
++ unsigned int hblank;
++ unsigned int vblank;
++ unsigned long clkfrequency;
++
++};
++
++
++int write_reg(unsigned char ucRegAddr, unsigned char ucRegValue);
++void cx25871_on(unsigned char value);
++void cx25871_off(unsigned char value);
++void cx25871_config(unsigned char value);
++
++static void philips_lb064v02_on(unsigned char value);
++static void philips_lb064v02_off(unsigned char value);
++
++
++#define FBIO_EP93XX_CURSOR 0x000046c1
++#define FBIO_EP93XX_LINE 0x000046c2
++#define FBIO_EP93XX_FILL 0x000046c3
++#define FBIO_EP93XX_BLIT 0x000046c4
++#define FBIO_EP93XX_COPY 0x000046c5
++
++
++#define CURSOR_BLINK 0x00000001
++#define CURSOR_MOVE 0x00000002
++#define CURSOR_SETSHAPE 0x00000004
++#define CURSOR_SETCOLOR 0x00000008
++#define CURSOR_ON 0x00000010
++#define CURSOR_OFF 0x00000020
++
++
++/*
++* ioctl(fd, FBIO_EP93XX_CURSOR, ep93xx_cursor *)
++*
++* "data" points to an array of pixels that define the cursor; each row should
++* be a multiple of 32-bit values (i.e. 16 pixels). Each pixel is two bits,
++* where the values are:
++*
++* 00 => transparent 01 => invert 10 => color1 11 => color2
++*
++* The data is arranged as follows (per word):
++*
++* bits: |31-30|29-28|27-26|25-24|23-22|21-20|19-18|17-16|
++* pixel: | 12 | 13 | 14 | 15 | 8 | 9 | 10 | 11 |
++* bits: |15-14|13-12|11-10| 9-8 | 7-6 | 5-4 | 3-2 | 1-0 |
++* pixel: | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 |
++*
++* Regardless of the frame buffer color depth, "color1", "color2",
++* "blinkcolor1", and "blinkcolor2" are 24-bit colors since the cursor is
++* injected into the data stream right before the video DAC.
++*
++* When "blinkrate" is not zero, pixel value 10 will alternate between "color1"
++* and "blinkcolor1" (similar for pixel value 11 and "color2"/"blinkcolor2").
++*
++* "blinkrate" ranges between 0 and 255. When 0, blinking is disabled. 255 is
++* the fastest blink rate and 1 is the slowest.
++*
++* Both "width" and "height" must be between 1 and 64; it is preferable to have
++* "width" a multiple of 16.
++*/
++struct ep93xx_cursor {
++ unsigned long flags;
++ unsigned long dx; // Only used if CURSOR_MOVE is set
++ unsigned long dy; // Only used if CURSOR_MOVE is set
++ unsigned long width; // Only used if CURSOR_SETSHAPE is set
++ unsigned long height; // Only used if CURSOR_SETSHAPE is set
++ const char *data; // Only used if CURSOR_SETSHAPE is set
++ unsigned long blinkrate; // Only used if CURSOR_BLINK is set
++ unsigned long color1; // Only used if CURSOR_SETCOLOR is set
++ unsigned long color2; // Only used if CURSOR_SETCOLOR is set
++ unsigned long blinkcolor1; // Only used if CURSOR_SETCOLOR is set
++ unsigned long blinkcolor2; // Only used if CURSOR_SETCOLOR is set
++};
++
++
++/*
++ * The bits in the flags field of ep93xx_line.
++*/
++/*
++* ioctl(fd, FBIO_EP93XX_LINE, ep93xx_line *)
++*
++* The line starts at ("x1","y1") and ends at ("x2","y2"). This means that
++* when using a pattern, the two coordinates are not transitive (i.e. swapping
++* ("x1","y1") with ("x2","y2") will not necessarily draw the exact same line,
++* pattern-wise).
++*
++* "pattern" is a 2 to 16 bit pattern (since a 1 bit pattern isn't much of a
++* pattern). The lower 16 bits define the pattern (1 being foreground, 0 being
++* background or transparent), and bits 19-16 define the length of the pattern
++* (as pattern length - 1). So, for example, "0xf00ff" defines a 16 bit
++* with the first 8 pixels in the foreground color and the next 8 pixels in the
++* background color or transparent.
++*
++* LINE_PRECISE is used to apply angularly corrected patterns to line. It
++* should only be used when LINE_PATTERN is also set. The pattern will be
++* applied along the length of the line, instead of along the length of the
++* major axis. This may result in the loss of fine details in the pattern, and
++* will take more time to draw the line in most cases.
++*/
++
++#define LINE_PATTERN 0x00000001
++#define LINE_PRECISE 0x00000002
++#define LINE_BACKGROUND 0x00000004
++
++struct ep93xx_line {
++ unsigned long flags;
++ unsigned long x1;
++ unsigned long y1;
++ unsigned long x2;
++ unsigned long y2;
++ unsigned long fgcolor;
++ unsigned long bgcolor; // Only used if LINE_BACKGROUND is set
++ unsigned long pattern; // Only used if LINE_PATTERN is set
++};
++
++#endif /* __EP93XXFB_H__ */
++
+--- /dev/null
++++ b/drivers/video/ep93xxfb_mono.c
+@@ -0,0 +1,1281 @@
++/*
++ * drivers/video/ep93xxfb_mono.c -- grayscale on mono LCD driver for
++ * Cirrus Logic EP93xx.
++ *
++ * Copyright (C) 2007 Cirrus Logic
++ *
++ * 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.
++ *
++ * This driver works for the following two LCD:
++ * SHARP LM121VB1T01 - A dual scan 640x480 monochrome LCD.
++ * HOSIDEN HLM6323 - A single scan 320x240 monochrome LCD.
++ *
++ * And support two gray modes:
++ * 8 levels of gray - Actually is 7 levels of gray. Two of the levels
++ * have the same gray.
++ * 16 levels of gray - Extending the gray levels by switching the LUT
++ * for each frame.
++ *
++ * HW connection for SHARP LM121VB1T01:
++ * P12 <------> LCD_U0
++ * P8 <------> LCD_U1
++ * P4 <------> LCD_U2
++ * P0 <------> LCD_U3
++ * P14 <------> LCD_L0
++ * P10 <------> LCD_L1
++ * P6 <------> LCD_L2
++ * P2 <------> LCD_L3
++ * HW connection for HOSIDEN HLM6323:
++ * P12 <------> LCD_0
++ * P8 <------> LCD_1
++ * P4 <------> LCD_2
++ * P0 <------> LCD_3
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/reboot.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <mach/hardware.h>
++
++
++#include <linux/platform_device.h>
++
++#define CONFIG_EP93XX_SDCS0
++
++#undef DEBUG
++#ifdef DEBUG
++#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )
++#else
++#define DPRINTK( fmt, arg... )
++#endif
++
++#define FBDEV_NAME "ep93xxfb"
++
++#define ep93xxfb_lock_outl(value, reg) \
++{ \
++ outl(RASTER_SWLOCK_VALUE, RASTER_SWLOCK); \
++ outl(value, reg); \
++ DPRINTK(#reg"=0x%08x\n", (unsigned int)(value)); \
++}
++
++#define ep93xxfb_outl(value, reg) \
++{ \
++ outl(value, reg); \
++ DPRINTK(#reg"=0x%08x\n", (unsigned int)(value)); \
++}
++
++static unsigned int pseudo_palette[256];
++
++struct ep93xxfb_mono_videomodes
++{
++ const char *name;
++
++ unsigned long hres; // Horizontal Valid
++ unsigned long vres; // Vertical Valid
++ unsigned int freq;
++ unsigned int dualscan;
++ unsigned int bpp;
++ unsigned int graylevel;
++
++ void (*configure)(unsigned char value);
++ void (*on)(unsigned char value);
++ void (*off)(unsigned char value);
++};
++
++struct ep93xxfb_mono_info
++{
++ dma_addr_t fb_phys;
++ void *fb_log;
++ unsigned long fb_size;
++ unsigned long fb_actsize;
++
++ unsigned int xres;
++ unsigned int yres;
++
++ unsigned int freq;
++ unsigned int dualscan;
++ unsigned int bpp;
++ unsigned int graylevel;
++
++ void (*configure)(unsigned char value);
++ void (*on)(unsigned char value);
++ void (*off)(unsigned char value);
++};
++
++
++void LM121VB1T01_configure(unsigned char value);
++void HOSIDEN_HLM6323_configure(unsigned char value);
++
++static int vmode = 1;
++
++static struct ep93xxfb_mono_info epinfo;
++static struct ep93xxfb_mono_videomodes ep93xxfb_vmods[] =
++{
++ {
++ "SHARP-LM121VB1T01-8GRAY",
++ 640, 480, 100,
++ 1, //dual scan
++ 4, //4bpp
++ 8, //8-level grayscale
++ LM121VB1T01_configure,
++ NULL,
++ NULL,
++ },
++ {
++ "SHARP-LM121VB1T01-16GRAY",
++ 640, 480, 120,
++ 1, //dual scan
++ 4, //4bpp
++ 16, //16-level grayscale
++ LM121VB1T01_configure,
++ NULL,
++ NULL,
++ },
++ {
++ "HOSIDEN HLM6323",
++ 320, 240, 115,
++ 0, //single scan
++ 4, //4bpp
++ 8, //8-level grayscale
++ HOSIDEN_HLM6323_configure,
++ NULL,
++ NULL,
++ },
++ {
++ "HOSIDEN HLM6323",
++ 320, 240, 115,
++ 0, //single scan
++ 4, //4bpp
++ 16, //16-level grayscale
++ HOSIDEN_HLM6323_configure,
++ NULL,
++ NULL,
++ },
++};
++
++
++#define EP93XX_GS_OFFSET(lut, frame, pixel) ( (lut) + ( (pixel) << 2) + ((frame) << 5 ))
++
++static unsigned long DY_LUT[2][16];
++
++static unsigned long GSLUT[32] =
++{
++ 0x00070000, 0x00070000, 0x00070000, 0x00070000, /*0%*/
++ 0x00078241, 0x00074182, 0x00071428, 0x00072814, /*25%*/
++ 0x00000412, 0x00000241, 0x00000124, 0x00000000, /*33%*/
++ 0x0007aa55, 0x000755aa, 0x000755aa, 0x0007aa55, /*50%*/
++ 0x00000bed, 0x00000dbe, 0x00000edb, 0x00000000, /*66%*/
++ 0x00077dbe, 0x0007be7d, 0x0007ebd7, 0x0007d7eb, /*75%*/
++ 0x0007ffff, 0x0007ffff, 0x0007ffff, 0x0007ffff, /*100%*/
++ 0x0007ffff, 0x0007ffff, 0x0007ffff, 0x0007ffff,
++};
++
++static void ep93xxfb_8gray_palette_init(void)
++{
++ unsigned int cont, i, n;
++ unsigned int frame, pixval, gslut;
++ cont = inl(LUTCONT);
++ for (i=0; i< 16; i++)
++ {
++ n = (i & 0xe) << 4;
++ outl( n, (COLOR_LUT+(i<<2)) );
++ }
++ for (pixval=0; pixval < 8; pixval++)
++ {
++ for (frame=0; frame < 4; frame++)
++ {
++ gslut = GSLUT[pixval*4 + frame];
++ outl(gslut,EP93XX_GS_OFFSET(GS_LUT, frame, pixval));
++ }
++ }
++ outl( cont ^ LUTCONT_RAM1, LUTCONT );
++}
++
++static void ep93xxfb_16gray_palette_switch(int index)
++{
++ unsigned int cont, i, n;
++ cont = inl(LUTCONT);
++ n = index & 0x1;
++ for (i=0; i< 16; i++)
++ {
++ outl( DY_LUT[n][i], (COLOR_LUT+(i<<2)) );
++ }
++ outl( cont ^ LUTCONT_RAM1, LUTCONT );
++}
++
++static void ep93xxfb_16gray_palette_init(void)
++{
++ int i;
++ unsigned int cont;
++ unsigned int frame, pixval, gslut;
++ int split_table[16][2] =
++ {
++ {0, 0 },
++ {0, 2 },
++ {1, 1 },
++ {3, 0 },
++
++ {2, 2 },
++ {4, 0 },
++ {3, 2 },
++ {4, 2 },
++
++ {3, 3 }, // {6, 0 },
++ {3, 4 },
++ {4, 4 },
++ {6, 2 },
++
++ {5, 5 },
++ {3, 6 },
++ {4, 6 },
++ {6, 6 },
++ };
++
++ cont = inl(LUTCONT);
++ for (i=0; i< 16; i++)
++ {
++ DY_LUT[0][i]=split_table[i][0] << 5;
++ DY_LUT[1][i]=split_table[i][1] << 5;
++
++ outl( DY_LUT[0][i], (COLOR_LUT+(i<<2)) );
++ }
++
++ for (pixval=0; pixval < 8; pixval++)
++ {
++ for (frame=0; frame < 4; frame++)
++ {
++ gslut = GSLUT[pixval*4 + frame];
++ outl(gslut,EP93XX_GS_OFFSET(GS_LUT, frame, pixval));
++ outl(gslut,EP93XX_GS_OFFSET(GS_LUT2, frame, pixval));
++ outl(gslut,EP93XX_GS_OFFSET(GS_LUT3, frame, pixval));
++ }
++ }
++ outl( cont ^ LUTCONT_RAM1, LUTCONT );
++}
++
++static int ep93xxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
++{
++ struct fb_var_screeninfo tmp_var;
++ DPRINTK("ep93xxfb_check_var - enter\n");
++
++ memcpy (&tmp_var, var, sizeof (tmp_var));
++
++ if (var->xres_virtual != var->xres)
++ var->xres_virtual = var->xres;
++ if (var->yres_virtual < var->yres)
++ var->yres_virtual = var->yres;
++
++ if (var->xoffset < 0)
++ var->xoffset = 0;
++ if (var->yoffset < 0)
++ var->yoffset = 0;
++
++ switch (tmp_var.bits_per_pixel)
++ {
++ case 4:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ DPRINTK("ep93xxfb_check_var - exit\n");
++ return 0;
++}
++
++static int ep93xxfb_set_par(struct fb_info *info)
++{
++ DPRINTK("ep93xxfb_set_par\n");
++ switch (info->var.bits_per_pixel) {
++ case 4:
++ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++
++static int ep93xxfb_blank(int blank_mode,struct fb_info *info)
++{
++ unsigned long attribs;
++ DPRINTK("ep93xxfb_blank - enter\n");
++ attribs = inl(VIDEOATTRIBS);
++
++ if (blank_mode) {
++ if (epinfo.off)
++ (epinfo.off)( 0 );
++
++ ep93xxfb_lock_outl(attribs & ~(VIDEOATTRIBS_DATAEN |
++ VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_PCLKEN |
++ VIDEOATTRIBS_EN), VIDEOATTRIBS);
++ }
++ else {
++
++ if (epinfo.configure)
++ (epinfo.configure)( (unsigned char) epinfo.graylevel );
++ if (epinfo.on)
++ (epinfo.on)( 0 );
++ }
++ return 0;
++}
++
++static void ep93xxfb_get_par(struct fb_info *info)
++{
++
++ DPRINTK("ep93xxfb_get_par - enter\n");
++
++ epinfo.configure = ep93xxfb_vmods[vmode].configure;
++ epinfo.on = ep93xxfb_vmods[vmode].on;
++ epinfo.off = ep93xxfb_vmods[vmode].off;
++
++ epinfo.freq = ep93xxfb_vmods[vmode].freq;
++ epinfo.dualscan = ep93xxfb_vmods[vmode].dualscan;
++ epinfo.bpp = ep93xxfb_vmods[vmode].bpp;
++ epinfo.graylevel = ep93xxfb_vmods[vmode].graylevel;
++
++ epinfo.xres = ep93xxfb_vmods[vmode].hres;
++ epinfo.yres = ep93xxfb_vmods[vmode].vres;
++
++}
++
++static int ep93xxfb_alloc_videomem(void)
++{
++ unsigned long adr,size,pgsize;
++ int order;
++
++ DPRINTK("ep93xxfb_alloc_videomem - enter \n");
++
++ epinfo.fb_log = NULL;
++ epinfo.fb_size = epinfo.xres*epinfo.yres*epinfo.bpp/8;
++ order = get_order( epinfo.fb_size );
++ epinfo.fb_log = (void*) __get_free_pages( GFP_KERNEL, order );
++
++ if (epinfo.fb_log) {
++ epinfo.fb_phys = __virt_to_phys((int) epinfo.fb_log );
++ adr = (unsigned long)epinfo.fb_log;
++ size = epinfo.fb_size;
++ pgsize = 1 << order;
++ do {
++ adr += pgsize;
++ SetPageReserved(virt_to_page(adr));
++ } while(size -= pgsize);
++ }
++ else
++ return -ENOMEM;
++
++ memset(epinfo.fb_log,0x00,epinfo.fb_size);
++
++ DPRINTK(" fb_log_addres = 0x%x\n", (unsigned int)epinfo.fb_log);
++ DPRINTK(" fb_phys_address = 0x%x\n", (unsigned int)epinfo.fb_phys);
++ DPRINTK(" fb_size = %lu\n", (unsigned long)epinfo.fb_size);
++ DPRINTK(" fb_page_order = %d\n", (unsigned int)order);
++ DPRINTK("ep93xxfb_alloc_videomem - exit \n");
++ return 0;
++}
++
++static void ep93xxfb_release_videomem(void)
++{
++ unsigned long adr,size,psize;
++ int order;
++
++ DPRINTK("ep93xxfb_release_videomem - enter \n");
++ if (epinfo.fb_log) {
++ order = get_order(epinfo.fb_size);
++ adr = (unsigned long)epinfo.fb_log;
++ size = epinfo.fb_size;
++ psize = 1 << order ;
++ do {
++ adr += psize;
++ ClearPageReserved(virt_to_page(adr));
++ } while(size -= psize);
++ free_pages((unsigned long)epinfo.fb_log, order );
++ }
++ DPRINTK("ep93xxfb_release_videomem - exit \n");
++}
++
++static void ep93xxfb_setinfo(struct fb_info *info)
++{
++
++ DPRINTK("ep93xxfb_setinfo - enter \n");
++ info->pseudo_palette = pseudo_palette;
++ info->var.xres = epinfo.xres;
++ info->var.yres = epinfo.yres;
++ info->var.xres_virtual = epinfo.xres;
++ info->var.yres_virtual = epinfo.yres;
++
++ info->var.bits_per_pixel = epinfo.bpp;
++ info->var.red.length = epinfo.bpp;
++ info->var.green.length = epinfo.bpp;
++ info->var.blue.length = epinfo.bpp;
++ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ info->var.red.offset = 0;
++ info->var.green.offset =0;
++ info->var.blue.offset = 0;
++
++ info->fix.smem_start = epinfo.fb_phys;
++ info->fix.smem_len = epinfo.fb_size;
++ info->fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fix.line_length = (epinfo.xres * epinfo.bpp) / 8;
++ info->fix.accel = FB_ACCEL_NONE;
++ info->screen_base = epinfo.fb_log;
++ info->fix.ypanstep = 1;
++ info->fix.ywrapstep = 1;
++
++ DPRINTK("ep93xxfb_setinfo - exit \n");
++}
++
++static int ep93xxfb_config(struct fb_info *info)
++{
++ DPRINTK("ep93xxfb_config - enter\n");
++
++ ep93xxfb_get_par( info );
++ if( ep93xxfb_alloc_videomem() != 0 ) {
++ printk("Unable to allocate video memory\n");
++ return -ENOMEM;
++ }
++
++ /* set video memory parameters */
++ ep93xxfb_outl(epinfo.fb_phys, VIDSCRNPAGE);
++ if(epinfo.dualscan)
++ {
++ ep93xxfb_outl(epinfo.fb_phys + (epinfo.bpp*epinfo.xres*epinfo.yres/16)
++ , VIDSCRNHPG);
++ }
++
++ DPRINTK(" fb_phys = 0x%x\n", inl(VIDSCRNPAGE) );
++ DPRINTK(" fb_phys_hpg = 0x%x\n", inl(VIDSCRNHPG));
++
++ ep93xxfb_outl(epinfo.yres , SCRNLINES);
++ ep93xxfb_outl(((epinfo.xres * epinfo.bpp) / 32) - 1, LINELENGTH);
++ ep93xxfb_outl((epinfo.xres * epinfo.bpp) / 32, VLINESTEP);
++
++ if(epinfo.configure)
++ (epinfo.configure)( (unsigned char) epinfo.graylevel );
++
++ ep93xxfb_setinfo( info );
++
++
++ DPRINTK("ep93xxfb_config - exit\n");
++ return 0;
++}
++
++static unsigned long ep93xx_get_pll_frequency(unsigned long pll)
++{
++ unsigned long fb1, fb2, ipd, ps, freq;
++
++ if (pll == 1)
++ pll = inl(EP93XX_SYSCON_CLOCK_SET1);
++ else if (pll == 2)
++ pll = inl(EP93XX_SYSCON_CLOCK_SET2);
++ else
++ return 0;
++
++ ps = (pll & SYSCON_CLKSET1_PLL1_PS_MASK) >> SYSCON_CLKSET1_PLL1_PS_SHIFT;
++ fb1 = ((pll & SYSCON_CLKSET1_PLL1_X1FBD1_MASK) >> SYSCON_CLKSET1_PLL1_X1FBD1_SHIFT);
++ fb2 = ((pll & SYSCON_CLKSET1_PLL1_X2FBD2_MASK) >> SYSCON_CLKSET1_PLL1_X2FBD2_SHIFT);
++ ipd = ((pll & SYSCON_CLKSET1_PLL1_X2IPD_MASK) >> SYSCON_CLKSET1_PLL1_X2IPD_SHIFT);
++
++ freq = (((0x00e10000 * (fb1+1)) / (ipd+1)) * (fb2+1)) >> ps;
++ return freq;
++}
++
++static int ep93xx_set_video_div(unsigned long freq)
++{
++ unsigned long pdiv = 0, div = 0, psel = 0, esel = 0;
++ unsigned long err, f, i, j, k;
++
++ err = -1;
++
++ for (i = 0; i < 3; i++) {
++ if (i == 0)
++ f = 14745600 * 2;
++ else if (i == 1)
++ f = ep93xx_get_pll_frequency(1) * 2;
++ else
++ f = ep93xx_get_pll_frequency(2) * 2;
++
++ for (j = 4; j <= 6; j++) {
++ k = f / (freq * j);
++ if (k < 2)
++ continue;
++
++ if (abs(((f / (j * k))) - freq ) < err ) {
++ pdiv = j - 3;
++ div = k;
++ psel = (i == 2) ? 1 : 0;
++ esel = (i == 0) ? 0 : 1;
++ err = (f / (j * k)) - freq;
++ }
++ }
++ }
++
++ if (err == -1)
++ return -1;
++
++ f = SYSCON_VIDDIV_VENA | (esel ? SYSCON_VIDDIV_ESEL : 0) |
++ (psel ? SYSCON_VIDDIV_PSEL : 0) |
++ (pdiv << SYSCON_VIDDIV_PDIV_SHIFT) |
++ (div << SYSCON_VIDDIV_VDIV_SHIFT);
++ outl(0xaa, EP93XX_SYSCON_SWLOCK);
++ outl(f, SYSCON_VIDDIV);
++
++ return freq + err;
++}
++
++static int interrupt_hooked = 0;
++static int vs_counter = 0;
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)
++static irqreturn_t ep93xxfb_irq_handler(int i, void *blah)
++#else
++static irqreturn_t ep93xxfb_irq_handler(int i, void *blah, struct pt_regs *regs)
++#endif
++{
++
++ outl(RASTER_SWLOCK_VALUE, RASTER_SWLOCK);
++ outl(
++#ifdef CONFIG_EP93XX_SDCS0
++ (0 << VIDEOATTRIBS_SDSEL_SHIFT) |
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ (1 << VIDEOATTRIBS_SDSEL_SHIFT) |
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ (2 << VIDEOATTRIBS_SDSEL_SHIFT) |
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ (3 << VIDEOATTRIBS_SDSEL_SHIFT) |
++#endif
++ VIDEOATTRIBS_VCPOL | VIDEOATTRIBS_HSPOL |
++ VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_INVCLK |
++ VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_EN | VIDEOATTRIBS_INTEN ,
++ VIDEOATTRIBS );
++
++ ep93xxfb_16gray_palette_switch(vs_counter++);
++
++ return IRQ_HANDLED;
++}
++
++void LM121VB1T01_configure(unsigned char value)
++{
++
++ int n;
++ unsigned long attribs;
++ printk("LM121VB1T01_configure\n");
++
++ switch(value)
++ {
++ case 8:
++ ep93xxfb_8gray_palette_init();
++ break;
++ case 16:
++ ep93xxfb_16gray_palette_init();
++ break;
++ default:
++ return;
++ }
++
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, (inl(EP93XX_SYSCON_DEVICE_CONFIG) & ~EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE) | EP93XX_SYSCON_DEVCFG_RasOnP3);
++
++ ep93xx_set_video_div(epinfo.freq*240*1280);
++
++ ep93xxfb_lock_outl( 0x00000000 , VIDEOATTRIBS );
++
++ n = 240;
++ ep93xxfb_lock_outl( n + 3 , VLINESTOTAL );
++ ep93xxfb_lock_outl( ((n)<<16) + n+1 , VSYNCSTRTSTOP );
++ ep93xxfb_lock_outl( ((2)<<16) + n+2 , VACTIVESTRTSTOP );
++ ep93xxfb_lock_outl( ((3)<<16) + n+3 , VBLANKSTRTSTOP );
++ ep93xxfb_lock_outl( ((n+3)<<16) + n+3 , VCLKSTRTSTOP );
++
++ n = 1280;
++ ep93xxfb_lock_outl( n + 15 , HCLKSTOTAL );
++ ep93xxfb_lock_outl( ((n+5)<<16) + n+ 14 , HSYNCSTRTSTOP );
++ ep93xxfb_lock_outl( ((15)<<16) + n + 15 , HACTIVESTRTSTOP );
++ ep93xxfb_lock_outl( ((n+15)<<16) + 15 , HBLANKSTRTSTOP );
++ ep93xxfb_lock_outl( ((n)<<16) + n , HCLKSTRTSTOP );
++
++ ep93xxfb_lock_outl( 14 , LINECARRY );
++
++ attribs = 0;
++
++#ifdef CONFIG_EP93XX_SDCS0
++ attribs |= 0 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ attribs |= 1 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ attribs |= 2 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ attribs |= 3 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++
++ switch(value)
++ {
++ case 8:
++ ep93xxfb_lock_outl( PIXELMODE_DSCAN |
++ PIXELMODE_S_8PPC | PIXELMODE_P_4BPP |
++ PIXELMODE_C_GSLUT , PIXELMODE );
++
++ ep93xxfb_lock_outl(
++ attribs | VIDEOATTRIBS_VCPOL | VIDEOATTRIBS_HSPOL |
++ VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_INVCLK |
++ VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_EN ,
++ VIDEOATTRIBS );
++ break;
++ case 16:
++ if(!interrupt_hooked)
++ {
++ request_irq(IRQ_EP93XX_VSYNC, ep93xxfb_irq_handler, IRQF_DISABLED, "lut switch interrupt", NULL);
++ interrupt_hooked = 1;
++ }
++ ep93xxfb_lock_outl( PIXELMODE_DSCAN |
++ PIXELMODE_S_8PPC | PIXELMODE_P_4BPP | PIXELMODE_C_GSLUT, PIXELMODE );
++
++ ep93xxfb_lock_outl(
++ attribs | VIDEOATTRIBS_VCPOL | VIDEOATTRIBS_HSPOL |
++ VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_INVCLK |
++ VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_EN | VIDEOATTRIBS_INTEN,
++ VIDEOATTRIBS );
++ break;
++ default:
++ return;
++ }
++
++}
++
++void HOSIDEN_HLM6323_configure(unsigned char value)
++{
++ int n;
++ unsigned long attribs;
++
++ printk("HOSIDEN_HLM6323_configure\n");
++
++ switch(value)
++ {
++ case 8:
++ ep93xxfb_8gray_palette_init();
++ break;
++ case 16:
++ ep93xxfb_16gray_palette_init();
++ break;
++ default:
++ return;
++ }
++
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, inl(EP93XX_SYSCON_DEVICE_CONFIG) |EP93XX_SYSCON_DEVCFG_RasOnP3);
++
++ ep93xxfb_lock_outl( 0x00000000 , VIDEOATTRIBS );
++
++ ep93xx_set_video_div(epinfo.freq*320*240);
++ mdelay(10);
++
++ n = 240;
++ ep93xxfb_lock_outl( n + 3 , VLINESTOTAL );
++ ep93xxfb_lock_outl( ((n+1)<<16) + n +2 , VSYNCSTRTSTOP );
++ ep93xxfb_lock_outl( ((3)<<16) + n +3 , VACTIVESTRTSTOP );
++ ep93xxfb_lock_outl( ((3)<<16) + n +3 , VBLANKSTRTSTOP );
++ ep93xxfb_lock_outl( ((n+3)<<16) + n +3, VCLKSTRTSTOP );
++
++ n = 320;
++ ep93xxfb_lock_outl( n + 3, HCLKSTOTAL );
++ ep93xxfb_lock_outl( ((n+1)<<16) + n+2 , HSYNCSTRTSTOP );
++ ep93xxfb_lock_outl( ((3)<<16) + n+3 , HACTIVESTRTSTOP );
++ ep93xxfb_lock_outl( ((3)<<16) + n+3 , HBLANKSTRTSTOP );
++ ep93xxfb_lock_outl( ((n+3)<<16) + n+3 , HCLKSTRTSTOP );
++
++ ep93xxfb_lock_outl( 3 , LINECARRY );
++
++ attribs = 0;
++
++#ifdef CONFIG_EP93XX_SDCS0
++ attribs |= 0 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS1
++ attribs |= 1 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS2
++ attribs |= 2 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++#ifdef CONFIG_EP93XX_SDCS3
++ attribs |= 3 << VIDEOATTRIBS_SDSEL_SHIFT;
++#endif
++
++ switch(value)
++ {
++ case 8:
++ ep93xxfb_lock_outl(
++ PIXELMODE_S_4PPC | PIXELMODE_P_4BPP | PIXELMODE_C_GSLUT, PIXELMODE );
++ ep93xxfb_lock_outl(
++ attribs | VIDEOATTRIBS_VCPOL | VIDEOATTRIBS_HSPOL |
++ VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_INVCLK |
++ VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_EN ,
++ VIDEOATTRIBS );
++ break;
++ case 16:
++ ep93xxfb_lock_outl(
++ PIXELMODE_S_4PPC | PIXELMODE_P_4BPP | PIXELMODE_C_GSLUT, PIXELMODE );
++ if(!interrupt_hooked)
++ {
++ request_irq(IRQ_EP93XX_VSYNC, ep93xxfb_irq_handler, IRQF_DISABLED, "lut switch interrupt", NULL);
++ interrupt_hooked = 1;
++ }
++ ep93xxfb_lock_outl(
++ attribs | VIDEOATTRIBS_VCPOL | VIDEOATTRIBS_HSPOL |
++ VIDEOATTRIBS_DATAEN | VIDEOATTRIBS_SYNCEN | VIDEOATTRIBS_INVCLK |
++ VIDEOATTRIBS_PCLKEN | VIDEOATTRIBS_EN | VIDEOATTRIBS_INTEN,
++ VIDEOATTRIBS );
++ break;
++ default:
++ return;
++ }
++}
++
++#define FB_WRITEL fb_writel
++#define FB_READL fb_readl
++#define LEFT_POS(bpp) (0)
++#define SHIFT_HIGH(val, bits) ((val) << (bits))
++#define SHIFT_LOW(val, bits) ((val) >> (bits))
++static inline void color_imageblit(const struct fb_image *image,
++ struct fb_info *p, u8 *dst1,
++ u32 start_index,
++ u32 pitch_index)
++{
++ /* Draw the penguin */
++ u32 *dst, *dst2;
++ u32 color = 0, val, shift;
++ int i, n, bpp = p->var.bits_per_pixel;
++ u32 null_bits = 32 - bpp;
++ u32 *palette = (u32 *) p->pseudo_palette;
++ const u8 *src = image->data;
++
++ dst2 = (u32 *) dst1;
++ for (i = image->height; i--; ) {
++ n = image->width;
++ dst = (u32 *) dst1;
++ shift = 0;
++ val = 0;
++
++ if (start_index) {
++ u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
++ val = FB_READL(dst) & start_mask;
++ shift = start_index;
++ }
++ while (n--) {
++ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
++ p->fix.visual == FB_VISUAL_DIRECTCOLOR )
++ color = palette[*src];
++ else
++ color = *src;
++ color <<= LEFT_POS(bpp);
++ val |= SHIFT_HIGH(color, shift);
++ if (shift >= null_bits) {
++ FB_WRITEL(val, dst++);
++
++ val = (shift == null_bits) ? 0 :
++ SHIFT_LOW(color, 32 - shift);
++ }
++ shift += bpp;
++ shift &= (32 - 1);
++ src++;
++ }
++ if (shift) {
++ u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
++
++ FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
++ }
++ dst1 += p->fix.line_length;
++ if (pitch_index) {
++ dst2 += p->fix.line_length;
++ dst1 = (u8 *)((long __force)dst2 & ~(sizeof(u32) - 1));
++
++ start_index += pitch_index;
++ start_index &= 32 - 1;
++ }
++ }
++}
++
++static const int reversebit[]=
++{
++ 7, 6, 5, 4, 3, 2, 1, 0,
++ 15,14,13,12,11,10, 9, 8,
++ 23,22,21,20,19,18,17,16,
++ 31,30,29,28,27,26,25,24,
++};
++static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
++ u8 *dst1, u32 fgcolor,
++ u32 bgcolor,
++ u32 start_index,
++ u32 pitch_index)
++{
++ u32 shift, color = 0, bpp = p->var.bits_per_pixel;
++ u32 *dst, *dst2;
++ u32 val, pitch = p->fix.line_length;
++ u32 null_bits = 32 - bpp;
++ u32 spitch = (image->width+7)/8;
++ const u8 *src = image->data, *s;
++ u32 i, j, l;
++
++ dst2 = (u32 *) dst1;
++ fgcolor <<= LEFT_POS(bpp);
++ bgcolor <<= LEFT_POS(bpp);
++ for (i = image->height; i--; ) {
++ shift = val = 0;
++ l = 8;
++ j = image->width;
++ dst = (u32 *) dst1;
++ s = src;
++
++ /* write leading bits */
++ if (start_index) {
++ u32 start_mask = ~(SHIFT_HIGH(~(u32)0,start_index));
++ val = FB_READL(dst) & start_mask;
++ shift = start_index;
++ }
++
++ while (j--) {
++ l--;
++ color = (*s & (1 << l)) ? fgcolor : bgcolor;
++ val |= SHIFT_HIGH(color, reversebit[shift]);
++ /* Did the bitshift spill bits to the next long? */
++ if (shift >= null_bits) {
++ FB_WRITEL(val, dst++);
++ val = (shift == null_bits) ? 0 :
++ SHIFT_LOW(color, 32 - reversebit[shift]);
++ }
++ shift += bpp;
++ shift &= (32 - 1);
++ if (!l) { l = 8; s++; };
++ }
++
++ /* write trailing bits */
++ if (shift) {
++ u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
++
++ FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
++ }
++
++ dst1 += pitch;
++ src += spitch;
++ if (pitch_index) {
++ dst2 += pitch;
++ dst1 = (u8 *)((long __force)dst2 & ~(sizeof(u32) - 1));
++ start_index += pitch_index;
++ start_index &= 32 - 1;
++ }
++
++ }
++}
++
++static void ep93xx_imageblit(struct fb_info *p, const struct fb_image *image)
++{
++ u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
++ u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
++ u32 dx = image->dx, dy = image->dy;
++ u8 *dst1;
++
++ if (p->state != FBINFO_STATE_RUNNING)
++ return;
++
++ bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
++ start_index = bitstart & (32 - 1);
++ pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
++
++ bitstart /= 8;
++ bitstart &= ~(bpl - 1);
++ dst1 = p->screen_base + bitstart;
++
++ if (p->fbops->fb_sync)
++ p->fbops->fb_sync(p);
++
++ if (image->depth == 1) {
++ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
++ p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
++ fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
++ bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
++ } else {
++ fgcolor = image->fg_color;
++ bgcolor = image->bg_color;
++ }
++ slow_imageblit(image, p, dst1, fgcolor, bgcolor,
++ start_index, pitch_index);
++ } else
++ color_imageblit(image, p, dst1, start_index, pitch_index);
++}
++
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
++
++int ep93xxfb_ioctl(struct fb_info *info,unsigned int cmd, unsigned long arg)
++{
++ return 0;
++}
++
++static int ep93xxfb_mmap(struct fb_info *info,struct vm_area_struct *vma)
++{
++ unsigned long off, start, len;
++
++ DPRINTK("ep93xxfb_mmap - enter\n");
++
++ off = vma->vm_pgoff << PAGE_SHIFT;
++ start = info->fix.smem_start;
++ len = PAGE_ALIGN(start & ~PAGE_MASK) + info->fix.smem_len;
++ start &= PAGE_MASK;
++ if ((vma->vm_end - vma->vm_start + off) > len)
++ return -EINVAL;
++
++ off += start;
++ vma->vm_pgoff = off >> PAGE_SHIFT;
++
++ vma->vm_flags |= VM_IO;
++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
++ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
++ DPRINTK("ep93xxfb_mmap error\n");
++ return -EAGAIN;
++ }
++
++ DPRINTK("ep93xxfb_mmap - exit\n");
++ return 0;
++}
++
++
++static struct fb_ops ep93xxfb_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = ep93xxfb_check_var,
++ .fb_set_par = ep93xxfb_set_par,
++ .fb_blank = ep93xxfb_blank,
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ // .fb_imageblit = cfb_imageblit,
++ .fb_imageblit = ep93xx_imageblit,
++ .fb_ioctl = ep93xxfb_ioctl,
++ .fb_mmap = ep93xxfb_mmap,
++};
++
++
++static struct resource ep93xxfb_raster_resources = {
++ .start = EP93XX_RASTER_PHYS_BASE,
++ .end = EP93XX_RASTER_PHYS_BASE + 0x1ffff,
++ .flags = IORESOURCE_MEM,
++};
++
++
++static int __init ep93xxfb_probe(struct platform_device *device)
++{
++ struct fb_info *info = NULL;
++ struct resource *res = NULL;
++ int ret = 0;
++ int arb = 0;
++
++ DPRINTK("ep93xxfb_probe - enter \n");
++
++ if(!device) {
++ printk("error : to_platform_device\n");
++ return -ENODEV;
++ }
++ res = platform_get_resource( device, IORESOURCE_MEM, 0);
++ if(!res) {
++ printk("error : platform_get_resource \n");
++ return -ENODEV;
++ }
++ if (!request_mem_region(res->start,res->end - res->start + 1, FBDEV_NAME ))
++ return -EBUSY;
++
++ info = framebuffer_alloc(sizeof(u32) * 256, &device->dev);
++
++ if(!info) {
++ printk("Unable to allocate memory for frame buffer\n");
++ return -ENOMEM;
++ }
++
++ info->flags = FBINFO_DEFAULT;
++ strncpy(info->fix.id, FBDEV_NAME, sizeof(info->fix.id));
++ info->fix.mmio_start = res->start;
++ info->fix.mmio_len = res->end - res->start + 1;
++ info->fbops = &ep93xxfb_ops;
++ info->pseudo_palette = info->par;
++ info->state = FBINFO_STATE_RUNNING;
++
++ printk("mmio_start = 0x%08x\n", res->start);
++ printk("mmio_len = 0x%08x\n", res->end - res->start + 1);
++
++ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
++ ret = -ENOMEM;
++ goto fbuff;
++ }
++
++ if ((ret = ep93xxfb_config(info)) < 0)
++ goto clmap;
++
++ if (register_framebuffer(info) < 0) {
++ printk(KERN_ERR "Unable to register ep93xxfb frame buffer\n");
++ ret = -EINVAL;
++ goto clmap;
++ }
++ platform_set_drvdata(device, info);
++ printk(KERN_INFO "fb%d: EP93xx frame buffer at %dx%dx%dbpp\n", info->node,
++ info->var.xres, info->var.yres, info->var.bits_per_pixel);
++
++ /*change the raster arb to the highest one--Bo*/
++ arb = inl(EP93XX_SYSCON_BMAR);
++ arb = (arb & 0x3f8) | 0x01;
++ ep93xxfb_outl(arb,EP93XX_SYSCON_BMAR);
++
++ DPRINTK("ep93xxfb_probe - exit \n");
++ return 0;
++
++clmap:
++ fb_dealloc_cmap(&info->cmap);
++
++fbuff:
++ framebuffer_release(info);
++ return ret;
++}
++
++static int ep93xxfb_remove(struct platform_device *device)
++{
++ struct resource *res;
++ struct fb_info *info;
++
++ DPRINTK("ep93xxfb_remove - enter \n");
++
++ info = platform_get_drvdata(device);
++
++ ep93xxfb_release_videomem();
++
++ res = platform_get_resource( device, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, res->end - res->start + 1);
++
++ platform_set_drvdata(device, NULL);
++ unregister_framebuffer(info);
++
++ fb_dealloc_cmap(&info->cmap);
++ framebuffer_release(info);
++
++ ep93xxfb_blank( 1, info );
++
++ DPRINTK("ep93xxfb_remove - exit \n");
++ return 0;
++}
++
++static void ep93xxfb_platform_release(struct device *device)
++{
++ DPRINTK("ep93xxfb_platform_release - enter\n");
++}
++
++
++static struct platform_driver ep93xxfb_driver = {
++ .probe = ep93xxfb_probe,
++ .remove = ep93xxfb_remove,
++ .driver = {
++ .name = FBDEV_NAME,
++ },
++};
++
++static struct platform_device ep93xxfb_device = {
++ .name = FBDEV_NAME,
++ .id = -1,
++ .dev = {
++ .release = ep93xxfb_platform_release,
++ },
++ .num_resources = 1,
++ .resource = &ep93xxfb_raster_resources,
++};
++
++int __init ep93xxfb_init(void)
++{
++ int ret = 0;
++
++ DPRINTK("ep93xxfb_init - enter\n");
++
++ ret = platform_driver_register(&ep93xxfb_driver);
++
++ if (!ret) {
++ ret = platform_device_register(&ep93xxfb_device);
++ if (ret)
++ platform_driver_unregister(&ep93xxfb_driver);
++ }
++ DPRINTK("ep93xxfb_init - exit\n");
++ return ret;
++}
++
++static void __exit ep93xxfb_exit(void)
++{
++ DPRINTK("ep93xxfb_exit - enter\n");
++ platform_driver_unregister(&ep93xxfb_driver);
++ platform_device_unregister(&ep93xxfb_device);
++ DPRINTK("ep93xxfb_exit - exit\n");
++}
++
++#else // LINUX_VERSION_CODE
++
++
++int ep93xxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info)
++{
++ return 0;
++}
++static struct fb_ops ep93xxfb_ops = {
++ .owner = THIS_MODULE,
++ .fb_setcolreg = ep93xxfb_setcolreg,
++ .fb_check_var = ep93xxfb_check_var,
++ .fb_set_par = ep93xxfb_set_par,
++ .fb_blank = ep93xxfb_blank,
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = ep93xx_imageblit,
++ .fb_cursor = soft_cursor,
++};
++
++static int __init ep93xxfb_probe(struct device *device)
++{
++ struct platform_device *pdev = to_platform_device(device);
++ struct fb_info *info = NULL;
++ struct resource *res = NULL;
++ int ret = 0;
++ int arb = 0;
++
++ DPRINTK("ep93xxfb_probe - enter \n");
++
++
++ if(!device) {
++ printk("error : to_platform_device\n");
++ return -ENODEV;
++ }
++ res = platform_get_resource( pdev, IORESOURCE_MEM, 0);
++ if(!res) {
++ printk("error : platform_get_resource \n");
++ return -ENODEV;
++ }
++ if (!request_mem_region(res->start,res->end - res->start + 1, FBDEV_NAME ))
++ return -EBUSY;
++
++ info = framebuffer_alloc(sizeof(u32) * 256, &pdev->dev);
++
++ if(!info) {
++ printk("Unable to allocate memory for frame buffer\n");
++ return -ENOMEM;
++ }
++
++ info->flags = FBINFO_DEFAULT;
++ strncpy(info->fix.id, FBDEV_NAME, sizeof(info->fix.id));
++ info->fix.mmio_start = res->start;
++ info->fix.mmio_len = res->end - res->start + 1;
++ info->fbops = &ep93xxfb_ops;
++ info->pseudo_palette = info->par;
++ info->state = FBINFO_STATE_RUNNING;
++
++ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
++ ret = -ENOMEM;
++ goto fbuff;
++ }
++
++ if ((ret = ep93xxfb_config(info)) < 0)
++ goto clmap;
++
++ if (register_framebuffer(info) < 0) {
++ printk(KERN_ERR "Unable to register ep93xxfb frame buffer\n");
++ ret = -EINVAL;
++ goto clmap;
++ }
++ dev_set_drvdata(device, info);
++ printk(KERN_INFO "fb%d: EP93xx frame buffer at %dx%dx%dbpp\n", info->node,
++ info->var.xres, info->var.yres, info->var.bits_per_pixel);
++
++ /*change the raster arb to the highest one--Bo*/
++ arb = inl(EP93XX_SYSCON_BMAR);
++ arb = (arb & 0x3f8) | 0x01;
++ ep93xxfb_outl(arb,EP93XX_SYSCON_BMAR);
++
++ DPRINTK("ep93xxfb_probe - exit \n");
++ return 0;
++
++clmap:
++ fb_dealloc_cmap(&info->cmap);
++
++fbuff:
++ framebuffer_release(info);
++ return ret;
++}
++
++static int ep93xxfb_remove(struct device *device)
++{
++ struct platform_device *pdev = to_platform_device(device);
++ struct resource *res;
++ struct fb_info *info;
++
++ DPRINTK("ep93xxfb_remove - enter \n");
++
++ info = dev_get_drvdata(device);
++
++ ep93xxfb_release_videomem();
++
++ res = platform_get_resource( pdev, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, res->end - res->start + 1);
++
++ dev_set_drvdata(device, NULL);
++ unregister_framebuffer(info);
++
++ fb_dealloc_cmap(&info->cmap);
++ framebuffer_release(info);
++
++ ep93xxfb_blank( 1, info );
++
++ DPRINTK("ep93xxfb_remove - exit \n");
++ return 0;
++}
++static struct device_driver ep93xxfb_driver = {
++ .name = FBDEV_NAME,
++ .bus = &platform_bus_type,
++ .probe = ep93xxfb_probe,
++ .remove = ep93xxfb_remove,
++};
++int __init ep93xxfb_init(void)
++{
++ DPRINTK("ep93xxfb_init\n");
++ return driver_register(&ep93xxfb_driver);
++}
++
++static void __exit ep93xxfb_exit(void)
++{
++ DPRINTK("ep93xxfb_exit\n");
++ return driver_unregister(&ep93xxfb_driver);
++}
++
++int __init ep93xxfb_setup(char *options)
++{
++ DPRINTK("ep93xxfb_setup\n");
++ return 0;
++}
++
++#endif // LINUX_VERSION_CODE
++
++
++module_init(ep93xxfb_init);
++module_exit(ep93xxfb_exit);
++MODULE_AUTHOR("John Zheng <yujiang.zheng@cirrus.com>");
++MODULE_LICENSE("GPL");
++
+--- a/arch/arm/mach-ep93xx/include/mach/hardware.h
++++ b/arch/arm/mach-ep93xx/include/mach/hardware.h
+@@ -7,6 +7,7 @@
+ #include "ep93xx-regs.h"
+
+ #define pcibios_assign_all_busses() 0
++#include "regs_raster.h"
+ #include "regs_touch.h"
+
+ #include "platform.h"
+--- a/arch/arm/mach-ep93xx/include/mach/irqs.h
++++ b/arch/arm/mach-ep93xx/include/mach/irqs.h
+@@ -34,7 +34,8 @@
+ #define IRQ_EP93XX_UART3TX 28
+ #define IRQ_EP93XX_KEY 29
+ #define IRQ_EP93XX_TOUCH 30
+-#define EP93XX_VIC1_VALID_IRQ_MASK 0x7ffffffc
++#define IRQ_EP93XX_GRAPHICS 31
++#define EP93XX_VIC1_VALID_IRQ_MASK 0xfffffffc
+
+ #define IRQ_EP93XX_EXT0 32
+ #define IRQ_EP93XX_EXT1 33
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/include/mach/regs_raster.h
+@@ -0,0 +1,347 @@
++/*=============================================================================
++ *
++ * FILE: regs_raster.h
++ *
++ * DESCRIPTION: ep93xx Raster Engine Register Definition
++ *
++ * Copyright Cirrus Logic, 2001-2003
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *=============================================================================
++ */
++#ifndef _REGS_RASTER_H_
++#define _REGS_RASTER_H_
++
++//-----------------------------------------------------------------------------
++// VLINESTOTAL Register Definitions
++//-----------------------------------------------------------------------------
++#define VLINESTOTAL_MASK 0x000007ff
++
++//-----------------------------------------------------------------------------
++// VSYNCSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define VSYNCSTRTSTOP_STRT_MASK 0x07ff0000
++#define VSYNCSTRTSTOP_STRT_SHIFT 0
++#define VSYNCSTRTSTOP_STOP_MASK 0x000007ff
++#define VSYNCSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// VACTIVESTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define VACTIVESTRTSTOP_STRT_MASK 0x07ff0000
++#define VACTIVESTRTSTOP_STRT_SHIFT 0
++#define VACTIVESTRTSTOP_STOP_MASK 0x000007ff
++#define VACTIVESTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// VCLKSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define VCLKSTRTSTOP_STRT_MASK 0x07ff0000
++#define VCLKSTRTSTOP_STRT_SHIFT 0
++#define VCLKSTRTSTOP_STOP_MASK 0x000007ff
++#define VCLKSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// VBLANKSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define VBLANKSTRTSTOP_STRT_MASK 0x07ff0000
++#define VBLANKSTRTSTOP_STRT_SHIFT 0
++#define VBLANKSTRTSTOP_STOP_MASK 0x000007ff
++#define VBLANKSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// HSYNCSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define HSYNCSTRTSTOP_STRT_MASK 0x07ff0000
++#define HSYNCSTRTSTOP_STRT_SHIFT 0
++#define HSYNCSTRTSTOP_STOP_MASK 0x000007ff
++#define HSYNCSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// HACTIVESTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define HACTIVESTRTSTOP_STRT_MASK 0x07ff0000
++#define HACTIVESTRTSTOP_STRT_SHIFT 0
++#define HACTIVESTRTSTOP_STOP_MASK 0x000007ff
++#define HACTIVESTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// HCLKSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define HCLKSTRTSTOP_STRT_MASK 0x07ff0000
++#define HCLKSTRTSTOP_STRT_SHIFT 0
++#define HCLKSTRTSTOP_STOP_MASK 0x000007ff
++#define HCLKSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// BRIGHTNESS Register Definitions
++//-----------------------------------------------------------------------------
++#define BRIGHTNESS_MASK 0x0000ffff
++#define BRIGHTNESS_CNT_MASK 0x000000ff
++#define BRIGHTNESS_CNT_SHIFT 0
++#define BRIGHTNESS_CMP_MASK 0x0000ff00
++#define BRIGHTNESS_CMP_SHIFT 8
++
++//-----------------------------------------------------------------------------
++// VIDEOATTRIBS Register Definitions
++//-----------------------------------------------------------------------------
++#define VIDEOATTRIBS_MASK 0x001fffff
++#define VIDEOATTRIBS_EN 0x00000001
++#define VIDEOATTRIBS_PCLKEN 0x00000002
++#define VIDEOATTRIBS_SYNCEN 0x00000004
++#define VIDEOATTRIBS_DATAEN 0x00000008
++#define VIDEOATTRIBS_CSYNC 0x00000010
++#define VIDEOATTRIBS_VCPOL 0x00000020
++#define VIDEOATTRIBS_HSPOL 0x00000040
++#define VIDEOATTRIBS_BLKPOL 0x00000080
++#define VIDEOATTRIBS_INVCLK 0x00000100
++#define VIDEOATTRIBS_ACEN 0x00000200
++#define VIDEOATTRIBS_LCDEN 0x00000400
++#define VIDEOATTRIBS_CCIREN 0x00001000
++#define VIDEOATTRIBS_PIFEN 0x00002000
++#define VIDEOATTRIBS_INTEN 0x00004000
++#define VIDEOATTRIBS_INT 0x00008000
++#define VIDEOATTRIBS_INTRLC 0x00010000
++#define VIDEOATTRIBS_EQUSER 0x00020000
++#define VIDEOATTRIBS_DHORZ 0x00040000
++#define VIDEOATTRIBS_DVERT 0x00080000
++#define VIDEOATTRIBS_BKPXD 0x00100000
++
++#define VIDEOATTRIBS_SDSEL_MASK 0x00600000
++#define VIDEOATTRIBS_SDSEL_SHIFT 21
++
++//-----------------------------------------------------------------------------
++// HBLANKSTRTSTOP Register Definitions
++//-----------------------------------------------------------------------------
++#define HBLANKSTRTSTOP_STRT_MASK 0x07ff0000
++#define HBLANKSTRTSTOP_STRT_SHIFT 0
++#define HBLANKSTRTSTOP_STOP_MASK 0x000007ff
++#define HBLANKSTRTSTOP_STOP_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// LINECARRY Register Definitions
++//-----------------------------------------------------------------------------
++#define LINECARRY_LCARY_MASK 0x000007ff
++#define LINECARRY_LCARY_SHIFT 0
++
++//-----------------------------------------------------------------------------
++// BLINKRATE Register Definitons
++//-----------------------------------------------------------------------------
++#define BLINKRATE_MASK 0x000000ff
++
++//-----------------------------------------------------------------------------
++// BLINKMASK Register Definitons
++//-----------------------------------------------------------------------------
++#define BLINKMASK_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// VIDSCRNPAGE Register Definitons
++//-----------------------------------------------------------------------------
++#define VIDSCRNPAGE_PAGE_MASK 0x0ffffffc
++
++//-----------------------------------------------------------------------------
++// VIDSCRNHPG Register Definitons
++//-----------------------------------------------------------------------------
++#define VIDSCRNHPG_MASK 0x0ffffffc
++
++//-----------------------------------------------------------------------------
++// SCRNLINES Register Definitons
++//-----------------------------------------------------------------------------
++#define SCRNLINES_MASK 0x000007ff
++
++//-----------------------------------------------------------------------------
++// LINELENGTH Register Definitons
++//-----------------------------------------------------------------------------
++#define LINELENGTH_MASK 0x000007ff
++
++//-----------------------------------------------------------------------------
++// VLINESTEP Register Definitons
++//-----------------------------------------------------------------------------
++#define VLINESTEP_MASK 0x00000fff
++
++//-----------------------------------------------------------------------------
++// RASTER_SWLOCK Register Definitons
++//-----------------------------------------------------------------------------
++#define RASTER_SWLOCK_MASK_WR 0xff
++#define RASTER_SWLOCK_MASK_R 0x1
++#define RASTER_SWLOCK_VALUE 0xaa
++
++//-----------------------------------------------------------------------------
++// LUTCONT Register Definitions
++//-----------------------------------------------------------------------------
++#define LUTCONT_MASK 0x00000003
++#define LUTCONT_SWTCH 0x00000001
++#define LUTCONT_STAT 0x00000002
++#define LUTCONT_RAM0 0
++#define LUTCONT_RAM1 1
++
++//-----------------------------------------------------------------------------
++// CURSORBLINK1 Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORBLINK1_MASK 0x00ffffff
++//-----------------------------------------------------------------------------
++// CURSORBLINK2 Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORBLINK2_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// CURSORBLINK Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORBLINK_MASK 0x000001ff
++#define CURSORBLINK_RATE_MASK 0x000000ff
++#define CURSORBLINK_RATE_SHIFT 0
++#define CURSORBLINK_EN 0x00000100
++
++//-----------------------------------------------------------------------------
++// BLINKPATRN Register Definitions
++//-----------------------------------------------------------------------------
++#define BLINKPATRN_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// PATRNMASK Register Definitions
++//-----------------------------------------------------------------------------
++#define PATRNMASK_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// BG_OFFSET Register Definitions
++//-----------------------------------------------------------------------------
++#define BG_OFFSET_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// PIXELMODE Register Definitions
++//-----------------------------------------------------------------------------
++#define PIXELMODE_P_MASK 0x00000007
++#define PIXELMODE_P_MUX_DISABLE 0x00000000
++#define PIXELMODE_P_4BPP 0x00000001
++#define PIXELMODE_P_8BPP 0x00000002
++#define PIXELMODE_P_16BPP 0x00000004
++#define PIXELMODE_P_24BPP 0x00000006
++#define PIXELMODE_P_32BPP 0x00000007
++
++#define PIXELMODE_S_MASK 0x00000038
++#define PIXELMODE_S_1PPC 0x00000000
++#define PIXELMODE_S_1PPCMAPPED 0x00000008
++#define PIXELMODE_S_2PPC 0x00000010
++#define PIXELMODE_S_4PPC 0x00000018
++#define PIXELMODE_S_8PPC 0x00000020
++#define PIXELMODE_S_223PPC 0x00000028
++#define PIXELMODE_S_DS223PPC 0x00000030
++#define PIXELMODE_S_UNDEF 0x00000038
++
++#define PIXELMODE_M_MASK 0x000003c0
++#define PIXELMODE_M_NOBLINK 0x00000000
++#define PIXELMODE_M_ANDBLINK 0x00000040
++#define PIXELMODE_M_ORBLINK 0x00000080
++#define PIXELMODE_M_XORBLINK 0x000000c0
++#define PIXELMODE_M_BGBLINK 0x00000100
++#define PIXELMODE_M_OFFSINGBLINK 0x00000140
++#define PIXELMODE_M_OFF888BLINK 0x00000180
++#define PIXELMODE_M_DIMBLINK 0x00000300
++#define PIXELMODE_M_BRTBLINK 0x00000340
++#define PIXELMODE_M_DIM888BLINK 0x00000380
++#define PIXELMODE_M_BRT888BLINK 0x000003c0
++
++#define PIXELMODE_C_MASK 0x00003c00
++#define PIXELMODE_C_LUT 0x00000000
++#define PIXELMODE_C_888 0x00001000
++#define PIXELMODE_C_565 0x00001400
++#define PIXELMODE_C_555 0x00001800
++#define PIXELMODE_C_GSLUT 0x00002000
++
++#define PIXELMODE_DSCAN 0x00004000
++#define PIXELMODE_TRBSW 0x00008000
++
++//-----------------------------------------------------------------------------
++//PARLLIFOUT Register Defintions
++//-----------------------------------------------------------------------------
++#define PARLLIFOUT_DAT_MASK 0x0000000f
++#define PARLLIFOUT_DAT_SHIFT 0
++#define PARLLIFOUT_RD 0x00000010
++
++//-----------------------------------------------------------------------------
++//PARLLIFIN Register Defintions
++//-----------------------------------------------------------------------------
++#define PARLLIFIN_DAT_MASK 0x0000000f
++#define PARLLIFIN_DAT_SHIFT 0
++#define PARLLIFIN_CNT_MASK 0x000f0000
++#define PARLLIFIN_CNT_SHIFT 16
++#define PARLLIFIN_ESTRT_MASK 0x00f00000
++#define PARLLIFIN_ESTRT_SHIFT 20
++
++//-----------------------------------------------------------------------------
++// CURSORADRSTART Register Defintions
++//-----------------------------------------------------------------------------
++#define CURSOR_ADR_START_MASK 0xfffffffc
++
++//-----------------------------------------------------------------------------
++// CURSORADRSTART Register Defintions
++//-----------------------------------------------------------------------------
++#define CURSOR_ADR_RESET_MASK 0xfffffffc
++
++//-----------------------------------------------------------------------------
++// CURSORCOLOR1 Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORCOLOR1_MASK 0x00ffffff
++//-----------------------------------------------------------------------------
++// CURSORCOLOR2 Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORCOLOR2_MASK 0x00ffffff
++
++//-----------------------------------------------------------------------------
++// CURSORXYLOC Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORXYLOC_MASK 0x07ff87ff
++#define CURSORXYLOC_XLOC_MASK 0x000007ff
++#define CURSORXYLOC_XLOC_SHIFT 0
++#define CURSORXYLOC_CEN 0x00008000
++#define CURSORXYLOC_YLOC_MASK 0x07ff0000
++#define CURSORXYLOC_YLOC_SHIFT 16
++
++//-----------------------------------------------------------------------------
++// CURSOR_DSCAN_LH_YLOC Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSOR_DSCAN_LH_YLOC_MASK 0x000087ff
++
++#define CURSOR_DSCAN_LH_YLOC_YLOC_MASK 0x000007ff
++#define CURSOR_DSCAN_LH_YLOC_YLOC_SHIFT 0
++#define CURSOR_DSCAN_LH_YLOC_CLHEN 0x00008000
++
++//-----------------------------------------------------------------------------
++// CURSORSIZE Register Definitions
++//-----------------------------------------------------------------------------
++#define CURSORSIZE_MASK 0x0000ffff
++
++#define CURSORSIZE_CWID_MASK 0x00000003
++#define CURSORSIZE_CWID_SHIFT 0
++#define CURSORSIZE_CWID_1_WORD 0
++#define CURSORSIZE_CWID_2_WORD 1
++#define CURSORSIZE_CWID_3_WORD 2
++#define CURSORSIZE_CWID_4_WORD 3
++
++#define CURSORSIZE_CLINS_MASK 0x000000fc
++#define CURSORSIZE_CLINS_SHIFT 2
++
++#define CURSORSIZE_CSTEP_MASK 0x00000300
++#define CURSORSIZE_CSTEP_SHIFT 8
++#define CURSORSIZE_CSTEP_1_WORD 0
++#define CURSORSIZE_CSTEP_2_WORD 1
++#define CURSORSIZE_CSTEP_3_WORD 2
++#define CURSORSIZE_CSTEP_4_WORD 3
++
++#define CURSORSIZE_DLNS_MASK 0x0000fc00
++#define CURSORSIZE_DLNS_SHIFT 10
++
++#endif /* _REGS_RASTER_H_ */
diff --git a/target/linux/ep93xx/patches-2.6.30/010-ep93xx-snd-ac97.patch b/target/linux/ep93xx/patches-2.6.30/010-ep93xx-snd-ac97.patch
new file mode 100644
index 0000000000..36f316be17
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/010-ep93xx-snd-ac97.patch
@@ -0,0 +1,3808 @@
+--- a/arch/arm/mach-ep93xx/include/mach/hardware.h
++++ b/arch/arm/mach-ep93xx/include/mach/hardware.h
+@@ -5,6 +5,7 @@
+ #define __ASM_ARCH_HARDWARE_H
+
+ #include "ep93xx-regs.h"
++#include "regs_ac97.h"
+
+ #define pcibios_assign_all_busses() 0
+ #include "regs_raster.h"
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/include/mach/regs_ac97.h
+@@ -0,0 +1,180 @@
++/*=============================================================================
++ * FILE: regs_ac97.h
++ *
++ * DESCRIPTION: Ac'97 Register Definition
++ *
++ * Copyright Cirrus Logic, 2001-2003
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *=============================================================================
++ */
++#ifndef _REGS_AC97_H_
++#define _REGS_AC97_H_
++
++//-----------------------------------------------------------------------------
++// Bit definitionses
++//-----------------------------------------------------------------------------
++#define AC97ISR_RIS 8
++#define AC97ISR_TIS 4
++#define AC97ISR_RTIS 2
++#define AC97ISR_TCIS 1
++
++#define AC97RGIS_SLOT1TXCOMPLETE 0x01
++#define AC97RGIS_SLOT2RXVALID 0x02
++#define AC97RGIS_GPIOTXCOMPLETE 0x04
++#define AC97RGIS_GPIOINTRX 0x08
++#define AC97RGIS_RWIS 0x10
++#define AC97RGIS_CODECREADY 0x20
++#define AC97RGIS_SLOT2TXCOMPLETE 0x40
++
++#define AC97SR_RXFE 0x0001
++#define AC97SR_TXFE 0x0002
++#define AC97SR_RXFF 0x0004
++#define AC97SR_TXFF 0x0008
++#define AC97SR_TXBUSY 0x0010
++#define AC97SR_RXOE 0x0020
++#define AC97SR_TXUE 0x0040
++
++#define AC97GSR_IFE 0x1
++#define AC97GSR_LOOP 0x2
++#define AC97GSR_OVERRIDECODECREADY 0x4
++
++#define AC97RESET_TIMEDRESET 0x1
++#define AC97RESET_FORCEDRESET 0x2
++#define AC97RESET_EFORCER 0x4
++
++#define AC97RXCR_REN 0x1
++
++#define AC97TXCR_TEN 0x1
++
++
++//****************************************************************************
++//
++// The Ac97 Codec registers, accessable through the Ac-link.
++// These are not controller registers and are not memory mapped.
++// Includes registers specific to CS4202 (Beavis).
++//
++//****************************************************************************
++#define AC97_REG_OFFSET_MASK 0x0000007E
++
++#define AC97_00_RESET 0x00000000
++#define AC97_02_MASTER_VOL 0x00000002
++#define AC97_04_HEADPHONE_VOL 0x00000004
++#define AC97_06_MONO_VOL 0x00000006
++#define AC97_08_TONE 0x00000008
++#define AC97_0A_PC_BEEP_VOL 0x0000000A
++#define AC97_0C_PHONE_VOL 0x0000000C
++#define AC97_0E_MIC_VOL 0x0000000E
++#define AC97_10_LINE_IN_VOL 0x00000010
++#define AC97_12_CD_VOL 0x00000012
++#define AC97_14_VIDEO_VOL 0x00000014
++#define AC97_16_AUX_VOL 0x00000016
++#define AC97_18_PCM_OUT_VOL 0x00000018
++#define AC97_1A_RECORD_SELECT 0x0000001A
++#define AC97_1C_RECORD_GAIN 0x0000001C
++#define AC97_1E_RESERVED_1E 0x0000001E
++#define AC97_20_GENERAL_PURPOSE 0x00000020
++#define AC97_22_3D_CONTROL 0x00000022
++#define AC97_24_MODEM_RATE 0x00000024
++#define AC97_26_POWERDOWN 0x00000026
++#define AC97_28_EXT_AUDIO_ID 0x00000028
++#define AC97_2A_EXT_AUDIO_POWER 0x0000002A
++#define AC97_2C_PCM_FRONT_DAC_RATE 0x0000002C
++#define AC97_2E_PCM_SURR_DAC_RATE 0x0000002E
++#define AC97_30_PCM_LFE_DAC_RATE 0x00000030
++#define AC97_32_PCM_LR_ADC_RATE 0x00000032
++#define AC97_34_MIC_ADC_RATE 0x00000034
++#define AC97_36_6CH_VOL_C_LFE 0x00000036
++#define AC97_38_6CH_VOL_SURROUND 0x00000038
++#define AC97_3A_SPDIF_CONTROL 0x0000003A
++#define AC97_3C_EXT_MODEM_ID 0x0000003C
++#define AC97_3E_EXT_MODEM_POWER 0x0000003E
++#define AC97_40_LINE1_CODEC_RATE 0x00000040
++#define AC97_42_LINE2_CODEC_RATE 0x00000042
++#define AC97_44_HANDSET_CODEC_RATE 0x00000044
++#define AC97_46_LINE1_CODEC_LEVEL 0x00000046
++#define AC97_48_LINE2_CODEC_LEVEL 0x00000048
++#define AC97_4A_HANDSET_CODEC_LEVEL 0x0000004A
++#define AC97_4C_GPIO_PIN_CONFIG 0x0000004C
++#define AC97_4E_GPIO_PIN_TYPE 0x0000004E
++#define AC97_50_GPIO_PIN_STICKY 0x00000050
++#define AC97_52_GPIO_PIN_WAKEUP 0x00000052
++#define AC97_54_GPIO_PIN_STATUS 0x00000054
++#define AC97_56_RESERVED 0x00000056
++#define AC97_58_RESERVED 0x00000058
++#define AC97_5A_CRYSTAL_REV_N_FAB_ID 0x0000005A
++#define AC97_5C_TEST_AND_MISC_CTRL 0x0000005C
++#define AC97_5E_AC_MODE 0x0000005E
++#define AC97_60_MISC_CRYSTAL_CONTROL 0x00000060
++#define AC97_62_VENDOR_RESERVED 0x00000062
++#define AC97_64_DAC_SRC_PHASE_INCR 0x00000064
++#define AC97_66_ADC_SRC_PHASE_INCR 0x00000066
++#define AC97_68_RESERVED_68 0x00000068
++#define AC97_6A_SERIAL_PORT_CONTROL 0x0000006A
++#define AC97_6C_VENDOR_RESERVED 0x0000006C
++#define AC97_6E_VENDOR_RESERVED 0x0000006E
++#define AC97_70_BDI_CONFIG 0x00000070
++#define AC97_72_BDI_WAKEUP 0x00000072
++#define AC97_74_VENDOR_RESERVED 0x00000074
++#define AC97_76_CAL_ADDRESS 0x00000076
++#define AC97_78_CAL_DATA 0x00000078
++#define AC97_7A_VENDOR_RESERVED 0x0000007A
++#define AC97_7C_VENDOR_ID1 0x0000007C
++#define AC97_7E_VENDOR_ID2 0x0000007E
++
++
++#ifndef __ASSEMBLY__
++
++//
++// enum type for use with reg AC97_RECORD_SELECT
++//
++typedef enum{
++ RECORD_MIC = 0x0000,
++ RECORD_CD = 0x0101,
++ RECORD_VIDEO_IN = 0x0202,
++ RECORD_AUX_IN = 0x0303,
++ RECORD_LINE_IN = 0x0404,
++ RECORD_STEREO_MIX = 0x0505,
++ RECORD_MONO_MIX = 0x0606,
++ RECORD_PHONE_IN = 0x0707
++} Ac97RecordSources;
++
++#endif /* __ASSEMBLY__ */
++
++//
++// Sample rates supported directly in AC97_PCM_FRONT_DAC_RATE and
++// AC97_PCM_LR_ADC_RATE.
++//
++#define Ac97_Fs_8000 0x1f40
++#define Ac97_Fs_11025 0x2b11
++#define Ac97_Fs_16000 0x3e80
++#define Ac97_Fs_22050 0x5622
++#define Ac97_Fs_32000 0x7d00
++#define Ac97_Fs_44100 0xac44
++#define Ac97_Fs_48000 0xbb80
++
++//
++// RSIZE and TSIZE in AC97RXCR and AC97TXCR
++//
++#define Ac97_SIZE_20 2
++#define Ac97_SIZE_18 1
++#define Ac97_SIZE_16 0
++#define Ac97_SIZE_12 3
++
++//=============================================================================
++//=============================================================================
++
++
++#endif /* _REGS_AC97_H_ */
+--- a/sound/arm/Kconfig
++++ b/sound/arm/Kconfig
+@@ -11,6 +11,23 @@ menuconfig SND_ARM
+
+ if SND_ARM
+
++config SND_EP93XX_AC97
++ tristate "AC97 driver for the Cirrus EP93xx chip"
++ depends on ARCH_EP93XX && SND
++ select SND_EP93XX_PCM
++ select SND_AC97_CODEC
++ help
++ Say Y here to use AC'97 audio with a Cirrus Logic EP93xx chip.
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-ep93xx-ac97.
++
++config SND_EP93XX_PCM
++ tristate
++ select SND_PCM
++ help
++ Generic PCM module for EP93xx
++
+ config SND_ARMAACI
+ tristate "ARM PrimeCell PL041 AC Link support"
+ depends on ARM_AMBA
+--- a/sound/arm/Makefile
++++ b/sound/arm/Makefile
+@@ -5,6 +5,9 @@
+ obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
+ snd-aaci-objs := aaci.o devdma.o
+
++obj-$(CONFIG_SND_EP93XX_AC97) += snd-ep93xx-ac97.o
++snd-ep93xx-ac97-objs := ep93xx-ac97.o
++
+ obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
+ snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
+
+--- /dev/null
++++ b/sound/arm/ep93xx-ac97.c
+@@ -0,0 +1,3482 @@
++/*
++ * linux/sound/arm/ep93xx-ac97.c -- ALSA PCM interface for the edb93xx ac97 audio
++ */
++
++#include <linux/autoconf.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/soundcard.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/control.h>
++#include <sound/initval.h>
++#include <sound/ac97_codec.h>
++
++#include <asm/irq.h>
++#include <asm/semaphore.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/arch/dma.h>
++#include "ep93xx-ac97.h"
++
++#define DRIVER_VERSION "01/05/2009"
++#define DRIVER_DESC "EP93xx AC97 Audio driver"
++static int ac_link_enabled = 0;
++static int codec_supported_mixers;
++
++//#define DEBUG 1
++#ifdef DEBUG
++#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )
++#else
++#define DPRINTK( fmt, arg... )
++#endif
++
++#define WL16 0
++#define WL24 1
++
++#define AUDIO_NAME "ep93xx-ac97"
++#define AUDIO_SAMPLE_RATE_DEFAULT 44100
++#define AUDIO_DEFAULT_VOLUME 0
++#define AUDIO_MAX_VOLUME 181
++#define AUDIO_DEFAULT_DMACHANNELS 3
++#define PLAYBACK_DEFAULT_DMACHANNELS 3
++#define CAPTURE_DEFAULT_DMACHANNELS 3
++
++#define CHANNEL_FRONT (1<<0)
++#define CHANNEL_REAR (1<<1)
++#define CHANNEL_CENTER_LFE (1<<2)
++
++static void snd_ep93xx_dma_tx_callback( ep93xx_dma_int_t DMAInt,
++ ep93xx_dma_dev_t device,
++ unsigned int user_data);
++static void snd_ep93xx_dma_rx_callback( ep93xx_dma_int_t DMAInt,
++ ep93xx_dma_dev_t device,
++ unsigned int user_data);
++
++static const struct snd_pcm_hardware ep93xx_ac97_pcm_hardware = {
++
++
++ .info = ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE ),
++ .formats = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
++ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
++ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
++ SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE ),
++ .rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000 ),
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .channels_min = 1,/*2,*/
++ .channels_max = 2,
++
++ .period_bytes_min = 1 * 1024,
++ .period_bytes_max = 32 * 1024,
++ .periods_min = 1,
++ .periods_max = 32,
++ .buffer_bytes_max = 32 * 1024,
++ .fifo_size = 0,
++};
++
++static audio_stream_t output_stream;
++static audio_stream_t input_stream;
++
++static audio_state_t audio_state =
++{
++ .output_stream =&output_stream,
++ .output_dma[0] =DMATx_AAC1,
++ .output_id[0] ="Ac97 out",
++
++ .input_stream =&input_stream,
++ .input_dma[0] =DMARx_AAC1,
++ .input_id[0] ="Ac97 in",
++
++ .sem = __SEMAPHORE_INIT(audio_state.sem,1),
++ .codec_set_by_playback = 0,
++ .codec_set_by_capture = 0,
++ .DAC_bit_width =16,
++ .bCompactMode =0,
++};
++
++
++
++/*
++ * peek
++ *
++ * Reads an AC97 codec register. Returns -1 if there was an error.
++ */
++static int peek(unsigned int uiAddress)
++{
++ unsigned int uiAC97RGIS;
++
++ if( !ac_link_enabled )
++ {
++ printk("ep93xx ac97 peek: attempt to peek before enabling ac-link.\n");
++ return -1;
++ }
++
++ /*
++ * Check to make sure that the address is aligned on a word boundary
++ * and is 7E or less.
++ */
++ if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e))
++ {
++ return -1;
++ }
++
++ /*
++ * How it is supposed to work is:
++ * - The ac97 controller sends out a read addr in slot 1.
++ * - In the next frame, the codec will echo that address back in slot 1
++ * and send the data in slot 2. SLOT2RXVALID will be set to 1.
++ *
++ * Read until SLOT2RXVALID goes to 1. Reading the data in AC97S2DATA
++ * clears SLOT2RXVALID.
++ */
++
++ /*
++ * First, delay one frame in case of back to back peeks/pokes.
++ */
++ mdelay( 1 );
++
++ /*
++ * Write the address to AC97S1DATA, delay 1 frame, read the flags.
++ */
++ outl( uiAddress, AC97S1DATA);
++ udelay( 21 * 4 );
++ uiAC97RGIS = inl( AC97RGIS );
++
++ /*
++ * Return error if we timed out.
++ */
++ if( ((uiAC97RGIS & AC97RGIS_SLOT1TXCOMPLETE) == 0 ) &&
++ ((uiAC97RGIS & AC97RGIS_SLOT2RXVALID) == 0 ) )
++ {
++ printk( "ep93xx-ac97 - peek failed reading reg 0x%02x.\n", uiAddress );
++ return -1;
++ }
++
++ return ( inl(AC97S2DATA) & 0x000fffff);
++}
++
++/*
++ * poke
++ *
++ * Writes an AC97 codec Register. Return -1 if error.
++ */
++static int poke(unsigned int uiAddress, unsigned int uiValue)
++{
++ unsigned int uiAC97RGIS;
++
++ if( !ac_link_enabled )
++ {
++ printk("ep93xx ac97 poke: attempt to poke before enabling ac-link.\n");
++ return -1;
++ }
++
++ /*
++ * Check to make sure that the address is align on a word boundary and
++ * is 7E or less. And that the value is a 16 bit value.
++ */
++ if( ((uiAddress & 0x1)!=0) || (uiAddress > 0x007e))
++ {
++ printk("ep93xx ac97 poke: address error.\n");
++ return -1;
++ }
++
++ /*stop the audio loop from the input to the output directly*/
++
++ if((uiAddress==AC97_0E_MIC_VOL)||(uiAddress==AC97_10_LINE_IN_VOL))
++ {
++ uiValue = (uiValue | 0x8000);
++
++ }
++
++ /*
++ * First, delay one frame in case of back to back peeks/pokes.
++ */
++ mdelay( 1 );
++
++ /*
++ * Write the data to AC97S2DATA, then the address to AC97S1DATA.
++ */
++ outl( uiValue, AC97S2DATA );
++ outl( uiAddress, AC97S1DATA );
++
++ /*
++ * Wait for the tx to complete, get status.
++ */
++ udelay( 30 );/*21*/
++ uiAC97RGIS = inl(AC97RGIS);
++
++ /*
++ * Return error if we timed out.
++ */
++ if( !(inl(AC97RGIS) & AC97RGIS_SLOT1TXCOMPLETE) )
++ {
++ printk( "ep93xx-ac97: poke failed writing reg 0x%02x value 0x%02x.\n", uiAddress, uiValue );
++ return -1;
++ }
++
++ return 0;
++}
++
++
++/*
++ * When we get to the multichannel case the pre-fill and enable code
++ * will go to the dma driver's start routine.
++ */
++static void ep93xx_audio_enable( int input_or_output_stream )
++{
++ unsigned int uiTemp;
++
++ DPRINTK("ep93xx_audio_enable :%x\n",input_or_output_stream);
++
++ /*
++ * Enable the rx or tx channel depending on the value of
++ * input_or_output_stream
++ */
++ if( input_or_output_stream )
++ {
++ uiTemp = inl(AC97TXCR1);
++ outl( (uiTemp | AC97TXCR_TEN), AC97TXCR1 );
++ }
++ else
++ {
++ uiTemp = inl(AC97RXCR1);
++ outl( (uiTemp | AC97RXCR_REN), AC97RXCR1 );
++ }
++
++
++ //DDEBUG("ep93xx_audio_enable - EXIT\n");
++}
++
++static void ep93xx_audio_disable( int input_or_output_stream )
++{
++ unsigned int uiTemp;
++
++ DPRINTK("ep93xx_audio_disable\n");
++
++ /*
++ * Disable the rx or tx channel depending on the value of
++ * input_or_output_stream
++ */
++ if( input_or_output_stream )
++ {
++ uiTemp = inl(AC97TXCR1);
++ outl( (uiTemp & ~AC97TXCR_TEN), AC97TXCR1 );
++ }
++ else
++ {
++ uiTemp = inl(AC97RXCR1);
++ outl( (uiTemp & ~AC97RXCR_REN), AC97RXCR1 );
++ }
++
++ //DDEBUG("ep93xx_audio_disable - EXIT\n");
++}
++
++
++
++/*=======================================================================================*/
++/*
++ * ep93xx_setup_src
++ *
++ * Once the ac-link is up and all is good, we want to set the codec to a
++ * usable mode.
++ */
++static void ep93xx_setup_src(void)
++{
++ int iTemp;
++
++ /*
++ * Set the VRA bit to enable the SRC.
++ */
++ iTemp = peek( AC97_2A_EXT_AUDIO_POWER );
++ poke( AC97_2A_EXT_AUDIO_POWER, (iTemp | 0x1) );
++
++ /*
++ * Set the DSRC/ASRC bits to enable the variable rate SRC.
++ */
++ iTemp = peek( AC97_60_MISC_CRYSTAL_CONTROL );
++ poke( AC97_60_MISC_CRYSTAL_CONTROL, (iTemp | 0x0300) );
++}
++
++/*
++ * ep93xx_set_samplerate
++ *
++ * lFrequency - Sample Rate in Hz
++ * bCapture - 0 to set Tx sample rate; 1 to set Rx sample rate
++ */
++static void ep93xx_set_samplerate( long lSampleRate, int bCapture )
++{
++ unsigned short usDivider, usPhase;
++
++ DPRINTK( "ep93xx_set_samplerate - Fs = %d\n", (int)lSampleRate );
++
++ if( (lSampleRate < 7200) || (lSampleRate > 48000) )
++ {
++ printk( "ep93xx_set_samplerate - invalid Fs = %d\n",
++ (int)lSampleRate );
++ return;
++ }
++
++ /*
++ * Calculate divider and phase increment.
++ *
++ * divider = round( 0x1770000 / lSampleRate )
++ * Note that usually rounding is done by adding 0.5 to a floating
++ * value and then truncating. To do this without using floating
++ * point, I multiply the fraction by two, do the division, then add one,
++ * then divide the whole by 2 and then truncate.
++ * Same effect, no floating point math.
++ *
++ * Ph incr = trunc( (0x1000000 / usDivider) + 1 )
++ */
++
++ usDivider = (unsigned short)( ((2 * 0x1770000 / lSampleRate) + 1) / 2 );
++
++ usPhase = (0x1000000 / usDivider) + 1;
++
++ /*
++ * Write them in the registers. Spec says divider must be
++ * written after phase incr.
++ */
++ if(!bCapture)
++ {
++ poke( AC97_2C_PCM_FRONT_DAC_RATE, usDivider);
++ poke( AC97_64_DAC_SRC_PHASE_INCR, usPhase);
++ }
++ else
++ {
++
++ poke( AC97_32_PCM_LR_ADC_RATE, usDivider);
++ poke( AC97_66_ADC_SRC_PHASE_INCR, usPhase);
++ }
++
++ DPRINTK( "ep93xx_set_samplerate - phase = %d, divider = %d\n",
++ (unsigned int)usPhase, (unsigned int)usDivider );
++
++ /*
++ * We sorta should report the actual samplerate back to the calling
++ * application. But some applications freak out if they don't get
++ * exactly what they asked for. So we fudge and tell them what
++ * they want to hear.
++ */
++ //audio_samplerate = lSampleRate;
++
++ DPRINTK( "ep93xx_set_samplerate - EXIT\n" );
++}
++
++/*
++ * ep93xx_set_hw_format
++ *
++ * Sets up whether the controller is expecting 20 bit data in 32 bit words
++ * or 16 bit data compacted to have a stereo sample in each 32 bit word.
++ */
++static void ep93xx_set_hw_format( long format,long channel )
++{
++ int bCompactMode;
++
++ switch( format )
++ {
++ /*
++ * Here's all the <=16 bit formats. We can squeeze both L and R
++ * into one 32 bit sample so use compact mode.
++ */
++ case SNDRV_PCM_FORMAT_U8:
++ case SNDRV_PCM_FORMAT_S8:
++ case SNDRV_PCM_FORMAT_S16_LE:
++ case SNDRV_PCM_FORMAT_U16_LE:
++ bCompactMode = 1;
++ break;
++
++ /*
++ * Add any other >16 bit formats here...
++ */
++ case SNDRV_PCM_FORMAT_S32_LE:
++ default:
++ bCompactMode = 0;
++ break;
++ }
++
++ if( bCompactMode )
++ {
++ DPRINTK("ep93xx_set_hw_format - Setting serial mode to 16 bit compact.\n");
++
++ /*
++ * Turn on Compact Mode so we can fit each stereo sample into
++ * a 32 bit word. Twice as efficent for DMA and FIFOs.
++ */
++ if(channel==2){
++ outl( 0x00008018, AC97RXCR1 );
++ outl( 0x00008018, AC97TXCR1 );
++ }
++ else {
++ outl( 0x00008018, AC97RXCR1 );
++ outl( 0x00008018, AC97TXCR1 );
++ }
++
++
++ audio_state.DAC_bit_width = 16;
++ audio_state.bCompactMode = 1;
++ }
++ else
++ {
++ DPRINTK("ep93xx_set_hw_format - Setting serial mode to 20 bit non-CM.\n");
++
++ /*
++ * Turn off Compact Mode so we can do > 16 bits per channel
++ */
++ if(channel==2){
++ outl( 0x00004018, AC97RXCR1 );
++ outl( 0x00004018, AC97TXCR1 );
++ }
++ else{
++ outl( 0x00004018, AC97RXCR1 );
++ outl( 0x00004018, AC97TXCR1 );
++ }
++
++ audio_state.DAC_bit_width = 20;
++ audio_state.bCompactMode = 0;
++ }
++
++}
++
++/*
++ * ep93xx_stop_loop
++ *
++ * Once the ac-link is up and all is good, we want to set the codec to a
++ * usable mode.
++ */
++static void ep93xx_stop_loop(void)
++{
++ int iTemp;
++
++ /*
++ * Set the AC97_0E_MIC_VOL MUTE bit to enable the LOOP.
++ */
++ iTemp = peek( AC97_0E_MIC_VOL );
++ poke( AC97_0E_MIC_VOL, (iTemp | 0x8000) );
++
++ /*
++ * Set the AC97_10_LINE_IN_VOL MUTE bit to enable the LOOP.
++ */
++ iTemp = peek( AC97_10_LINE_IN_VOL );
++ poke( AC97_10_LINE_IN_VOL, (iTemp | 0x8000) );
++}
++
++/*
++ * ep93xx_init_ac97_controller
++ *
++ * This routine sets up the Ac'97 Controller.
++ */
++static void ep93xx_init_ac97_controller(void)
++{
++ unsigned int uiDEVCFG, uiTemp;
++
++ DPRINTK("ep93xx_init_ac97_controller - enter\n");
++
++ /*
++ * Configure the multiplexed Ac'97 pins to be Ac97 not I2s.
++ * Configure the EGPIO4 and EGPIO6 to be GPIOS, not to be
++ * SDOUT's for the second and third I2S controller channels.
++ */
++ uiDEVCFG = inl(EP93XX_SYSCON_DEVICE_CONFIG);
++
++ uiDEVCFG &= ~(EP93XX_SYSCON_DEVCFG_CONFIG_I2SONAC97 |
++ EP93XX_SYSCON_DEVCFG_A1onG |
++ EP93XX_SYSCON_DEVCFG_A2onG);
++
++ SysconSetLocked(EP93XX_SYSCON_DEVICE_CONFIG, uiDEVCFG);
++
++ /*
++ * Disable the AC97 controller internal loopback.
++ * Disable Override codec ready.
++ */
++ outl( 0, AC97GCR );
++
++ /*
++ * Enable the AC97 Link.
++ */
++ uiTemp = inl(AC97GCR);
++ outl( (uiTemp | AC97GSR_IFE), AC97GCR );
++
++ /*
++ * Set the TIMEDRESET bit. Will cause a > 1uSec reset of the ac-link.
++ * This bit is self resetting.
++ */
++ outl( AC97RESET_TIMEDRESET, AC97RESET );
++
++ /*
++ * Delay briefly, but let's not hog the processor.
++ */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout( 5 ); /* 50 mSec */
++
++ /*
++ * Read the AC97 status register to see if we've seen a CODECREADY
++ * signal from the AC97 codec.
++ */
++ if( !(inl(AC97RGIS) & AC97RGIS_CODECREADY))
++ {
++ printk( "ep93xx-ac97 - FAIL: CODECREADY still low!\n");
++ return;
++ }
++
++ /*
++ * Delay for a second, not hogging the processor
++ */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout( HZ ); /* 1 Sec */
++
++ /*
++ * Now the Ac-link is up. We can read and write codec registers.
++ */
++ ac_link_enabled = 1;
++
++ /*
++ * Set up the rx and tx channels
++ * Set the CM bit, data size=16 bits, enable tx slots 3 & 4.
++ */
++ ep93xx_set_hw_format( EP93XX_DEFAULT_FORMAT,EP93XX_DEFAULT_NUM_CHANNELS );
++
++ DPRINTK( "ep93xx-ac97 -- AC97RXCR1: %08x\n", inl(AC97RXCR1) );
++ DPRINTK( "ep93xx-ac97 -- AC97TXCR1: %08x\n", inl(AC97TXCR1) );
++
++ DPRINTK("ep93xx_init_ac97_controller - EXIT - success\n");
++
++}
++
++#ifdef alsa_ac97_debug
++static void ep93xx_dump_ac97_regs(void)
++{
++ int i;
++ unsigned int reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7;
++
++ DPRINTK( "---------------------------------------------\n");
++ DPRINTK( " : 0 2 4 6 8 A C E\n" );
++
++ for( i=0 ; i < 0x80 ; i+=0x10 )
++ {
++ reg0 = 0xffff & (unsigned int)peek( i );
++ reg1 = 0xffff & (unsigned int)peek( i + 0x2 );
++ reg2 = 0xffff & (unsigned int)peek( i + 0x4 );
++ reg3 = 0xffff & (unsigned int)peek( i + 0x6 );
++ reg4 = 0xffff & (unsigned int)peek( i + 0x8 );
++ reg5 = 0xffff & (unsigned int)peek( i + 0xa );
++ reg6 = 0xffff & (unsigned int)peek( i + 0xc );
++ reg7 = 0xffff & (unsigned int)peek( i + 0xe );
++
++ DPRINTK( " %02x : %04x %04x %04x %04x %04x %04x %04x %04x\n",
++ i, reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7);
++ }
++
++ DPRINTK( "---------------------------------------------\n");
++}
++#endif
++
++
++#define supported_mixer(FOO) \
++ ( (FOO >= 0) && \
++ (FOO < SOUND_MIXER_NRDEVICES) && \
++ codec_supported_mixers & (1<<FOO) )
++
++/*
++ * Available record sources.
++ * LINE1 refers to AUX in.
++ * IGAIN refers to input gain which means stereo mix.
++ */
++#define AC97_RECORD_MASK \
++ (SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN | SOUND_MASK_VIDEO |\
++ SOUND_MASK_LINE1 | SOUND_MASK_LINE | SOUND_MASK_PHONEIN)
++
++#define AC97_STEREO_MASK \
++ (SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_CD | \
++ SOUND_MASK_ALTPCM | SOUND_MASK_IGAIN | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO)
++
++#define AC97_SUPPORTED_MASK \
++ (AC97_STEREO_MASK | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
++ SOUND_MASK_SPEAKER | SOUND_MASK_MIC | \
++ SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT)
++
++
++
++
++/* this table has default mixer values for all OSS mixers. */
++typedef struct {
++ int mixer;
++ unsigned int value;
++} mixer_defaults_t;
++
++/*
++ * Default mixer settings that are set up during boot.
++ *
++ * These values are 16 bit numbers in which the upper byte is right volume
++ * and the lower byte is left volume or mono volume for mono controls.
++ *
++ * OSS Range for each of left and right volumes is 0 to 100 (0x00 to 0x64).
++ *
++ */
++static mixer_defaults_t mixer_defaults[SOUND_MIXER_NRDEVICES] =
++{
++ /* Outputs */
++ {SOUND_MIXER_VOLUME, 0x6464}, /* 0 dB */ /* -46.5dB to 0 dB */
++ {SOUND_MIXER_ALTPCM, 0x6464}, /* 0 dB */ /* -46.5dB to 0 dB */
++ {SOUND_MIXER_PHONEOUT, 0x6464}, /* 0 dB */ /* -46.5dB to 0 dB */
++
++ /* PCM playback gain */
++ {SOUND_MIXER_PCM, 0x4b4b}, /* 0 dB */ /* -34.5dB to +12dB */
++
++ /* Record gain */
++ {SOUND_MIXER_IGAIN, 0x0000}, /* 0 dB */ /* -34.5dB to +12dB */
++
++ /* Inputs */
++ {SOUND_MIXER_MIC, 0x0000}, /* mute */ /* -34.5dB to +12dB */
++ {SOUND_MIXER_LINE, 0x4b4b}, /* 0 dB */ /* -34.5dB to +12dB */
++
++ /* Inputs that are not connected. */
++ {SOUND_MIXER_SPEAKER, 0x0000}, /* mute */ /* -45dB to 0dB */
++ {SOUND_MIXER_PHONEIN, 0x0000}, /* mute */ /* -34.5dB to +12dB */
++ {SOUND_MIXER_CD, 0x0000}, /* mute */ /* -34.5dB to +12dB */
++ {SOUND_MIXER_VIDEO, 0x0000}, /* mute */ /* -34.5dB to +12dB */
++ {SOUND_MIXER_LINE1, 0x0000}, /* mute */ /* -34.5dB to +12dB */
++
++ {-1,0} /* last entry */
++};
++
++/* table to scale scale from OSS mixer value to AC97 mixer register value */
++typedef struct {
++ unsigned int offset;
++ int scale;
++} ac97_mixer_hw_t;
++
++static ac97_mixer_hw_t ac97_hw[SOUND_MIXER_NRDEVICES] =
++{
++ [SOUND_MIXER_VOLUME] = {AC97_02_MASTER_VOL, 64},
++ [SOUND_MIXER_BASS] = {0, 0},
++ [SOUND_MIXER_TREBLE] = {0, 0},
++ [SOUND_MIXER_SYNTH] = {0, 0},
++ [SOUND_MIXER_PCM] = {AC97_18_PCM_OUT_VOL, 32},
++ [SOUND_MIXER_SPEAKER] = {AC97_0A_PC_BEEP_VOL, 32},
++ [SOUND_MIXER_LINE] = {AC97_10_LINE_IN_VOL, 32},
++ [SOUND_MIXER_MIC] = {AC97_0E_MIC_VOL, 32},
++ [SOUND_MIXER_CD] = {AC97_12_CD_VOL, 32},
++ [SOUND_MIXER_IMIX] = {0, 0},
++ [SOUND_MIXER_ALTPCM] = {AC97_04_HEADPHONE_VOL, 64},
++ [SOUND_MIXER_RECLEV] = {0, 0},
++ [SOUND_MIXER_IGAIN] = {AC97_1C_RECORD_GAIN, 16},
++ [SOUND_MIXER_OGAIN] = {0, 0},
++ [SOUND_MIXER_LINE1] = {AC97_16_AUX_VOL, 32},
++ [SOUND_MIXER_LINE2] = {0, 0},
++ [SOUND_MIXER_LINE3] = {0, 0},
++ [SOUND_MIXER_DIGITAL1] = {0, 0},
++ [SOUND_MIXER_DIGITAL2] = {0, 0},
++ [SOUND_MIXER_DIGITAL3] = {0, 0},
++ [SOUND_MIXER_PHONEIN] = {AC97_0C_PHONE_VOL, 32},
++ [SOUND_MIXER_PHONEOUT] = {AC97_06_MONO_VOL, 64},
++ [SOUND_MIXER_VIDEO] = {AC97_14_VIDEO_VOL, 32},
++ [SOUND_MIXER_RADIO] = {0, 0},
++ [SOUND_MIXER_MONITOR] = {0, 0},
++};
++
++
++/* the following tables allow us to go from OSS <-> ac97 quickly. */
++enum ac97_recsettings
++{
++ AC97_REC_MIC=0,
++ AC97_REC_CD,
++ AC97_REC_VIDEO,
++ AC97_REC_AUX,
++ AC97_REC_LINE,
++ AC97_REC_STEREO, /* combination of all enabled outputs.. */
++ AC97_REC_MONO, /*.. or the mono equivalent */
++ AC97_REC_PHONE
++};
++
++static const unsigned int ac97_rm2oss[] =
++{
++ [AC97_REC_MIC] = SOUND_MIXER_MIC,
++ [AC97_REC_CD] = SOUND_MIXER_CD,
++ [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
++ [AC97_REC_AUX] = SOUND_MIXER_LINE1,
++ [AC97_REC_LINE] = SOUND_MIXER_LINE,
++ [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
++ [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
++};
++
++/* indexed by bit position */
++static const unsigned int ac97_oss_rm[] =
++{
++ [SOUND_MIXER_MIC] = AC97_REC_MIC,
++ [SOUND_MIXER_CD] = AC97_REC_CD,
++ [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
++ [SOUND_MIXER_LINE1] = AC97_REC_AUX,
++ [SOUND_MIXER_LINE] = AC97_REC_LINE,
++ [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
++ [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
++};
++
++
++/*
++ * ep93xx_write_mixer
++ *
++ */
++static void ep93xx_write_mixer
++(
++ int oss_channel,
++ unsigned int left,
++ unsigned int right
++)
++{
++ u16 val = 0;
++ ac97_mixer_hw_t * mh = &ac97_hw[oss_channel];
++
++ DPRINTK("ac97_codec: wrote OSS %2d (ac97 0x%02x), "
++ "l:%2d, r:%2d:",
++ oss_channel, mh->offset, left, right);
++
++ if( !mh->scale )
++ {
++ printk( "ep93xx-ac97.c: ep93xx_write_mixer - not a valid OSS channel\n");
++ return;
++ }
++
++ if( AC97_STEREO_MASK & (1 << oss_channel) )
++ {
++ /* stereo mixers */
++ if (left == 0 && right == 0)
++ {
++ val = 0x8000;
++ }
++ else
++ {
++ if (oss_channel == SOUND_MIXER_IGAIN)
++ {
++ right = (right * mh->scale) / 100;
++ left = (left * mh->scale) / 100;
++ if (right >= mh->scale)
++ right = mh->scale-1;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ }
++ else
++ {
++ right = ((100 - right) * mh->scale) / 100;
++ left = ((100 - left) * mh->scale) / 100;
++ if (right >= mh->scale)
++ right = mh->scale-1;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ }
++ val = (left << 8) | right;
++ }
++ }
++ else if(left == 0)
++ {
++ val = 0x8000;
++ }
++ else if( (oss_channel == SOUND_MIXER_SPEAKER) ||
++ (oss_channel == SOUND_MIXER_PHONEIN) ||
++ (oss_channel == SOUND_MIXER_PHONEOUT) )
++ {
++ left = ((100 - left) * mh->scale) / 100;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ val = left;
++ }
++ else if (oss_channel == SOUND_MIXER_MIC)
++ {
++ val = peek( mh->offset) & ~0x801f;
++ left = ((100 - left) * mh->scale) / 100;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ val |= left;
++ }
++ /*
++ * For bass and treble, the low bit is optional. Masking it
++ * lets us avoid the 0xf 'bypass'.
++ * Do a read, modify, write as we have two contols in one reg.
++ */
++ else if (oss_channel == SOUND_MIXER_BASS)
++ {
++ val = peek( mh->offset) & ~0x0f00;
++ left = ((100 - left) * mh->scale) / 100;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ val |= (left << 8) & 0x0e00;
++ }
++ else if (oss_channel == SOUND_MIXER_TREBLE)
++ {
++ val = peek( mh->offset) & ~0x000f;
++ left = ((100 - left) * mh->scale) / 100;
++ if (left >= mh->scale)
++ left = mh->scale-1;
++ val |= left & 0x000e;
++ }
++
++ DPRINTK(" 0x%04x", val);
++
++ poke( mh->offset, val );
++
++#ifdef alsa_ac97_debug
++ val = peek( mh->offset );
++ DEBUG(" -> 0x%04x\n", val);
++#endif
++
++}
++
++/* a thin wrapper for write_mixer */
++static void ep93xx_set_mixer
++(
++ unsigned int oss_mixer,
++ unsigned int val
++)
++{
++ unsigned int left,right;
++
++ /* cleanse input a little */
++ right = ((val >> 8) & 0xff) ;
++ left = (val & 0xff) ;
++
++ if (right > 100) right = 100;
++ if (left > 100) left = 100;
++
++ /*mixer_state[oss_mixer] = (right << 8) | left;*/
++ ep93xx_write_mixer( oss_mixer, left, right);
++}
++
++static void ep93xx_init_mixer(void)
++{
++ u16 cap;
++ int i;
++
++ /* mixer masks */
++ codec_supported_mixers = AC97_SUPPORTED_MASK;
++
++ cap = peek( AC97_00_RESET );
++ if( !(cap & 0x04) )
++ {
++ codec_supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
++ }
++ if( !(cap & 0x10) )
++ {
++ codec_supported_mixers &= ~SOUND_MASK_ALTPCM;
++ }
++
++ /*
++ * Detect bit resolution of output volume controls by writing to the
++ * 6th bit (not unmuting yet)
++ */
++ poke( AC97_02_MASTER_VOL, 0xa020 );
++ if( peek( AC97_02_MASTER_VOL) != 0xa020 )
++ {
++ ac97_hw[SOUND_MIXER_VOLUME].scale = 32;
++ }
++
++ poke( AC97_04_HEADPHONE_VOL, 0xa020 );
++ if( peek( AC97_04_HEADPHONE_VOL) != 0xa020 )
++ {
++ ac97_hw[AC97_04_HEADPHONE_VOL].scale = 32;
++ }
++
++ poke( AC97_06_MONO_VOL, 0x8020 );
++ if( peek( AC97_06_MONO_VOL) != 0x8020 )
++ {
++ ac97_hw[AC97_06_MONO_VOL].scale = 32;
++ }
++
++ /* initialize mixer channel volumes */
++ for( i = 0;
++ (i < SOUND_MIXER_NRDEVICES) && (mixer_defaults[i].mixer != -1) ;
++ i++ )
++ {
++ if( !supported_mixer( mixer_defaults[i].mixer) )
++ {
++ continue;
++ }
++
++ ep93xx_set_mixer( mixer_defaults[i].mixer, mixer_defaults[i].value);
++ }
++
++}
++
++static int ep93xx_set_recsource( int mask )
++{
++ unsigned int val;
++
++ /* Arg contains a bit for each recording source */
++ if( mask == 0 )
++ {
++ return 0;
++ }
++
++ mask &= AC97_RECORD_MASK;
++
++ if( mask == 0 )
++ {
++ return -EINVAL;
++ }
++
++ /*
++ * May have more than one bit set. So clear out currently selected
++ * record source value first (AC97 supports only 1 input)
++ */
++ val = (1 << ac97_rm2oss[peek( AC97_1A_RECORD_SELECT ) & 0x07]);
++ if (mask != val)
++ mask &= ~val;
++
++ val = ffs(mask);
++ val = ac97_oss_rm[val-1];
++ val |= val << 8; /* set both channels */
++
++ /*
++ *
++ */
++ val = peek( AC97_1A_RECORD_SELECT ) & 0x0707;
++ if ((val&0x0404)!=0)
++ val=0x0404;
++ else if((val&0x0000)!=0)
++ val=0x0101;
++
++
++ DPRINTK("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
++
++ poke( AC97_1A_RECORD_SELECT, val);
++
++ return 0;
++}
++
++/*
++ * ep93xx_init_ac97_codec
++ *
++ * Program up the external Ac97 codec.
++ *
++ */
++static void ep93xx_init_ac97_codec( void )
++{
++ DPRINTK("ep93xx_init_ac97_codec - enter\n");
++
++ ep93xx_setup_src();
++ ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 0 );
++ ep93xx_set_samplerate( AUDIO_SAMPLE_RATE_DEFAULT, 1 );
++ ep93xx_init_mixer();
++
++ DPRINTK("ep93xx_init_ac97_codec - EXIT\n");
++
++}
++
++
++/*
++ * ep93xx_audio_init
++ * Audio interface
++ */
++static void ep93xx_audio_init(void)
++{
++ DPRINTK("ep93xx_audio_init - enter\n");
++ /*
++ * Init the controller, enable the ac-link.
++ * Initialize the codec.
++ */
++ ep93xx_init_ac97_controller();
++ ep93xx_init_ac97_codec();
++ /*stop the audio loop from the input to the output directly*/
++ ep93xx_stop_loop();
++
++#ifdef alsa_ac97_debug
++ ep93xx_dump_ac97_regs();
++#endif
++ DPRINTK("ep93xx_audio_init - EXIT\n");
++}
++
++/*====================================================================================*/
++
++
++static void print_audio_format( long format )
++{
++ switch( format ){
++ case SNDRV_PCM_FORMAT_S8:
++ DPRINTK( "AFMT_S8\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ DPRINTK( "AFMT_U8\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ DPRINTK( "AFMT_S16_LE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_BE:
++ DPRINTK( "AFMT_S16_BE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ DPRINTK( "AFMT_U16_LE\n" );
++ break;
++ case SNDRV_PCM_FORMAT_U16_BE:
++ DPRINTK( "AFMT_U16_BE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ DPRINTK( "AFMT_S24_LE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_BE:
++ DPRINTK( "AFMT_S24_BE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ DPRINTK( "AFMT_U24_LE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U24_BE:
++ DPRINTK( "AFMT_U24_BE\n" );
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ DPRINTK( "AFMT_S24_LE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_S32_BE:
++ DPRINTK( "AFMT_S24_BE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U32_LE:
++ DPRINTK( "AFMT_U24_LE\n" );
++ break;
++
++ case SNDRV_PCM_FORMAT_U32_BE:
++ DPRINTK( "AFMT_U24_BE\n" );
++ break;
++ default:
++ DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" );
++ break;
++ }
++}
++
++static void audio_set_format( audio_stream_t * s, long val )
++{
++ DPRINTK( "ep93xx_i2s_audio_set_format enter. Format requested (%d) %d ",
++ (int)val,SNDRV_PCM_FORMAT_S16_LE);
++ print_audio_format( val );
++
++ switch( val ){
++ case SNDRV_PCM_FORMAT_S8:
++ s->audio_format = SNDRV_PCM_FORMAT_S8;
++ s->audio_stream_bitwidth = 8;
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ s->audio_format = SNDRV_PCM_FORMAT_U8;
++ s->audio_stream_bitwidth = 8;
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ case SNDRV_PCM_FORMAT_S16_BE:
++ s->audio_format = SNDRV_PCM_FORMAT_S16_LE;
++ s->audio_stream_bitwidth = 16;
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ case SNDRV_PCM_FORMAT_U16_BE:
++ s->audio_format = SNDRV_PCM_FORMAT_U16_LE;
++ s->audio_stream_bitwidth = 16;
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ case SNDRV_PCM_FORMAT_S24_BE:
++ s->audio_format = SNDRV_PCM_FORMAT_S24_LE;
++ s->audio_stream_bitwidth = 24;
++ break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ case SNDRV_PCM_FORMAT_U24_BE:
++ s->audio_format = SNDRV_PCM_FORMAT_U24_LE;
++ s->audio_stream_bitwidth = 24;
++ break;
++
++ case SNDRV_PCM_FORMAT_U32_LE:
++ case SNDRV_PCM_FORMAT_U32_BE:
++ case SNDRV_PCM_FORMAT_S32_LE:
++ case SNDRV_PCM_FORMAT_S32_BE:
++ s->audio_format = SNDRV_PCM_FORMAT_S32_LE;
++ s->audio_stream_bitwidth = 32;
++ break;
++ default:
++ DPRINTK( "ep93xx_i2s_Unsupported Audio Format\n" );
++ break;
++ }
++
++ DPRINTK( "ep93xx_i2s_audio_set_format EXIT format set to be (%d) ", (int)s->audio_format );
++ print_audio_format( (long)s->audio_format );
++}
++
++static __inline__ unsigned long copy_to_user_S24_LE
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++
++ int total_to_count = to_count;
++ int *user_ptr = (int *)to; /* 32 bit user buffer */
++ int count;
++
++ count = 8 * stream->dma_num_channels;
++
++ while (to_count > 0){
++
++ __put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
++ __put_user( (int)( *dma_buffer_0++ ), user_ptr++ );
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
++ __put_user( (int)( *dma_buffer_1++ ), user_ptr++ );
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
++ __put_user( (int)( *dma_buffer_2++ ), user_ptr++ );
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U24_LE
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++
++ int total_to_count = to_count;
++ unsigned int * user_ptr = (unsigned int *)to; /* 32 bit user buffer */
++ int count;
++
++ count = 8 * stream->dma_num_channels;
++
++ while (to_count > 0){
++ __put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned int)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned int)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned int)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_S16_LE
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int total_to_count = to_count;
++ short * user_ptr = (short *)to; /* 16 bit user buffer */
++ int count;
++
++ count = 4 * stream->dma_num_channels;
++
++ while (to_count > 0){
++
++ __put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
++
++ if( stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
++ }
++
++ if( stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U16_LE
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ short * user_ptr = (short *)to; /* 16 bit user buffer */
++
++ count = 4 * stream->dma_num_channels;
++
++ while (to_count > 0){
++
++ __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_S8
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ char * user_ptr = (char *)to; /* 8 bit user buffer */
++
++ count = 2 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (to_count > 0){
++
++ __put_user( (char)( *dma_buffer_0 ), user_ptr++ );
++ dma_buffer_0 += 4;
++ __put_user( (char)( *dma_buffer_0 ), user_ptr++ );
++ dma_buffer_0 += 4;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
++ dma_buffer_1 += 4;
++ __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
++ dma_buffer_2 += 4;
++ __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
++ dma_buffer_2 += 4;
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U8
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ char * user_ptr = (char *)to; /* 8 bit user buffer */
++
++ count = 2 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (to_count > 0){
++
++ __put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
++ dma_buffer_0 += 4;
++ __put_user( (char)( *dma_buffer_0 ) ^ 0x80, user_ptr++ );
++ dma_buffer_0 += 4;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
++ dma_buffer_1 += 4;
++ __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
++ dma_buffer_2 += 4;
++ __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
++ dma_buffer_2 += 4;
++ }
++ to_count -= count;
++ }
++ return total_to_count;
++}
++
++
++
++
++static __inline__ unsigned long copy_to_user_S16_LE_CM
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ short *dma_buffer_0 = (short *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int total_to_count = to_count;
++ short * user_ptr = (short *)to; /* 16 bit user buffer */
++ int count;
++
++
++ count = 4 * stream->dma_num_channels;
++
++ while (to_count > 0){
++ if(stream->audio_num_channels == 2){
++ __put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
++ to_count -= count;
++ }
++ else{
++ dma_buffer_0++;
++ __put_user( (short)( *dma_buffer_0++ ), user_ptr++ );
++ to_count -= 2;
++ }
++
++ if( stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_1++ ), user_ptr++ );
++ }
++
++ if( stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
++ __put_user( (short)( *dma_buffer_2++ ), user_ptr++ );
++ }
++ //to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U16_LE_CM
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ unsigned short * user_ptr = (unsigned short *)to; /* 16 bit user buffer */
++
++ count = 4 * stream->dma_num_channels;
++
++ while (to_count > 0){
++
++ if(stream->audio_num_channels == 2){
++ __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++ to_count -= count;
++ }
++ else{
++ dma_buffer_0++;
++ __put_user( ((unsigned short)( *dma_buffer_0++ )) ^ 0x8000, user_ptr++ );
++ to_count -= 2;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_1++ )) ^ 0x8000, user_ptr++ );
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ __put_user( ((unsigned short)( *dma_buffer_2++ )) ^ 0x8000, user_ptr++ );
++ }
++ //to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_S8_CM
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ char * user_ptr = (char *)to; /* 8 bit user buffer */
++
++ count = 2 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (to_count > 0){
++ if(stream->audio_num_channels == 2){
++ __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
++ //dma_buffer_0 += 4;
++ __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
++ //dma_buffer_0 += 4;
++ to_count -= count;
++ }
++ else{
++ dma_buffer_0++ ;
++ __put_user( (char)( *dma_buffer_0++ >> 8), user_ptr++ );
++
++ to_count -= 1;
++ }
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
++ dma_buffer_1 += 4;
++ __put_user( (char)( *dma_buffer_1 ), user_ptr++ );
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
++ dma_buffer_2 += 4;
++ __put_user( (char)( *dma_buffer_2 ), user_ptr++ );
++ dma_buffer_2 += 4;
++ }
++ //to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U8_CM
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ unsigned short *dma_buffer_0 = (unsigned short *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ int total_to_count = to_count;
++ char * user_ptr = (char *)to; /* 8 bit user buffer */
++
++ count = 2 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (to_count > 0){
++ if(stream->audio_num_channels == 2){
++ __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ );
++ //dma_buffer_0 += 4;
++ __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ );
++ //dma_buffer_0 += 4;
++ to_count -= count;
++ }
++ else{
++ dma_buffer_0++;
++ __put_user( (char)( *dma_buffer_0++ >>8) ^ 0x80, user_ptr++ );
++ //dma_buffer_0 += 4;
++ to_count--;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
++ dma_buffer_1 += 4;
++ __put_user( (char)( *dma_buffer_1 ) ^ 0x80, user_ptr++ );
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
++ dma_buffer_2 += 4;
++ __put_user( (char)( *dma_buffer_2 ) ^ 0x80, user_ptr++ );
++ dma_buffer_2 += 4;
++ }
++ //to_count -= count;
++ }
++ return total_to_count;
++}
++
++static __inline__ unsigned long copy_to_user_U32
++(
++ audio_stream_t *stream,
++ const char *to,
++ unsigned long to_count
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++
++ if(__copy_to_user( (char *)to, dma_buffer_0, to_count))
++ {
++ return -EFAULT;
++ }
++ return to_count;
++}
++
++static __inline__ int copy_to_user_with_conversion
++(
++ audio_stream_t *stream,
++ const char *to,
++ int toCount,
++ int bCompactMode
++)
++{
++ int ret = 0;
++
++ if( toCount == 0 ){
++ DPRINTK("ep93xx_i2s_copy_to_user_with_conversion - nothing to copy!\n");
++ }
++
++ if( bCompactMode == 1 ){
++
++ switch( stream->audio_format ){
++
++ case SNDRV_PCM_FORMAT_S8:
++ ret = copy_to_user_S8_CM( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ ret = copy_to_user_U8_CM( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ ret = copy_to_user_S16_LE_CM( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ ret = copy_to_user_U16_LE_CM( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ //ret = copy_to_user_S24_LE( stream, to, toCount );
++ //break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ //ret = copy_to_user_U24_LE( stream, to, toCount );
++ //break;
++
++ case SNDRV_PCM_FORMAT_S32_LE:
++ default:
++ DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format );
++ break;
++ }
++
++ }
++ else{
++
++ switch( stream->audio_format ){
++
++ case SNDRV_PCM_FORMAT_S8:
++ ret = copy_to_user_S8( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ ret = copy_to_user_U8( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ ret = copy_to_user_S16_LE( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ ret = copy_to_user_U16_LE( stream, to, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ //ret = copy_to_user_S24_LE( stream, to, toCount );
++ //break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ //ret = copy_to_user_U24_LE( stream, to, toCount );
++ //break;
++ DPRINTK( "ep93xx_i2s copy to user unsupported audio format %x\n",stream->audio_format );
++ break;
++
++ case SNDRV_PCM_FORMAT_S32_LE:
++
++ //__copy_to_user( (char *)to, from, toCount);
++ ret = copy_to_user_U32( stream, to, toCount );
++ break;
++ default:
++ DPRINTK( "ep93xx_i2s copy to user unsupported audio format\n" );
++ break;
++ }
++
++ }
++ return ret;
++}
++
++static __inline__ int copy_from_user_S24_LE
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++
++ unsigned int * user_buffer = (unsigned int *)from;
++ unsigned int data;
++
++ int toCount0 = toCount;
++ count = 8 * stream->dma_num_channels;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = (unsigned int)data;
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = (unsigned int)data;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = (unsigned int)data;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = (unsigned int)data;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = (unsigned int)data;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = (unsigned int)data;
++ }
++ toCount -= count;
++ }
++ return toCount0 / 2;
++}
++
++static __inline__ int copy_from_user_U24_LE
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++ unsigned int * user_buffer = (unsigned int *)from;
++ unsigned int data;
++
++ int toCount0 = toCount;
++ count = 8 * stream->dma_num_channels;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ }
++ toCount -= count;
++ }
++ return toCount0 / 2;
++}
++
++static __inline__ int copy_from_user_S16_LE
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ unsigned short *user_buffer = (unsigned short *)from;
++ unsigned short data;
++
++ int toCount0 = toCount;
++ int count;
++ count = 8 * stream->dma_num_channels;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = data;
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0++ = data;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = data;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = data;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = data;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = data;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 4;
++ }
++ return toCount0 / 2;
++}
++
++static __inline__ int copy_from_user_U16_LE
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++ unsigned short * user_buffer = (unsigned short *)from;
++ unsigned short data;
++
++ int toCount0 = toCount;
++ count = 8 * stream->dma_num_channels;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 4;
++ }
++ return toCount0 / 2;
++}
++
++static __inline__ int copy_from_user_S8
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ unsigned char * user_buffer = (unsigned char *)from;
++ unsigned char data;
++
++ int toCount0 = toCount;
++ count = 8 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (toCount > 0){
++ __get_user(data, user_buffer++);
++ *dma_buffer_0 = data;
++ dma_buffer_0 += 4;
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0 = data;
++ dma_buffer_0 += 4;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = data;
++ dma_buffer_1 += 4;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = data;
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = data;
++ dma_buffer_2 += 4;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = data;
++ dma_buffer_2 += 4;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 8;
++ }
++ return toCount0 / 4;
++}
++
++static __inline__ int copy_from_user_U8
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ unsigned char *user_buffer = (unsigned char *)from;
++ unsigned char data;
++
++ int toCount0 = toCount;
++ count = 8 * stream->dma_num_channels;
++
++ dma_buffer_0 ++;
++ dma_buffer_1 ++;
++ dma_buffer_2 ++;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0 = ((unsigned char)data ^ 0x80);
++ dma_buffer_0 += 4;
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0 = ((unsigned char)data ^ 0x80);
++ dma_buffer_0 += 4;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = ((unsigned char)data ^ 0x80);
++ dma_buffer_1 += 4;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = ((unsigned char)data ^ 0x80);
++ dma_buffer_1 += 4;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = ((unsigned char)data ^ 0x80);
++ dma_buffer_2 += 4;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = ((unsigned char)data ^ 0x80);
++ dma_buffer_2 += 4;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 8;
++ }
++ return toCount0 / 4;
++}
++
++static __inline__ int copy_from_user_S16_LE_CM
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ unsigned int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ unsigned int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ unsigned int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ unsigned short *user_buffer = (unsigned short *)from;
++ short data;
++ unsigned int val;
++ int toCount0 = toCount;
++ int count;
++ count = 4 * stream->dma_num_channels;
++
++ //printk("count=%x tocount\n",count,toCount);
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ //*dma_buffer_0++ = data;
++ val = (unsigned int)data & 0x0000ffff;
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0++ = ((unsigned int)data << 16) | val;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ //*dma_buffer_1++ = data;
++ val = (unsigned int)data & 0x0000ffff;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1++ = ((unsigned int)data << 16) | val;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ //*dma_buffer_2++ = data;
++ val = (unsigned int)data & 0x0000ffff;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2++ = ((unsigned int)data << 16) | val;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 /2 ;
++ }
++
++ return toCount0 ;
++}
++
++static __inline__ int copy_from_user_U16_LE_CM
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ int *dma_buffer_0 = (int *)stream->hwbuf[0];
++ int *dma_buffer_1 = (int *)stream->hwbuf[1];
++ int *dma_buffer_2 = (int *)stream->hwbuf[2];
++ int count;
++ unsigned short * user_buffer = (unsigned short *)from;
++ unsigned short data;
++ unsigned int val;
++ int toCount0 = toCount;
++ count = 4 * stream->dma_num_channels;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ //*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++ val = (unsigned int)data & 0x0000ffff;
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ //*dma_buffer_0++ = ((unsigned int)data ^ 0x8000);
++ *dma_buffer_0++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ val = (unsigned int)data & 0x0000ffff;
++ __get_user(data, user_buffer++);
++ //*dma_buffer_1++ = ((unsigned int)data ^ 0x8000);
++ *dma_buffer_1++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ val = (unsigned int)data & 0x0000ffff;
++ __get_user(data, user_buffer++);
++ //*dma_buffer_2++ = ((unsigned int)data ^ 0x8000);
++ *dma_buffer_2++ = (((unsigned int)data << 16) | val) ^ 0x80008000;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0/2;
++ }
++ return toCount0 ;
++}
++
++static __inline__ int copy_from_user_S8_CM
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++ char *dma_buffer_1 = (char *)stream->hwbuf[1];
++ char *dma_buffer_2 = (char *)stream->hwbuf[2];
++ int count;
++ unsigned char * user_buffer = (unsigned char *)from;
++ unsigned char data;
++ int toCount0 = toCount;
++ count = 4 * stream->dma_num_channels;
++
++ dma_buffer_0++;
++ dma_buffer_1++;
++ dma_buffer_2++;
++
++ while (toCount > 0){
++ __get_user(data, user_buffer++);
++ *dma_buffer_0 = data;
++ *(dma_buffer_0 +1 ) = 0;
++ dma_buffer_0 += 2;
++
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0 = data;
++ *(dma_buffer_0 +1 ) = 0;
++ dma_buffer_0 += 2;
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = data;
++ dma_buffer_1 += 2;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = data;
++ dma_buffer_1 += 2;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = data;
++ dma_buffer_2 += 2;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = data;
++ dma_buffer_2 += 2;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 4;
++ }
++
++ return toCount0 / 2;
++}
++
++static __inline__ int copy_from_user_U8_CM
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ unsigned char *dma_buffer_0 = (unsigned char *)stream->hwbuf[0];
++ unsigned char *dma_buffer_1 = (unsigned char *)stream->hwbuf[1];
++ unsigned char *dma_buffer_2 = (unsigned char *)stream->hwbuf[2];
++ int count;
++ unsigned char *user_buffer = (unsigned char *)from;
++ unsigned char data;
++
++ int toCount0 = toCount;
++ count = 4 * stream->dma_num_channels;
++
++ dma_buffer_0 ++;
++ dma_buffer_1 ++;
++ dma_buffer_2 ++;
++
++ while (toCount > 0){
++
++ __get_user(data, user_buffer++);
++ *dma_buffer_0 = ((unsigned char)data ^ 0x80);
++ *(dma_buffer_0 +1 ) = 0;
++ dma_buffer_0 += 2;
++
++ if(stream->audio_num_channels == 2){
++ __get_user(data, user_buffer++);
++ }
++ *dma_buffer_0 = ((unsigned char)data ^ 0x80);
++ *(dma_buffer_0 +1 ) = 0;
++ dma_buffer_0 += 2;
++
++
++ if(stream->audio_channels_flag & CHANNEL_REAR ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = ((unsigned char)data ^ 0x80);
++ dma_buffer_1 += 2;
++ __get_user(data, user_buffer++);
++ *dma_buffer_1 = ((unsigned char)data ^ 0x80);
++ dma_buffer_1 += 2;
++ }
++
++ if(stream->audio_channels_flag & CHANNEL_CENTER_LFE ){
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = ((unsigned char)data ^ 0x80);
++ dma_buffer_2 += 2;
++ __get_user(data, user_buffer++);
++ *dma_buffer_2 = ((unsigned char)data ^ 0x80);
++ dma_buffer_2 += 2;
++ }
++ toCount -= count;
++ }
++
++ if(stream->audio_num_channels == 1){
++ return toCount0 / 4;
++ }
++
++ return toCount0 / 2;
++}
++
++static int copy_from_user_U32
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount
++)
++{
++ char *dma_buffer_0 = (char *)stream->hwbuf[0];
++
++ if (copy_from_user( (char *)dma_buffer_0, from, toCount))
++ {
++ return -EFAULT;
++ }
++
++ return toCount;
++
++}
++
++/*
++ * Returns negative for error
++ * Returns # of bytes transferred out of the from buffer
++ * for success.
++ */
++static __inline__ int copy_from_user_with_conversion
++(
++ audio_stream_t *stream,
++ const char *from,
++ int toCount,
++ int bCompactMode
++)
++{
++ int ret = 0;
++// DPRINTK("copy_from_user_with_conversion\n");
++ if( toCount == 0 ){
++ DPRINTK("ep93xx_i2s_copy_from_user_with_conversion - nothing to copy!\n");
++ }
++
++ if( bCompactMode == 1){
++
++ switch( stream->audio_format ){
++
++ case SNDRV_PCM_FORMAT_S8:
++ DPRINTK("SNDRV_PCM_FORMAT_S8 CM\n");
++ ret = copy_from_user_S8_CM( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ DPRINTK("SNDRV_PCM_FORMAT_U8 CM\n");
++ ret = copy_from_user_U8_CM( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S16_LE CM\n");
++ ret = copy_from_user_S16_LE_CM( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_U16_LE CM\n");
++ ret = copy_from_user_U16_LE_CM( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S24_LE CM\n");
++ //ret = copy_from_user_S24_LE( stream, from, toCount );
++ //break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_U24_LE CM\n");
++ //ret = copy_from_user_U24_LE( stream, from, toCount );
++ //break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S32_LE CM\n");
++ //break;
++ default:
++ DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
++ break;
++ }
++ }
++ else{
++ switch( stream->audio_format ){
++
++ case SNDRV_PCM_FORMAT_S8:
++ DPRINTK("SNDRV_PCM_FORMAT_S8\n");
++ ret = copy_from_user_S8( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U8:
++ DPRINTK("SNDRV_PCM_FORMAT_U8\n");
++ ret = copy_from_user_U8( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S16_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S16_LE\n");
++ ret = copy_from_user_S16_LE( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_U16_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_U16_LE\n");
++ ret = copy_from_user_U16_LE( stream, from, toCount );
++ break;
++
++ case SNDRV_PCM_FORMAT_S24_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S24_LE\n");
++ //ret = copy_from_user_S24_LE( stream, from, toCount );
++ //break;
++
++ case SNDRV_PCM_FORMAT_U24_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_U24_LE\n");
++ //ret = copy_from_user_U24_LE( stream, from, toCount );
++ //break;
++ DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ DPRINTK("SNDRV_PCM_FORMAT_S32_LE\n");
++ ret = copy_from_user_U32( stream, from, toCount );
++ break;
++ default:
++ DPRINTK( "ep93xx_i2s copy from user unsupported audio format\n" );
++ break;
++ }
++ }
++
++ return ret;
++}
++
++
++
++/*
++ * For audio playback, we convert samples of arbitrary format to be 32 bit
++ * for our hardware. We're scaling a user buffer to a dma buffer. So when
++ * report byte counts, we scale them acording to the ratio of DMA sample
++ * size to user buffer sample size. When we report # of DMA fragments,
++ * we don't scale that. So:
++ *
++ * Also adjust the size and number of dma fragments if sample size changed.
++ *
++ * Input format Input sample Output sample size ratio (out:in)
++ * bits channels size (bytes) CM non-CM CM non-CM
++ * 8 stereo 2 4 8 2:1 4:1
++ * 16 stereo 4 4 8 1:1 2:1
++ * 24 stereo 6 4 8 X 8:6 not a real case
++ *
++ */
++static void snd_ep93xx_dma2usr_ratio( audio_stream_t * stream,int bCompactMode )
++{
++ unsigned int dma_sample_size, user_sample_size;
++
++ if(bCompactMode == 1){
++ dma_sample_size = 4; /* each stereo sample is 2 * 32 bits */
++ }
++ else{
++ dma_sample_size = 8;
++ }
++
++ // If stereo 16 bit, user sample is 4 bytes.
++ // If stereo 8 bit, user sample is 2 bytes.
++ if(stream->audio_num_channels == 1){
++ user_sample_size = stream->audio_stream_bitwidth / 8;
++ }
++ else{
++ user_sample_size = stream->audio_stream_bitwidth / 4;
++ }
++
++ stream->dma2usr_ratio = dma_sample_size / user_sample_size;
++}
++
++/*---------------------------------------------------------------------------------------------*/
++
++static int snd_ep93xx_dma_free(struct snd_pcm_substream *substream ){
++
++
++ audio_state_t *state = substream->private_data;
++ audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_stream:state->input_stream;
++ int i;
++
++
++ DPRINTK("snd_ep93xx_dma_free - enter\n");
++ for( i = 0 ; i < stream->dma_num_channels ;i++ ){
++ ep93xx_dma_free( stream->dmahandles[i] );
++ }
++ DPRINTK("snd_ep93xx_dma_free - exit\n");
++ return 0;
++}
++
++static int snd_ep93xx_dma_config(struct snd_pcm_substream *substream ){
++
++ audio_state_t *state = substream->private_data;
++ audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_stream:state->input_stream;
++ int i,err = 0;
++
++ DPRINTK("snd_ep93xx_dma_config - enter\n");
++
++ for( i = 0 ; i < stream->dma_num_channels ;i++ ){
++
++ err = ep93xx_dma_request(&stream->dmahandles[i],
++ stream->devicename,
++ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_dma[i]:state->input_dma[i] );
++ if (err){
++ printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
++ return err;
++ }
++ err = ep93xx_dma_config( stream->dmahandles[i],
++ IGNORE_CHANNEL_ERROR,
++ 0,
++ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ snd_ep93xx_dma_tx_callback:snd_ep93xx_dma_rx_callback,
++ (unsigned int)substream );
++ if (err){
++ printk("snd_ep93xx_dma_config - exit ERROR dma request failed\n");
++ return err;
++ }
++ }
++
++ DPRINTK("snd_ep93xx_dma_config - enter\n");
++ return err;
++}
++
++static void snd_ep93xx_dma_start( audio_state_t * state, audio_stream_t * stream )
++{
++ int err,i;
++
++ DPRINTK("snd_ep93xx_dma_start - enter\n");
++
++ for(i = 0 ;i < stream->dma_num_channels;i++)
++ err = ep93xx_dma_start( stream->dmahandles[i], 1,(unsigned int *) stream->dmahandles );
++
++ stream->active = 1;
++
++ DPRINTK("snd_ep93xx_dma_start - exit\n");
++}
++
++static void snd_ep93xx_dma_pause( audio_state_t * state, audio_stream_t * stream )
++{
++ int i;
++
++ DPRINTK("snd_ep93xx_dma_pause - enter\n");
++
++ for(i = 0 ;i < stream->dma_num_channels;i++)
++ ep93xx_dma_pause( stream->dmahandles[i], 1,(unsigned int *)stream->dmahandles );
++
++ stream->active = 0;
++ DPRINTK("snd_ep93xx_dma_pause - exit\n");
++
++}
++
++static void snd_ep93xx_dma_flush( audio_state_t * state, audio_stream_t * stream ){
++
++ int i;
++
++ DPRINTK("snd_ep93xx_dma_flush - enter\n");
++
++ for( i = 0 ; i < stream->dma_num_channels ; i++ )
++ ep93xx_dma_flush( stream->dmahandles[i] );
++
++ DPRINTK("snd_ep93xx_dma_flush - exit\n");
++}
++
++static void snd_ep93xx_deallocate_buffers( struct snd_pcm_substream *substream, audio_stream_t *stream )
++{
++ int i;
++ audio_channel_t *dma_chan;
++
++ DPRINTK("snd_ep93xx_deallocate_buffers - enter\n");
++
++ if( stream->dma_channels ){
++
++ for(i = 0;i < stream->dma_num_channels;i++){
++
++ dma_chan = &stream->dma_channels[i];
++
++ if( dma_chan->area ){
++
++ if( dma_chan->audio_buffers ){
++
++ kfree(dma_chan->audio_buffers);
++ dma_chan->audio_buffers = NULL;
++
++ }
++
++ kfree(dma_chan->area);
++ dma_chan->area = NULL;
++ }
++ }
++ kfree(stream->dma_channels);
++ stream->dma_channels = NULL;
++ }
++ DPRINTK("snd_ep93xx_deallocate_buffers - exit\n");
++}
++
++static int snd_ep93xx_allocate_buffers(struct snd_pcm_substream *substream, audio_stream_t *stream)
++{
++ audio_channel_t *channel;
++ unsigned int size,tmpsize,bufsize,bufextsize;
++ int i,j;
++
++
++ DPRINTK("snd_ep93xx_allocate_buffers - enter\n" );
++
++ if (stream->dma_channels){
++ printk("ep93xx_i2s %s BUSY\n",__FUNCTION__);
++ return -EBUSY;
++ }
++
++ stream->dma_channels = (audio_channel_t *)kmalloc(sizeof(audio_channel_t) * stream->dma_num_channels , GFP_KERNEL);
++
++ if (!stream->dma_channels){
++ printk(AUDIO_NAME ": unable to allocate dma_channels memory\n");
++ return - ENOMEM;
++ }
++
++ size = ( stream->dmasize / stream->dma_num_channels ) * stream->dma2usr_ratio;
++
++ for( i = 0; i < stream->dma_num_channels;i++){
++ channel = &stream->dma_channels[i];
++
++ channel->area = kmalloc( size, GFP_DMA );
++
++ if(!channel->area){
++ printk(AUDIO_NAME ": unable to allocate audio memory\n");
++ return -ENOMEM;
++ }
++ channel->bytes = size;
++ channel->addr = __virt_to_phys((int) channel->area);
++ memset( channel->area, 0, channel->bytes );
++
++ bufsize = ( stream->fragsize / stream->dma_num_channels ) * stream->dma2usr_ratio;
++ channel->audio_buff_count = size / bufsize;
++ bufextsize = size % bufsize;
++
++ if( bufextsize > 0 ){
++ channel->audio_buff_count++;
++ }
++
++ channel->audio_buffers = (audio_buf_t *)kmalloc(sizeof(audio_buf_t) * channel->audio_buff_count , GFP_KERNEL);
++
++ if (!channel->audio_buffers){
++ printk(AUDIO_NAME ": unable to allocate audio memory\n ");
++ return -ENOMEM;
++ }
++
++ tmpsize = size;
++
++ for( j = 0; j < channel->audio_buff_count; j++){
++
++ channel->audio_buffers[j].dma_addr = channel->addr + j * bufsize;
++
++ if( tmpsize >= bufsize ){
++ tmpsize -= bufsize;
++ channel->audio_buffers[j].bytes = bufsize;
++ channel->audio_buffers[j].reportedbytes = bufsize / stream->dma2usr_ratio;
++ }
++ else{
++ channel->audio_buffers[j].bytes = bufextsize;
++ channel->audio_buffers[j].reportedbytes = bufextsize / stream->dma2usr_ratio;
++ }
++ }
++ }
++
++ DPRINTK("snd_ep93xx_allocate_buffers -- exit SUCCESS\n" );
++ return 0;
++}
++
++/*
++ * DMA callback functions
++ */
++
++static void snd_ep93xx_dma_tx_callback
++(
++ ep93xx_dma_int_t DMAInt,
++ ep93xx_dma_dev_t device,
++ unsigned int user_data
++)
++{
++ int handle;
++ int i,chan;
++ unsigned int buf_id;
++
++ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
++ audio_state_t *state = (audio_state_t *)(substream->private_data);
++ audio_stream_t *stream = state->output_stream;
++ audio_buf_t *buf;
++
++ switch( device )
++ {
++ case DMATx_I2S3:
++ DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S3\n");
++ i = 2;
++ break;
++ case DMATx_I2S2:
++ DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S2\n");
++ i = 1;
++ break;
++ case DMATx_I2S1:
++ default:
++ DPRINTK( "snd_ep93xx_dma_tx_callback - DMATx_I2S1\n");
++ i = 0;
++ break;
++ }
++
++ if(stream->audio_num_channels == 1){
++ chan = 0;
++ }
++ else{
++ chan = stream->audio_num_channels / 2 - 1;
++ }
++ handle = stream->dmahandles[i];
++
++ if(stream->stopped == 0){
++
++ if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
++
++ buf = (audio_buf_t *)buf_id;
++ stream->bytecount += buf->reportedbytes;
++ ep93xx_dma_add_buffer( stream->dmahandles[i],
++ (unsigned int)buf->dma_addr,
++ 0,
++ buf->bytes,
++ 0,
++ (unsigned int) buf );
++ if(chan == i)
++ snd_pcm_period_elapsed(substream);
++ }
++ }
++}
++
++static void snd_ep93xx_dma_rx_callback
++(
++ ep93xx_dma_int_t DMAInt,
++ ep93xx_dma_dev_t device,
++ unsigned int user_data
++)
++{
++ int handle,i,chan;
++ unsigned int buf_id;
++ audio_buf_t *buf;
++
++ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)user_data;
++ audio_state_t *state = (audio_state_t *)(substream->private_data);
++ audio_stream_t *stream = state->input_stream;
++
++ switch( device ){
++
++ case DMARx_I2S3:
++ DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S3\n");
++ i = 2;
++ break;
++ case DMARx_I2S2:
++ DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S2\n");
++ i = 1;
++ break;
++ case DMARx_I2S1:
++ default:
++ DPRINTK( "snd_ep93xx_dma_rx_callback - DMARx_I2S1\n");
++ i = 0;
++ break;
++ }
++
++ if(stream->audio_num_channels == 1){
++ chan = 0;
++ }
++ else{
++ chan = stream->audio_num_channels / 2 - 1;
++ }
++ handle = stream->dmahandles[i];
++
++ if( stream->stopped == 0 ){
++
++ if( ep93xx_dma_remove_buffer( handle, &buf_id ) >= 0 ){
++
++ buf = (audio_buf_t *)buf_id;
++ stream->bytecount += buf->reportedbytes;
++ ep93xx_dma_add_buffer( stream->dmahandles[i],
++ (unsigned int)buf->dma_addr,
++ 0,
++ buf->bytes,
++ 0,
++ (unsigned int) buf );
++ if( i == chan )
++ snd_pcm_period_elapsed(substream);
++ }
++ }
++}
++
++static int snd_ep93xx_release(struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *)substream->private_data;
++ audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_stream : state->input_stream;
++
++ DPRINTK("snd_ep93xx_release - enter\n");
++
++ down(&state->sem);
++ stream->active = 0;
++ stream->stopped = 0;
++ snd_ep93xx_deallocate_buffers(substream, stream);
++ up(&state->sem);
++
++ DPRINTK("snd_ep93xx_release - exit\n");
++
++ return 0;
++}
++
++static int ep93xx_ac97_pcm_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int r;
++ int iTempMasterVol,iTempHeadphoneVol,iTempMonoVol,iTempRecordSelect;
++ /*save the old mixer*/
++ iTempRecordSelect = peek(AC97_1A_RECORD_SELECT);
++ iTempMasterVol = peek( AC97_02_MASTER_VOL);
++ iTempHeadphoneVol = peek( AC97_04_HEADPHONE_VOL);
++ iTempMonoVol = peek( AC97_06_MONO_VOL);
++
++ runtime->hw.channels_min = 1;
++ runtime->hw.channels_max = 2;
++
++ ep93xx_audio_init();
++ /*ep93xx_init_ac97_controller();*/
++
++ /*reset the old output mixer*/
++ poke( AC97_02_MASTER_VOL, iTempMasterVol);
++ poke( AC97_04_HEADPHONE_VOL,iTempHeadphoneVol );
++ poke( AC97_06_MONO_VOL, iTempMonoVol);
++ poke( AC97_1A_RECORD_SELECT,iTempRecordSelect);
++
++ r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
++
++ DPRINTK(" ep93xx_ac97_pcm_startup=%x\n",r);
++
++ return 0;
++}
++
++
++static int snd_ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ DPRINTK("snd_ep93xx_pcm_hw_params - enter\n");
++ return snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(params));
++}
++
++static int snd_ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++
++ DPRINTK("snd_ep93xx_pcm_hw_free - enter\n");
++ return snd_pcm_lib_free_pages(substream);
++}
++
++/*
++ *snd_ep93xx_pcm_prepare: need to finish these functions as lower
++ *chip_set_sample_format
++ *chip_set_sample_rate
++ *chip_set_channels
++ *chip_set_dma_setup
++ */
++
++static int snd_ep93xx_pcm_prepare_playback( struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *) substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->output_stream;
++
++ DPRINTK("snd_ep93xx_pcm_prepare_playback - enter\n");
++
++ ep93xx_audio_disable(1);
++ ep93xx_ac97_pcm_startup(substream);
++
++ snd_ep93xx_deallocate_buffers(substream,stream);
++
++ //if(runtime->channels % 2 != 0)
++ // return -1;
++
++ DPRINTK("The runtime item : \n");
++ DPRINTK("runtime->dma_addr = 0x%x\n", runtime->dma_addr);
++ DPRINTK("runtime->dma_area = 0x%x\n", runtime->dma_area);
++ DPRINTK("runtime->dma_bytes = %d\n", runtime->dma_bytes);
++ DPRINTK("runtime->frame_bits = %d\n", runtime->frame_bits);
++ DPRINTK("runtime->buffer_size = %d\n", runtime->buffer_size);
++ DPRINTK("runtime->period_size = %d\n", runtime->period_size);
++ DPRINTK("runtime->periods = %d\n", runtime->periods);
++ DPRINTK("runtime->rate = %d\n", runtime->rate);
++ DPRINTK("runtime->format = %d\n", runtime->format);
++ DPRINTK("runtime->channels = %d\n", runtime->channels);
++
++ /* set requestd format when available */
++ stream->audio_num_channels = runtime->channels;
++ if(stream->audio_num_channels == 1){
++ stream->dma_num_channels = 1;
++ }
++ else{
++ stream->dma_num_channels = runtime->channels / 2;
++ }
++
++ stream->audio_channels_flag = CHANNEL_FRONT;
++ if(stream->dma_num_channels == 2)
++ stream->audio_channels_flag |= CHANNEL_REAR;
++ if(stream->dma_num_channels == 3)
++ stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
++
++ stream->dmasize = runtime->dma_bytes;
++ stream->nbfrags = runtime->periods;
++ stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
++ stream->bytecount = 0;
++
++ if( !state->codec_set_by_capture ){
++ state->codec_set_by_playback = 1;
++
++ if( stream->audio_rate != runtime->rate ){
++ ep93xx_set_samplerate( runtime->rate,0 );
++ }
++ //if( stream->audio_format != runtime->format ){
++ // snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
++ //}
++ }
++ else{
++ audio_stream_t *s = state->input_stream;
++ if( runtime->format != s->audio_format)
++ return -1;
++ if( runtime->rate != s->audio_rate )
++ return -1;
++ }
++
++ stream->audio_format = runtime->format ;
++ ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels);
++
++
++ stream->audio_rate = runtime->rate;
++ audio_set_format( stream, runtime->format );
++ snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode );
++
++ if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
++ snd_ep93xx_deallocate_buffers( substream, stream );
++ return -1;
++ }
++
++ ep93xx_audio_enable(1);
++
++ DPRINTK("snd_ep93xx_pcm_prepare_playback - exit\n");
++ return 0;
++}
++
++static int snd_ep93xx_pcm_prepare_capture( struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *) substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->input_stream;
++
++ ep93xx_audio_disable(0);
++ ep93xx_ac97_pcm_startup(substream);
++
++ snd_ep93xx_deallocate_buffers(substream,stream);
++
++ //if(runtime->channels % 2 != 0)
++ //return -1;
++
++ DPRINTK("snd_ep93xx_pcm_prepare_capture - enter\n");
++
++// printk("The runtime item : \n");
++// printk("runtime->dma_addr = 0x%x\n", runtime->dma_addr);
++// printk("runtime->dma_area = 0x%x\n", runtime->dma_area);
++// printk("runtime->dma_bytes = %d\n", runtime->dma_bytes);
++// printk("runtime->frame_bits = %d\n", runtime->frame_bits);
++// printk("runtime->buffer_size = %d\n", runtime->buffer_size);
++// printk("runtime->period_size = %d\n", runtime->period_size);
++// printk("runtime->periods = %d\n", runtime->periods);
++// printk("runtime->rate = %d\n", runtime->rate);
++// printk("runtime->format = %d\n", runtime->format);
++// printk("runtime->channels = %d\n", runtime->channels);
++
++ /* set requestd format when available */
++ stream->audio_num_channels = runtime->channels;
++ if(stream->audio_num_channels == 1){
++ stream->dma_num_channels = 1;
++ }
++ else{
++ stream->dma_num_channels = runtime->channels / 2;
++ }
++
++ stream->audio_channels_flag = CHANNEL_FRONT;
++ if(stream->dma_num_channels == 2)
++ stream->audio_channels_flag |= CHANNEL_REAR;
++ if(stream->dma_num_channels == 3)
++ stream->audio_channels_flag |= CHANNEL_REAR | CHANNEL_CENTER_LFE;
++
++ stream->dmasize = runtime->dma_bytes;
++ stream->nbfrags = runtime->periods;
++ stream->fragsize = frames_to_bytes (runtime, runtime->period_size);
++ stream->bytecount = 0;
++
++ if( !state->codec_set_by_playback ){
++ state->codec_set_by_capture = 1;
++
++ /*rate*/
++ if( stream->audio_rate != runtime->rate ){
++ ep93xx_set_samplerate( runtime->rate,1 );
++ }
++
++ /*mixer*/
++ ep93xx_set_recsource(SOUND_MASK_MIC|SOUND_MASK_LINE1 | SOUND_MASK_LINE);
++ poke( AC97_1C_RECORD_GAIN, 0);
++
++ /*format*/
++ //if( stream->audio_format != runtime->format ){
++ // snd_ep93xx_i2s_init((stream->audio_stream_bitwidth == 24));
++ //}
++ }
++ else{
++ audio_stream_t *s = state->output_stream;
++ if( runtime->format != s->audio_format)
++ return -1;
++ if( runtime->rate != s->audio_rate )
++ return -1;
++ }
++
++ stream->audio_format = runtime->format ;
++ ep93xx_set_hw_format(stream->audio_format,stream->audio_num_channels);
++
++
++ stream->audio_rate = runtime->rate;
++ audio_set_format( stream, runtime->format );
++ snd_ep93xx_dma2usr_ratio( stream,state->bCompactMode );
++
++ if( snd_ep93xx_allocate_buffers( substream, stream ) != 0 ){
++ snd_ep93xx_deallocate_buffers( substream, stream );
++ return -1;
++ }
++
++ ep93xx_audio_enable(0);
++
++ DPRINTK("snd_ep93xx_pcm_prepare_capture - exit\n");
++ return 0;
++}
++/*
++ *start/stop/pause dma translate
++ */
++static int snd_ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ audio_state_t *state = (audio_state_t *)substream->private_data;
++ audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_stream:state->input_stream;
++ audio_buf_t *buf;
++ audio_channel_t *dma_channel;
++ int i,count,ret = 0;
++ unsigned long flags;
++
++ DPRINTK("snd_ep93xx_pcm_triger %d - enter \n",cmd);
++
++ switch (cmd){
++
++ case SNDRV_PCM_TRIGGER_START:
++
++ snd_ep93xx_dma_config( substream );
++
++ stream->stopped = 0;
++
++ if( !stream->active && !stream->stopped ){
++ stream->active = 1;
++ snd_ep93xx_dma_start( state, stream );
++ }
++
++ local_irq_save(flags);
++
++ for (i = 0; i < stream->dma_num_channels; i++){
++ dma_channel = &stream->dma_channels[i];
++
++ for(count = 0 ;count < dma_channel->audio_buff_count; count++){
++
++ buf = &dma_channel->audio_buffers[count];
++ ep93xx_dma_add_buffer( stream->dmahandles[i],
++ (unsigned int)buf->dma_addr,
++ 0,
++ buf->bytes,
++ 0,
++ (unsigned int) buf );
++ }
++ }
++
++ local_irq_restore(flags);
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ stream->stopped = 1;
++ snd_ep93xx_dma_pause( state, stream );
++ snd_ep93xx_dma_flush( state, stream );
++ snd_ep93xx_dma_free( substream );
++ break;
++
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ DPRINTK("snd_ep93xx_pcm_triger %d - exit \n",cmd);
++ return ret;
++}
++
++static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_playback(struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *)(substream->private_data);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->output_stream;
++ snd_pcm_uframes_t pointer = 0;
++
++ pointer = bytes_to_frames( runtime,stream->bytecount );
++
++ if (pointer >= runtime->buffer_size){
++ pointer = 0;
++ stream->bytecount = 0;
++ }
++
++ DPRINTK("snd_ep93xx_pcm_pointer_playback - exit\n");
++ return pointer;
++}
++
++static snd_pcm_uframes_t snd_ep93xx_pcm_pointer_capture(struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *)(substream->private_data);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->input_stream;
++ snd_pcm_uframes_t pointer = 0;
++
++ pointer = bytes_to_frames( runtime,stream->bytecount );
++
++ if (pointer >= runtime->buffer_size){
++ pointer = 0;
++ stream->bytecount = 0;
++ }
++
++ DPRINTK("snd_ep93xx_pcm_pointer_capture - exit\n");
++ return pointer;
++}
++
++static int snd_ep93xx_pcm_open(struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++ state->output_stream:state->input_stream;
++
++ DPRINTK("snd_ep93xx_pcm_open - enter\n");
++
++ down(&state->sem);
++
++ runtime->hw = ep93xx_ac97_pcm_hardware;
++
++ stream->dma_num_channels = AUDIO_DEFAULT_DMACHANNELS;
++ stream->dma_channels = NULL;
++ stream->audio_rate = 0;
++ stream->audio_stream_bitwidth = 0;
++
++ up(&state->sem);
++
++ DPRINTK("snd_ep93xx_pcm_open - exit\n");
++ return 0;
++}
++
++/*
++ *free the HW dma channel
++ *free the HW dma buffer
++ *free the Hw dma decrotion using function :kfree
++ */
++static int snd_ep93xx_pcm_close(struct snd_pcm_substream *substream)
++{
++ audio_state_t *state = (audio_state_t *)(substream->private_data);
++
++ DPRINTK("snd_ep93xx_pcm_close - enter\n");
++
++ snd_ep93xx_release(substream);
++
++ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ state->codec_set_by_playback = 0;
++ else
++ state->codec_set_by_capture = 0;
++
++ DPRINTK("snd_ep93xx_pcm_close - exit\n");
++ return 0;
++}
++
++static int snd_ep93xx_pcm_copy_playback(struct snd_pcm_substream * substream,int channel,
++ snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
++{
++
++ audio_state_t *state = (audio_state_t *)substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->output_stream ;
++ audio_channel_t *dma_channel;
++ int i;
++ int tocount = frames_to_bytes(runtime,count);
++
++ for( i = 0; i < stream->dma_num_channels; i++ ){
++
++ dma_channel = &stream->dma_channels[i];
++ stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
++
++ }
++
++ if(copy_from_user_with_conversion(stream ,(const char*)src,(tocount * stream->dma2usr_ratio),state->bCompactMode) <=0 ){
++ DPRINTK(KERN_ERR "copy_from_user_with_conversion() failed\n");
++ return -EFAULT;
++ }
++
++ DPRINTK("snd_ep93xx_pcm_copy_playback - exit\n");
++ return 0;
++}
++
++
++static int snd_ep93xx_pcm_copy_capture(struct snd_pcm_substream * substream,int channel,
++ snd_pcm_uframes_t pos,void __user *src, snd_pcm_uframes_t count)
++{
++ audio_state_t *state = (audio_state_t *)substream->private_data;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ audio_stream_t *stream = state->input_stream ;
++ audio_channel_t *dma_channel;
++ int i;
++
++ int tocount = frames_to_bytes(runtime,count);
++
++ for( i = 0; i < stream->dma_num_channels; i++ ){
++
++ dma_channel = &stream->dma_channels[i];
++ stream->hwbuf[i] = dma_channel->area + ( frames_to_bytes(runtime,pos) * stream->dma2usr_ratio / stream->dma_num_channels );
++
++ }
++
++ if(copy_to_user_with_conversion(stream,(const char*)src,tocount,state->bCompactMode) <=0 ){
++
++ DPRINTK(KERN_ERR "copy_to_user_with_conversion() failed\n");
++ return -EFAULT;
++ }
++
++ DPRINTK("snd_ep93xx_pcm_copy_capture - exit\n");
++ return 0;
++}
++
++/*----------------------------------------------------------------------------------*/
++static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
++{
++ int val = -1;
++ /*volatile u32 *reg_addr;*/
++
++ DPRINTK(" number of codec:%x reg=%x\n",ac97->num,reg);
++ val=peek(reg);
++ if(val==-1){
++ printk(KERN_ERR "%s: read error (ac97_reg=%d )val=%x\n",
++ __FUNCTION__, reg, val);
++ return 0;
++ }
++
++ return val;
++}
++
++static void ep93xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
++{
++ /*volatile u32 *reg_addr;*/
++ int ret;
++
++ DPRINTK(" number of codec:%x rge=%x val=%x\n",ac97->num,reg,val);
++ ret=poke(reg, val);
++ if(ret!=0){
++ printk(KERN_ERR "%s: write error (ac97_reg=%d val=%x)\n",
++ __FUNCTION__, reg, val);
++ }
++
++}
++
++static void ep93xx_ac97_reset(struct snd_ac97 *ac97)
++{
++
++ DPRINTK(" ep93xx_ac97_reset\n");
++ ep93xx_audio_init();
++
++}
++
++static struct snd_ac97_bus_ops ep93xx_ac97_ops = {
++ .read = ep93xx_ac97_read,
++ .write = ep93xx_ac97_write,
++ .reset = ep93xx_ac97_reset,
++};
++
++static struct snd_pcm *ep93xx_ac97_pcm;
++static struct snd_ac97 *ep93xx_ac97_ac97;
++
++static struct snd_pcm_ops snd_ep93xx_pcm_playback_ops = {
++ .open = snd_ep93xx_pcm_open,
++ .close = snd_ep93xx_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_ep93xx_pcm_hw_params,
++ .hw_free = snd_ep93xx_pcm_hw_free,
++ .prepare = snd_ep93xx_pcm_prepare_playback,
++ .trigger = snd_ep93xx_pcm_trigger,
++ .pointer = snd_ep93xx_pcm_pointer_playback,
++ .copy = snd_ep93xx_pcm_copy_playback,
++
++};
++
++static struct snd_pcm_ops snd_ep93xx_pcm_capture_ops = {
++ .open = snd_ep93xx_pcm_open,
++ .close = snd_ep93xx_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_ep93xx_pcm_hw_params,
++ .hw_free = snd_ep93xx_pcm_hw_free,
++ .prepare = snd_ep93xx_pcm_prepare_capture,
++ .trigger = snd_ep93xx_pcm_trigger,
++ .pointer = snd_ep93xx_pcm_pointer_capture,
++ .copy = snd_ep93xx_pcm_copy_capture,
++};
++
++/*--------------------------------------------------------------------------*/
++
++
++static int snd_ep93xx_pcm_new(struct snd_card *card, audio_state_t *state, struct snd_pcm **rpcm)
++{
++ struct snd_pcm *pcm;
++ int play = state->output_stream? 1 : 0;/*SNDRV_PCM_STREAM_PLAYBACK*/
++ int capt = state->input_stream ? 1 : 0;/*SNDRV_PCM_STREAM_CAPTURE*/
++ int ret = 0;
++
++ DPRINTK("snd_ep93xx_pcm_new - enter\n");
++
++ /* Register the new pcm device interface */
++ ret = snd_pcm_new(card, "EP93xx-AC97-PCM", 0, play, capt, &pcm);
++
++ if (ret){
++ DPRINTK("%s--%x:card=%x,play=%x,capt=%x,&pcm=%x\n",__FUNCTION__,ret,(int)card,play,capt,(int)pcm);
++ goto out;
++ }
++
++ /* allocate the pcm(DMA) memory */
++ ret = snd_pcm_lib_preallocate_pages_for_all(pcm, /*SNDRV_DMA_TYPE_DEV,0,*/SNDRV_DMA_TYPE_CONTINUOUS,snd_dma_continuous_data(GFP_KERNEL),128*1024,128*1024);
++
++ DPRINTK("The substream item : \n");
++ DPRINTK("pcm->streams[0].substream->dma_buffer.addr = 0x%x\n", pcm->streams[0].substream->dma_buffer.addr);
++ DPRINTK("pcm->streams[0].substream->dma_buffer.area = 0x%x\n", pcm->streams[0].substream->dma_buffer.area);
++ DPRINTK("pcm->streams[0].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[0].substream->dma_buffer.bytes);
++ DPRINTK("pcm->streams[1].substream->dma_buffer.addr = 0x%x\n", pcm->streams[1].substream->dma_buffer.addr);
++ DPRINTK("pcm->streams[1].substream->dma_buffer.area = 0x%x\n", pcm->streams[1].substream->dma_buffer.area);
++ DPRINTK("pcm->streams[1].substream->dma_buffer.bytes = 0x%x\n", pcm->streams[1].substream->dma_buffer.bytes);
++
++ pcm->private_data = state;
++
++ /* seem to free the pcm data struct-->self dma buffer */
++ pcm->private_free = (void*) snd_pcm_lib_preallocate_free_for_all;
++
++ /* alsa pcm ops setting for SNDRV_PCM_STREAM_PLAYBACK */
++ if (play) {
++ int stream = SNDRV_PCM_STREAM_PLAYBACK;
++ snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_playback_ops);
++ }
++
++ /* alsa pcm ops setting for SNDRV_PCM_STREAM_CAPTURE */
++ if (capt) {
++ int stream = SNDRV_PCM_STREAM_CAPTURE;
++ snd_pcm_set_ops(pcm, stream, &snd_ep93xx_pcm_capture_ops);
++ }
++
++ if (rpcm)
++ *rpcm = pcm;
++ DPRINTK("snd_ep93xx_pcm_new - exit\n");
++out:
++ return ret;
++}
++
++#ifdef CONFIG_PM
++
++int ep93xx_ac97_do_suspend(struct snd_card *card, unsigned int state)
++{
++ if (card->power_state != SNDRV_CTL_POWER_D3cold) {
++ snd_pcm_suspend_all(ep93xx_ac97_pcm);
++ snd_ac97_suspend(ep93xx_ac97_ac97);
++ snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
++ }
++
++ return 0;
++}
++
++int ep93xx_ac97_do_resume(struct snd_card *card, unsigned int state)
++{
++ if (card->power_state != SNDRV_CTL_POWER_D0) {
++
++ snd_ac97_resume(ep93xx_ac97_ac97);
++ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
++ }
++
++ return 0;
++}
++
++int ep93xx_ac97_suspend(struct platform_device *_dev, u32 state, u32 level)
++{
++ struct snd_card *card = platform_get_drvdata(_dev);
++ int ret = 0;
++
++ if (card && level == SUSPEND_DISABLE)
++ ret = ep93xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold);
++
++ return ret;
++}
++
++int ep93xx_ac97_resume(struct platform_device *_dev, u32 level)
++{
++ struct snd_card *card = platform_get_drvdata(_dev);
++ int ret = 0;
++
++ if (card && level == RESUME_ENABLE)
++ ret = ep93xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
++
++ return ret;
++}
++
++#else
++/*
++#define ep93xx_ac97_do_suspend NULL
++#define ep93xx_ac97_do_resume NULL
++#define ep93xx_ac97_suspend NULL
++#define ep93xx_ac97_resume NULL
++*/
++
++int ep93xx_ac97_do_suspend(struct snd_card *card, unsigned int state)
++{
++ return 0;
++}
++
++int ep93xx_ac97_do_resume(struct snd_card *card, unsigned int state)
++{
++ return 0;
++}
++
++int ep93xx_ac97_resume(struct platform_device *_dev, u32 level)
++{
++ struct snd_card *card = platform_get_drvdata(_dev);
++ int ret = 0;
++
++ //if (card && level == RESUME_ENABLE)
++ ret = ep93xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
++
++ return ret;
++}
++
++int ep93xx_ac97_suspend(struct platform_device *_dev, u32 state, u32 level)
++{
++ struct snd_card *card = platform_get_drvdata(_dev);
++ int ret = 0;
++
++ //if (card && level == SUSPEND_DISABLE)
++ ret = ep93xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold);
++
++ return ret;
++}
++
++#endif
++
++
++
++/* module init & exit */
++static int __devinit ep93xx_ac97_probe(struct platform_device *dev)
++{
++ struct snd_card *card;
++ struct snd_ac97_bus *ac97_bus;
++ struct snd_ac97_template ac97_template;
++ int err = -ENOMEM;
++ struct resource *res = NULL;
++
++ DPRINTK("snd_ep93xx_probe - enter\n");
++
++ /* Enable audio early on, give the DAC time to come up. */
++ res = platform_get_resource( dev, IORESOURCE_MEM, 0);
++
++ if(!res) {
++ printk("error : platform_get_resource \n");
++ return -ENODEV;
++ }
++
++ if (!request_mem_region(res->start,res->end - res->start + 1, "snd-ac97-cs4202" )){
++ printk("error : request_mem_region\n");
++ return -EBUSY;
++ }
++
++ /*enable ac97 codec*/
++ ep93xx_audio_init();
++
++ /* register the soundcard */
++ card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
++ THIS_MODULE, 0);
++ if (!card){
++ printk("AC97: snd_card_new error\n");
++ goto error;
++ }
++
++ card->dev = &dev->dev;
++ /*regist the new pcm device*/
++ err = snd_ep93xx_pcm_new(card, &audio_state, &ep93xx_ac97_pcm);
++ if (err){
++ printk("AC97: ep93xx_ac97_pcm_new error\n");
++ goto error;
++ }
++ if (card == NULL) {
++ DPRINTK(KERN_ERR "snd_card_new() failed\n");
++ goto error;
++ }
++
++ /*driver name*/
++ strcpy(card->driver, "CS4202A");
++ strcpy(card->shortname, "Cirrus Logic AC97 Audio ");
++ strcpy(card->longname, "Cirrus Logic AC97 Audio with CS4202A");
++
++ /*regist the new ac97 device*/
++ err = snd_ac97_bus(card, 0, &ep93xx_ac97_ops, NULL, &ac97_bus);
++ if (err){
++ printk("AC97: snd_ac97_bus error\n");
++ goto error;
++ }
++
++ memset(&ac97_template, 0, sizeof(ac97_template));
++ err = snd_ac97_mixer(ac97_bus, &ac97_template, &ep93xx_ac97_ac97);
++ if (err){
++ printk("AC97: snd_ac97_mixer error\n");
++ goto error;
++ }
++
++ /**/
++ ep93xx_audio_init();
++ /*setting the card device callback*/
++ //err = snd_card_set_pm_callback(card, ep93xx_ac97_do_suspend,ep93xx_ac97_do_resume, (void*)NULL);
++ //if(err != 0){
++ // printk("snd_card_set_pm_callback error\n");
++ //}
++
++ /*regist the new CARD device*/
++ err = snd_card_register(card);
++ if (err == 0) {
++ printk( KERN_INFO "Cirrus Logic ep93xx ac97 audio initialized\n" );
++ platform_set_drvdata(dev,card);
++ DPRINTK("snd_ep93xx_probe - exit\n");
++ return 0;
++ }
++
++error:
++ snd_card_free(card);
++ printk("snd_ep93xx_probe - error\n");
++ return err;
++
++return 0;
++}
++
++static int __devexit ep93xx_ac97_remove(struct platform_device *dev)
++{
++ struct resource *res;
++ struct snd_card *card = platform_get_drvdata(dev);
++
++ res = platform_get_resource( dev, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, res->end - res->start + 1);
++
++ DPRINTK("snd_ep93xx_ac97_remove - enter\n");
++
++ if (card) {
++ snd_card_free(card);
++ platform_set_drvdata(dev, NULL);
++ }
++ DPRINTK("snd_ep93xx_remove - exit\n");
++
++return 0;
++}
++
++
++static struct platform_driver ep93xx_ac97_driver = {
++ .probe = ep93xx_ac97_probe,
++ .remove = __devexit_p (ep93xx_ac97_remove),
++ .suspend = ep93xx_ac97_suspend,
++ .resume = ep93xx_ac97_resume,
++ .driver = {
++ .name = "ep93xx-ac97",
++ },
++};
++
++
++static int __init ep93xx_ac97_init(void)
++{
++ int ret;
++
++ DPRINTK(KERN_INFO "%s: version %s\n", DRIVER_DESC, DRIVER_VERSION);
++ DPRINTK("snd_ep93xx_AC97_init - enter\n");
++ ret = platform_driver_register(&ep93xx_ac97_driver);
++ DPRINTK("snd_ep93xx_AC97_init - exit\n");
++ return ret;
++}
++
++static void __exit ep93xx_ac97_exit(void)
++{
++ DPRINTK("ep93xx_ac97_exit - enter\n");
++ return platform_driver_unregister(&ep93xx_ac97_driver);
++}
++
++module_init(ep93xx_ac97_init);
++module_exit(ep93xx_ac97_exit);
++
++MODULE_DESCRIPTION("Cirrus Logic audio module");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/sound/arm/ep93xx-ac97.h
+@@ -0,0 +1,89 @@
++/*
++ * linux/sound/arm/ep93xx-ac97.h -- ALSA PCM interface for the edb93xx ac97 audio
++ *
++ * Author: Fred Wei
++ * Created: July 19, 2005
++ * Copyright: Cirrus Logic, 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.
++ */
++
++#define EP93XX_DEFAULT_NUM_CHANNELS 2
++#define EP93XX_DEFAULT_FORMAT SNDRV_PCM_FORMAT_S16_LE
++#define EP93XX_DEFAULT_BIT_WIDTH 16
++#define MAX_DEVICE_NAME 20
++
++/*
++ * Buffer Management
++ */
++
++typedef struct {
++
++ unsigned char *area; /* virtual pointer */
++ dma_addr_t dma_addr; /* physical address */
++ size_t bytes;
++ size_t reportedbytes; /* buffer size */
++ int sent; /* indicates that dma has the buf */
++ char *start; /* points to actual buffer */
++
++} audio_buf_t;
++
++
++typedef struct {
++
++ unsigned char *area; /* virtual pointer */
++ dma_addr_t addr; /* physical address */
++ size_t bytes; /* buffer size in bytes */
++ unsigned char *buff_pos; /* virtual pointer */
++ audio_buf_t *audio_buffers; /* array of audio buffer structures */
++ int audio_buff_count;
++
++
++} audio_channel_t;
++
++typedef struct audio_stream_s {
++
++ /* dma stuff */
++ int dmahandles[3]; /* handles for dma driver instances */
++ char devicename[MAX_DEVICE_NAME]; /* string - name of device */
++ int dma_num_channels; /* 1, 2, or 3 DMA channels */
++ audio_channel_t *dma_channels;
++ u_int nbfrags; /* nbr of fragments i.e. buffers */
++ u_int fragsize; /* fragment i.e. buffer size */
++ u_int dmasize;
++ int bytecount; /* nbr of processed bytes */
++ int externedbytecount; /* nbr of processed bytes */
++ volatile int active; /* actually in progress */
++ volatile int stopped; /* might be active but stopped */
++ char *hwbuf[3];
++ long audio_rate;
++ long audio_num_channels; /* Range: 1 to 6 */
++ int audio_channels_flag;
++ long audio_format;
++ long audio_stream_bitwidth; /* Range: 8, 16, 24 */
++ int dma2usr_ratio;
++
++} audio_stream_t;
++
++
++/*
++ * State structure for one instance
++ */
++typedef struct {
++
++ audio_stream_t *output_stream;
++ audio_stream_t *input_stream;
++ ep93xx_dma_dev_t output_dma[3];
++ ep93xx_dma_dev_t input_dma[3];
++ char *output_id[3];
++ char *input_id[3];
++ struct semaphore sem; /* to protect against races in attach() */
++ int codec_set_by_playback;
++ int codec_set_by_capture;
++ int DAC_bit_width; /* 16, 20, 24 bits */
++ int bCompactMode; /* set if 32bits = a stereo sample */
++
++} audio_state_t;
++
diff --git a/target/linux/ep93xx/patches-2.6.30/011-simone-board-def.patch b/target/linux/ep93xx/patches-2.6.30/011-simone-board-def.patch
new file mode 100644
index 0000000000..d499259adb
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/011-simone-board-def.patch
@@ -0,0 +1,1658 @@
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/mach-ep93xx/Kconfig 2009-11-26 00:13:04.000000000 +0100
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/Kconfig 2009-11-26 00:13:15.000000000 +0100
+@@ -88,6 +88,12 @@
+ Say 'Y' here if you want your kernel to support the
+ Contec Hypercontrol Micro9-L board.
+
++config MACH_SIM_ONE
++ bool "Support SIM.ONE board "
++ help
++ Say 'Y' here if you want your kernel to support the
++ Simplemachines SIM.ONE board.
++
+ config MACH_TS72XX
+ bool "Support Technologic Systems TS-72xx SBC"
+ help
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/mach-ep93xx/Makefile 2009-11-26 00:13:04.000000000 +0100
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/Makefile 2009-11-26 00:13:15.000000000 +0100
+@@ -16,4 +16,5 @@
+ obj-$(CONFIG_MACH_EDB9315A) += edb9315a.o
+ obj-$(CONFIG_MACH_GESBC9312) += gesbc9312.o
+ obj-$(CONFIG_MACH_MICRO9) += micro9.o
++obj-$(CONFIG_MACH_SIM_ONE) += simone.o
+ obj-$(CONFIG_MACH_TS72XX) += ts72xx.o
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/Makefile.boot
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/mach-ep93xx/Makefile.boot 2009-11-26 00:13:04.000000000 +0100
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/Makefile.boot 2009-11-26 00:13:15.000000000 +0100
+@@ -1,2 +1,4 @@
+ zreladdr-y := 0x00008000
+ params_phys-y := 0x00000100
++ zreladdr-$(CONFIG_MACH_SIM_ONE) := 0xc0008000
++params_phys-$(CONFIG_MACH_SIM_ONE) := 0xc0000100
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/simone.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/simone.c 2009-11-26 00:14:09.000000000 +0100
+@@ -0,0 +1,217 @@
++/*
++ * arch/arm/mach-ep93xx/simone.c
++ * Simplemachines SIM.ONE support.
++ *
++ * Copyright (C) 2009 Simplemachines
++ * MMC support by Peter Ivanov <ivanovp@gmail.com>, 2007
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/mtd/physmap.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/mmc_spi.h>
++#include <linux/mmc/host.h>
++#include <linux/jiffies.h>
++#include <linux/irq.h>
++
++#include <asm/io.h>
++#include <mach/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/mach/arch.h>
++#include <asm/gpio.h>
++
++static struct physmap_flash_data simone_flash_data = {
++ .width = 2,
++};
++
++static struct resource simone_flash_resource = {
++ .start = 0x60000000,
++ .end = 0x60000000+ SZ_8M - 1,
++ .flags = IORESOURCE_MEM,
++};
++
++static struct platform_device simone_flash = {
++ .name = "physmap-flash",
++ .id = 0,
++ .dev = {
++ .platform_data = &simone_flash_data,
++ },
++ .num_resources = 1,
++ .resource = &simone_flash_resource,
++};
++
++
++static struct resource ep93xx_ac97_resources[] = {
++ [0] = {
++ .start = EP93XX_AC97_PHY_BASE,
++ .end = EP93XX_AC97_PHY_BASE + 0x6C,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = NO_IRQ,
++ .end = NO_IRQ,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static u64 ep93xx_ac97_dma_mask = 0xffffffffUL;
++
++static struct platform_device ep93xx_ac97_device = {
++ .name = "ep93xx-ac97",
++ .id = 0,
++ .num_resources = 2,
++ .resource = ep93xx_ac97_resources,
++ .dev = {
++ .dma_mask = &ep93xx_ac97_dma_mask,
++ .coherent_dma_mask = 0xffffffffUL,
++ },
++};
++
++
++#ifdef CONFIG_SPI
++static struct resource ep93xx_spi_resources[] = {
++ [0] = {
++ .start = EP93XX_SPI_BASE_PHYS,
++ .end = EP93XX_SPI_BASE_PHYS + 0x0fff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_EP93XX_SSP,
++ .end = IRQ_EP93XX_SSP,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device ep93xx_spi_device = {
++ .name = "ep93xx-spi",
++ .id = 0,
++ .resource = ep93xx_spi_resources,
++ .num_resources = ARRAY_SIZE(ep93xx_spi_resources),
++};
++
++
++#define EP93XX_MMC_SPI_CARD_PRESENT EP93XX_GPIO_LINE_A(0)
++
++/*
++ * Initializes SPI system to communicate with MMC/SD card
++ */
++int ep93xx_mmc_spi_init (struct device *pdev, irqreturn_t (*card_det_irq_handler)(int, void *),
++ void *mmc)
++{
++ int rv;
++
++ rv = gpio_request(EP93XX_MMC_SPI_CARD_PRESENT, "ep93xx-mmc-spi");
++ if (rv) {
++ dev_info(pdev, "failed to request MMC/SD gpio pin\n");
++ return rv;
++ }
++
++ gpio_direction_input (EP93XX_MMC_SPI_CARD_PRESENT);
++
++ if ((rv = request_irq (gpio_to_irq( EP93XX_MMC_SPI_CARD_PRESENT),
++ card_det_irq_handler,
++ IRQF_DISABLED | IRQ_TYPE_EDGE_FALLING, /* flags */
++ "ep93xx-mmc-spi", /* devname */
++ mmc /* void *devid */
++ )) == 0)
++ {
++ dev_info (pdev, "MMC/SD card detection IRQ %i assigned.\n",
++ gpio_to_irq(EP93XX_MMC_SPI_CARD_PRESENT));
++ }
++ else
++ {
++ dev_err (pdev, "Cannot assign MMC/SD card detection IRQ (%i)!\n",
++ gpio_to_irq(EP93XX_MMC_SPI_CARD_PRESENT));
++ return rv;
++ }
++ return 0;
++}
++
++void ep93xx_mmc_spi_exit (struct device *pdev, void *mmc)
++{
++ free_irq (EP93XX_MMC_SPI_CARD_PRESENT, mmc);
++}
++
++static struct mmc_spi_platform_data ep93xx_spi_pdata = {
++ .init = &ep93xx_mmc_spi_init,
++ .exit = &ep93xx_mmc_spi_exit,
++ .get_ro = NULL,
++ .detect_delay = 500, /* card detection delay in msec */
++ .ocr_mask = MMC_VDD_33_34,
++};
++
++static struct spi_board_info ep93xx_spi_board_info[] __initdata = {
++ {
++ .modalias = "mmc_spi",
++ .max_speed_hz = 7.4E6, /* max spi clock (SCK) speed in HZ */
++ .bus_num = 0,
++ .chip_select = 0,
++ .platform_data = (void*) &ep93xx_spi_pdata,
++ .controller_data = NULL,
++ .mode = SPI_MODE_0,
++ }
++};
++#endif
++static struct ep93xx_eth_data ep93xx_eth_data = {
++ .dev_addr = { 0x00, 0xba, 0xd0, 0x0b, 0xad, 0x00 },
++};
++
++static struct resource ep93xx_eth_resource[] = {
++ {
++ .start = EP93XX_ETHERNET_PHYS_BASE,
++ .end = EP93XX_ETHERNET_PHYS_BASE + 0xffff,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .start = IRQ_EP93XX_ETHERNET,
++ .end = IRQ_EP93XX_ETHERNET,
++ .flags = IORESOURCE_IRQ,
++ }
++};
++
++static struct platform_device ep93xx_eth_device = {
++ .name = "ep93xx-eth",
++ .id = -1,
++ .dev = {
++ .platform_data = &ep93xx_eth_data,
++ },
++ .num_resources = ARRAY_SIZE(ep93xx_eth_resource),
++ .resource = ep93xx_eth_resource,
++};
++
++static void __init simone_init_machine(void)
++{
++ ep93xx_init_devices();
++ /* Switch off the LCD backlight*/
++ gpio_request(EP93XX_GPIO_LINE_B(5), "lcd");
++ gpio_direction_output (EP93XX_GPIO_LINE_B(5), 0);
++ platform_device_register(&simone_flash);
++ platform_device_register(&ep93xx_ac97_device);
++ platform_device_register(&ep93xx_eth_device);
++#if defined(CONFIG_SPI_EP93XX) || defined(CONFIG_SPI_EP93XX_MODULE)
++ dev_set_name(&ep93xx_spi_device.dev, "apb:spi");
++ platform_device_register(&ep93xx_spi_device);
++ spi_register_board_info(ep93xx_spi_board_info,ARRAY_SIZE(ep93xx_spi_board_info));
++#endif
++}
++
++MACHINE_START(SIM_ONE, "Simplemachine SimONE Board")
++ /* Maintainer: Nuccio Raciti Simplemachine <nuccio.raciti@gmail.com>*/
++ .phys_io = EP93XX_APB_PHYS_BASE,
++ .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
++ .boot_params = 0x00000100,
++ .map_io = ep93xx_map_io,
++ .init_irq = ep93xx_init_irq,
++ .timer = &ep93xx_timer,
++ .init_machine = simone_init_machine,
++MACHINE_END
+Index: linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/memory.h
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/mach-ep93xx/include/mach/memory.h 2009-11-26 00:13:04.000000000 +0100
++++ linux-2.6.30.9/arch/arm/mach-ep93xx/include/mach/memory.h 2009-11-26 00:13:15.000000000 +0100
+@@ -5,6 +5,10 @@
+ #ifndef __ASM_ARCH_MEMORY_H
+ #define __ASM_ARCH_MEMORY_H
+
++#if defined(CONFIG_MACH_SIM_ONE)
++#define PHYS_OFFSET UL(0xc0000000)
++#else
+ #define PHYS_OFFSET UL(0x00000000)
++#endif
+
+ #endif
+Index: linux-2.6.30.9/arch/arm/configs/simone_defconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/arch/arm/configs/simone_defconfig 2009-11-26 00:13:15.000000000 +0100
+@@ -0,0 +1,1380 @@
++#
++# Automatically generated make config: don't edit
++# Linux kernel version: 2.6.24.7
++# Tue May 12 17:49:25 2009
++#
++CONFIG_ARM=y
++CONFIG_SYS_SUPPORTS_APM_EMULATION=y
++# CONFIG_GENERIC_GPIO is not set
++# CONFIG_GENERIC_TIME is not set
++# CONFIG_GENERIC_CLOCKEVENTS is not set
++CONFIG_MMU=y
++# CONFIG_NO_IOPORT is not set
++CONFIG_GENERIC_HARDIRQS=y
++CONFIG_STACKTRACE_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_ZONE_DMA=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=y
++CONFIG_SWAP=y
++CONFIG_SYSVIPC=y
++CONFIG_SYSVIPC_SYSCTL=y
++# CONFIG_POSIX_MQUEUE is not set
++# CONFIG_BSD_PROCESS_ACCT is not set
++# CONFIG_TASKSTATS is not set
++# CONFIG_USER_NS is not set
++# CONFIG_PID_NS is not set
++# CONFIG_AUDIT is not set
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=16
++# CONFIG_CGROUPS is not set
++CONFIG_FAIR_GROUP_SCHED=y
++CONFIG_FAIR_USER_SCHED=y
++# CONFIG_FAIR_CGROUP_SCHED is not set
++CONFIG_SYSFS_DEPRECATED=y
++# CONFIG_RELAY is not set
++# CONFIG_BLK_DEV_INITRD is not set
++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
++CONFIG_SYSCTL=y
++CONFIG_EMBEDDED=y
++CONFIG_UID16=y
++CONFIG_SYSCTL_SYSCALL=y
++CONFIG_KALLSYMS=y
++# CONFIG_KALLSYMS_EXTRA_PASS is not set
++CONFIG_HOTPLUG=y
++CONFIG_PRINTK=y
++CONFIG_BUG=y
++CONFIG_ELF_CORE=y
++CONFIG_BASE_FULL=y
++CONFIG_FUTEX=y
++CONFIG_ANON_INODES=y
++CONFIG_EPOLL=y
++CONFIG_SIGNALFD=y
++CONFIG_EVENTFD=y
++CONFIG_SHMEM=y
++CONFIG_VM_EVENT_COUNTERS=y
++CONFIG_SLAB=y
++# CONFIG_SLUB is not set
++# CONFIG_SLOB is not set
++CONFIG_SLABINFO=y
++CONFIG_RT_MUTEXES=y
++# CONFIG_TINY_SHMEM is not set
++CONFIG_BASE_SMALL=0
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_MODULE_FORCE_UNLOAD=y
++# 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
++
++#
++# IO Schedulers
++#
++CONFIG_IOSCHED_NOOP=y
++# CONFIG_IOSCHED_AS is not set
++CONFIG_IOSCHED_DEADLINE=y
++# CONFIG_IOSCHED_CFQ is not set
++# CONFIG_DEFAULT_AS is not set
++CONFIG_DEFAULT_DEADLINE=y
++# CONFIG_DEFAULT_CFQ is not set
++# CONFIG_DEFAULT_NOOP is not set
++CONFIG_DEFAULT_IOSCHED="deadline"
++
++#
++# 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_CO285 is not set
++# CONFIG_ARCH_EBSA110 is not set
++CONFIG_ARCH_EP93XX=y
++# 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_KS8695 is not set
++# CONFIG_ARCH_NS9XXX is not set
++# CONFIG_ARCH_MXC 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
++
++#
++# Cirrus EP93xx Implementation Options
++#
++CONFIG_CRUNCH=y
++
++#
++# EP93xx Platforms
++#
++# CONFIG_MACH_ADSSPHERE is not set
++# CONFIG_MACH_EDB9302 is not set
++# CONFIG_MACH_EDB9302A is not set
++# CONFIG_MACH_EDB9307 is not set
++# CONFIG_MACH_EDB9312 is not set
++# CONFIG_MACH_EDB9315 is not set
++# CONFIG_MACH_EDB9315A is not set
++# CONFIG_MACH_GESBC9312 is not set
++# CONFIG_MACH_MICRO9 is not set
++# CONFIG_MACH_MICRO9H is not set
++# CONFIG_MACH_MICRO9M is not set
++# CONFIG_MACH_MICRO9L is not set
++CONFIG_MACH_SIM_ONE=y
++# CONFIG_MACH_TS72XX is not set
++
++#
++# Boot options
++#
++
++#
++# Power management
++#
++
++#
++# Processor Type
++#
++CONFIG_CPU_32=y
++CONFIG_CPU_ARM920T=y
++CONFIG_CPU_32v4T=y
++CONFIG_CPU_ABRT_EV4T=y
++CONFIG_CPU_CACHE_V4WT=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_OUTER_CACHE is not set
++CONFIG_ARM_VIC=y
++
++#
++# Bus support
++#
++CONFIG_ARM_AMBA=y
++# CONFIG_PCI_SYSCALL is not set
++# CONFIG_ARCH_SUPPORTS_MSI is not set
++# CONFIG_PCCARD is not set
++
++#
++# Kernel Features
++#
++# CONFIG_TICK_ONESHOT is not set
++CONFIG_PREEMPT=y
++# CONFIG_NO_IDLE_HZ is not set
++CONFIG_HZ=100
++CONFIG_AEABI=y
++CONFIG_OABI_COMPAT=y
++# CONFIG_ARCH_DISCONTIGMEM_ENABLE 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_SPARSEMEM_STATIC is not set
++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
++CONFIG_SPLIT_PTLOCK_CPUS=4096
++# CONFIG_RESOURCES_64BIT is not set
++CONFIG_ZONE_DMA_FLAG=1
++CONFIG_BOUNCE=y
++CONFIG_VIRT_TO_BUS=y
++CONFIG_ALIGNMENT_TRAP=y
++
++#
++# Boot options
++#
++CONFIG_ZBOOT_ROM_TEXT=0x0
++CONFIG_ZBOOT_ROM_BSS=0x0
++CONFIG_CMDLINE="console=ttyAM0,115200 root=/dev/ram"
++# CONFIG_XIP_KERNEL is not set
++# CONFIG_KEXEC is not set
++
++#
++# Floating point emulation
++#
++
++#
++# At least one emulation must be selected
++#
++CONFIG_FPE_NWFPE=y
++CONFIG_FPE_NWFPE_XP=y
++# CONFIG_FPE_FASTFPE is not set
++
++#
++# Userspace binary formats
++#
++CONFIG_BINFMT_ELF=y
++# CONFIG_BINFMT_AOUT is not set
++# CONFIG_BINFMT_MISC is not set
++# CONFIG_ARTHUR is not set
++
++#
++# Power management options
++#
++# CONFIG_PM is not set
++CONFIG_SUSPEND_UP_POSSIBLE=y
++
++#
++# Networking
++#
++CONFIG_NET=y
++
++#
++# Networking options
++#
++CONFIG_PACKET=y
++CONFIG_PACKET_MMAP=y
++CONFIG_UNIX=y
++CONFIG_XFRM=y
++# CONFIG_XFRM_USER is not set
++# CONFIG_XFRM_SUB_POLICY is not set
++# CONFIG_XFRM_MIGRATE is not set
++CONFIG_NET_KEY=y
++# CONFIG_NET_KEY_MIGRATE is not set
++CONFIG_INET=y
++# CONFIG_IP_MULTICAST is not set
++# 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_ARPD is not set
++CONFIG_SYN_COOKIES=y
++# 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=y
++CONFIG_INET_XFRM_MODE_TUNNEL=y
++CONFIG_INET_XFRM_MODE_BEET=y
++# 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=y
++# CONFIG_IPV6_PRIVACY is not set
++# CONFIG_IPV6_ROUTER_PREF is not set
++# CONFIG_IPV6_OPTIMISTIC_DAD is not set
++# CONFIG_INET6_AH is not set
++# CONFIG_INET6_ESP is not set
++# CONFIG_INET6_IPCOMP is not set
++# CONFIG_IPV6_MIP6 is not set
++# CONFIG_INET6_XFRM_TUNNEL is not set
++# CONFIG_INET6_TUNNEL is not set
++# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET6_XFRM_MODE_BEET is not set
++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
++# CONFIG_IPV6_SIT is not set
++# CONFIG_IPV6_TUNNEL is not set
++# CONFIG_IPV6_MULTIPLE_TABLES 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_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
++CONFIG_NET_SCH_FIFO=y
++
++#
++# Network testing
++#
++# CONFIG_NET_PKTGEN is not set
++# CONFIG_HAMRADIO is not set
++# CONFIG_IRDA is not set
++# CONFIG_BT is not set
++# CONFIG_AF_RXRPC is not set
++
++#
++# Wireless
++#
++CONFIG_CFG80211=y
++CONFIG_NL80211=y
++CONFIG_WIRELESS_EXT=y
++CONFIG_MAC80211=m
++CONFIG_MAC80211_RCSIMPLE=y
++# CONFIG_MAC80211_DEBUG 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_IEEE80211_SOFTMAC is not set
++# 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 is not set
++# CONFIG_SYS_HYPERVISOR is not set
++# CONFIG_CONNECTOR is not set
++CONFIG_MTD=y
++# CONFIG_MTD_DEBUG is not set
++# CONFIG_MTD_CONCAT is not set
++CONFIG_MTD_PARTITIONS=y
++# CONFIG_MTD_REDBOOT_PARTS is not set
++# CONFIG_MTD_CMDLINE_PARTS is not set
++# CONFIG_MTD_AFS_PARTS is not set
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLKDEVS=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++# CONFIG_NFTL is not set
++# CONFIG_INFTL is not set
++# CONFIG_RFD_FTL is not set
++# CONFIG_SSFDC is not set
++# CONFIG_MTD_OOPS is not set
++
++#
++# RAM/ROM/Flash chip drivers
++#
++CONFIG_MTD_CFI=y
++# CONFIG_MTD_JEDECPROBE is not set
++CONFIG_MTD_GEN_PROBE=y
++CONFIG_MTD_CFI_ADV_OPTIONS=y
++CONFIG_MTD_CFI_NOSWAP=y
++# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
++# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
++# CONFIG_MTD_CFI_GEOMETRY 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_OTP is not set
++CONFIG_MTD_CFI_INTELEXT=y
++CONFIG_MTD_CFI_AMDSTD=y
++CONFIG_MTD_CFI_STAA=y
++CONFIG_MTD_CFI_UTIL=y
++CONFIG_MTD_RAM=y
++# CONFIG_MTD_ROM is not set
++# CONFIG_MTD_ABSENT is not set
++
++#
++# Mapping drivers for chip access
++#
++# CONFIG_MTD_COMPLEX_MAPPINGS is not set
++CONFIG_MTD_PHYSMAP=y
++CONFIG_MTD_PHYSMAP_START=0x0
++CONFIG_MTD_PHYSMAP_LEN=0x0
++CONFIG_MTD_PHYSMAP_BANKWIDTH=1
++# CONFIG_MTD_ARM_INTEGRATOR 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 is not set
++# CONFIG_MTD_ONENAND is not set
++
++#
++# UBI - Unsorted block images
++#
++# CONFIG_MTD_UBI is not set
++# CONFIG_PARPORT is not set
++CONFIG_BLK_DEV=y
++# CONFIG_BLK_DEV_COW_COMMON is not set
++CONFIG_BLK_DEV_LOOP=y
++# CONFIG_BLK_DEV_CRYPTOLOOP is not set
++CONFIG_BLK_DEV_NBD=y
++# CONFIG_BLK_DEV_UB is not set
++# CONFIG_BLK_DEV_RAM is not set
++# CONFIG_CDROM_PKTCDVD is not set
++# CONFIG_ATA_OVER_ETH is not set
++# CONFIG_MISC_DEVICES is not set
++CONFIG_EEPROM_93CX6=m
++
++#
++# SCSI device support
++#
++# CONFIG_RAID_ATTRS is not set
++CONFIG_SCSI=y
++CONFIG_SCSI_DMA=y
++# CONFIG_SCSI_TGT is not set
++# CONFIG_SCSI_NETLINK is not set
++# CONFIG_SCSI_PROC_FS is not set
++
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++CONFIG_BLK_DEV_SD=y
++# CONFIG_CHR_DEV_ST is not set
++# CONFIG_CHR_DEV_OSST is not set
++# CONFIG_BLK_DEV_SR is not set
++# CONFIG_CHR_DEV_SG is not set
++# CONFIG_CHR_DEV_SCH is not set
++
++#
++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
++#
++# CONFIG_SCSI_MULTI_LUN is not set
++# CONFIG_SCSI_CONSTANTS is not set
++# CONFIG_SCSI_LOGGING is not set
++# CONFIG_SCSI_SCAN_ASYNC is not set
++CONFIG_SCSI_WAIT_SCAN=m
++
++#
++# SCSI Transports
++#
++# CONFIG_SCSI_SPI_ATTRS is not set
++# CONFIG_SCSI_FC_ATTRS is not set
++# CONFIG_SCSI_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_ATA is not set
++# CONFIG_MD is not set
++CONFIG_NETDEVICES=y
++# CONFIG_NETDEVICES_MULTIQUEUE is not set
++# 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 is not set
++CONFIG_NET_ETHERNET=y
++CONFIG_MII=y
++CONFIG_EP93XX_ETH=y
++# CONFIG_AX88796 is not set
++# CONFIG_SMC91X is not set
++# CONFIG_DM9000 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_B44 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 is not set
++# CONFIG_USB_ZD1201 is not set
++CONFIG_RTL8187=m
++# CONFIG_HOSTAP is not set
++# CONFIG_B43 is not set
++# CONFIG_B43LEGACY is not set
++# CONFIG_RT2X00 is not set
++
++#
++# USB Network Adapters
++#
++CONFIG_USB_CATC=y
++# CONFIG_USB_KAWETH is not set
++# CONFIG_USB_PEGASUS is not set
++CONFIG_USB_RTL8150=y
++# CONFIG_USB_USBNET is not set
++# CONFIG_WAN is not set
++CONFIG_PPP=m
++# CONFIG_PPP_MULTILINK is not set
++# CONFIG_PPP_FILTER is not set
++# CONFIG_PPP_ASYNC is not set
++CONFIG_PPP_SYNC_TTY=m
++# CONFIG_PPP_DEFLATE is not set
++CONFIG_PPP_BSDCOMP=m
++# CONFIG_PPP_MPPE is not set
++CONFIG_PPPOE=m
++# CONFIG_PPPOL2TP is not set
++# CONFIG_SLIP is not set
++CONFIG_SLHC=m
++# CONFIG_SHAPER 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=y
++CONFIG_INPUT_MOUSEDEV_PSAUX=y
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
++# CONFIG_INPUT_JOYDEV is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_EVBUG is not set
++
++#
++# Input Device Drivers
++#
++CONFIG_INPUT_KEYBOARD=y
++CONFIG_KEYBOARD_ATKBD=y
++# 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_INPUT_MOUSE=y
++CONFIG_MOUSE_PS2=y
++CONFIG_MOUSE_PS2_ALPS=y
++# CONFIG_MOUSE_PS2_LOGIPS2PP is not set
++# CONFIG_MOUSE_PS2_SYNAPTICS is not set
++# CONFIG_MOUSE_PS2_LIFEBOOK is not set
++# CONFIG_MOUSE_PS2_TRACKPOINT is not set
++# CONFIG_MOUSE_PS2_TOUCHKIT is not set
++# CONFIG_MOUSE_SERIAL is not set
++# CONFIG_MOUSE_APPLETOUCH is not set
++# CONFIG_MOUSE_VSXXXAA 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_MK712 is not set
++# CONFIG_TOUCHSCREEN_PENMOUNT is not set
++# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
++# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
++# CONFIG_TOUCHSCREEN_UCB1400 is not set
++# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
++CONFIG_TOUCHSCREEN_EP93XX=y
++# CONFIG_INPUT_MISC is not set
++
++#
++# Hardware I/O ports
++#
++CONFIG_SERIO=y
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_SERIO_AMBAKMI is not set
++CONFIG_SERIO_LIBPS2=y
++# CONFIG_SERIO_RAW is not set
++# CONFIG_GAMEPORT is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++CONFIG_VT_CONSOLE=y
++CONFIG_HW_CONSOLE=y
++# CONFIG_VT_HW_CONSOLE_BINDING is not set
++# CONFIG_SERIAL_NONSTANDARD is not set
++
++#
++# Serial drivers
++#
++# CONFIG_SERIAL_8250 is not set
++
++#
++# Non-8250 serial port support
++#
++CONFIG_SERIAL_AMBA_PL010=y
++CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
++# CONFIG_SERIAL_AMBA_PL011 is not set
++CONFIG_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++# CONFIG_LEGACY_PTYS is not set
++# 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=y
++
++#
++# I2C Algorithms
++#
++CONFIG_I2C_ALGOBIT=y
++# CONFIG_I2C_ALGOPCF is not set
++# CONFIG_I2C_ALGOPCA is not set
++
++#
++# I2C Hardware Bus support
++#
++CONFIG_I2C_EP93XX=y
++# CONFIG_I2C_OCORES is not set
++# CONFIG_I2C_PARPORT_LIGHT is not set
++# CONFIG_I2C_SIMTEC is not set
++# CONFIG_I2C_TAOS_EVM is not set
++# CONFIG_I2C_STUB is not set
++# CONFIG_I2C_TINY_USB is not set
++
++#
++# Miscellaneous I2C Chip support
++#
++CONFIG_SENSORS_DS1337=y
++# CONFIG_SENSORS_DS1374 is not set
++# CONFIG_DS1682 is not set
++# CONFIG_SENSORS_EEPROM is not set
++# CONFIG_SENSORS_PCF8574 is not set
++# CONFIG_SENSORS_PCA9539 is not set
++# CONFIG_SENSORS_PCF8591 is not set
++# CONFIG_SENSORS_MAX6875 is not set
++# CONFIG_SENSORS_TSL2550 is not set
++# CONFIG_I2C_DEBUG_CORE is not set
++# CONFIG_I2C_DEBUG_ALGO is not set
++# CONFIG_I2C_DEBUG_BUS is not set
++# CONFIG_I2C_DEBUG_CHIP is not set
++
++#
++# SPI support
++#
++CONFIG_SPI=y
++CONFIG_SPI_MASTER=y
++
++#
++# SPI Master Controller Drivers
++#
++CONFIG_SPI_BITBANG=y
++CONFIG_SPI_EP93XX=y
++
++#
++# SPI Protocol Masters
++#
++# CONFIG_SPI_AT25 is not set
++# CONFIG_SPI_SPIDEV is not set
++# CONFIG_SPI_TLE62X0 is not set
++# CONFIG_W1 is not set
++# CONFIG_POWER_SUPPLY is not set
++# CONFIG_HWMON is not set
++CONFIG_WATCHDOG=y
++# CONFIG_WATCHDOG_NOWAYOUT is not set
++
++#
++# Watchdog Device Drivers
++#
++# CONFIG_SOFT_WATCHDOG is not set
++CONFIG_EP93XX_WATCHDOG=y
++
++#
++# USB-based Watchdog Cards
++#
++# CONFIG_USBPCWATCHDOG is not set
++
++#
++# Sonics Silicon Backplane
++#
++CONFIG_SSB_POSSIBLE=y
++# CONFIG_SSB is not set
++
++#
++# Multifunction device drivers
++#
++# CONFIG_MFD_SM501 is not set
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV is not set
++# CONFIG_DVB_CORE is not set
++# CONFIG_DAB is not set
++
++#
++# Graphics support
++#
++# CONFIG_VGASTATE is not set
++CONFIG_VIDEO_OUTPUT_CONTROL=y
++CONFIG_FB=y
++# CONFIG_FIRMWARE_EDID is not set
++# CONFIG_FB_DDC 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_SYS_FOPS is not set
++CONFIG_FB_DEFERRED_IO=y
++# CONFIG_FB_SVGALIB is not set
++# CONFIG_FB_MACMODES is not set
++# CONFIG_FB_BACKLIGHT is not set
++# CONFIG_FB_MODE_HELPERS is not set
++# CONFIG_FB_TILEBLITTING is not set
++
++#
++# Frame buffer hardware drivers
++#
++CONFIG_FB_EP93XX=y
++# CONFIG_FB_EP93XX_MONO is not set
++# CONFIG_FB_ARMCLCD is not set
++# CONFIG_FB_S1D13XXX is not set
++# CONFIG_FB_VIRTUAL 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 is not set
++CONFIG_FONTS=y
++CONFIG_FONT_8x8=y
++CONFIG_FONT_8x16=y
++# CONFIG_FONT_6x11 is not set
++# CONFIG_FONT_7x14 is not set
++# CONFIG_FONT_PEARL_8x8 is not set
++# CONFIG_FONT_ACORN_8x8 is not set
++# CONFIG_FONT_MINI_4x6 is not set
++# CONFIG_FONT_SUN8x16 is not set
++# CONFIG_FONT_SUN12x22 is not set
++# CONFIG_FONT_10x18 is not set
++CONFIG_LOGO=y
++CONFIG_LOGO_LINUX_MONO=y
++CONFIG_LOGO_LINUX_VGA16=y
++CONFIG_LOGO_LINUX_CLUT224=y
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++
++#
++# Advanced Linux Sound Architecture
++#
++CONFIG_SND=y
++CONFIG_SND_TIMER=y
++CONFIG_SND_PCM=y
++# CONFIG_SND_SEQUENCER is not set
++CONFIG_SND_OSSEMUL=y
++CONFIG_SND_MIXER_OSS=y
++CONFIG_SND_PCM_OSS=y
++CONFIG_SND_PCM_OSS_PLUGINS=y
++# CONFIG_SND_DYNAMIC_MINORS is not set
++# CONFIG_SND_SUPPORT_OLD_API is not set
++# CONFIG_SND_VERBOSE_PROCFS is not set
++# CONFIG_SND_VERBOSE_PRINTK is not set
++# CONFIG_SND_DEBUG is not set
++
++#
++# Generic devices
++#
++CONFIG_SND_AC97_CODEC=y
++# CONFIG_SND_DUMMY is not set
++# CONFIG_SND_MTPAV is not set
++# CONFIG_SND_SERIAL_U16550 is not set
++# CONFIG_SND_MPU401 is not set
++
++#
++# ALSA ARM devices
++#
++CONFIG_SND_EP93XX_AC97=y
++CONFIG_SND_EP93XX_PCM=y
++# CONFIG_SND_ARMAACI is not set
++
++#
++# SPI devices
++#
++
++#
++# USB devices
++#
++# CONFIG_SND_USB_AUDIO is not set
++# CONFIG_SND_USB_CAIAQ is not set
++
++#
++# System on Chip audio support
++#
++# CONFIG_SND_SOC is not set
++
++#
++# SoC Audio support for SuperH
++#
++
++#
++# Open Sound System
++#
++# CONFIG_SOUND_PRIME is not set
++CONFIG_AC97_BUS=y
++CONFIG_HID_SUPPORT=y
++CONFIG_HID=y
++# CONFIG_HID_DEBUG is not set
++# CONFIG_HIDRAW is not set
++
++#
++# USB Input Devices
++#
++CONFIG_USB_HID=y
++# CONFIG_USB_HIDINPUT_POWERBOOK is not set
++# CONFIG_HID_FF is not set
++# CONFIG_USB_HIDDEV is not set
++CONFIG_USB_SUPPORT=y
++CONFIG_USB_ARCH_HAS_HCD=y
++CONFIG_USB_ARCH_HAS_OHCI=y
++# CONFIG_USB_ARCH_HAS_EHCI is not set
++CONFIG_USB=y
++# CONFIG_USB_DEBUG is not set
++
++#
++# Miscellaneous USB options
++#
++CONFIG_USB_DEVICEFS=y
++CONFIG_USB_DEVICE_CLASS=y
++CONFIG_USB_DYNAMIC_MINORS=y
++# CONFIG_USB_OTG is not set
++
++#
++# USB Host Controller Drivers
++#
++# CONFIG_USB_ISP116X_HCD is not set
++CONFIG_USB_OHCI_HCD=y
++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
++CONFIG_USB_OHCI_LITTLE_ENDIAN=y
++# CONFIG_USB_SL811_HCD is not set
++# CONFIG_USB_R8A66597_HCD is not set
++
++#
++# USB Device Class drivers
++#
++CONFIG_USB_ACM=m
++CONFIG_USB_PRINTER=m
++
++#
++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
++#
++
++#
++# may also be needed; see USB_STORAGE Help for more information
++#
++CONFIG_USB_STORAGE=y
++# 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_LIBUSUAL is not set
++
++#
++# USB Imaging devices
++#
++# CONFIG_USB_MDC800 is not set
++# CONFIG_USB_MICROTEK is not set
++# CONFIG_USB_MON is not set
++
++#
++# USB port drivers
++#
++
++#
++# USB Serial Converter support
++#
++CONFIG_USB_SERIAL=y
++CONFIG_USB_SERIAL_CONSOLE=y
++# CONFIG_USB_SERIAL_GENERIC is not set
++# CONFIG_USB_SERIAL_AIRCABLE is not set
++# CONFIG_USB_SERIAL_AIRPRIME is not set
++# CONFIG_USB_SERIAL_ARK3116 is not set
++# CONFIG_USB_SERIAL_BELKIN is not set
++# CONFIG_USB_SERIAL_CH341 is not set
++# CONFIG_USB_SERIAL_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_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_NAVMAN is not set
++CONFIG_USB_SERIAL_PL2303=y
++# CONFIG_USB_SERIAL_OTI6858 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_AUERSWALD is not set
++# CONFIG_USB_RIO500 is not set
++# CONFIG_USB_LEGOTOWER is not set
++# CONFIG_USB_LCD is not set
++# CONFIG_USB_BERRY_CHARGE is not set
++# CONFIG_USB_LED is not set
++# CONFIG_USB_CYPRESS_CY7C63 is not set
++# CONFIG_USB_CYTHERM is not set
++# CONFIG_USB_PHIDGET is not set
++# CONFIG_USB_IDMOUSE is not set
++# CONFIG_USB_FTDI_ELAN is not set
++# CONFIG_USB_APPLEDISPLAY is not set
++# CONFIG_USB_LD is not set
++# CONFIG_USB_TRANCEVIBRATOR is not set
++# CONFIG_USB_IOWARRIOR is not set
++# CONFIG_USB_TEST is not set
++
++#
++# USB DSL modem support
++#
++
++#
++# USB Gadget Support
++#
++# CONFIG_USB_GADGET is not set
++CONFIG_MMC=y
++# CONFIG_MMC_DEBUG is not set
++# CONFIG_MMC_UNSAFE_RESUME is not set
++
++#
++# MMC/SD Card Drivers
++#
++CONFIG_MMC_BLOCK=y
++CONFIG_MMC_BLOCK_BOUNCE=y
++# CONFIG_SDIO_UART is not set
++
++#
++# MMC/SD Host Controller Drivers
++#
++# CONFIG_MMC_ARMMMCI is not set
++CONFIG_MMC_SPI=y
++#
++# SimOne LCD support
++#
++CONFIG_LCD_LINUX=m
++CONFIG_LCD_HD44780=m
++#
++# CONFIG_NEW_LEDS 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=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
++
++#
++# SPI RTC drivers
++#
++# CONFIG_RTC_DRV_RS5C348 is not set
++# CONFIG_RTC_DRV_MAX6902 is not set
++
++#
++# Platform RTC drivers
++#
++# CONFIG_RTC_DRV_CMOS is not set
++# CONFIG_RTC_DRV_DS1553 is not set
++# CONFIG_RTC_DRV_STK17TA8 is not set
++# CONFIG_RTC_DRV_DS1742 is not set
++# CONFIG_RTC_DRV_M48T86 is not set
++# CONFIG_RTC_DRV_M48T59 is not set
++# CONFIG_RTC_DRV_V3020 is not set
++
++#
++# on-CPU RTC drivers
++#
++CONFIG_RTC_DRV_EP93XX=y
++CONFIG_RTC_DRV_EP93XX_DS1337=y
++# CONFIG_RTC_DRV_PL031 is not set
++
++#
++# File systems
++#
++CONFIG_EXT2_FS=y
++CONFIG_EXT2_FS_XATTR=y
++CONFIG_EXT2_FS_POSIX_ACL=y
++CONFIG_EXT2_FS_SECURITY=y
++# CONFIG_EXT2_FS_XIP is not set
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_FS_XATTR is not set
++# CONFIG_EXT4DEV_FS is not set
++CONFIG_JBD=y
++CONFIG_FS_MBCACHE=y
++# CONFIG_REISERFS_FS is not set
++# CONFIG_JFS_FS is not set
++CONFIG_FS_POSIX_ACL=y
++# CONFIG_XFS_FS is not set
++# CONFIG_GFS2_FS is not set
++# CONFIG_OCFS2_FS is not set
++# CONFIG_MINIX_FS is not set
++CONFIG_ROMFS_FS=y
++CONFIG_INOTIFY=y
++CONFIG_INOTIFY_USER=y
++# CONFIG_QUOTA is not set
++CONFIG_DNOTIFY=y
++# CONFIG_AUTOFS_FS is not set
++CONFIG_AUTOFS4_FS=y
++# CONFIG_FUSE_FS is not set
++
++#
++# CD-ROM/DVD Filesystems
++#
++CONFIG_ISO9660_FS=y
++CONFIG_JOLIET=y
++# CONFIG_ZISOFS is not set
++CONFIG_UDF_FS=y
++CONFIG_UDF_NLS=y
++
++#
++# DOS/FAT/NT Filesystems
++#
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_CODEPAGE=437
++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
++# CONFIG_NTFS_FS is not set
++
++#
++# Pseudo filesystems
++#
++CONFIG_PROC_FS=y
++CONFIG_PROC_SYSCTL=y
++CONFIG_SYSFS=y
++CONFIG_TMPFS=y
++# CONFIG_TMPFS_POSIX_ACL is not set
++# CONFIG_HUGETLB_PAGE is not set
++# CONFIG_CONFIGFS_FS is not set
++
++#
++# Miscellaneous filesystems
++#
++# CONFIG_ADFS_FS is not set
++# CONFIG_AFFS_FS is not set
++# CONFIG_HFS_FS is not set
++# CONFIG_HFSPLUS_FS is not set
++# CONFIG_BEFS_FS is not set
++# CONFIG_BFS_FS is not set
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_JFFS2_FS_WRITEBUFFER=y
++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
++# CONFIG_JFFS2_SUMMARY is not set
++# CONFIG_JFFS2_FS_XATTR is not set
++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
++CONFIG_JFFS2_ZLIB=y
++# CONFIG_JFFS2_LZO is not set
++CONFIG_JFFS2_RTIME=y
++# CONFIG_JFFS2_RUBIN is not set
++# CONFIG_CRAMFS is not set
++# CONFIG_VXFS_FS is not set
++# CONFIG_HPFS_FS is not set
++# CONFIG_QNX4FS_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_NFS_DIRECTIO is not set
++# CONFIG_NFSD is not set
++CONFIG_ROOT_NFS=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=y
++CONFIG_NFS_COMMON=y
++CONFIG_SUNRPC=y
++# CONFIG_SUNRPC_BIND34 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=y
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 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 is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++# CONFIG_DLM is not set
++# CONFIG_INSTRUMENTATION 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_MAGIC_SYSRQ is not set
++# CONFIG_UNUSED_SYMBOLS is not set
++# CONFIG_DEBUG_FS is not set
++# CONFIG_HEADERS_CHECK is not set
++# CONFIG_DEBUG_KERNEL is not set
++# CONFIG_DEBUG_BUGVERBOSE is not set
++CONFIG_FRAME_POINTER=y
++# CONFIG_SAMPLES is not set
++CONFIG_DEBUG_USER=y
++
++#
++# Security options
++#
++# CONFIG_KEYS is not set
++# CONFIG_SECURITY is not set
++# CONFIG_SECURITY_FILE_CAPABILITIES is not set
++CONFIG_CRYPTO=y
++CONFIG_CRYPTO_ALGAPI=y
++CONFIG_CRYPTO_BLKCIPHER=y
++CONFIG_CRYPTO_MANAGER=y
++# CONFIG_CRYPTO_HMAC is not set
++# CONFIG_CRYPTO_XCBC is not set
++# CONFIG_CRYPTO_NULL is not set
++# CONFIG_CRYPTO_MD4 is not set
++CONFIG_CRYPTO_MD5=y
++CONFIG_CRYPTO_SHA1=y
++# CONFIG_CRYPTO_SHA256 is not set
++# CONFIG_CRYPTO_SHA512 is not set
++# CONFIG_CRYPTO_WP512 is not set
++# CONFIG_CRYPTO_TGR192 is not set
++# CONFIG_CRYPTO_GF128MUL is not set
++CONFIG_CRYPTO_ECB=m
++CONFIG_CRYPTO_CBC=m
++CONFIG_CRYPTO_PCBC=y
++# CONFIG_CRYPTO_LRW is not set
++# CONFIG_CRYPTO_XTS is not set
++# CONFIG_CRYPTO_CRYPTD is not set
++CONFIG_CRYPTO_DES=y
++# CONFIG_CRYPTO_FCRYPT is not set
++# CONFIG_CRYPTO_BLOWFISH is not set
++# CONFIG_CRYPTO_TWOFISH is not set
++# CONFIG_CRYPTO_SERPENT is not set
++CONFIG_CRYPTO_AES=y
++# CONFIG_CRYPTO_CAST5 is not set
++# CONFIG_CRYPTO_CAST6 is not set
++# CONFIG_CRYPTO_TEA is not set
++CONFIG_CRYPTO_ARC4=y
++# CONFIG_CRYPTO_KHAZAD is not set
++# CONFIG_CRYPTO_ANUBIS is not set
++# CONFIG_CRYPTO_SEED is not set
++# CONFIG_CRYPTO_DEFLATE is not set
++CONFIG_CRYPTO_MICHAEL_MIC=y
++# CONFIG_CRYPTO_CRC32C is not set
++# CONFIG_CRYPTO_CAMELLIA is not set
++# CONFIG_CRYPTO_TEST is not set
++# CONFIG_CRYPTO_AUTHENC is not set
++# CONFIG_CRYPTO_HW is not set
++
++#
++# Library routines
++#
++CONFIG_BITREVERSE=y
++# CONFIG_CRC_CCITT is not set
++# CONFIG_CRC16 is not set
++CONFIG_CRC_ITU_T=y
++CONFIG_CRC32=y
++CONFIG_CRC7=y
++CONFIG_LIBCRC32C=y
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
++CONFIG_PLIST=y
++CONFIG_HAS_IOMEM=y
++CONFIG_HAS_IOPORT=y
++CONFIG_HAS_DMA=y
diff --git a/target/linux/ep93xx/patches-2.6.30/012-ep93xx-cpuinfo.patch b/target/linux/ep93xx/patches-2.6.30/012-ep93xx-cpuinfo.patch
new file mode 100644
index 0000000000..d2845c910a
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/012-ep93xx-cpuinfo.patch
@@ -0,0 +1,32 @@
+Index: linux-2.6.30.9/arch/arm/kernel/setup.c
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/kernel/setup.c 2009-11-24 21:00:10.000000000 +0100
++++ linux-2.6.30.9/arch/arm/kernel/setup.c 2009-11-24 21:00:46.000000000 +0100
+@@ -42,6 +42,10 @@
+ #include <asm/traps.h>
+ #include <asm/unwind.h>
+
++#if defined(CONFIG_ARCH_EP93XX)
++#include <mach/ep93xx-regs.h>
++#endif
++
+ #include "compat.h"
+ #include "atags.h"
+
+@@ -844,9 +848,16 @@
+ seq_puts(m, "\n");
+
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
++#if defined(CONFIG_ARCH_EP93XX)
++ seq_printf(m, "Revision\t: %04x\n",
++ *((unsigned int *)EP93XX_SYSCON_CHIP_ID) >> 28);
++ seq_printf(m, "Serial\t\t: %016x\n",
++ *((unsigned int *)EP93XX_SECURITY_UNIQID));
++#else
+ seq_printf(m, "Revision\t: %04x\n", system_rev);
+ seq_printf(m, "Serial\t\t: %08x%08x\n",
+ system_serial_high, system_serial_low);
++#endif
+
+ return 0;
+ }