diff options
author | Florian Fainelli <florian@openwrt.org> | 2009-11-25 23:43:48 +0000 |
---|---|---|
committer | Florian Fainelli <florian@openwrt.org> | 2009-11-25 23:43:48 +0000 |
commit | 4bfa2947eeca220e3cff41dbad9fbaea25245ed0 (patch) | |
tree | bb7af150cb3f5cd5c354b5bcdeb00eb1f235b940 /target/linux/ep93xx/patches-2.6.30 | |
parent | 850fe2320fc1056b45aab08bbac8e3a3da806203 (diff) | |
download | mtk-20170518-4bfa2947eeca220e3cff41dbad9fbaea25245ed0.zip mtk-20170518-4bfa2947eeca220e3cff41dbad9fbaea25245ed0.tar.gz mtk-20170518-4bfa2947eeca220e3cff41dbad9fbaea25245ed0.tar.bz2 |
add support for the Simplemachines Sim.One board
SVN-Revision: 18540
Diffstat (limited to 'target/linux/ep93xx/patches-2.6.30')
12 files changed, 20247 insertions, 0 deletions
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 0000000..9b3128b --- /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 0000000..7480b28 --- /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 0000000..aaf4d3c --- /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 0000000..69f2c5a --- /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 0000000..3664132 --- /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 0000000..7ea8edc --- /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 0000000..cd715bf --- /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 0000000..8292bbc --- /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 0000000..d7816bd --- /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 0000000..36f316b --- /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 0000000..d499259 --- /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 0000000..d2845c9 --- /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; + } |